Zum Hauptinhalt springen

Docker Compose

YAML

  • textbasiertes Dateiformat
  • zur Datenserialisierung
  • rekursives Akronym: "YAML Ain't Markup Language"

Links

Syntax

# this is a comment

# key-value: with colon+space
key: value

# section
---

--- # lists: with hyphen+space
list:
- Casablanca
- North by Northwest
- The Man Who Wasn't There
- key1OfObjectInList: value # block in list
key2OfObjectInList: value2
key3OfObjectInList:
- item1
- item2: 90 # block in list

inlineList: [ milk, pumpkin pie, eggs, juice ]

--- # blocks / objects
# IMPORTANT: shifting must consist of two spaces! No tabs!
intendedBlock:
name: John Smith
age: 33
inlineBlock: { name: John Smith, age: 33 }

--- # text
text1: "This is a text"
text2: 'This is a text'
text3: This is a text # most used
text4: "This is a text with an escaped sequence\nand a second line"

multiLineText1: |
In this text, newlines are preserved.
This is a second line.
By default, indentation of the first line is ignored.

So he carefully sat on the ceiling
multiLineText2: >
In this text, newlines are not preserved.
Wrapped lines
will be folded into
a single paragraph.

But blank lines denote
paragraph breaks.

--- # numbers
integer: 123 # an integer
numberAsString: "123" # a string, disambiguated by quotes
float: 123.0 # a float

--- # booleans
booleanTrue: true
booleanFalse: false

--- # explicit data types (prefixed by !!)
explicitFloat: !!float 123
explicitStringOfInteger: !!str 123
explicitStringOfBoolean: !!str true

--- # binary
picture: !!binary |
R0lGODdhDQAIAIAAAAAAANn
Z2SwAAAAADQAIAAACF4SDGQ
ar3xxbJ9p0qa7R0YxwzaFME
1IAADs=

--- # anchor and reference to it
- step: &id001 # anchor
instrument: Lasik 2000
pulseEnergy: 5.4

- step: &id002
instrument: Lasik 2000
pulseEnergy: 5.0
- Instrument1: *id001 # refers to the first step (anchor &id001)
- Instrument2: *id002 # refers to the second step (anchor &id002)

Hinweis: Booleans werden in YAML 1.1 anders als in YAML 1.2.2 definiert. Siehe:

Grundlagen

  • Definierung von Applikations-Umgebungen aus mehreren Containern
  • Auswertung der Datei docker-compose.yml
  • docker-compose.yml
    • alle Konfigurationen für einzelne Container

Installation

Docker Compose sollte bereits mit Docker installiert sein. Wenn nicht, kann es mit dem folgenden Befehl installiert werden:

sudo apt install docker-compose-plugin

Überprüfung der Version:

docker compose version

docker-compose.yml

Referenz: Compose file reference

Grundlegender Aufbau

services:
dienstname1:
schlüsselwort1: einstellung1
schlüsselwort2: einstellung2
# ...
dienstname2:
schlüsselwort1: einstellung1
schlüsselwort2: einstellung2
# ...
volumes:
volume1:
driver: local
volume2:
driver: local
# ...
networks:
netzwerk1:
driver: bridge
netzwerk2:
driver: bridge
# ...
secrets:
secret1:
file: ./secret1.txt
secret2:
file: ./secret2.txt
# ...

Top-Level-Schlüsselwörter

Top-Level-SchlüsselwortBedeutung
servicesDefinition der Container (Services). Dienstnamen (Keys) für Container frei wählbar
networksDefinition der Netzwerke
volumesDefinition der benannten Volumes
configsKonfigurierung. Lässt Services ihr Verhalten anpassen, ohne das Docker image neu bilden zu müssen
secretsDefinition der Passwortdateien

services

SchlüsselwortBedeutung
imageBasisimage eines Services
buildVerzeichnis mit Dockerfile, aus dem Image erstellt wird
container_nameName für Container
restartalways, on-failure oder unless-stopped
environmentListe mit Umgebungsvariablen
portsListe der Portweiterleitungen
exposePorts für Kommunikation zwischen Containern
networksVerweis auf ein im Top-Level-Schlüsselwort definiertes Netzwerk

image

  • Angabe des Basisimages
  • wenn Basisimage im Docker Hub vorhanden ist, kann es direkt verwendet werden, sonst wird es heruntergeladen
services:
my-service:
image: ubuntu:latest
# ...

build

  • wird ein Image benötigt, kann dieses aus Dockerfile erstellt werden
  • gibt Verzeichnis an, in dem sich das Dockerfile befindet
  • Beispiele: ., ./path/to/dockerfile/, /path/to/dockerfile/
services:
my-custom-app:
build: /path/to/dockerfile/
# ...

container_name

  • Name des Containers
  • analog --name bei docker run
  • wird dies nicht angegeben, werden für Namen folgende Werte zusammengesetzt:
    • Arbeitsverzeichnis
    • Servicename
    • Laufnummer
    • Beispiel: workdir-my-custom-app-1
  • Name des Services ist nicht gleich Name des Containers
services:
my-custom-app:
container_name: my-container
# ...

restart

  • Definierung der Restart-Policy für Container
WertBedeutung
nokein automatischer Neustart
on-failure[:max-retries]automatischer Neustart bei Fehler (Exit-Code ungleich 0), max-retries: maximale Versuche
alwaysimmer automatischer Neustart, insbesondere bei Reboot. Bei manuellem Stopp: Container-Neustart bei Dockerdaemon-Neustart
unless-stoppedimmer automatischer Neustart. Bei manuellem Stopp: kein Container-Neustart bei Dockerdaemon-Neustart
services:
my-custom-app:
restart: unless-stopped
# ...

ports

  • Portweiterleitungen
services:
my-custom-app:
image: myapp:latest
ports:
- 8080:3000
- "8081:4000"
# ...

expose

  • Ports für Kommunikation zwischen Containern
  • wenn Basisimage bereits Port exponiert, ist diese Angabe nicht notwendig
services:
network-example-service:
image: karthequian/helloworld:latest
expose:
- 80

networks

  • Angabe eines unter dem Top-Level-Schlüsselwort networks definierten Netzwerks
services:
container:
image: karthequian/helloworld:latest
networks:
- my_network
# ...

# Top-Level-Schlüsselwort
networks:
my_network:

Normalerweise werden bei der Definition des Netzwerks keine weiteren Optionen angegeben. Docker kümmert sich dann selbst um die konkrete Definition.

Möchte man selbst die IP-Adresse(n) und den Gateway angeben, kann dies wie folgt gemacht werden:

services:
network-example-service:
image: karthequian/helloworld:latest
networks:
my_network:
ipv4_address: 192.168.0.10
# ...
another-service-in-the-same-network:
image: alpine:latest
networks:
my_network:
ipv4_address: 192.168.0.11
# ...

networks:
my_network:
ipam:
config:
- subnet: 192.168.0.0/24
gateway: 192.168.0.1

Weist man den Containern keine IP-Adressen hinzu, werden sie automatisch (per Docker-internem DHCP) zugewiesen.

volumes

  • Angabe eines unter dem Top-Level-Schlüsselwort volumes definierten Volumes
  • Mounten eines Containervolumes auf Host
  • benannte Volumes
    • müssen im Top-Level-Schlüsselwort volumes angegeben werden
    • können von mehreren Containern gleichzeitig verwendet werden
  • unbenannte Volumes und Volumes in eigenen Verzeichnissen
    • müssen nicht im Top-Level-Volume aufgeführt werden
    • es können auch einzelne Dateien gemountet werden
  • Volume-Zusätze
    • :ro: Read-Only
  • Kurze Syntax: VOLUME:CONTAINER_PATH[:ACCESS_MODE]
services:
volumes-example-service:
image: alpine:latest
volumes:
- my-named-global-volume:/my-volumes/named-global-volume
- /tmp:/my-volumes/host-volume
- /home:/my-volumes/readonly-host-volume:ro
# ...
another-volumes-example-service:
image: alpine:latest
volumes:
- my-named-global-volume:/another-path/the-same-named-global-volume
# ...
volumes:
my-named-global-volume:

depends_on

  • Angabe der Abhängigkeiten zwischen den Containern

kurze Syntax:

services:
kafka:
image: wurstmeister/kafka:2.11-0.11.0.3
depends_on:
- zookeeper
# ...
zookeeper:
image: wurstmeister/zookeeper
# ...

lange Syntax:

  • restart
    • true: Container wird neu gestartet, wenn der abhängige Container geupdated wird
    • false
  • condition
    • service_started
    • service_healthy
    • service_completed_successfluly
  • required
    • true
    • false: Compose warnt einen nur, wenn abhängige Container nicht gestartet werden konnte oder unverfügbar ist
services:
my-container:
image: my-image
depends_on:
other-container:
restart: <true/false>
condition: service_started
# ...

Um auf einen Container zu warten, bis dieser bereit ist, kann der healthcheck-Befehl beim anderen Container verwendet werden. Bei depends_on muss dabei dann condition: service_healthy angegeben werden.

services:

wordpress:
# ...
depends_on:
db:
condition: service_healthy
# ...

db:
# ...
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 2s
timeout: 5s
retries: 5
start_period: 2s
# ...

# ...

environment

  • Definierung statischer und dynamischer Umgebungsvariablen
  • dynamischer Zugriff: mit ${ENV_VAR_NAME}
services:
database:
image: postgres:${POSTGRES_VERSION}
environment:
DB: mydb
USER: "${USER}"

Dies ist äquivalent zu:

services:
database:
image: postgres:${POSTGRES_VERSION}
environment:
- DB=mydb
- USER="${USER}"

Dynamische Variablen können dabei in einer .env-Datei im selben Verzeichnis als Key-Value-Paare definiert werden.

POSTGRES_VERSION=alpine
USER=foo

Dateinamen:

  • .env: Docker verwendet diese Datei automatisch
  • andere Namen: müssen explizit mit env_file: <filename>angegeben werden

secrets

  • Angabe eines unter dem Top-Level-Schlüsselwort secrets definierten Secrets
  • Top-Level-Schlüsselwort secrets gibt an, wo sich die Passwortdatei befindet
  • Einträge bei Services referenzieren Top-Level-Definition
services:
db:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_PASSWORD_FILE: /run/secrets/db_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
secrets:
- db_root_password
- db_password
# ...
secrets:
db_password:
file: db_password.txt
db_root_password:
file: db_root_password.txt

Vorgehen

  1. Secret-Datei erstellen
  2. secrets-Eintrag in docker-compose.yml definieren
  3. services.<container>.secrets-Einträge in docker-compose.yml definieren
  4. /run/secrets/<secretname> im Container verwenden

healthcheck

  • Angabe eines Healthchecks/Gesundheitscheck
  • andere Services/Container, welche von diesem Container abhängig sind, werden erst gestartet, wenn der Healthcheck/Test dieses Containers erfolgreich ist
joomla:
container_name: joomla
image: joomla
depends_on:
db:
condition: service_healthy
# networks, volumes, ports, environment
# ...

db:
# healthcheck: joomla will wait and check all 10 seconds if db is ready and up
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s

Anwendung

Referenz: dockerdocs - CLI reference - docker compose

docker compose up - Container-Umgebung erstellen

  • Container-Umgebung wird nach docker-compose.yml im aktuellen Verzeichnis erstellt und gestartet
  • deshalb: zuerst in Verzeichnis wechseln, in der sich die docker-compose.yml-Datei befindet
docker compose up [OPTIONS] [SERVICE...]
OptionBeschreibung
-ddaemon, Applikation wird im Hintergrund gestartet
--buildImages werden neu gebaut, bevor die Container gestartet werden

Beispiel:

docker compose up -d --build

docker compose down - Container-Umgebung stoppen

  • stoppt Container-Umgebung nach docker-compose.yml im aktuellen Verzeichnis und löscht alle Container sowie Netzwerke
docker compose down [OPTIONS] [SERVICES]
OptionBeschreibung
-ventfernt zusätzlich die Volumes

Beispiel:

docker compose down

docker compose exec

  • Ausführen eines Befehls in einem laufenden Container
docker compose exec [OPTIONS] SERVICE COMMAND [ARGS...]

Beispiel:

docker compose exec db mysqladmin ping -h localhost -uroot -p
docker compose exec -it wordpress sh
docker compose exec -it wordpress /bin/bash

docker compose logs

  • Anzeigen der konsolidierten Logs aller Services
docker compose logs [OPTIONS] [SERVICE...]
OptionBeschreibung
-fzeigt Live-Logs an, analog zu tail -f

Beispiel:

docker compose logs -f web

docker compose build

  • baut Container-Images gemäss der docker-compose.yml
docker compose build [OPTIONS] [SERVICE...]

Beispiel

Joomla

Normale Kommandos

docker network create gbsnet

docker run -d --name db --network gbsnet -v gbsdb:/var/lib/mysql /
-e MYSQL_DATABASE=joomla_db -e MYSQL_ROOT_PASSWORD=-gbs- mysql:8.3

docker run -d --name joomla --network gbsnet -v gbshtml:/var/www/html /
-p 80:80 -e JOOMLA_DB_HOST=db:3306 -e JOOMLA_DB_NAME=joomla_db /
-e JOOMLA_DB_USER=root -e JOOMLA_DB_PASSWORD=-gbs- joomla

Mit Docker Compose

services:
db:
container_name: db
networks:
- gbsnet
volumes:
- gbsdb:/var/lib/mysql
environment:
MYSQL_DATABASE: joomla_db
MYSQL_ROOT_PASSWORD: -gbs-
image: mysql:8.3
joomla:
container_name: joomla
networks:
- gbsnet
volumes:
- gbshtml:/var/www/html
ports:
- 80:80
environment:
JOOMLA_DB_HOST: db:3306
JOOMLA_DB_NAME: joomla_DB
JOOMLA_DB_USER: root
JOOMLA_DB_PASSWORD: -gbs-
image: joomla


volumes:
gbsdb:
gbshtml:

networks:
gbsnet:

Mit eigenem Netzwerk, benutzerdefinierten IP-Adressen und Healthcheck:

services:
db:
container_name: db
networks:
- gbsnet
volumes:
- gbsdb:/var/lib/mysql
environment:
MYSQL_DATABASE: joomla_db
MYSQL_ROOT_PASSWORD: -gbs-
image: mysql:8.3

healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s

joomla:
container_name: joomla
networks:
- gbsnet
volumes:
- gbshtml:/var/www/html
ports:
- 80:80
environment:
JOOMLA_DB_HOST: db:3306
JOOMLA_DB_NAME: joomla_DB
JOOMLA_DB_USER: root
JOOMLA_DB_PASSWORD: -gbs-
image: joomla

# healthcheck: joomla will wait and check all 10 seconds if db is ready and up
depends_on:
db:
condition: service_healthy

volumes:
gbsdb:
gbshtml:

networks:
gbsnet:

Wordpress

Ohne Secrets

services:

wordpress:
container_name: wordpress
image: wordpress
restart: on-failure
volumes:
- wordpress-html:/var/www/html
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: ${DB_NAME}
WORDPRESS_DB_USER: ${DB_USER}
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
depends_on:
- db

db:
container_name: db
image: mysql:9.3
volumes:
- wordpress-db:/var/lib/mysql
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
restart: on-failure
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 2s
timeout: 5s
retries: 5
start_period: 2s


volumes:
wordpress-db:
wordpress-html:

Mit Secrets

services:

wordpress:
container_name: wordpress
image: wordpress
restart: on-failure
volumes:
- wordpress-html:/var/www/html
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: ${DB_NAME}
WORDPRESS_DB_USER: ${DB_USER}
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
depends_on:
db:
condition: service_healthy
secrets:
- db_password

db:
container_name: db
image: mysql:9.3
volumes:
- wordpress-db:/var/lib/mysql
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD_FILE: /run/secrets/db_password
restart: on-failure
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
interval: 2s
timeout: 5s
retries: 5
start_period: 2s
secrets:
- db_password
- db_root_password


volumes:
wordpress-db:
wordpress-html:

secrets:
db_password:
file: secret_db_password.txt
db_root_password:
file: secret_db_root_password.txt