Was bedeutet eigentlich „Ein Hamburger-Menü, bitte“?
Letztes Mal haben wir mit einem Dockerfile unser eigenes Docker-Image erstellt. Das ist wie ein ‘nie veränderliches Rezept’, in dem genau festgelegt ist, in welcher Reihenfolge das Patty auf das Brötchen und der Salat darauf kommt.
Jetzt ist es an der Zeit, mit diesem Rezept das eigentliche Geschäft, also den Service, zu starten. Aber sobald man Docker benutzt, tauchen verwirrende Begriffe auf. Container, Service, Stack… sie klingen alle ähnlich. Was genau ist also der Unterschied?
Wenn man diese Begriffe nicht sauber auseinanderhält, irrt man später beim Schreiben von Deploy-Skripten herum und fragt sich: „Warum habe ich den Container gestartet, aber der Service läuft trotzdem nicht?“ Um heute diesen Stammbaum zu ordnen, habe ich die universellste Sprache der Welt mitgebracht: die Hamburgerladen-Metapher.

1. Container: der auf Bestellung zubereitete Hamburger
- Definition: das ‘konkrete Objekt’, das entsteht, wenn man ein Image ausführt und in den Arbeitsspeicher lädt.
- Merkmal: Dem Kunden kann man nicht das Rezeptblatt, also das Image, servieren. Er muss einen nach Rezept zubereiteten, frisch gebratenen ‘Hamburger’ bekommen, also den Container.
- Befehl:
docker run my-burger
Wenn ich eine Serverinstanz starte, bedeutet das, dass ich ‘einen Container’ ausführe.
2. Service: Bestandsverwaltung für dasselbe Menü
Es ist Mittagszeit und die Kunden strömen herein. Mit der Geschwindigkeit, mit der ein einzelner Hamburger gebraten wird, kommt man nicht mehr hinterher. Also beschließen wir, mehr Küchenpersonal einzusetzen und jeweils drei Cheeseburger auf Vorrat zuzubereiten. Ab diesem Moment verwalte ich nicht mehr nur „einen Cheeseburger“, sondern die ganze „Cheeseburger-Linie“, also den Service.
- Definition: eine ‘Gruppe von Containern’, die aus demselben Image erstellt wurden.
- Merkmal: Man kann ‘Richtlinien’ festlegen wie „Von diesem Menü immer 3 Stück vorrätig halten (Replicas=3)“. Fällt ein Hamburger herunter, also bei einem Fehler, wird automatisch ein neuer gebraten.
- Zweck: ‘Scale-out’. Verteilung des Traffics und Stabilität.
3. Stack: das Menü, also das Gesamtpaket
Ein Hamburger allein macht noch keine vollständige Mahlzeit. Man braucht auch Pommes, die DB, und Cola, Redis. Erst wenn all das zusammenkommt, entsteht eine ‘vollständige Mahlzeit’, also die Anwendung.
- Definition: die ‘oberste Gruppe’, die verschiedene Services wie Web, DB und Cache zusammenfasst.
- Merkmal: Hamburger und Cola isst man am bequemsten, wenn sie auf demselben Tablett stehen, sprich: Netzwerkverbindung. Sobald sie zu einem Stack gebündelt sind, entsteht ein eigener Raum (Network), in dem sie miteinander kommunizieren können.
Das Problem: Beim Bestellen geht mir schon die Luft aus
Das Konzept klingt verständlich, aber wenn man das Ganze wirklich betreiben will, wird es schnell anstrengend. Der Webservice, also der Stack, den wir bauen wollen, braucht normalerweise die folgenden Bestandteile.
- Spring Boot (Burger): 2 Hauptgerichte (Redundanz)
- PostgreSQL (Fries): 1 Beilage
- Redis (Coke): 1 Getränk
All das mit nackten Docker-Befehlen, docker run, zu starten, ist so, als würde man an der Theke so bestellen.
„Entschuldigung, ich hätte gern einen Cheeseburger, Spring, aber bitte das Patty medium-well, ohne Gurken, dafür extra viel Soße. Ach ja, und Pommes, DB, aber bitte ohne Salz und mit drei Ketchup-Portionen. Und als Letztes eine Cola, Redis, aber bitte Zero Cola und nur halb so viel Eis. Und packen Sie alles auf ein einziges Tablett, Network.“
# In der Praxis muss man jedes Mal solche Befehle eintippen
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...
Kann man sich bei jedem Deployment diese gesamte Bestellung fehlerfrei merken? Und was passiert, wenn die Reihenfolge durcheinandergeht und die Pommes später kommen als der Burger? Der Kunde, also der User, wird nicht begeistert sein.
Der Problemlöser: Docker Compose, „Wie immer, bitte“
Also dachten Entwickler weiter. „Könnte man diesen komplizierten Bestellinhalt nicht im Voraus auf einer Speisekarte, also in einem Dokument, festhalten und dann einfach sagen: ‘Einmal Menü Nummer 1 bitte’?“
Genau so entstand Docker Compose. Wenn wir in einer Datei namens docker-compose.yml unser eigenes ‘individuelles Menü’ definieren, übernimmt Docker die Bestellung für die Küche und stellt alles sauber auf dem Tablett zusammen.

[Action] Das eigene Menü zusammenstellen
Reden wir nicht nur darüber, sondern probieren es wirklich aus. Lassen wir den Spring-Server und die DB auf meinem lokalen Rechner mit nur einem Befehl hochfahren. Alles, was man tun muss, ist, im Projektwurzelordner eine Datei namens docker-compose.yml anzulegen und den folgenden Inhalt einzufügen.
version: '3.8'
services:
# 1. Pommes (DB)
my-database:
image: postgres:14
environment:
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
# 2. Cheeseburger (Web)
my-web-server:
image: my-spring-app:latest
ports:
- "8080:8080"
depends_on:
- my-database # "Pommes muessen zuerst fertig sein, bevor der Burger rausgeht" (Reihenfolge garantiert)
environment:
DB_URL: jdbc:postgresql://my-database:5432/mydb
Und jetzt spricht man im Terminal die magischen Worte.
# "Einmal Menue 1 (docker-compose.yml), bitte!"
$ docker-compose up -d
Ergebnis: Docker liest diese Menüdatei, startet zuerst die DB und startet dann den Spring-Server, sobald die DB bereit ist. Genau hier beginnt das Konzept von IaC, Infrastructure as Code, also Infrastruktur über Code zu verwalten. Ab jetzt reicht es, für selbst komplexe Serverkonfigurationen einfach eine einzige Menüdatei zu teilen.
Zum Schluss: Works on My Machine? Works on ANY Machine!
Die Ausrede, mit der diese Serie begann, „Auf meinem Rechner funktioniert es aber…“, zieht jetzt nicht mehr. Wir haben Linux, das OS, verstanden, mit Docker, dem Container, Umgebungen isoliert und mit Docker Compose komplexe Services in einer einzigen Datei beschrieben.
Jetzt kann ich mit nur der von mir geschriebenen docker-compose.yml-Datei auf meinem Rechner, auf dem Rechner meines Teamleiters oder auf einem AWS-Server in nur einer Minute exakt dasselbe Hamburger-Menü servieren.
Wir sind jetzt nicht mehr nur ‘einfach Coder’. Wir sind Ingenieure geworden, die nicht nur den Code selbst, sondern auch die Welt, also die Umgebung, entwerfen können, in der dieser Code laufen wird. Damit endet die Serie [Works on My Machine].
Ab der nächsten Serie tauchen wir auf dieser soliden Infrastruktur in die Welt von [The Pipeline: CI/CD-Automatisierung] ein, in der wir den mühsamen Deployment-Prozess an Maschinen übergeben.


