Docker Compose Tutorial: Multi-Container-Anwendungen einfach orchestrieren
Docker Compose ist das Werkzeug der Wahl, wenn Sie mehrere Docker-Container als zusammenhängende Anwendung betreiben möchten. In diesem Tutorial lernen Sie, wie Sie mit einer einfachen YAML-Datei komplexe Multi-Container-Setups definieren, starten und verwalten.
Was ist Docker Compose?
Docker Compose ist ein Tool zum Definieren und Ausführen von Multi-Container-Docker-Anwendungen. Statt jeden Container einzeln mit langen docker run-Befehlen zu starten, beschreiben Sie Ihre gesamte Anwendungsinfrastruktur in einer docker-compose.yml-Datei. Ein einziger Befehl startet dann alle Services, konfiguriert Netzwerke und mountet Volumes.
Typische Anwendungsfälle sind Entwicklungsumgebungen mit Datenbank, Cache und Webserver, Microservice-Architekturen sowie lokale Repliken von Production-Setups zum Testen.
Installation von Docker Compose
Seit Docker Desktop 3.4 und Docker Engine 20.10.13 ist Docker Compose V2 als docker compose (ohne Bindestrich) integriert. Die separate Installation ist in den meisten Fällen nicht mehr nötig.
Prüfen der Installation
# Docker Compose Version prüfen (V2 Syntax)
docker compose version
# Ausgabe: Docker Compose version v2.24.0 (oder höher)
# Alternative: Alte V1 Syntax (veraltet)
docker-compose --version
Hinweis: Docker Compose V2 verwendet „docker compose“ (mit Leerzeichen) statt „docker-compose“ (mit Bindestrich). Die V2-Syntax ist schneller und bietet zusätzliche Features. Verwenden Sie in neuen Projekten immer V2.
Die docker-compose.yml verstehen
Die docker-compose.yml ist das Herzstück jeder Compose-Anwendung. Sie definiert alle Services, Netzwerke und Volumes in einer deklarativen YAML-Syntax. Beginnen wir mit einem grundlegenden Beispiel einer Web-Anwendung mit Datenbank:
# docker-compose.yml
version: '3.8'
services:
# Web-Anwendung (Node.js)
app:
build: ./app
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://user:password@db:5432/myapp
volumes:
- ./app:/usr/src/app
- /usr/src/app/node_modules
depends_on:
- db
- redis
# PostgreSQL Datenbank
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Redis Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Die wichtigsten Konfigurationsoptionen
| Option | Beschreibung | Beispiel |
|---|---|---|
| image | Docker Image aus Registry | postgres:16-alpine |
| build | Pfad zum Dockerfile | ./app oder {context: ., dockerfile: Dockerfile.dev} |
| ports | Port-Mapping Host:Container | „3000:3000“ |
| volumes | Datei-/Verzeichnis-Mounts | ./data:/app/data |
| environment | Umgebungsvariablen | NODE_ENV=production |
| depends_on | Startreihenfolge definieren | [db, redis] |
| networks | Netzwerk-Zugehörigkeit | [frontend, backend] |
| restart | Neustart-Policy | unless-stopped, always |
Wichtige Docker Compose Befehle
# Alle Services starten (im Hintergrund)
docker compose up -d
# Services starten und neu bauen
docker compose up -d --build
# Logs aller Services anzeigen
docker compose logs -f
# Logs eines bestimmten Services
docker compose logs -f app
# Laufende Container anzeigen
docker compose ps
# Services stoppen
docker compose stop
# Services stoppen und Container entfernen
docker compose down
# Alles entfernen (inkl. Volumes und Images)
docker compose down -v --rmi all
# In einen Container einsteigen
docker compose exec app sh
# Einmaligen Befehl ausführen
docker compose run --rm app npm test
# Services skalieren
docker compose up -d --scale app=3
Umgebungsvariablen und .env-Dateien
Sensible Daten wie Passwörter oder API-Keys sollten nicht direkt in der docker-compose.yml stehen. Verwenden Sie stattdessen .env-Dateien oder externe Umgebungsvariablen.
# .env Datei im gleichen Verzeichnis wie docker-compose.yml
POSTGRES_USER=myuser
POSTGRES_PASSWORD=supersecret123
POSTGRES_DB=production_db
NODE_ENV=production
API_KEY=sk-abc123xyz
# docker-compose.yml mit Variablen-Referenzen
version: '3.8'
services:
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
app:
build: ./app
environment:
- NODE_ENV=${NODE_ENV}
- DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
- API_KEY=${API_KEY}
Praxis-Tipp: Fügen Sie .env immer zu Ihrer .gitignore hinzu. Erstellen Sie eine .env.example mit Platzhaltern als Vorlage für andere Entwickler.
Netzwerke in Docker Compose
Docker Compose erstellt automatisch ein Netzwerk für alle Services in einer Compose-Datei. Die Services können sich gegenseitig über ihren Service-Namen erreichen. Für komplexere Setups können Sie eigene Netzwerke definieren:
version: '3.8'
services:
frontend:
image: nginx:alpine
networks:
- frontend_network
backend:
build: ./api
networks:
- frontend_network
- backend_network
db:
image: postgres:16-alpine
networks:
- backend_network
networks:
frontend_network:
driver: bridge
backend_network:
driver: bridge
internal: true # Kein Zugriff von außen
In diesem Setup kann das Frontend nur mit dem Backend kommunizieren. Die Datenbank ist nur vom Backend erreichbar und vom Internet isoliert.
Volumes und Datenpersistenz
Docker Container sind flüchtig. Wenn Sie sie löschen, gehen alle Daten verloren. Volumes sorgen für Datenpersistenz und ermöglichen das Teilen von Daten zwischen Containern.
version: '3.8'
services:
db:
image: postgres:16-alpine
volumes:
# Named Volume (empfohlen für Datenbanken)
- postgres_data:/var/lib/postgresql/data
# Bind Mount für Initialisierungsskripte
- ./init-scripts:/docker-entrypoint-initdb.d:ro
app:
build: ./app
volumes:
# Bind Mount für Live-Reload in Entwicklung
- ./app/src:/usr/src/app/src
# Anonymous Volume für node_modules
- /usr/src/app/node_modules
volumes:
postgres_data:
driver: local
# Externes Volume (muss vorher erstellt werden)
shared_data:
external: true
Volume-Typen im Überblick
| Typ | Syntax | Verwendung |
|---|---|---|
| Named Volume | volume_name:/path | Datenbanken, persistente Daten |
| Bind Mount | ./host/path:/container/path | Entwicklung, Konfigurationsdateien |
| Anonymous Volume | /container/path | Temporäre Daten, node_modules |
Healthchecks und Dependencies
depends_on startet Services in der richtigen Reihenfolge, wartet aber nicht darauf, dass ein Service wirklich bereit ist. Healthchecks lösen dieses Problem:
version: '3.8'
services:
app:
build: ./app
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 3
Praxisbeispiel: WordPress mit MySQL und Nginx
Ein vollständiges Beispiel für ein WordPress-Setup mit separatem Webserver:
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- wordpress_data:/var/www/html:ro
depends_on:
- wordpress
restart: unless-stopped
wordpress:
image: wordpress:6-php8.2-fpm
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: ${WP_DB_USER}
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
WORDPRESS_DB_NAME: ${WP_DB_NAME}
volumes:
- wordpress_data:/var/www/html
- ./wp-content/uploads:/var/www/html/wp-content/uploads
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${WP_DB_NAME}
MYSQL_USER: ${WP_DB_USER}
MYSQL_PASSWORD: ${WP_DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
wordpress_data:
mysql_data:
Multi-Stage Compose: Development und Production
Mit Compose Profiles und Override-Dateien können Sie verschiedene Umgebungen definieren:
# docker-compose.yml (Basis-Konfiguration)
version: '3.8'
services:
app:
build: ./app
environment:
- NODE_ENV=production
db:
image: postgres:16-alpine
# docker-compose.override.yml (automatisch für Development geladen)
version: '3.8'
services:
app:
build:
context: ./app
dockerfile: Dockerfile.dev
volumes:
- ./app:/usr/src/app
environment:
- NODE_ENV=development
- DEBUG=true
ports:
- "3000:3000"
- "9229:9229" # Debug-Port
db:
ports:
- "5432:5432"
# docker-compose.prod.yml (explizit für Production)
version: '3.8'
services:
app:
restart: always
deploy:
resources:
limits:
cpus: '2'
memory: 2G
db:
restart: always
volumes:
- /mnt/backup:/backup
# Starten mit verschiedenen Konfigurationen
# Development (lädt automatisch override)
docker compose up
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Best Practices für Docker Compose
Fazit
Docker Compose vereinfacht die Verwaltung komplexer Multi-Container-Anwendungen erheblich. Mit einer einzigen YAML-Datei definieren Sie Ihre gesamte Infrastruktur, von der Datenbank über den Cache bis zum Webserver. Die deklarative Konfiguration macht Setups reproduzierbar und versionierbar. Beginnen Sie mit einfachen Setups und erweitern Sie diese schrittweise um Healthchecks, Netzwerke und Umgebungs-spezifische Konfigurationen.
Docker-Setup für Ihr Projekt?
Ich helfe Ihnen bei der Containerisierung Ihrer Anwendung und der Einrichtung einer robusten Docker-Infrastruktur.