Docker-Compose erklärt

Docker Compose ermöglicht es, mehrere Container in einer einzigen Datei zu definieren und ihre Beziehungen untereinander zu definieren. Mit einem einzigen Befehl können dann alle Container gestartet werden. Dies ist besonders praktisch, da die Datei einfach geteilt werden kann, um eine einheitliche Serverumgebung zu gewährleisten.

Warum müssen verschiedene Container eigentlich miteinander verknüpft werden? Ein Docker-Container ist von Natur aus isoliert, es können keine Verbindungen nach außen oder nach innen aufgebaut werden. Es ist jedoch oft notwendig, dass Container miteinander kommunizieren müssen, zum Beispiel ein Webserver mit einer Datenbank oder ein Container mit dem Hostcomputer, auf dem er gestartet wurde.

Die Verknüpfung ermöglicht uns, bestimmte Ports freizugeben, um eine Verbindung von außen zu ermöglichen oder ein bestimmtes Verzeichnis mit dem Hostsystem zu teilen. Bei nur zwei Containern ist das noch einfach, aber in einer typischen Webumgebung müssen mehrere Container miteinander kommunizieren, wie zum Beispiel ein Webserver (z.B. Apache2), PHP und eine Datenbank.

Es wird schwierig, den Überblick über all diese verschiedenen Tools zu behalten. Docker Compose hilft uns, dies ganz einfach zu orchestrieren, auch wenn wir noch viele weitere Tools wie NodeJS, Cacheserver und Load Balancer hinzufügen.

Docker-Compose Dateiformat .yaml

Der zentrale Bestandteil von Docker Compose ist die YAML-Datei. In dieser Datei werden die Container definiert, die gestartet werden sollen, sowie ihre Beziehungen zueinander. Es können Verzeichnisse freigegeben, Ports freigeschaltet und Verknüpfungen hergestellt werden.

Auf der ersten Ebene der Datei werden die Namen der einzelnen Container festgelegt. Diese sind wichtig, da sie als Referenz auf andere Container verwendet werden können. In der nächsten Ebene werden die Eigenschaften jedes Containers beschrieben, von denen einige im Folgenden näher erläutert werden. Nachfolgend ein Beispiel:

container1:
  image: image1

container2:
  image: image2

Dieses Beispiel würde zwei Container starten, die jedoch nichts Besonderes tun würden, außer möglicherweise ihre definierten Images auszuführen. Um die Sache spannender zu gestalten, wird nachfolgend auf die verschiedenen Optionen von Docker-Compose eingegangen.

Eigenschaft image

Zunächst wird die wichtigste Eigenschaft betrachtet: image. Mit dieser Eigenschaft legen wir fest, auf welchem Docker-Image ein Container aufbauen soll. Docker sucht nach dem angegebenen Image im Docker-Hub, lädt es herunter und startet von diesem aus einen Container automatisch. Da viele moderne Tools (wie Webserver etc.) bereits vorgefertigte Docker-Images bereitstellen, kann damit viel Arbeit erspart werden.

Ein Beispiel wäre nachfolgend (analog zu dem obigen):

apache2:
  image: httpd

Eigenschaft build

Im Vergleich zu image stellt Build das Gegenstück dar, wobei beide Eigenschaften exklusiv sind, das heißt, ein Container kann nur eine von beiden haben. Hier wird der Pfad zum Dockerfile angegeben – relativ zum Ort der docker-compose.yml Datei – das beim Starten des Containers ausgeführt werden soll. Mit dieser Eigenschaft ist es möglich, alle Container individuell anzupassen, indem für jeden ein eigenes Dockerfile angeben wird. Wenn sich das Dockerfile im selben Verzeichnis wie die docker-compose.yml Datei befindet, geben wir einfach einen „.“ an. Wenn das Dockerfile einen anderen Namen hat, kann der Name mit der Eigenschaft „dockerfile“ geändert werden.

Beispiel:

apache2:
  build: ./apache2

Eigenschaft ports

Die Eigenschaft ports öffnet die angegebenen Ports in einem Container und verbindet sie mit einem Port auf dem Host. Dies ermöglicht es, dass zwei Programme, die denselben Port benutzen, auf demselben Server laufen können, indem man einem Programm einen anderen Port auf dem Host zuweist als dem anderen. Es gibt verschiedene Schreibweisen für diese Eigenschaft:

  • Eine einzelne Zahl öffnet einen einzelnen Port, und falls für den Host kein Port angegeben ist, wird ein zufälliger Port bestimmt.
  • Mit Doppelpunkten getrennte Zahlen mappen Ports von dem Host System in den Container.
  • Mit Bindestrich getrennte Zahlen öffnen eine Reihe von Ports.

Diese Eigenschaft ist nützlich, wenn Programme wie Webserver oder MariaDB in einem Container ausgeführt werden sollen und andere Container darauf zugreifen müssen. Dabei müssen die entsprechenden Ports, wie beispielsweise 80/443 für den Webserver oder 3306 für MariaDB, freigegeben werden.

Beispiel:

apache2:
  image: httpd
  ports: 
   - 80:80
   - 443:443

Eigenschaft volumes

interne Volumes

Durch die volumes-Eigenschaft können Verzeichnisse (oder auch mehrere) vom Host im Container zugänglich gemacht werden. Der Pfad auf dem Host, wo das Verzeichnis zu finden ist, wird einfach angegeben. Optional kann auch ein Pfad angegeben werden, auf den das Verzeichnis im Container gemappt werden soll. Änderungen, die auf einer Seite vorgenommen werden (z. B. im Container), sind automatisch auch auf der anderen Seite (auf dem Host) verfügbar. Dies ist eine einfache Möglichkeit, Datenkonsistenz zu gewährleisten, da normalerweise Daten, die in einem Container gespeichert sind, beim beenden des Containers gelöscht werden. Wenn sie jedoch über ein solches Volume auf dem Hostrechner gespeichert werden, bleiben sie auch nach einem Container-Neustart erhalten.

Beispiel:

apache2:
  image: httpd
  volumes: 
   - /var/project_Blog/html:/var/www/html

externe Volumes

Das Verwenden eines externen Volumes unterscheidet sich sowohl in der Anwendung als auch darin, dass es zuvor angelegt werden muss. Ein externes Volume bleibt auch nach dem Beenden des Containers erhalten. Alle Volumes werden unter /var/lib/docker/volumes/VOLUME-NAME abgelegt.

Beispiel:

Zunächst wird ein externes Volume für MariaDB erstellt. Im Anschluss wird eine Docker Compose Datei gezeigt, die demonstriert, wie das externe Volume eingebunden wird.

docker volume create mariadbV1

Details zu dem gerade erstellten Volume kann mittels folgendem Befehl angezeigt werden:

docker volume inspect mariadbV1

version: '3'

services:
  mariadb:
    image: mariadb
    container_name: mariadb
    ports:
      - 3306:3306
    volumes:
      - mariadb:/var/lib/mysql

volumes:
    mariadb:
     external: true
     name:
        mariadbV1

volumes_from

Die Eigenschaft volumes_from ähnelt sehr der vorherigen, ist jedoch nicht identisch. Sie ermöglicht es uns, alle Volumes, die wir in einem anderen Container haben, in diesen Container zu übernehmen. Das ist äußerst praktisch, um immer Zugriff auf dieselben Daten zu haben. Das ist zum Beispiel wichtig, wenn wir einen Container für einen Webserver und einen weiteren für PHP haben. Beide benötigen Zugriff auf die PHP-Dateien. Hier wird der Name des Containers angegeben, wie in der docker-compose.yml Datei.

Eigenschaft container Name

Durch die Verwendung von container_name kann einem Docker-Container ein spezifischer Name zugewiesen werden. Das ist besonders nützlich, wenn viele Container gleichzeitig laufen, da sonst zufällige Namen vergeben werden und es schwierig wird, den Überblick zu behalten.

Eigenschaft networks

Allgemeine Verwendung

Um Container miteinander zu verknüpfen, wird dies in Netzwerken realisiert. Netzwerke sind vergleichbar mit einem lokalen Netzwerk zu Hause, wo die einzelnen Geräte miteinander kommunizieren können, aber von außen (Internet / Router) nicht zugänglich sind. Ein Beispiel für die Verwendung von Netzwerken sieht folgendermaßen aus. Um Netzwerke nutzen zu können, muss der Block „networks“ auf der gleichen Ebene wie „services“ angelegt werden. In diesem Block spezifizieren wir das Netzwerk, das auch von den Containern verwendet werden soll. Hier ist ein Beispiel:

apache2:
  image: httpd
  ports: 
   - 80:80
   - 443:443
  networks: 
   - NETZWERKNAME
  volumes:
   - /var/project_Blog/html:/var/www/html

php-fpm:
  image: php:fpm
  volumes_from:
   - apache2
  networks: 
   - NETZWERKNAME

network:
   - NETZWERKNAME:

Spezifische IP-Adressen

Um Container spezifische IP-Adressen zuweisen zu können, ist es notwendig, das Subnetz des erstellten Netzwerks festzulegen. Für IPv4-Netzwerke mit einem /29-Präfix sind beispielsweise 6 Clients möglich, wobei die .0-Adresse die Subnetz-Adresse und die letzte Adresse die Broadcast-Adresse ist. Hier ist ein konkretes Beispiel:

version: "3"

services:
  pihole:
    ...
    networks:
      pihole_network:
        ipv4_address: 10.0.0.1


networks:
  pihole_network:
    ipam:
      config:
        - subnet: 10.0.0.0/29

Gibt man keine spezifische IP-Adresse an, wird dem Container eine zufällige Adresse zugewiesen.

Eigenschaft environment / env_file

Durch die Verwendung von Environments können Kommandos innerhalb eines Containers ausgeführt werden. Im nachfolgenden Beispiel wird beim ersten Start des Containers eine Datenbank erstellt, zusammen mit einem Benutzer und einem Passwort für die Datenbank. Es wird auch ein Root-Benutzer für die Datenbank erstellt, da sonst das Bearbeiten oder Erstellen von Datenbanken nicht möglich ist.

version: '3'

services:
  mariadb:
    image: mariadb
    container_name: mariadb
    environment:
      - MYSQL_ROOT_PASSWORD=---------------
      - MYSQL_PASSWORD=---------------
      - MYSQL_DATABASE=---------------
      - MYSQL_USER=---------------

Wenn viele Variablen in einer Compose Datei stehen, kann dies schnell unübersichtlich werden. Außerdem kann es vorkommen, dass die Docker-Compose Datei mit einer anderen Person geteilt werden soll, aber nicht die Zugangsdaten. In einem solchen Fall ist die sogenannte env_file Eigenschaft nützlich. Damit können die oben gezeigten Werte in eine Datei ausgelagert werden. Die Datei sollte im gleichen Verzeichnis liegen wie die Compose Datei.

version: '3'

services:
  apache2:
    image: httpd
    env_file:
      - a.env
      - b.env

Die Datei ist simpel im VAR=VAL-Format aufgebaut. VAR stellt den Variablennamen dar und VAL der entsprechende Wert. Sollten die selben Variablennamen in mehreren Dateien genutzt werden, so wird der letzte Wert aus der letzten Datei als entgültiger angenommen. Dadurch kann die Reihenfolge der Dateien im Compose File eine Rolle spielen.

Eigenschaft restart

Um die Neustart-Richtlinie für einen Container zu konfigurieren, kann die Eigenschaft restart verwendet werden. Der Wert des kann einer der folgenden sein:

  • no: Der Container wird nicht automatisch neu gestartet (Standard).
  • on-failure[:max-retries]: Der Container wird neu gestartet, wenn er aufgrund eines Fehlers mit einem Exit-Code ungleich Null beendet wird. Optional kann mit der Option :max-retries die Anzahl der Versuche des Docker-Dämons begrenzt werden, den Container neu zu starten.
  • always: Der Container wird immer neu gestartet, wenn er gestoppt wird. Wenn er manuell gestoppt wird, wird er nur neu gestartet, wenn der Docker-Daemon neu gestartet wird oder der Container selbst manuell neu gestartet wird.
  • unless-stopped: Analog zu always, außer dass der Container nicht neu gestartet wird, wenn er manuell oder anderweitig gestoppt wird, selbst wenn der Docker-Daemon neu gestartet wird.

Eigenschaft depends_on

Um die Beziehungen zwischen den Diensten in Docker Compose auszudrücken, können Abhängigkeiten definiert werden. Diese bestimmen das Start- und Stop-Verhalten der Dienste:

  • Wenn man docker-compose up ausführt, werden die Dienste in der Reihenfolge ihrer Abhängigkeiten gestartet. Im Beispiel werden db und redis vor web gestartet.
  • Wenn man docker-compose stop ausführt, werden die Dienste in umgekehrter Abhängigkeitsreihenfolge gestoppt. Im Beispiel wird web vor db und redis gestoppt.

version: '3'

services:
  web:
    image: httpd
    depends_on:
      - redis
      - db

  redis:
    image: redis

  db:
    image: mariadb

Aber ACHTUNG: depends_on wartet nicht darauf, dass db und redis lauffähig sind, bevor der Web-Dienst gestartet wird, sondern nur bis sie gestartet wurden.
Wenn man einen Stack im Schwarm-Modus mit einer Compose-Datei der Version 3 bereitstellt, wird die depends_on-Option ignoriert.

Eigenschaft deploy (Schwerpunkt Resourcen)

Um zu verhindern, dass der Kernel des Betriebssystems die Docker Engine bei einem Fehler eines Containers beendet, ist es empfehlenswert, die Ressourcen zu begrenzen. Dies kann durch Hinzufügen von Limits und Reservations in der deploy-Sektion der docker-compose-Datei erreicht werden.

Die Limits bestimmen die maximale Menge an Ressourcen, die ein Container verwenden kann, während die Reservations eine Mindestmenge an Ressourcen garantieren, die dem Container zur Verfügung stehen.

Folgende zwei Ressourcen Gruppen können beschränkt werden:

  • cpus: Diese Option gibt an, wie viel der verfügbaren CPU-Ressourcen ein Container nutzen kann.
  • memory:Diese Option gibt an, wie viel der verfügbaren RAM-Ressourcen ein Container nutzen kann.

Es ist auch möglich, die Optionen –cpu-shares und –memory-swap zu verwenden, um die Ressourcen auf andere Weise zu begrenzen. Weitere Informationen zu diesen Optionen finden in der Docker-Dokumentation.

Ab Compose Version 3 muss die Docker Engine im Swarm-Modus laufen, damit die Ressourcen Begrenzung greift. Wenn Sie jedoch den Kompatibilitätsmodus verwenden möchten, können Sie den Befehl „docker-compose –compatibility“ verwenden. Weitere Informationen dazu sind in der Docker-Dokumentation zu finden.

 

Grundlegende Befehle

Nach der Vorbereitung unseres docker-compose.yml-Files stellt sich die Frage: Wie starten oder beenden wir die konfigurierten Container? Hier sind einige Befehle, mit denen wir unsere Container verwalten können.

docker-compose up

Der erste Befehl ist docker-compose up. Dieser erstellt und startet die Container, die wir in unserem YAML-File definiert haben. Mit diesem einen Befehl können wir also alle unsere Container starten – genial.

docker-compose build

Der Befehl docker-compose build baut alle Container zusammen und taggt sie, damit sie beim nächsten Start schneller verfügbar sind. Wenn sich also etwas im Build-Verzeichnis oder im Dockerfile ändert, führen wir einfach diesen Befehl aus, um die Änderungen in unserer Docker Compose Umgebung zu integrieren.

docker-compose ps

Analog wie docker ps listet auch docker-compose ps die aktiven Container auf, die von Docker Compose verwaltet werden.

docker-compose start / stop

Mithilfe der Namen, die wir im docker-compose ps Befehl sehen können, können einzelne Container mit den Befehlen docker-compose start / stop gestartet bzw. gestoppt werden.

docker-compose rm

Mit dem Namen kann auch ein getaggtrn Container mit dem Befehl docker-compose rm entfernt werden. Wenn anschließend docker-compose up ausgeführt wird, dann wird der Container wieder neu erstellt.

Ich hoffe euch hat dieser Artikel zu Docker-Compose erklären gefallen. Habt Ihr Anmerkungen oder Wünsche? Dann teilt Sie mir gerne mit.

2 Kommentare zu „Docker-Compose erklärt“

  1. Vielen Dank für die gelungenen Erklärungen.
    Eine Frage bleibt für mich noch offen, die ich auch nirgends sonst beantwortet finde:
    worin liegt der Unterschied von:
    – /var/project_Blog/html:/var/www/html
    – . /var/project_Blog/html:/var/www/html
    – mariadb:/var/lib/mysql
    oder anders gefragt, was bedeuten und bewirken die Zeichen [ – ] ; [ -/ ] ; [ -./ ] am Anfang der Zeile?

    1. Hallo Jürgen,

      vielen Dank für dein Interesse und dein Lob zu den Erklärungen!

      Die Unterschiede zwischen den angegebenen Zeilen in Docker-Compose beziehen sich auf die Art und Weise, wie Volumes und Containerpfade definiert werden. Hier ist eine kurze Erläuterung zu den spezifischen Syntaxen:

    2. /var/project_Blog/html:/var/www/html: Das Verzeichnis auf dem Hostsystem (/var/project_Blog/html) wird direkt in das Verzeichnis im Container (/var/www/html) gemountet. Änderungen auf beiden Seiten wirken sich gegenseitig aus.
    3. ./var/project_Blog/html:/var/www/html: Der Punkt am Anfang (./) bezieht sich auf das aktuelle Arbeitsverzeichnis, also einen relativen Pfad. Hierbei wird das Verzeichnis var/project_Blog/html im aktuellen Arbeitsverzeichnis des Hostsystems in das Verzeichnis /var/www/html im Container gemountet. Hast du beispielsweise deine Docker-Compose Datei unter /home/jürgen gespeichert, so wird das Verzeichnis /home/jürgen/var/project_Blog/html in das Verzeichnis des Container unter /var/www/html gemountet. Auch hier wirken sich Änderungen auf beiden Seiten aus.
    4. mariadb:/var/lib/mysql: Hier wird ein sogenanntes „named volume“ verwendet. Docker erstellt automatisch ein Volumen namens „mariadb“, und der Pfad /var/lib/mysql im Container wird mit diesem benannten Volumen verbunden. Named volumes sind persistenter und unabhängig vom Dateisystem des Hosts. Man kann Volumes z.B. mittels dem Kommando „docker volume create mariadb“ erstellen. Mehr findest du hierzu unter Docker Dokumentation Volumes
    5. Hoffentlich klärt das deine Frage! Wenn du weitere Unklarheiten hast, stehe ich gerne zur Verfügung.

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen