Zum Inhalt springen
Farbschema

Orchestrierung mit Docker Compose

Bereits in unserem kleinen Beispiel bräuchten wir normalerweise mehr als einen Container. Es wäre schön, wenn wir neben der Datenbank, auch eine Web-basierte UI für die Verwaltung der Datenbank zur Hand hätten (gibt es ja fast für alle Datenbanken). Damit müssten wir nicht für jede Datenbank auch noch ein Tool installieren, mit dem man die Daten anschauen kann.

Aber bereits 2 Container bringen eine gewisse Komplexität. Zum Beispiel müssen wir die UI für die Datenbank erst starten, wenn die Datenbank bereits hochgefahren und verfügbar ist. Mit mehr Containern (z.B. Redis für Caching, Prometheus für Monitoring, Logging und Tracing, Grafana für Visualisierung dieser Daten, usw.). Alle haben eigene Voraussetzungen, Abhängigkeiten usw.

Wäre es nicht schön, so eine “Laufzeitumgebung” ein Mal zu definieren und immer wieder (sauber) ausführen zu können? Eventuell diese Konfiguration direkt mit dem Projekt auch mit den Kollegen teilen?

So eine Orchestrierung von unterschiedlichen Containern können wir mit “Docker Compose” umsetzten. Das ist praktisch ein kleiner Bruder von Kubernetes, das für die professionelle, hoch skalierbare, Orchestrierungen gedacht ist.

Die Container, die mit “Docker Compose” orchestriert werden sollen, werden in einer YAML Datei definiert und konfiguriert. Die CLI dafür wird mit “Docker Desktop” mitgeliefert. Ist diese auf Ihrem System nicht installiert, können Sie den Installationsanweisungen von der offiziellen Docker Compose Seite folgen.

Die Datei wird standardmäßig docker-compose.yml genannt und befindet sich in dem gleichen Verzeichnis wie das Projekt. Hier ist der Grundaufbau der Datei:

# Liste der Services / Container
services:
# Datenbank Container
db:
# Container image
image: postgres:18-alpine
# Umgebungsvariablen
environment:
POSTGRES_PASSWORD: super-secret-password
# Port Mapping
ports:
- "5432:5432"
dbAdmin:
# Container image
image: dpage/pgadmin4
# Umgebungsvariablen
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: super-secret-password
# Port Mapping
ports:
- "8080:80"

Die Konfiguration enthält min. ein Service (Container) das gestartet werden soll.

  • In der Zeile 4 starten wir die Konfiguration von Postgres Datenbank. Der Service enthält den Namen db.
  • Zeile 6 definiert das Image, das verwendet wird (Postgres in diesem Fall).
  • In der Zeile 8 definieren wir die Umgebungsvariablen, die beim Starten des Containers gesetzt werden sollen. In diesem Fall POSTGRES_PASSWORD wird auf super-secret-password gesetzt.
  • Zeile 10 definiert das Port Mapping, mit dem Containerport 5432 mit dem Hostport 5432 verbunden wird.

Der zweite Service ist eine Admin-UI für Postgres. Sie wird mit dem Namen dbAdmin gestartet und verwendet die Image dpage/pgadmin4. Die Umgebungsvariablen werden ebenfalls gesetzt, um den Benutzername und das Passwort für die Admin-UI zu definieren.

Da bei der Definition des Images for dbAdmin keine Version angegeben wird (kein :tagName am Ende), wird der Tag automatisch auf latest gesetzt (Docker Konvention).

Um nun alle konfigurierten Services zu starten, nutzen wir die “Docker Compose” CLI. Die Aufrufe müssen in dem Ordner erfolgen, in dem die docker-compose.yml Datei liegt.

Terminal-Fenster
docker compose up -d
Docker Desktop zeigt das Compose-Projekt blazor-postgres-pgadmin mit zwei laufenden Containern: db-1 (postgres, Port 5432) und dbAdmin-1 (dpage/pgadmin4, Port 8080)
Gestarteten Container über Compose

Unter http://localhost:8080 können Sie sich nun mit den Zugangsdaten (aus der Konfiguration) anmelden und eine Verbindung zur Datenbank herstellen (Hostname db, wie der Service Name).

pgAdmin-Weboberfläche unter localhost mit verbundenem Server „DB“ und dem Dashboard der Datenbank
Postgres Admin Web-UI

Damit passt die aktuelle minimalistische Konfiguration aber noch nicht zu unseren Anforderungen der App.

  • Wir haben vorgegebene Anmeldedaten für die Datenbank
  • Wir haben einem, vom Standard abweichenden Datenbanknamen
  • Es wäre gut, wenn pgAdmin bereits die Verbindung zu unseren Datenbank hätte, ohne dass wir es jedes Mal nach Neustart eingeben müssten
  • pgAdmin sollte erst starten, wenn die Datenbank einsatzbereit ist (“health check”)

Die Konfiguration der Datenbank können wir leicht anpassen. Die entsprechenden Umgebungsvariablen-Werte kennen wir bereits aus dem direkten Start mit Docker.

services:
db:
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: devdb

Für die Prüfung, ob der Container wirklich einsatzbereit ist, müssen wir einen “health check” umsetzen. Dieser ist immer abhängig vom Service. Für unsere Postgres Datenbank könnte dieser wie folgt aussehen.

services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U devuser -d devdb"]
interval: 2s
retries: 10
start_period: 5s
timeout: 30s
  • Zeile 3 startet die Definition für “health check” (Health Check Doku)
  • Zeile 4 beschreibt den Befehl, mit dem die “Bereitschaft” geprüft werden kann. Bei Postgres können wir dazu pg_isready Tool nutzen. (Check Doku)
  • Zeile 5 gibt an, in welchen Zeitabständen der Check durchgeführt wird, nach dem Ablauf der start_period
  • Zeile 6 gibt an die Anzahl der Versuche, bis der Start als fehlerhaft markiert wird
  • Zeile 7 gibt die üblich Startzeit an. In dieser Zeit wir der Container zwar getestet, die Fehler zählen in dieser Zeit aber nicht zu Wiederholungen.
  • Zeile 8 gibt an, wie lange ein Check max. dauern soll, bis der als fehlerhaft erkannt wird

In pgAdmin können wir nun die Datenbank als Abhängigkeit eintragen, so dass der Container erst dann startet, wenn die Datenbank bereits verfügbar (auf Verbindungsversuche antwortet) ist.

services:
db:
...
dbAdmin:
depends_on:
db:
condition: service_healthy

Nun fehlt uns nur noch die Konfiguration von pgAdmin, so dass unsere Datenbank da bereits “hinterlegt” ist. Dazu müssen wir (leider) die Dokumentation von pgAdmin genauer ansehen.

Damit wir uns nicht jedes Mal anmelden müssen (wir sind ja auf unseren eigenen Rechner und betreiben keinen Server), können wir pgAdmin ich einen “nicht” Server Modus starten

services:
dbAdmin:
environment:
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
PGADMIN_CONFIG_SERVER_MODE: 'False'
  • PGADMIN_CONFIG_SERVER_MODE schaltet pgAdmin in single User Mode, so dass wir uns nicht mehr anmelden müssen
  • PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED erlaubt uns im nächsten Schritt die Konfiguration des Servers durchzureichen

Im letzten Schritt legen wir eine Konfigurationsdatei für die Serverkonfiguration, die wir an pgAdmin durchreichen werden. Diese können wir indirekt für uns erstellen lassen, in dem wir den Server zuerst in pgAdmin konfigurieren und diese Konfiguration dann als JSON-Datei exportieren. Die exportierte Datei muss dann über Docker Desktop aus dem Container exportiert werden.

{
"Servers": {
"1": {
"Name": "DB",
"Group": "Servers",
"Host": "db",
"Port": 5432,
"MaintenanceDB": "devdb",
"Username": "devuser",
"PasswordExecCommand": "echo \u0027devpassword\u0027"
}
}
}

In Zeile 10 übergeben wir das Passwort über ein echo Befehl, da die Konfiguration die Speicherung von unverschlüsselten Passwörtern nicht erlaubt.

services:
dbAdmin:
# Datei und Orner Mappings
volumes:
- ./db_server.json:/pgadmin4/servers.json

In Zeile 5 Mappen wir unsere Konfigurationsdatei servers.json in den Container hinein. pgAdmin erwartet die Serverkonfiguration im Ordner /pgadmin4 mit dem Dateinamen servers.json.

Nun können wir unsere Konfiguration starten. In der Kommandozeile sehen wir auch, dass pgAdmin erst starten, wenn Postgres in den “healthy” Status wechselt. Wenn wir nun die pgAdmin Web-Oberfläche öffnen, gelangen wir direkt, ohne Anmeldung, hinein. Auch der Postgres Server ist für uns bereits konfiguriert.

Terminal-Fenster
docker compose up -d
Terminal-Fenster
dotnet run
pgAdmin ohne Anmeldedialog: Server „DB“ ist bereits eingerichtet, die Tabelle Movie der Datenbank devdb zeigt fünf Einträge
pgAdmin ohne Anmeldung mit Serverkonfiguration