aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Tools & DevOps

Docker Compose: Multi-Service-App in einer Datei

Docker Compose ist das Werkzeug, mit dem du mehrere Container in einer einzigen YAML-Datei definierst und sie alle mit einem Befehl startest. Eine echte Anwendung besteht selten aus einem einzigen Prozess: Ein Webserver, eine Datenbank und meistens auch eine Cache-Schicht laufen zusammen. Sie einzeln mit docker run zu starten, ist mühsam und fehleranfällig. In diesem Artikel bauen wir einen typischen Stack aus Web-, PostgreSQL- und Redis-Diensten von Grund auf und behandeln dabei die Kommunikation zwischen Diensten, persistente Daten und Umgebungsvariablen anhand praktischer Beispiele.

Welches Problem löst Docker Compose?

Einen einzelnen Container zu starten ist einfach, aber in echten Projekten wird es schnell kompliziert. Jeder Dienst hat ein eigenes Image, einen Port, ein Netzwerk und eigene Umgebungsvariablen — und oft müssen sie in einer bestimmten Reihenfolge starten. Compose verlagert diese Komplexität in eine deklarative Datei:

  • Eine Quelle: Der gesamte Stack steckt in compose.yaml; ein Teamkollege klont das Repo und tippt einfach docker compose up.
  • Automatisches Netzwerk: Compose richtet ein gemeinsames Netzwerk für deine Dienste ein, und Container erreichen einander über den Dienstnamen.
  • Reproduzierbarkeit: Dieselbe Datei erzeugt denselben Stack auf der Entwicklermaschine und in der CI.

Hinweis: In modernem Docker lautet der Befehl docker compose (mit Leerzeichen); die alte docker-compose-Binärdatei (mit Bindestrich) funktioniert noch, wurde aber durch Compose V2 abgelöst.

Deine erste compose.yaml-Datei

Erstelle einen Ordner und lege darin eine compose.yaml an. Beginnen wir mit nur einem Web-Dienst und einer Datenbank:

services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Ein paar wichtige Details: Der Eintrag "8080:80" unter ports verbindet Port 8080 auf dem Host mit Port 80 im Container. depends_on sorgt dafür, dass der Web-Dienst nach db startet (garantiert aber nicht, dass db tatsächlich bereit ist — dazu gleich mehr). Der volumes-Block erstellt ein persistentes Volume, das die Datenbankdateien erhält, selbst wenn der Container entfernt wird.

Eine Cache-Schicht hinzufügen: Redis

Die meisten Anwendungen nutzen Redis für Sessions, Warteschlangen oder Caching. Einen dritten Dienst hinzuzufügen ist nur eine Frage eines weiteren Blocks:

services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    depends_on:
      - db
      - cache

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - db-data:/var/lib/postgresql/data

  cache:
    image: redis:7-alpine
    command: ["redis-server", "--save", "60", "1"]

volumes:
  db-data:

Das Schöne daran: Aus dem Web-Container erreichst du die Datenbank über den Hostnamen db und Redis über cache. Das interne DNS, das Compose einrichtet, löst den Dienstnamen automatisch zur richtigen IP auf. In deiner Anwendungskonfiguration lauten die Verbindungszeichenfolgen also postgres://app:secret@db:5432/appdb und redis://cache:6379 — fest codierte IPs sind nicht nötig.

Umgebungsvariablen und die .env-Datei

Passwörter fest in die YAML zu schreiben ist eine schlechte Idee. Compose liest automatisch eine benachbarte .env-Datei und ersetzt Variablen mit der ${...}-Syntax:

# .env
POSTGRES_PASSWORD=ein-sehr-geheimer-wert
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: appdb

Füge .env zu deiner .gitignore hinzu, damit Geheimnisse niemals im Repository landen. Eine Beispieldatei .env.example für den Rest des Teams zu hinterlassen ist eine gute Angewohnheit.

Echte Bereitschaft mit Health Checks

depends_on steuert nur die Startreihenfolge; es garantiert nicht, dass die Datenbank Verbindungen annimmt. Der Web-Dienst kann versuchen, sich zu verbinden, während db noch startet, und scheitern. Die Lösung ist, einen healthcheck zu definieren und depends_on mit condition: service_healthy zu kombinieren:

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
      interval: 5s
      timeout: 3s
      retries: 5

  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    depends_on:
      db:
        condition: service_healthy

Jetzt startet der Web-Container erst, nachdem der Befehl pg_isready erfolgreich zurückkehrt. Das beseitigt die meisten dieser "Die Datenbank ist noch nicht bereit"-Fehler.

Im Alltag: die wichtigsten Befehle

Das sind die Befehle, zu denen du am häufigsten greifst, um den Stack zu betreiben und zu verwalten:

  • docker compose up -d — startet alle Dienste im Hintergrund.
  • docker compose ps — listet die laufenden Dienste und ihren Status auf.
  • docker compose logs -f web — verfolgt die Logs eines bestimmten Dienstes live.
  • docker compose exec db psql -U app appdb — führt einen Befehl im laufenden db-Container aus.
  • docker compose down — stoppt und entfernt die Dienste. Mit -v werden auch die Volumes entfernt.

Wenn du ein Image geändert hast (zum Beispiel baust du dein eigenes Dockerfile), baue mit docker compose up -d --build neu.

Häufige Fragen

Was ist der Unterschied zwischen docker-compose und docker compose?

docker-compose (mit Bindestrich) ist das alte, in Python geschriebene V1-Werkzeug. docker compose (mit Leerzeichen) ist das auf Go basierende V2-Plug-in, das in die Docker-CLI integriert ist. Auf aktuellen Installationen solltest du die Version mit Leerzeichen verwenden; die YAML-Syntax ist weitgehend identisch.

Gehen meine Daten verloren, wenn der Container entfernt wird?

Nein — benannte Volumes, die unter volumes definiert sind, sind unabhängig vom Lebenszyklus des Containers. docker compose down entfernt die Container, aber das Volume bleibt; deine Daten sind beim nächsten up wieder da. Nur down -v löscht auch die Volumes.

Eignet sich Compose für die Produktion?

Für kleine bis mittlere Single-Server-Setups ist es bestens geeignet. Für Systeme mit mehreren Knoten, automatischer Skalierung und Selbstheilung ist ein Orchestrator wie Kubernetes die bessere Wahl. Denke an Compose für Entwicklung und einfache Produktionsszenarien und an Kubernetes für große Maßstäbe.

Möchtest du deinen Stack in einer Datei zusammenfassen? Um deine Entwicklung und Bereitstellung mit Docker Compose zu vereinfachen, nimm Kontakt mit mir auf — lass uns gemeinsam die Infrastruktur deines Projekts planen.

Bu kategorideki tüm yazılar →

Devamı için