Docker und Container
Der Satz, den es abschaffen soll
„Bei mir läuft es doch."
Diesen Satz hat jede Entwicklerin schon gesagt und jeder Entwickler schon gehört. Die Software läuft auf dem einen Rechner und auf dem anderen nicht — und niemand weiß auf Anhieb, warum.
Die Gründe sind fast immer dieselben:
| Andere Version | Bei dir Java 26, auf dem Server Java 21 |
| Fehlendes Zubehör | Bei dir läuft eine PostgreSQL, auf dem Testrechner nicht |
| Andere Einstellung | Eine Umgebungsvariable, die nur bei dir gesetzt ist |
| Andere Reihenfolge | Die Datenbank war schon da, als die Anwendung startete — beim Kollegen nicht |
Jeder einzelne Punkt ist behebbar. Zusammen kosten sie in jedem Projekt Tage.
Die Idee von Docker: Nicht nur das Programm ausliefern, sondern das Programm samt seiner Umgebung. Was einmal läuft, läuft überall gleich.
Was ein Container ist
Ein Container ist ein Prozess, der so abgeschottet läuft, als hätte er den Rechner für sich allein. Er bringt alles mit, was er braucht: seine Programmbibliotheken, seine Konfiguration, seine Dateien. Was er nicht mitbringt, ist ein eigenes Betriebssystem.
Genau darin unterscheidet er sich von einer virtuellen Maschine.
Daraus folgt der praktische Unterschied:
| Virtuelle Maschine | Container | |
|---|---|---|
| Startzeit | Minute(n) — das Betriebssystem fährt hoch | Sekunden — nur ein Prozess startet |
| Größe | Gigabyte | Megabyte |
| Wie viele parallel? | eine Handvoll | Dutzende |
| Isolation | sehr stark (eigener Kern) | stark, aber gemeinsamer Kern |
„Ein Container ist eine kleine virtuelle Maschine."
Nein. In einem Container läuft kein Betriebssystem. Es läuft ein Prozess, den der Kern des Wirtssystems so abschottet, dass er die anderen nicht sieht.
Der Beweis liegt direkt vor dir: Öffne die Prozessliste deines Rechners, während ein Container läuft. Der Datenbankprozess steht dort — als gewöhnlicher Prozess. Es gibt keine zweite Maschine, in der er sich versteckt.
Die praktische Folge: Auf einem Linux-Kern laufen nur Linux-Container. Dass es unter Windows trotzdem funktioniert, liegt daran, dass Docker Desktop im Hintergrund eine schlanke Linux-VM betreibt — eine für alle Container, nicht eine pro Container.
Die drei Begriffe, die man ständig verwechselt
Image verhält sich zu Container wie Klasse zu Objekt.
Die Klasse Article beschreibt, was ein Artikel ist — sie liegt einmal im Projekt und ändert sich nicht, während das Programm läuft. Mit new Article() entstehen daraus beliebig viele Objekte, jedes mit eigenem Zustand.
Genauso: Das Image postgres:17.6 beschreibt eine PostgreSQL-Installation. Mit docker run entstehen daraus beliebig viele Container, jeder mit eigenen Daten.
Und wie beim Objekt gilt: Was du im Container änderst, ändert das Image nicht. Löschst du den Container, ist die Änderung weg — es sei denn, du hast sie in ein Volume geschrieben (siehe unten).
Was ein Anwendungsentwickler wissen muss
Du wirst in der Ausbildung selten Images bauen. Du wirst aber ständig welche benutzen — für Datenbanken, Message-Broker, Testumgebungen. Dafür reicht ein überschaubarer Vorrat an Wissen.
1. Der Tag ist Teil des Namens
postgres:17.6
└──┬───┘ └─┬─┘
│ └── Tag: die Version
└────────── Name des Images
latest ist keine Versionpostgres:latest bedeutet nicht „die neueste", sondern „das Image, das zuletzt mit diesem Tag versehen wurde". Es zeigt heute auf Version 17 und in einem Jahr auf 18 — ohne dass sich in deinem Projekt eine Zeile ändert.
Dann läuft es bei dir noch und beim Kollegen nicht mehr. Genau der Satz, den Docker abschaffen sollte.
Immer eine feste Version angeben.
2. Ports werden durchgereicht
Ein Container ist abgeschottet — auch im Netzwerk. Damit du von außen hineinkommst, musst du einen Port veröffentlichen:
ports:
- "5432:5432"
# ^^^^ ^^^^
# │ └── Port IM Container
# └─────── Port auf DEINEM Rechner
Die linke Zahl kannst du frei wählen, die rechte ist durch das Programm im Container vorgegeben. Ist der Port auf deinem Rechner schon belegt — weil dort bereits eine PostgreSQL läuft —, meldet Docker port is already allocated. Dann änderst du die linke Zahl, etwa auf 5433:5432, und trägst dieselbe Zahl in deiner Anwendungskonfiguration ein.
3. Container sind vergesslich — Volumes nicht
Alles, was ein Container in sein eigenes Dateisystem schreibt, verschwindet mit ihm. Für eine Datenbank wäre das fatal.
Ein Volume ist ein Stück Speicher, das außerhalb des Containers liegt und hineingereicht wird:
volumes:
- webshop-data:/var/lib/postgresql/data
# ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
# │ └── Ort IM Container
# └────────────── Name des Volumes
| Befehl | Was mit den Daten passiert |
|---|---|
docker compose stop | Container hält an, Daten bleiben |
docker compose down | Container wird gelöscht, Volume bleibt |
docker compose down -v | Container und Volume werden gelöscht — die Daten sind weg |
-v ist keine KleinigkeitDas -v in docker compose down -v löscht die Datenbank. Nicht die Tabellen — den ganzen Inhalt.
Im Unterricht ist das oft genau das, was du willst: sauber von vorn anfangen. Im Betrieb ist es der Befehl, nach dem man das Backup braucht.
4. Die Befehle, die du wirklich brauchst
docker compose up -d # startet alles aus der docker-compose.yml, im Hintergrund
docker compose ps # was läuft gerade?
docker compose logs -f # was sagt der Container? (-f = mitlaufen lassen)
docker compose stop # anhalten, Daten bleiben
docker compose down # anhalten und Container löschen
Das -d steht für detached: Der Container läuft weiter, auch wenn du das Terminal schließt. Ohne -d blockiert der Container dein Terminal und stirbt mit ihm.
docker compose logs ist das erste Mittel bei jedem Problem. Eine Datenbank, die nicht startet, sagt dort, warum — meist in der letzten oder vorletzten Zeile.
Der zweite Blick gilt docker compose ps: Steht dort Up (healthy), läuft der Container und antwortet. Steht dort Exit 1, ist er gestartet und sofort wieder gestorben.
5. Die Datei, die alles zusammenhält
Ein einzelner Container lässt sich mit docker run starten. Sobald mehrere zusammenspielen — Anwendung, Datenbank, vielleicht ein Cache —, schreibt man sie in eine docker-compose.yml. Diese Datei gehört ins Projekt und in die Versionsverwaltung: Sie ist die Beschreibung der Umgebung, und damit Teil des Quelltextes.
Genau so eine Datei benutzt du im Webshop-Tutorial:
services:
webshop-db:
image: postgres:17.6
container_name: webshop-db
environment:
POSTGRES_DB: webshop
POSTGRES_USER: webshop
POSTGRES_PASSWORD: geheim
ports:
- "5432:5432"
volumes:
- webshop-data:/var/lib/postgresql/data
volumes:
webshop-data:
| Abschnitt | Bedeutung |
|---|---|
services | Die Container, die zusammen eine Umgebung bilden |
image | Welche Vorlage — mit Version |
environment | Einstellungen, die beim Start hineingereicht werden |
ports | Was von außen erreichbar sein soll |
volumes | Wo die Daten liegen, die den Container überleben |
Dass Zugangsdaten hier im Klartext stehen, ist für eine Übungsumgebung in Ordnung. Im Betrieb kommen sie aus Umgebungsvariablen oder einem Geheimnisspeicher — nie aus einer Datei im Repository.
Wie ein Image entsteht
Auch wenn du selten eines baust: Du solltest ein Dockerfile lesen können. Es ist eine Bauanleitung, Zeile für Zeile.
FROM eclipse-temurin:26-jre
WORKDIR /app
COPY target/webshop-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
| Anweisung | Bedeutung |
|---|---|
FROM | Auf welchem Image wird aufgebaut — hier eine Java-Laufzeitumgebung |
WORKDIR | In welchem Verzeichnis wird gearbeitet |
COPY | Was aus dem Projekt kommt mit hinein |
EXPOSE | Welchen Port das Programm im Container benutzt (Dokumentation) |
ENTRYPOINT | Was beim Start ausgeführt wird |
Jede Anweisung erzeugt eine Schicht. Ändert sich eine Zeile, werden nur sie und die darunter neu gebaut — deshalb steht das, was sich selten ändert, oben.
Dass eine Spring-Boot-Anwendung ihren Webserver eingebaut mitbringt (siehe Tutorial 01), passt genau dazu: Eine einzige .jar-Datei, ein java -jar, fertig. Genau deshalb lassen sich Spring-Boot-Anwendungen so gut in Container packen.
Wozu das Ganze — jenseits des Klassenraums
| Anwendungsfall | Was Docker beiträgt |
|---|---|
| Entwicklungsumgebung | Jede neue Kollegin ist in Minuten arbeitsfähig: Repository klonen, docker compose up, fertig |
| Testen | Jeder Testlauf startet mit einer frischen, immer gleichen Datenbank |
| Ausliefern | Was getestet wurde, wird ausgeliefert — dasselbe Image, nicht ein neu gebautes |
| Betrieb | Mehrere Anwendungen auf einem Server, ohne dass sich ihre Bibliotheken in die Quere kommen |
| Skalieren | Von einem Container auf zehn, ohne dass jemand etwas installiert |
Der letzte Punkt ist der Grund, warum Docker und Microservices fast immer zusammen genannt werden: Wer viele kleine Dienste betreibt, braucht eine Verpackung, die sich schnell und einheitlich vervielfältigen lässt.
Docker startet Container auf einem Rechner. Sobald ein System auf vielen Rechnern läuft, braucht es jemanden, der entscheidet, welcher Container wo läuft, ihn neu startet, wenn er abstürzt, und den Verkehr verteilt.
Das ist die Aufgabe eines Orchestrators, und der verbreitetste heißt Kubernetes. Für die Ausbildung reicht es zu wissen, dass es ihn gibt und wozu — bedienen wirst du ihn frühestens im Betrieb.
Filme
Zum Ansehen statt Lesen — beide auf Deutsch:
| Film | Wofür |
|---|---|
| Was ist Docker? (the native web) | Der Einstieg: Warum es Container gibt und was sie von VMs unterscheidet |
| DOCKER Crashkurs — in 20 Minuten (Coding Crashkurse) | Die Praxis: Images, Volumes, Compose zum Mitmachen |
Wer tiefer einsteigen will: Docker lernen — Eine Einführung in 100 Minuten vom selben Kanal wie der erste Film.
Das Wichtigste in Kürze
- Ein Container ist ein abgeschotteter Prozess, keine virtuelle Maschine — er teilt sich den Betriebssystemkern mit allen anderen.
- Image ist die Vorlage, Container die laufende Instanz — wie Klasse und Objekt.
- Immer eine feste Version angeben, nie
latest. portsreicht einen Port nach außen: links dein Rechner, rechts der Container.- Daten überleben nur in einem Volume.
down -vlöscht es. - Die
docker-compose.ymlbeschreibt die Umgebung und gehört ins Repository. - Bei Problemen: erst
docker compose ps, danndocker compose logs.
Weiterlesen
- Microservices — warum Container und viele kleine Dienste zusammengehören
- Maven und Abhängigkeiten — dieselbe Grundfrage auf der Ebene der Bibliotheken: Wer sorgt dafür, dass überall dasselbe läuft?
- Webshop-Tutorial — dort startest du deine erste Datenbank im Container