- Authors

- Name
- Phillip Pham
- @ddppham
Docker Administration in der Praxis: Security, Monitoring und Lifecycle-Management
TL;DR
- Docker-Daemon-Defaults sind fuer Entwicklung optimiert, nicht fuer Produktion -- die
daemon.jsonmuss angepasst werden - Container sollten immer als Non-Root laufen, mit Read-Only Filesystem und gedroppten Capabilities
- Image-Groesse und Layer-Aufbau haben direkten Einfluss auf Startup-Zeit und Sicherheitsflaeche
- Monitoring mit Prometheus und cAdvisor ist Pflicht, Logging mit strukturiertem JSON-Output und Log-Rotation
- Regelmaessiges Aufraeumen von ungenutzten Images, Volumes und Netzwerken verhindert Disk-Probleme
Docker Daemon fuer Produktion konfigurieren
Die Standard-Installation von Docker ist fuer Entwickler gemacht. Fuer produktionsnahe Umgebungen muessen mehrere Defaults angepasst werden. Die zentrale Konfigurationsdatei ist /etc/docker/daemon.json:
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5",
"compress": "true"
},
"live-restore": true,
"userland-proxy": false,
"no-new-privileges": true,
"icc": false,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65536,
"Soft": 65536
}
},
"default-address-pools": [
{
"base": "172.20.0.0/16",
"size": 24
}
]
}
Die wichtigsten Einstellungen erklaert:
| Einstellung | Warum | Default |
|---|---|---|
live-restore: true | Container laufen weiter, wenn der Daemon neustartet | false |
icc: false | Container koennen sich nicht gegenseitig erreichen, ausser explizit verlinkt | true |
no-new-privileges: true | Prozesse im Container koennen keine neuen Privilegien erlangen | false |
userland-proxy: false | Nutzt iptables statt userland proxy -- besser fuer Performance | true |
log-opts.max-size | Verhindert, dass Logs die Disk fuellen | Unbegrenzt |
Nach der Aenderung: sudo systemctl restart docker. Wenn live-restore bereits aktiv war, laufen bestehende Container weiter.
Container-Security: Die wichtigsten Massnahmen
Ein unsicherer Container-Start sieht so aus: docker run -d nginx. Kein User gesetzt, alle Capabilities aktiv, beschreibbares Filesystem. In Produktion sollte das so aussehen:
docker run -d \
--name web \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--tmpfs /var/cache/nginx:rw,noexec,nosuid,size=32m \
--security-opt no-new-privileges:true \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--user 101:101 \
--memory 256m \
--cpus 0.5 \
--pids-limit 50 \
--restart unless-stopped \
--health-cmd "wget -q --spider http://localhost/ || exit 1" \
--health-interval 30s \
--health-timeout 5s \
--health-retries 3 \
nginx:1.27-alpine
Was hier passiert:
--read-only: Filesystem ist nicht beschreibbar. Angreifer koennen nichts persistieren.--tmpfs: Nur bestimmte Verzeichnisse sind beschreibbar, mit Groessenlimit.--cap-drop ALL --cap-add NET_BIND_SERVICE: Nur die Capability, die nginx braucht (Port 80 binden).--user 101:101: nginx-User statt Root.--pids-limit 50: Fork-Bombs werden begrenzt.
Fuer automatisiertes Vulnerability-Scanning empfehle ich Trivy:
# Image vor dem Deployment scannen
trivy image --severity HIGH,CRITICAL nginx:1.27-alpine
# Ergebnis als JSON fuer CI/CD-Integration
trivy image --format json --output results.json nginx:1.27-alpine
# Im CI: Build abbrechen bei CRITICAL Findings
trivy image --exit-code 1 --severity CRITICAL myapp:latest
Multi-Stage Builds: Kleine, sichere Images
Grosse Images sind ein Security- und Performance-Problem. Jedes zusaetzliche Package ist eine potenzielle Schwachstelle. Multi-Stage Builds trennen Build-Dependencies von der Runtime:
# Stage 1: Build
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app
# Stage 2: Runtime
FROM scratch
COPY /app /app
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534
ENTRYPOINT ["/app"]
Der Unterschied ist enorm:
| Variante | Image-Groesse | CVEs (typisch) |
|---|---|---|
| golang:1.22 (full) | ~850 MB | 50-100+ |
| golang:1.22-alpine | ~250 MB | 5-20 |
| Multi-Stage (scratch) | ~10 MB | 0 |
Das scratch-Image enthaelt buchstaeblich nichts -- kein Shell, kein Package Manager, keine Libraries. Angreifer haben nichts, womit sie arbeiten koennen.
Docker Compose fuer strukturierte Deployments
Fuer Umgebungen, die noch nicht Kubernetes nutzen (oder fuer lokale Development-Setups), ist Docker Compose das richtige Werkzeug. Ein Beispiel fuer eine typische Web-App mit Datenbank und Reverse Proxy:
services:
proxy:
image: nginx:1.27-alpine
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
app:
condition: service_healthy
networks:
- frontend
deploy:
resources:
limits:
memory: 128M
cpus: "0.25"
read_only: true
tmpfs:
- /tmp
- /var/cache/nginx
app:
build:
context: .
dockerfile: Dockerfile
environment:
- DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/appdb
- LOG_LEVEL=info
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
read_only: true
tmpfs:
- /tmp
user: "1000:1000"
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 10s
timeout: 5s
retries: 5
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
pgdata:
Wichtige Punkte: Das backend-Netzwerk ist internal: true -- kein direkter Internetzugang fuer die Datenbank. Passwort kommt aus einer .env-Datei, nicht hardcoded. Health Checks stellen sicher, dass proxy erst startet, wenn app bereit ist.
Monitoring und Logging
Ohne Monitoring sieht man Probleme erst, wenn sie in Produktion eskalieren. Das Minimum fuer Docker-Hosts:
Metriken mit Prometheus + cAdvisor:
# cAdvisor starten (sammelt Container-Metriken)
docker run -d \
--name cadvisor \
--volume /:/rootfs:ro \
--volume /var/run:/var/run:ro \
--volume /sys:/sys:ro \
--volume /var/lib/docker/:/var/lib/docker:ro \
--publish 8080:8080 \
gcr.io/cadvisor/cadvisor:v0.49.1
# Prometheus scrape config
# prometheus.yml
# scrape_configs:
# - job_name: cadvisor
# static_configs:
# - targets: ["cadvisor:8080"]
Logging: JSON-Output und Log-Rotation.
Container sollten nach stdout/stderr loggen. Docker faengt das ab und speichert es. Ohne max-size in der daemon.json wachsen Log-Dateien unbegrenzt. Das fuehrt frueher oder spaeter zu vollen Disks.
# Log-Groesse eines Containers pruefen
docker inspect --format='{{.LogPath}}' <container> | xargs ls -lh
# Alle Container-Logs: Gesamtgroesse
du -sh /var/lib/docker/containers/*/
Regelmaessige Wartung: Disk-Platz zurueckgewinnen
Docker raeumt nichts automatisch auf. Ueber Wochen und Monate sammeln sich ungenutzte Images, gestoppte Container und verwaiste Volumes:
# Ueberblick: Wie viel Platz nutzt Docker?
docker system df
# Alles aufraeumen, was nicht aktiv genutzt wird
docker system prune -a --volumes
# Weniger aggressiv: Nur Images ohne Tag und gestoppte Container
docker image prune
docker container prune
# Regelmaessig per Cron (z.B. wochentlich)
# /etc/cron.weekly/docker-cleanup
docker image prune -a --filter "until=168h" --force
docker volume prune --force
docker network prune --force
Wichtig: docker system prune -a --volumes loescht auch ungenutzte Named Volumes. Fuer Produktion lieber gezielt aufraeumen.
Backup-Strategie fuer Docker Volumes
Volumes enthalten den persistenten State. Ohne Backup-Strategie ist ein Datenverlust nur eine Frage der Zeit:
#!/bin/bash
# docker-volume-backup.sh
BACKUP_DIR="/opt/backups/docker"
DATE=$(date +%Y%m%d-%H%M%S)
RETENTION_DAYS=14
mkdir -p "$BACKUP_DIR"
# Alle Volumes mit Label "backup=true" sichern
for volume in $(docker volume ls -q --filter label=backup=true); do
echo "Backup: $volume"
docker run --rm \
-v "$volume":/source:ro \
-v "$BACKUP_DIR":/backup \
alpine tar czf "/backup/${volume}_${DATE}.tar.gz" -C /source .
done
# Alte Backups aufraeumen
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
echo "Backup abgeschlossen: $(ls -1 $BACKUP_DIR/*_${DATE}.tar.gz | wc -l) Volumes gesichert"
Wann von Docker zu Kubernetes wechseln?
Docker Compose und Docker Swarm haben ihre Berechtigung. Aber ab einem bestimmten Punkt stossen sie an Grenzen:
| Kriterium | Docker (Compose/Swarm) | Kubernetes |
|---|---|---|
| Anzahl Container | Bis ~50 | 50+ |
| Teams | 1-2 | 3+ |
| Auto-Scaling | Manuell/Swarm basic | HPA, VPA, Karpenter |
| Service Discovery | Docker DNS | CoreDNS, Service Mesh |
| Rolling Updates | Basis-Support | Feingranular steuerbar |
| Secrets Management | Docker Secrets | Secrets, External Secrets, Vault |
| Self-Healing | Restart Policies | Pod-Rescheduling, Liveness Probes |
| Ecosystem | Begrenzt | Riesig (Operators, CRDs) |
Der Wechsel zu Kubernetes lohnt sich, wenn die Komplexitaet der Docker-Workarounds die Komplexitaet von Kubernetes uebersteigt. Das passiert typischerweise bei 3+ Teams oder 50+ Containern.
Verwandte Artikel
- Docker und Kubernetes im Vergleich -- Detaillierter Vergleich der beiden Welten
- Containerd vs. Docker -- Was sich unter der Haube geaendert hat
- Container-Orchestrierung erklaert -- Grundlagen der Orchestrierung
- Kubernetes Security Hardening -- Security-Practices fuer den naechsten Schritt
Fazit
Docker-Administration ist mehr als docker run. Ein produktionsreifes Setup braucht gehaeertete Daemon-Konfiguration, Security-Defaults in jedem Container, strukturiertes Monitoring und regelmaessige Wartung.
Die gute Nachricht: Die meisten Massnahmen sind einmalig einzurichten und danach wartungsarm. Die daemon.json aendern, eine CI-Pipeline mit Trivy aufsetzen, cAdvisor deployen -- das sind ein paar Stunden Arbeit, die sich langfristig auszahlen.
Wenn Sie Unterstuetzung bei der Docker-Administration oder dem Umstieg auf Kubernetes brauchen, erreichen Sie uns unter /kontakt.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
Falco: Runtime Security für Kubernetes-Cluster
Falco erkennt verdächtiges Verhalten in Kubernetes-Containern zur Laufzeit. Installation, Custom Rules und Alerting praxisnah erklärt.
Podman vs Docker: Rootless Container im Vergleich
Podman und Docker im Vergleich: Rootless Container, daemonlose Architektur, Kubernetes-Integration und Migration-Guide mit praktischen Beispielen.
Kubernetes Automotive ASPICE 2026: Container für SDV und Compliance
Entdecken Sie, wie Kubernetes Automotive ASPICE 2026 Standards für die SDV-Entwicklung & Compliance revolutioniert. Effiziente Entwicklung, Tests und sichere Prozesse für OEMs in Deutschland – ein entscheidender Schritt für Kubernetes Compliance Deutschland.
Kubernetes Audit Logging richtig konfigurieren
Kubernetes Audit Logging einrichten: Audit Policies definieren, Log-Backends konfigurieren und compliance-relevante Ereignisse zuverlässig erfassen.
BSI-Grundschutz für Kubernetes-Cluster umsetzen
BSI IT-Grundschutz für Kubernetes umsetzen: Baustein SYS.1.6 Containerisierung mit Pod Security Standards, Audit-Logging und Netzwerksegmentierung.