Zum Inhalt springen
Alle Artikel
Lesen. Lernen. Anwenden.

Das ultimative Docker Compose Cheat Sheet

18. März 202429 Min. LesezeitDeutsch

Dieser Artikel wurde mit ChatGPT automatisch aus dem Englischen ins Deutsche übersetzt.

TLDR; Hol dir das Docker Compose Cheat Sheet als PDF oder Bild. Damit du mitarbeiten kannst, muss Docker auf deinem Entwicklungsrechner installiert sein. In diesem Artikel schreiben wir eine eigene compose.yaml, starten Anwendungen mit mehreren Containern und lernen, sie zu verwalten. Den vollständigen Quellcode findest du auf GitHub.

Das ultimative Docker Compose Cheat Sheet herunterladen

Lade das Docker Compose Cheat Sheet herunter, um den Artikel Schritt für Schritt durchzuarbeiten. Teile es gerne mit deinen Kolleginnen, Kollegen und Freunden.

Möchtest du weitere Ressourcen wie diese?

Komm in unsere Community und melde dich für unseren Newsletter an, um bei DevOps auf dem Laufenden zu bleiben. Du erhältst unsere neuesten Ressourcen und Erkenntnisse direkt in dein Postfach!

Was ist Docker Compose?

Docker Compose ist ein Werkzeug zum Definieren und Ausführen von Docker-Anwendungen mit mehreren Containern. Damit konfigurierst und verwaltest du Anwendungsdienste, Netzwerke und Volumes.

Compose verwendet Textdateien im YAML-Format. Darin definierst du zusammengehörige Docker-Container, die sich gemeinsam steuern lassen. Das vereinfacht die Bereitstellung und Skalierung von Anwendungen.

Was unterscheidet Docker und Docker Compose?

Mit Docker baust du Images, startest Container und erstellst Volumes und Netzwerke. Wenn du Unterstützung bei den Grundlagen brauchst, lies „Das ultimative Docker Cheat Sheet“ oder schau dir unsere Anleitungen auf YouTube an.

Docker Compose hilft dir, mehrere Container zu verwalten. Dazu gehören das Bauen von Images, das Starten von Containern sowie das Einrichten von Volumes und Netzwerken für die Kommunikation zwischen Containern.

Compose nutzt Docker, um mehrere Container mit einer Datei und einem CLI-Befehl zu verwalten.

Warum brauchst du Docker Compose?

Die gleichzeitige Arbeit mit mehreren Containern kann kompliziert werden. Für einen einzelnen Container reicht Docker allein gut aus. Doch bald braucht deine Anwendung weitere Dienste: etwa eine Datenbank, einen In-Memory-Cache, eine Nachrichtenwarteschlange oder einen Storage-Bucket. Die Liste ließe sich lange fortsetzen.

Mit Docker allein müsstest du jeden dieser Container einzeln verwalten. Hier hilft Docker Compose: Es vereinfacht die Konfiguration und Verwaltung mehrerer Container.

Was ist eine compose.yaml?

Diese YAML-Datei legt fest, wie Docker-Container in einer Anwendung zusammenarbeiten. Sie bildet die Grundlage von Docker Compose und die zentrale Stelle zur Konfiguration und Verwaltung aller Bestandteile. Darin definieren wir Services, Volumes und Netzwerke.

Was ist ein Service in Docker Compose?

Ein Service beschreibt die Container, die du starten möchtest. Darin laufen deine Anwendungen, etwa Skripte, Datenbanken, Webanwendungen oder Webserver. In diesem Artikel verwenden wir Container und Service im jeweiligen Beispiel weitgehend gleichbedeutend.

Was ist ein Volume in Docker Compose?

Volumes speichern Daten dauerhaft. Sie lassen sich als Verzeichnisse in Container einbinden. Die Dateien bleiben erhalten, auch wenn der Container entfernt wird, und stehen beim nächsten Start wieder zur Verfügung.

Was ist ein Netzwerk in Docker Compose?

Netzwerke ermöglichen die Kommunikation zwischen Containern. Darüber können isolierte Docker-Container kontrolliert Informationen austauschen.

Wie schreibst du eine compose.yaml?

Wir erstellen eine compose.yaml für eine Anwendung mit zwei Containern: einem Backend und einem Webfrontend. Im Laufe des Artikels ergänzen wir einen dritten Container mit einer Postgres-Datenbank. Diese verbinden wir mit einem Volume, damit unsere Daten erhalten bleiben, wenn wir den Container entfernen und neu erstellen. Ein Netzwerk ermöglicht die Kommunikation zwischen Backend und Datenbank. Den vollständigen Quellcode findest du auf GitHub.

bash
# change directory to the root of our application
$ cd /path/to/the/application/root
# now we create an empty file called compose.yaml
$ touch compose.yaml

In diesem Artikel betrachten wir die Einträge version, services, volumes und networks auf der obersten Ebene. version stammt aus älteren Compose-Dateiformaten; aktuelle Compose-Versionen benötigen diese Angabe nicht mehr.

yaml
# string that represents the version of docker compose used
# for backwards compatibility and just informational
version: '3'

# an object where each key represents a new service
# e.g., your client application, web server, database, ...
services:
  client:
    # define your client
    # e.g., image, ports, environment variables, networks, volumes, ...
  server:
    # define your server
    # e.g., image, ports, environment variables, networks, volumes, ...
  database:
    # define your database
    # e.g., image, ports, environment variables, networks, volumes, ...

# an object where each key represents a new volume
# e.g., to persist the database, store images, documents, ...
# volumes need to be explicitly bound to a service
volumes:
  database_volume:
    # define the settings of your volume
    # if you leave this empty, default values will be applied

# an object where each key represents a network
# e.g., to communicate with containers in the same network
# networks need to be explicitly bound to a service
# docker creates a default network for all services in a compose file
# every service joins the default network and can contact every other container
# by its name e.g., docker sets up a DNS entry in server
# for the client and database
# so a call from the server container to <protocol>://database:<port>
# is equivalent to <protocol>://<ip-address-of-database>:<port>
networks:
  # we can also define explicit networks
  # and let only some containers join
  # e.g., database and server
  server_database_network:
    # define the settings of your network
    # if you leave this empty, default values will be applied

# an object where each key represents a config
# e.g., to adapt behavior without the need for rebuilding an image
# configs need to be explicitly bound to a service
configs:
  some_config:

# an object where each key represents a secret
# e.g., to adapt behavior without the need for rebuilding an image
# secrets act like configs but with a specific focus on sensitive information
# secrets need to be explicitly bound to a service
secrets:
  some_secret:

In den nächsten Abschnitten konfigurieren wir Services, Volumes und Netzwerke. Anschließend kombinieren wir sie, um Webanwendung, Webserver und Datenbank zu starten. Server und Datenbank verbinden wir über ein Netzwerk. Ein Volume speichert die Datenbankinhalte, damit sie nach dem Entfernen und Neuerstellen des Containers weiter verfügbar sind.

Wie startest du einen Service aus einem Dockerfile?

Du kannst in Docker Compose ein Dockerfile als Grundlage angeben. Compose baut daraus das Image und startet anschließend den Container.

yaml
version: '3'

# all the services that we are defining
# services are running containers
services:
  # we are defining a service called client
  # this is the client side of our application
  client:
    # we use the build command to create the image
    # from the Dockerfile that we pass to this command
    # in this case "Dockerfile.client"
    # this image will then be used to create the container
    # we also pass the context of the build
    # as the compose.yml file is in the same directory
    # as the source code, we can use the .
    # to refer to the current directory
    build:
      context: .
      dockerfile: Dockerfile.client

Wie startest du einen Service aus einem Image?

Du kannst auch ein bereits gebautes image verwenden. Findet Docker es nicht lokal, versucht es, das Image von Docker Hub herunterzuladen. In unserem Beispiel verwenden wir Postgres in Version 16.1.

yaml
version: '3'

# all the services that we are defining
# services are running containers
services:
  # we are defining a service called database
  # this is the Postgres database of our application
  database:
    # this time we do not use the build command
    # we use the image command to use an existing image
    # by default, docker compose will look at the local registry
    # to find the image
    # if it is not available locally, it will pull it from
    # the docker hub registry
    image: postgres:16.1

Wie veröffentlichst du Ports mit Docker Compose?

Über ports im Service-Objekt machst du Container vom Host aus erreichbar. Unser Client soll unter http://localhost:80 im Browser verfügbar sein.

yaml
version: '3'

services:

  client:
    build:
      context: .
      dockerfile: Dockerfile.client
    # the ports that we want to publish
    # the first port is the port on the host system
    # the second port is the port inside the container
    # so we map the port 80 of the container to the
    # port 80 of the host
		# ports is an array, so you can add as many ports as needed
    ports:
      - "80:80"

Wie verwendest du Umgebungsvariablen in Docker Compose?

Manche Anwendungen benötigen Umgebungsvariablen, etwa für Passwörter, Domains, Ports, IP-Adressen oder API-Schlüssel. Du kannst sie als Objekt unter environment angeben oder mit env_file auf eine Datei verweisen, die sie enthält.

yaml
version: '3'

services:
  database:
    # this time we do not use the build command
    # we use the image command to use an existing image
    # by default, docker compose will look at the local registry
    # to find the image
    # if it is not locally available, it will pull it from
    # the docker hub registry
    image: postgres:16.1
    ports:
      # the ports that we want to publish
      # the first port is the port on the host system
      # the second port is the port inside the container
      # so we map the port 3000 of the container to the
      # port 3000 of the host
      - "5432:5432"
    # we can define and pass environment variables
    # to the container
    # we will use these variables to connect to the database
    # in our server
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_USER: user
      POSTGRES_DB: database
      # if you use a .env file
      # you can use ${YOUR_ENV_VAR}
      # and Docker will replace this value
      # with the value in your .env file
      YOUR_ENV_VAR: ${YOUR_ENV_VAR}
    # you can also pass complete .env files
    # even multiples
    env_file:
      - .env.local
      - .env.override
      # env files are structured like
      # `YOUR_ENV_VAR=your-env-value`
      # each line is a new environment variable

Wie startest du einen abgestürzten Service erneut?

Rechner, Anwendungen, Stromversorgung und Menschen können ausfallen oder Fehler machen. Das gilt auch für Docker-Container. Mit restart im Service-Objekt legst du fest, wann Compose beziehungsweise Docker einen gestoppten Service wieder starten soll.

yaml
version: '3'

services:
  client:
    build:
      context: .
      dockerfile: Dockerfile.client
    ports:
      - "80:80"
    # whenever our container stops, we want it to restart
    # unless explicitly stopped manually by us
    restart: always

Wie wartest du in Docker Compose auf einen anderen Service?

Zwischen Services bestehen oft Abhängigkeiten. Beispielsweise soll dein Backend erst starten, nachdem der Datenbank-Container gestartet wurde. Dafür trägst du den Service-Namen unter depends_on ein.

yaml
version: '3'

services:
  server:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    restart: always
    # we want our server to wait for the database to be ready
    # docker compose only checks if the container is running
    # not if the database is ready
    depends_on:
      - database

Wie wartest du auf einen bestimmten Zustand eines anderen Services?

Ohne zusätzliche Bedingung wartet Compose nur darauf, dass der Container gestartet wurde. Die Datenbank darin muss zu diesem Zeitpunkt noch nicht bereit sein. Um auf ihre Betriebsbereitschaft zu warten, ergänzen wir einen Healthcheck im Datenbank-Service und eine Bedingung bei depends_on. Unter dem Service-Namen, hier database, setzen wir condition auf service_healthy. Der abhängige Container startet dann erst, wenn der Healthcheck der Datenbank erfolgreich war. Im nächsten Abschnitt richten wir diesen Check ein.

yaml
version: '3'

services:
  server:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    restart: always
    # we want our server to wait for the database to be ready
    depends_on:
      # here we specify the name of the service
      # in our case, our service is named database
      database:
        # we add a condition
	      # only if this condition is met
	      # the server service will start
	      condition: service_healthy

Wie definierst du Healthchecks in Docker Compose?

Ein healthcheck prüft, ob ein Service funktionsfähig ist. Das Objekt bietet mehrere Optionen:

  • test: Der Befehl, der den Zustand des Services prüft.
  • interval: Der Abstand zwischen den Prüfungen.
  • timeout: Dauert eine einzelne Prüfung länger als diese Zeit, gilt sie als fehlgeschlagen.
  • start_period: Eine Anlaufphase für den Container. Fehlgeschlagene Prüfungen zählen während dieser Zeit zunächst nicht als Fehler. Eine erfolgreiche Prüfung setzt den Status bereits auf „healthy“.
  • retries: Die Anzahl aufeinanderfolgender fehlgeschlagener Prüfungen, bevor der Service als „unhealthy“ gilt.
yaml

services:
  database:
    image: postgres:16.1
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_USER: user
      POSTGRES_DB: database
    restart: always
    # in Docker Compose, we can define health checks
    # health checks are commands that are executed
    # to check whether the container is healthy or not
    healthcheck:
      # in this case, we check if the database is ready
      # by using the pg_isready command
      test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
      # we check if the database is ready every 2 seconds
      interval: 2s
      # when a duration of a check takes more than 2 seconds
      # we consider it a failure
      timeout: 2s
      # we retry 3 times before we set the status to unhealthy
      retries: 3
      # we give the container 2 seconds for bootstrapping
      # before we consider a failed health check
      start_period: 2s

Wie speicherst du Daten dauerhaft mit Volumes in Docker Compose?

Wenn wir Daten in die Datenbank schreiben und anschließend alle Container stoppen und entfernen, gehen ohne zusätzliche Speicherung auch die Datenbankinhalte verloren. Volumes sorgen dafür, dass sie unabhängig vom Container erhalten bleiben.

Bei einem benannten Volume gibst du einen Namen und einen Pfad im Container an. Docker verwaltet den Speicherort auf dem Host. Der Containerpfad bezeichnet den Teil des Dateisystems, dessen Daten erhalten bleiben sollen.

yaml
volumes:
  - name_of_volume:/path/inside/of/container

Ein Bind-Mount funktioniert ähnlich, verwendet als ersten Teil aber einen absoluten Pfad auf dem Host. Diesen Speicherort wählst du selbst.

yaml
volumes:
  - /path/on/host/system:/path/inside/of/container

Für unser Beispiel verwenden wir ein benanntes Volume und den Pfad, unter dem Postgres seine Daten speichert.

yaml
database:
  image: postgres:16.1
  ports:
    - "5432:5432"
  environment:
    POSTGRES_PASSWORD: password
    POSTGRES_USER: user
    POSTGRES_DB: database
  restart: always
  healthcheck:
    test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
    interval: 2s
    timeout: 2s
    retries: 3
    start_period: 2s
  # we want to persist the data of the database
  # so we use a volume
  # the volume is defined at the bottom of this file
  # we use a named volume
  # the name is postgres_data_volume
  # and we mount the path /var/lib/postgresql/data
  # from the container to the volume
  # when we create a named volume, docker will create
  # a directory on the host system to store the data
  # this is managed by docker
  # it follows the same rules as the port mapping
  # the first path (or name) is the path to the host system
  # the second path is the path inside the container
  volumes:
    - postgres_data_volume:/var/lib/postgresql/data

# here we define the volumes that we use
# if we want that a service uses a volume
# we need to explicitly use it in the service
volumes:
  # we create a named volume called
  # postgres_data_volume
  postgres_data_volume:
    # if we do not specify anything here
    # docker will use the default settings for this volume

Wie kommunizieren Services über Netzwerke in Docker Compose?

Docker-Netzwerke ermöglichen die Kommunikation zwischen Containern. Compose erstellt standardmäßig ein gemeinsames Netzwerk für die Services einer Compose-Datei. Um die Konfiguration zu zeigen, definieren wir selbst ein Netzwerk, das server und database verbindet. Docker legt für die Services darin DNS-Einträge an. So können sie einander über die Namen aus der Compose-Datei erreichen. Unser Backend verwendet database als Datenbank-Hostnamen. Aus dem Datenbank-Container ließe sich mit curl -X GET http://server:3000/ eine HTTP-Anfrage an den Server senden, sofern curl dort installiert ist.

yaml
version: '3'

services:

  client:
    build:
      context: .
      dockerfile: Dockerfile.client
    ports:
      - "80:80"
    restart: always

  server:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    restart: always
    # docker compose uses a default network if we do not specify one
    # but for this example we created our own network
    # that connects our database and our server
    # the definition of the network is at the bottom of this file
    networks:
      - server_database
    depends_on:
      database:
        condition: service_healthy

  database:
    image: postgres:16.1
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_USER: user
      POSTGRES_DB: database
    restart: always
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "user", "-d", "database"]
      interval: 2s
      timeout: 2s
      retries: 3
      start_period: 2s
    volumes:
      - postgres_data_volume:/var/lib/postgresql/data
    # docker compose uses a default network if we do not specify one
    # but for this example we created our own network
    # that connects our database and our server
    # the definition of the network is at the bottom of this file
    networks:
      - server_database

volumes:
  postgres_data_volume:

# here we define the networks that we use
# if we want that a service uses a network
# we need to explicitly use it in the service
networks:
  # we create a network called
  # server_database
  server_database:
    # if we do not specify anything here
    # docker will use the default settings for this network

Wie startest du mehrere Services mit Docker Compose?

Ein einziger Befehl startet alle in der compose.yaml definierten Container. Abhängigkeiten aus depends_on werden dabei berücksichtigt.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# start all containers
$ docker compose up
# if you want to start all containers in a background process
# add the flag --detach
$ docker compose up --detach

# use this for the following section as well
# verify all containers
# by listing all containers running on this system
$ docker container ls
# verify the volume
# by listing all volumes on this system
$ docker volume ls
# verify the network
# by listing all networks on this system
$ docker network ls

Wie startest du einen einzelnen Service mit Docker Compose?

Du kannst einen Service gezielt über seinen Namen starten. Für die Datenbank verwendest du beispielsweise folgenden Befehl:

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# start the database container
# docker compose up <service-name>
$ docker compose up database
# add the --detach flag to start it as a background process
$ docker compose server --detach
# this command will start the server and the database
# because the server depends on the database

Wie stoppst und startest du mehrere Services erneut?

Mit einem Befehl stoppst du alle Container gleichzeitig. Sie werden dabei nicht entfernt und lassen sich anschließend wieder starten.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop all containers
$ docker compose stop
# if you want to restart it
$ docker compose restart

Wie stoppst und startest du einen einzelnen Service erneut?

Gib den Service-Namen an, um nur diesen Service zu stoppen oder neu zu starten.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop the database container
# docker compose stop <service-name>
$ docker compose stop database
# if you want to restart it
$ docker compose restart database

Wie entfernst du mehrere Services mit Docker Compose?

Mit folgendem Befehl entfernst du alle gestoppten Container:

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# remove all stopped containers
$ docker compose rm
# this will ask you if you are sure about this action
# if you would like to remove them, confirm this action by typing
# "Y" in your terminal

Wie entfernst du einen einzelnen Service mit Docker Compose?

Gib den Service-Namen an, um nur dessen gestoppten Container zu entfernen.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# remove the database container
# the container needs to be stopped before you can remove it
# docker compose stop <service-name>
$ docker compose stop database
$ docker compose rm database

Wie stoppst und entfernst du mehrere Services gleichzeitig?

Mit folgendem Befehl stoppst und entfernst du die Container aus einer compose.yaml in einem Schritt:

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# stop and remove all containers
$ docker compose down

Wie greifst du auf mit Docker Compose gestartete Services zu?

Mit Docker verwendest du docker exec -it <container-name> <command>. Mehr dazu erfährst du in unserem Artikel zu den Docker-Grundlagen.

Mit Compose verwendest du docker compose exec und gibst den Service-Namen an.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# create an ssh like session with a service
# docker compose exec --interactive --tty <service-name> sh
$ docker compose exec --interactive --tty database sh
# now you are inside the container
# use exit to close the connection
$ exit

Wie liest du Container-Logs in Docker Compose?

Mit folgendem Befehl greifst du auf die Logs zu:

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# to access all container logs
$ docker compose logs

Wie liest du die Logs eines bestimmten Services?

Gib den Namen des Services an, um nur dessen Logs anzuzeigen.

bash
# make sure to be in the directory where the compose.yaml is located
$ cd /path/to/project/root
# to access the logs of the server
# docker container logs <service-name>
$ docker compose logs server

Fazit

Du kennst jetzt den Unterschied zwischen Docker und Docker Compose. Du kannst eine eigene compose.yaml schreiben und damit mehrere Container gemeinsam verwalten. Außerdem weißt du, wie du Daten mit Volumes dauerhaft speicherst, Services über Netzwerke verbindest und vom Host aus auf Container und Logs zugreifst.

Wenn du Unterstützung bei der Containerisierung brauchst, kontaktiere uns gerne. Oder komm für Fragen und Diskussionen in unsere neue Community – kostenlose Kekse für die ersten 42 Mitglieder, und nur noch 6 sind übrig 😱!

Komm in unsere Community

Hat dir dieser Artikel gefallen? Teile ihn mit deinen Kolleginnen, Kollegen und Freunden.

Melde dich für unseren Newsletter an!

Verpasse keine neuen Tipps, Anleitungen und Updates – melde dich jetzt für unseren Newsletter an! Wir schicken dir nur relevante und hilfreiche Informationen. Entdecke mit uns die Welt von Docker und darüber hinaus.

Mit deiner Anmeldung akzeptierst du die Datenschutzerklärung. Du kannst dich jederzeit über den Link am Ende unserer E-Mails abmelden.

Das ultimative Docker Compose Cheat Sheet