Files
lpic-sim/ai/common/docker.md
T
Björn RundenandClaude Opus 5.5 7f6b8ebe07 Basis aus 2nd-brain für LPIC-Prüfungsvorbereitung übernommen
Regeln, Standards, Kontextstruktur und Werkzeuge übernommen; 2nd-brain-
Inhalte (Stacks, Ansible-Inventar, Architektur, Knowledge) entfernt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:56:53 +01:00

190 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker-Compose-Standard (verbindlich)
> Gilt für **jeden** Docker-Stack, der hier entsteht. Nicht zur Diskussion stellen – vollständig „durchdeklinieren“.
> Abweichungen nur, wenn das Image es technisch erzwingt; dann im Stack-README begründen.
> Vorlagen: [`templates/docker/`](templates/docker/). Umgebungswerte (`<…>`) stehen in `ai/context/environment.md`.
## 1. Pflichtbestandteile je Service
| # | Vorgabe | Warum |
|---|---|---|
| 1 | `restart: unless-stopped` | stabiler Betrieb, manuelles Stoppen bleibt respektiert |
| 2 | `logging: *default-logging` (GELF → Graylog) | zentrales Logging |
| 3 | `container_name: ${COMPOSE_PROJECT_NAME}` (mehrere Services: `${COMPOSE_PROJECT_NAME}_<service>`) | eindeutige Namen, GELF-Tag passt |
| 4 | `env_file: .env` statt aufgeblähtem `environment:` | Stackfile bleibt transportabel |
| 5 | Persistenz nur per **Bind-Mount** aus `.env` (`${DIR_*}`), keine Named Volumes | Stack-Verzeichnis = komplettes Backup, umziehbar |
| 6 | `healthcheck` | „guter Ton“; Test passend zum Image (s. 4.) |
| 7 | Traefik-Labels (s. 3.) für jeden Service mit Web-Oberfläche/API | Exposition über Reverse Proxy |
| 8 | Image-Tag aus `.env` (`${<APP>_TAG}`), **nie** `latest` | reproduzierbar, Updates bewusst |
| 9 | **Alle** host-/umgebungsspezifischen Werte in `.env` (Hosts, IPs, Ports, Pfade, Domains, `extra_hosts`) | Stackfile enthält keine Umgebungsdaten |
| 10 | **Namen ohne Bindestrich – Unterstrich statt `-`:** `COMPOSE_PROJECT_NAME`, Stack-Verzeichnis, Service-, Container- und Router-Namen (z. B. `hashi_vault`, nicht `hashi-vault`) | GELF-Tag = Projektname; `_` läuft in der Elasticsearch-Suche sauber (`-` wird als Worttrenner zerlegt) |
| 11 | **Header** mit Verantwortlichen im `docker-compose.yml` (`rules/headers.md`) | Zuständigkeit sofort sichtbar |
## 2. Verzeichnislayout je Stack
```
<STACK_BASE>/<stack>/ # <stack> = COMPOSE_PROJECT_NAME
├── docker-compose.yml
├── .env # echte Werte inkl. Secrets – nie in Git/extern
├── .env.example # gleiche Schlüssel, Platzhalter statt Secrets
├── README.md # Stack-Doku (s. 5.)
└── data/ config/ … # Bind-Mount-Ziele (über DIR_* referenziert)
```
## 3. Vorlagen
### 3.1 `docker-compose.yml`
```yaml
x-logging: &default-logging
driver: gelf
options:
gelf-address: "udp://${GELF_HOST}:${GELF_PORT}"
tag: "${COMPOSE_PROJECT_NAME}"
labels: "com.docker.compose.project,com.docker.compose.service"
services:
app:
image: <hersteller>/<image>:${APP_TAG}
container_name: ${COMPOSE_PROJECT_NAME}
restart: unless-stopped
env_file: .env
# ports: nur wenn Zugriff ohne Traefik nötig ist
# - "${APP_PORT}:<intern>"
extra_hosts: # nur wenn benötigt; ein Eintrag je Variable
- "${EXTRA_HOST_1}"
volumes:
- ${DIR_DATA}:/<pfad-im-container>
- ${DIR_CONFIG}:/<config-pfad>:ro
networks:
- proxy
labels: # Traefik bleibt aus, bis TRAEFIK_ENABLE=true (s. 3.2)
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.docker.network=${TRAEFIK_NETWORK:-traefik-net}"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${APP_FQDN:-${COMPOSE_PROJECT_NAME}.invalid}`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls.certresolver=${TRAEFIK_CERTRESOLVER:-le}"
- "traefik.http.services.${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=${APP_INTERNAL_PORT:-80}"
# bei Bedarf manuell ergänzen: entrypoints, middlewares …
logging: *default-logging
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:<intern>/<pfad>"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
networks:
proxy:
name: ${TRAEFIK_NETWORK:-traefik-net}
external: true
```
### 3.2 Traefik-Labels: Dummy zuerst, Freischaltung per Schalter
- Labels stehen **immer** direkt im Stackfile und werden je Stack manuell angepasst (kein `extends`, keine Auslagerung).
- `traefik.enable=${TRAEFIK_ENABLE:-false}` → solange in `.env` nicht `TRAEFIK_ENABLE=true` steht, ignoriert Traefik den Container: **kein Router, keine Let's-Encrypt-Anfrage.**
- Ablauf: Stack ohne Traefik hochfahren → Healthcheck `healthy`, Funktion geprüft, FQDN/DNS stimmt → erst dann `TRAEFIK_ENABLE=true` + `docker compose up -d`.
- Dummy-FQDN `<stack>.invalid` (reservierte TLD) verhindert, dass ein vergessener Wert je eine echte Domain anfragt.
- Mehrere Web-Services: Router-/Service-Namen eindeutig halten (`${COMPOSE_PROJECT_NAME}_<service>`).
### 3.3 `.env` / `.env.example`
```dotenv
# --- Stack ---
COMPOSE_PROJECT_NAME=<stack> # nur a-z, 0-9, _ – kein Bindestrich (GELF-Tag)
APP_TAG=<version>
# --- Logging (Graylog) ---
GELF_HOST=<GELF_HOST>
GELF_PORT=12201
# --- Traefik (Defaults: traefik-net / le – nur bei Abweichung setzen) ---
TRAEFIK_ENABLE=false # erst auf true, wenn Stack sauber läuft (LE-Limits!)
APP_FQDN=<app.domain.tld>
APP_INTERNAL_PORT=<port>
#TRAEFIK_NETWORK=traefik-net
#TRAEFIK_CERTRESOLVER=le
# --- Hosts ---
EXTRA_HOST_1=<fqdn>:<ip>
# --- Pfade (Bind-Mounts, relativ zum Stack) ---
DIR_DATA=./data
DIR_CONFIG=./config
# --- Image-spezifische Variablen (laut Orig-Doku, s. README) ---
```
### 3.4 Geprüfte, aber verworfene Wege (Compose v5.0.2, 06.10.2026)
- **`label_file:`** – keine Variablen im Schlüssel möglich (Router-Name!) und Werte werden nicht interpoliert. Unbrauchbar für Traefik.
- **`extends` aus gemeinsamer `traefik.yml`** – technisch ok, vom Bediener abgelehnt: Labels gehören sichtbar und manuell anpassbar ins Stackfile.
- **Separate `.traefik.env` per `COMPOSE_ENV_FILES` in `.env`** – wirkt nicht; nur als Shell-Variable oder per `--env-file` bei jedem Aufruf → fehleranfällig.
## 4. Hinweise zur Umsetzung
- **`extra_hosts` in `.env`:** ja, möglich – Compose interpoliert Listenwerte. Ein Eintrag je Variable (`EXTRA_HOST_1=host01.example.org:192.0.2.10`). Schlüssel in Map-Syntax werden *nicht* interpoliert → Listen-Syntax verwenden. Leere Variable ergibt einen ungültigen Eintrag → Zeile nur aufnehmen, wenn genutzt.
- **Healthcheck:** vorher prüfen, ob `wget`/`curl` im Image existieren (`docker run --rm --entrypoint sh <image> -c 'command -v wget curl'`). Endpunkt wählen, der im Normalbetrieb nicht fälschlich failt (z. B. Vault: `/ui/` statt `/v1/sys/health`, weil versiegelt = 503).
- **`env_file: .env`** reicht auch Stack-Variablen (GELF_*, DIR_*, TRAEFIK_*) in den Container durch – ist so gewollt und in der Regel harmlos.
- Kommentare im Stackfile nur für Nicht-Offensichtliches (wie im Vault-Healthcheck).
## 5. Stack-README (Pflicht)
Jeder Stack bekommt ein `README.md` mit:
1. **Zweck** und Zugriff (FQDN, ggf. Port).
2. **Orig-Doku:** Links zu Image-Seite (Docker Hub / GHCR) und Hersteller-Doku, Abrufdatum.
3. **ENV-Variablen laut Orig-Doku:** Tabelle `Variable | Bedeutung | Default | gesetzt in .env?` – vollständig aus der Herstellerdoku übernommen, nicht geraten.
4. **Volumes/Pfade** laut Orig-Doku (welcher Container-Pfad persistiert was).
5. **Ports** intern/extern, Healthcheck-Endpunkt und Begründung.
6. **Besonderheiten** (Capabilities wie `IPC_LOCK`, Init-/Unseal-Schritte, Update-Hinweise, Abweichungen vom Standard).
Vor dem Schreiben eines Stacks die Orig-Doku per Web-Recherche holen (erlaubt laut `rules/web_search.md`).
## 6. Abnahme-Checkliste
- [ ] Punkte 1–11 aus Abschnitt 1 erfüllt
- [ ] Kein `-` und kein `__` in Projekt-, Service-, Container- und Router-Namen
- [ ] sultan-Host: Port veröffentlicht, kein externes Traefik-Netz, `entrypoints`-Label (7.1)
- [ ] Keine Hosts/IPs/Domains/Pfade/Secrets im Stackfile
- [ ] `.env.example` vollständig, ohne Secrets
- [ ] README mit Orig-Doku und ENV-Tabelle
- [ ] `docker compose config` läuft fehlerfrei
- [ ] Healthcheck wird nach Start `healthy` – **erst danach** `TRAEFIK_ENABLE=true`
## 7. Ergänzungen (25.10.2026)
### 7.1 sultan-Variante (Traefik auf anderer VM)
Gilt für Hosts, deren Traefik woanders läuft und per [sultan](../context/knowledge/services/sultan.md) angebunden ist.
- **Kein** externes Netz `proxy`/`traefik-net`, **kein** Label `traefik.docker.network`.
- Web-Service **veröffentlicht** seinen Port: `ports: ["${APP_PORT}:<intern>"]` (Bindung `0.0.0.0`; `127.0.0.1` verwirft der sultan-Agent).
- Label `traefik.http.routers.<name>.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}` gehört dazu; sultan prüft Entrypoint-/Resolver-Namen nicht.
- `loadbalancer.server.port` = Port **im** Container (der Agent rechnet auf den Host-Port um).
- Namen: kein doppelter Unterstrich `__` (Trenner bei sultan).
- Wartung: `docker compose stop` → Wartungsseite; `down` → 404. Für Updates `up -d` (ersetzt Container) ist unkritisch.
### 7.2 Stacks mit mehreren Services / Datenbank
- Datenbanken als **eigener Stack** (`postgres`), nie veröffentlicht. Der Stack legt ein benanntes Netz an (`postgres_net`);
Anwendungen hängen es als `external: true` an. Je Anwendung eigene Datenbank + eigener User.
- Innerhalb eines Stacks: `depends_on` mit `condition: service_healthy`.
- Anwendung braucht Internet/Port → zusätzlich Netz `default`.
### 7.3 `.env`: Erzeugung und Geheimnisse
- `.env.example` ist die **einzige Quelle** der Schlüssel. Platzhalter:
`@@SECRET@@` → beim ersten Deployment zufällig erzeugt (je Schlüssel eigener Wert); `<…>` → vom Betreiber zu setzen.
- Die echte `.env` erzeugt Ansible **nur, wenn sie fehlt**; danach gehört sie dem Betreiber (kein Überschreiben).
Später ersetzen Vault-Einträge die Werte (`rules/secrets.md`).
- Werte, die mehrere Stacks brauchen (DB-Passwort), stehen in der `.env` der **Anwendung**; Ansible liest sie dort und legt damit DB/User an (über stdin, nie als Argument).
### 7.4 Deployment per Ansible
- Rolle `compose_stack` (`ansible/roles/compose_stack`): Stack-Verzeichnis `<STACK_BASE>/<stack>/`, Dateien aus `stacks/<stack>/`,
Bind-Mount-Ziele mit Eigentümer/Rechten, `.env` (s. 7.3), `community.docker.docker_compose_v2` mit `wait`.
- Datenbank je Anwendung: Rolle `pg_app_db`.
### 7.5 Rechte, Dateisystem
- Bind-Mount-Ziele mit dem Eigentümer des Containers anlegen (z. B. `1000:1000`); im Stack-README dokumentieren.
- Auf btrfs: Datenbank-Verzeichnisse vor dem ersten Start `chattr +C` (kein Copy-on-Write, weniger Fragmentierung; Prüfsummen entfallen dort).
### 7.6 Backup & Update
- Datenbanken per Dump sichern (`pg_dumpall`/`pg_dump` vor dem Borgmatic-Lauf) – das reine Kopieren des Datenverzeichnisses im Betrieb ist kein konsistentes Backup.
- Update: neuen Tag in `.env` → Backup → `docker compose pull && docker compose up -d`. Rückweg: alter Tag + Restore.
**Kein automatisches `docker system prune -a`** – das alte Image bleibt für den schnellen Rückweg.
## Festgelegte Werte
`<STACK_BASE>`, `TRAEFIK_NETWORK`, `TRAEFIK_CERTRESOLVER`, `GELF_HOST`/`GELF_PORT` stehen in [`ai/context/environment.md`](../context/environment.md).
## Änderungsprotokoll
- 07.10.2026 – übernommen und verallgemeinert (Mandantenwerte → Platzhalter, Header-Pflicht ergänzt).
- 23.10.2026 – Mandantenbezug entfernt, Werte nur noch in `environment.md`; Verweis auf `web_search.md` korrigiert.
- 25.10.2026 – § 7: sultan-Variante, Mehrfach-Services/DB, `.env`-Erzeugung (`@@SECRET@@`), Deployment per Ansible, Rechte/btrfs, Backup/Update ohne Prune – Entscheidungen des Betreibers vom 23.–25.10.2026.