Docker Essentials: Unterschied zwischen den Versionen
(Die Seite wurde neu angelegt: „'''Docker – Grundlagen, Images, Container und Compose''' __TOC__ = Einführung = == Was ist ein Container == ;Definition Ein Container ist ein Prozess au…“) |
|||
| Zeile 25: | Zeile 25: | ||
| cgroups || Begrenzt Verbrauch: CPU, Arbeitsspeicher, Block-I/O | | cgroups || Begrenzt Verbrauch: CPU, Arbeitsspeicher, Block-I/O | ||
|} | |} | ||
| + | |||
| + | == Die Ausgangslage ohne Container == | ||
| + | |||
| + | ;"Auf meinem Rechner läuft es" | ||
| + | Eine Anwendung wird auf dem Arbeitsplatz entwickelt und getestet, auf dem Server läuft sie nicht. Ursache sind Unterschiede in der Betriebssystemversion, in den Versionen der installierten Software, in den Bibliotheken und in der Konfiguration. | ||
| + | |||
| + | ;Abhängigkeitskonflikte | ||
| + | Zwei Anwendungen auf demselben Server brauchen dieselbe Bibliothek in unterschiedlichen Versionen. Installiert werden kann nur eine davon. | ||
| + | |||
| + | ;Ressourcenverbrauch | ||
| + | Der klassische Ausweg war eine virtuelle Maschine je Anwendung. Jede davon bringt ein vollständiges Betriebssystem mit und belegt Arbeitsspeicher, Plattenplatz und CPU-Zeit, obwohl nur ein einzelner Dienst darin läuft. | ||
| + | |||
| + | ;Trennung von Diensten | ||
| + | Auf einem gemeinsam genutzten Server beeinflussen sich Dienste gegenseitig. Ein Update für den einen kann den anderen stillegen. | ||
== Container und virtuelle Maschine == | == Container und virtuelle Maschine == | ||
| Zeile 47: | Zeile 61: | ||
| Fremde Kernel || möglich (Windows auf Linux-Host) || nicht möglich | | Fremde Kernel || möglich (Windows auf Linux-Host) || nicht möglich | ||
|} | |} | ||
| + | |||
| + | ;Aufbau im Vergleich | ||
| + | <pre> | ||
| + | Virtuelle Maschine Container | ||
| + | +---------------------+ +---------------------+ | ||
| + | | Anwendung | | Anwendung | | ||
| + | +---------------------+ +---------------------+ | ||
| + | | Gast-OS | | Container-Laufzeit | | ||
| + | +---------------------+ +---------------------+ | ||
| + | | Hypervisor | | | | ||
| + | +---------------------+ | Host-OS | | ||
| + | | Host-OS | | | | ||
| + | +---------------------+ +---------------------+ | ||
| + | | Hardware | | Hardware | | ||
| + | +---------------------+ +---------------------+ | ||
| + | </pre> | ||
;Konsequenz für die Praxis | ;Konsequenz für die Praxis | ||
| Zeile 64: | Zeile 94: | ||
| Registry || Server, auf dem Images liegen (Docker Hub oder eine eigene Registry) | | Registry || Server, auf dem Images liegen (Docker Hub oder eine eigene Registry) | ||
|} | |} | ||
| + | |||
| + | ;Zusammenspiel | ||
| + | <pre> | ||
| + | Benutzer --> docker --> dockerd --> containerd --> Container | ||
| + | | | ||
| + | v | ||
| + | Registry | ||
| + | </pre> | ||
;Kommunikationsweg | ;Kommunikationsweg | ||
| Zeile 306: | Zeile 344: | ||
* <code>docker images</code> | * <code>docker images</code> | ||
| + | |||
| + | ;Ausgabe | ||
| + | <pre> | ||
| + | REPOSITORY TAG IMAGE ID CREATED SIZE | ||
| + | nginx 1.29 3b4a1c2f9e70 2 weeks ago 192MB | ||
| + | debian 13 7c9e5d1a4b83 3 weeks ago 117MB | ||
| + | </pre> | ||
{| class="wikitable" | {| class="wikitable" | ||
| Zeile 419: | Zeile 464: | ||
;Laufende Container | ;Laufende Container | ||
* <code>docker ps</code> | * <code>docker ps</code> | ||
| + | |||
| + | ;Ausgabe | ||
| + | <pre> | ||
| + | CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES | ||
| + | 9f31c07ab2d4 nginx:1.29 "/docker-entrypoint.…" Up 4 minutes 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp web | ||
| + | </pre> | ||
;Alle Container, auch beendete | ;Alle Container, auch beendete | ||
| Zeile 451: | Zeile 502: | ||
| <code>-t 30</code> || Wartezeit in Sekunden bis zum harten Abbruch | | <code>-t 30</code> || Wartezeit in Sekunden bis zum harten Abbruch | ||
|} | |} | ||
| + | |||
| + | ;Prozesse anhalten und fortsetzen | ||
| + | * <code>docker pause web</code> | ||
| + | * <code>docker unpause web</code> | ||
| + | |||
| + | Beim Anhalten werden die Prozesse im Container eingefroren, der belegte Arbeitsspeicher bleibt bestehen. Im Unterschied zu <code>docker stop</code> läuft der Container danach an derselben Stelle weiter. | ||
== Restart-Policy == | == Restart-Policy == | ||
| Zeile 559: | Zeile 616: | ||
Alles, was auf dem Host an Port 8080 ankommt, wird an Port 80 im Container weitergereicht. | Alles, was auf dem Host an Port 8080 ankommt, wird an Port 80 im Container weitergereicht. | ||
| + | |||
| + | <pre> | ||
| + | Client ---> Host:8080 ---> Container:80 ---> Dienst | ||
| + | </pre> | ||
== Port veröffentlichen == | == Port veröffentlichen == | ||
| Zeile 593: | Zeile 654: | ||
Die tatsächlich vergebenen Ports stehen in der Spalte PORTS der Ausgabe von <code>docker ps</code>. | Die tatsächlich vergebenen Ports stehen in der Spalte PORTS der Ausgabe von <code>docker ps</code>. | ||
| + | |||
| + | <pre> | ||
| + | PORTS | ||
| + | 0.0.0.0:32768->80/tcp | ||
| + | </pre> | ||
== Übung == | == Übung == | ||
| Zeile 652: | Zeile 718: | ||
Enthält ein Image die Anweisung <code>VOLUME</code>, legt Docker beim Start ein Volume ohne Namen an. Solche Volumes tauchen in <code>docker volume ls</code> nur mit einer langen Kennung auf und bleiben nach dem Löschen des Containers zurück. | Enthält ein Image die Anweisung <code>VOLUME</code>, legt Docker beim Start ein Volume ohne Namen an. Solche Volumes tauchen in <code>docker volume ls</code> nur mit einer langen Kennung auf und bleiben nach dem Löschen des Containers zurück. | ||
| + | |||
| + | * <code>vim Dockerfile</code> | ||
| + | |||
| + | <syntaxhighlight lang="docker"> | ||
| + | FROM debian:13 | ||
| + | VOLUME /app/data | ||
| + | CMD ["bash"] | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | * <code>docker build -t volumetest .</code> | ||
| + | * <code>docker run -it --rm volumetest</code> | ||
| + | * <code>docker volume ls</code> | ||
| + | |||
| + | == Volume in mehreren Containern == | ||
| + | |||
| + | Ein Named Volume kann von mehreren Containern gleichzeitig eingebunden werden. | ||
| + | |||
| + | * <code>docker run -d --name schreiber -v gemeinsam:/app/data debian:13 sleep 3600</code> | ||
| + | * <code>docker run -d --name leser -v gemeinsam:/app/data debian:13 sleep 3600</code> | ||
| + | * <code>docker exec schreiber sh -c 'echo Testeintrag > /app/data/test.txt'</code> | ||
| + | * <code>docker exec leser cat /app/data/test.txt</code> | ||
| + | |||
| + | {| class="wikitable" | ||
| + | ! Angabe !! Bedeutung | ||
| + | |- | ||
| + | | <code>sleep 3600</code> || Hält den Container am Leben, damit er für <code>docker exec</code> zur Verfügung steht | ||
| + | |- | ||
| + | | <code>sh -c</code> || Das Kommando wird in einer Shell ausgeführt, damit die Umleitung <code>></code> im Container wirkt und nicht auf dem Host | ||
| + | |} | ||
| + | |||
| + | ;Hinweis | ||
| + | Docker regelt den gleichzeitigen Zugriff nicht. Schreiben mehrere Container in dieselben Dateien, muss die Anwendung selbst dafür sorgen, dass sich das verträgt. | ||
== Volumes verwalten == | == Volumes verwalten == | ||
| Zeile 717: | Zeile 815: | ||
* Ändern Sie die Datei auf dem Host und laden Sie die Seite neu, ohne den Container anzufassen. | * Ändern Sie die Datei auf dem Host und laden Sie die Seite neu, ohne den Container anzufassen. | ||
* Legen Sie ein Named Volume an, hängen Sie es unter <code>/usr/share/nginx/html</code> ein und prüfen Sie, was Docker beim ersten Start hineinkopiert hat. | * Legen Sie ein Named Volume an, hängen Sie es unter <code>/usr/share/nginx/html</code> ein und prüfen Sie, was Docker beim ersten Start hineinkopiert hat. | ||
| + | * Binden Sie ein Named Volume in zwei Container gleichzeitig ein, schreiben Sie aus dem einen eine Datei hinein und lesen Sie sie aus dem anderen. | ||
* Sichern Sie das Volume nach <code>/backup</code>, löschen Sie es und spielen Sie es zurück. | * Sichern Sie das Volume nach <code>/backup</code>, löschen Sie es und spielen Sie es zurück. | ||
| Zeile 1.062: | Zeile 1.161: | ||
* <code>docker compose up -d --build</code> | * <code>docker compose up -d --build</code> | ||
* <code>docker compose exec web bash</code> | * <code>docker compose exec web bash</code> | ||
| + | * <code>docker compose run --rm app bash</code> | ||
| + | * <code>docker compose up -d --scale app=3</code> | ||
* <code>docker compose config</code> | * <code>docker compose config</code> | ||
| Zeile 1.073: | Zeile 1.174: | ||
| <code>up -d --build</code> || Vor dem Start werden die selbst gebauten Images neu erzeugt | | <code>up -d --build</code> || Vor dem Start werden die selbst gebauten Images neu erzeugt | ||
|- | |- | ||
| − | | <code>exec web bash</code> || Shell in einem laufenden Dienst | + | | <code>exec web bash</code> || Shell in einem bereits laufenden Dienst |
| + | |- | ||
| + | | <code>run --rm app bash</code> || Startet einen zusätzlichen, einmaligen Container des Dienstes; <code>--rm</code> löscht ihn danach | ||
| + | |- | ||
| + | | <code>up -d --scale app=3</code> || Startet den Dienst in drei Instanzen | ||
|- | |- | ||
| <code>config</code> || Zeigt die zusammengesetzte und geprüfte Konfiguration; erste Anlaufstelle bei YAML-Fehlern | | <code>config</code> || Zeigt die zusammengesetzte und geprüfte Konfiguration; erste Anlaufstelle bei YAML-Fehlern | ||
|} | |} | ||
| + | |||
| + | ;Grenzen von scale | ||
| + | Ein Dienst mit einem festen Eintrag unter <code>ports</code> oder mit <code>container_name</code> lässt sich nicht vervielfältigen, weil Host-Port und Name nur einmal vergeben werden können. Sinnvoll ist <code>--scale</code> daher nur für Dienste hinter einem Reverse Proxy. | ||
== Healthcheck und depends_on == | == Healthcheck und depends_on == | ||
| Zeile 1.407: | Zeile 1.515: | ||
|- | |- | ||
| <code>docker restart name</code> || Container neu starten | | <code>docker restart name</code> || Container neu starten | ||
| + | |- | ||
| + | | <code>docker pause name</code> || Prozesse im Container anhalten | ||
| + | |- | ||
| + | | <code>docker unpause name</code> || Angehaltene Prozesse fortsetzen | ||
|- | |- | ||
| <code>docker rm name</code> || Container löschen | | <code>docker rm name</code> || Container löschen | ||
| Zeile 1.455: | Zeile 1.567: | ||
|- | |- | ||
| <code>docker compose exec dienst bash</code> || Shell in einem Dienst | | <code>docker compose exec dienst bash</code> || Shell in einem Dienst | ||
| + | |- | ||
| + | | <code>docker compose run --rm dienst bash</code> || Einmaligen Zusatzcontainer starten | ||
| + | |- | ||
| + | | <code>docker compose up -d --scale dienst=3</code> || Dienst in mehreren Instanzen starten | ||
|- | |- | ||
| <code>docker compose config</code> || Konfiguration prüfen | | <code>docker compose config</code> || Konfiguration prüfen | ||
Aktuelle Version vom 6. September 2026, 08:43 Uhr
Docker – Grundlagen, Images, Container und Compose
Einführung
Was ist ein Container
- Definition
Ein Container ist ein Prozess auf dem Host-System, der durch Kernel-Funktionen von den übrigen Prozessen abgeschottet wird. Er bringt sein eigenes Dateisystem, seinen eigenen Netzwerk-Stack und seinen eigenen Prozessbaum mit, benutzt aber den Kernel des Hosts.
- Was ein Container mitbringt
- die Anwendung selbst
- die Laufzeitumgebung (z.B. Python, Node.js, Java)
- die benötigten Bibliotheken und Werkzeuge
- die Konfigurationsdateien
- die Umgebungsvariablen
- Die zwei Kernel-Mechanismen dahinter
| Mechanismus | Aufgabe |
|---|---|
| Namespaces | Trennt Sichtbarkeit: eigener Prozessbaum, eigenes Netzwerk, eigene Mountpoints, eigene Benutzer-IDs |
| cgroups | Begrenzt Verbrauch: CPU, Arbeitsspeicher, Block-I/O |
Die Ausgangslage ohne Container
- "Auf meinem Rechner läuft es"
Eine Anwendung wird auf dem Arbeitsplatz entwickelt und getestet, auf dem Server läuft sie nicht. Ursache sind Unterschiede in der Betriebssystemversion, in den Versionen der installierten Software, in den Bibliotheken und in der Konfiguration.
- Abhängigkeitskonflikte
Zwei Anwendungen auf demselben Server brauchen dieselbe Bibliothek in unterschiedlichen Versionen. Installiert werden kann nur eine davon.
- Ressourcenverbrauch
Der klassische Ausweg war eine virtuelle Maschine je Anwendung. Jede davon bringt ein vollständiges Betriebssystem mit und belegt Arbeitsspeicher, Plattenplatz und CPU-Zeit, obwohl nur ein einzelner Dienst darin läuft.
- Trennung von Diensten
Auf einem gemeinsam genutzten Server beeinflussen sich Dienste gegenseitig. Ein Update für den einen kann den anderen stillegen.
Container und virtuelle Maschine
- Virtuelle Maschine
Ein Hypervisor stellt virtuelle Hardware bereit. Darauf läuft ein vollständiges Betriebssystem mit eigenem Kernel.
- Container
Die Container-Laufzeit startet einen isolierten Prozess auf dem vorhandenen Kernel. Es wird kein zweites Betriebssystem gebootet.
| Virtuelle Maschine | Container | |
|---|---|---|
| Kernel | eigener Kernel je VM | gemeinsamer Kernel des Hosts |
| Startzeit | Sekunden bis Minuten | Millisekunden |
| Größe | mehrere Gigabyte | wenige Megabyte bis wenige hundert Megabyte |
| Isolation | durch Virtualisierung, sehr stark | durch Kernel-Funktionen, schwächer |
| Fremde Kernel | möglich (Windows auf Linux-Host) | nicht möglich |
- Aufbau im Vergleich
Virtuelle Maschine Container +---------------------+ +---------------------+ | Anwendung | | Anwendung | +---------------------+ +---------------------+ | Gast-OS | | Container-Laufzeit | +---------------------+ +---------------------+ | Hypervisor | | | +---------------------+ | Host-OS | | Host-OS | | | +---------------------+ +---------------------+ | Hardware | | Hardware | +---------------------+ +---------------------+
- Konsequenz für die Praxis
Auf einem Linux-Host laufen Container mit Debian, Ubuntu, Alpine oder Rocky Linux nebeneinander, weil nur die Userland-Dateien unterschiedlich sind. Ein Windows-Container läuft auf einem Linux-Host nicht.
Aufbau von Docker
| Komponente | Aufgabe |
|---|---|
Docker-Client (docker) |
Kommandozeilenwerkzeug, nimmt die Befehle entgegen und schickt sie an den Daemon |
Docker-Daemon (dockerd) |
Dienst auf dem Host, verwaltet Images, Container, Netzwerke und Volumes |
| containerd / runc | Startet und überwacht die eigentlichen Container-Prozesse |
| Registry | Server, auf dem Images liegen (Docker Hub oder eine eigene Registry) |
- Zusammenspiel
Benutzer --> docker --> dockerd --> containerd --> Container
|
v
Registry
- Kommunikationsweg
Der Client spricht den Daemon über den Unix-Socket /var/run/docker.sock an. Wer auf diesen Socket schreiben darf, hat vollen Zugriff auf den Host.
Begriffe
| Begriff | Bedeutung |
|---|---|
| Image | Unveränderliche Vorlage, aus der Container erzeugt werden |
| Container | Laufende Instanz eines Images |
| Dockerfile | Textdatei mit der Bauanleitung für ein Image |
| Registry | Ablage für Images |
| Volume | Von Docker verwalteter Speicherbereich für dauerhafte Daten |
| Compose | Werkzeug, um mehrere Container aus einer YAML-Datei gemeinsam zu betreiben |
Übung
- Zeichnen Sie auf dem Blatt zwei Stapel nebeneinander: einmal Hardware → Host-OS → Hypervisor → Gast-OS → Anwendung, einmal Hardware → Host-OS → Container-Laufzeit → Anwendung.
- Notieren Sie zu jeder der folgenden Anforderungen, ob Sie eine VM oder einen Container wählen würden, und warum: eigener Kernel mit eigenem Modul, 30 gleichartige Webdienste auf einem Host, Windows-Server-Dienst auf einem Debian-Host, Testumgebung die in Sekunden neu aufgebaut werden muss.
Installation unter Debian
Ausgangslage
Alle Übungen laufen auf einer Debian-VM in VirtualBox. Die Pakete aus dem Debian-Repository (docker.io) hinken der Entwicklung hinterher und enthalten das Compose-Plugin nicht. Deshalb wird das offizielle Docker-Repository eingebunden.
Paketquelle einbinden
- Grundpakete installieren
sudo apt updatesudo apt install -y ca-certificates curl
| Angabe | Bedeutung |
|---|---|
-y |
Rückfragen werden automatisch mit Ja beantwortet |
ca-certificates |
Wurzelzertifikate, damit HTTPS-Downloads geprüft werden können |
curl |
Werkzeug zum Herunterladen des Signaturschlüssels |
- Verzeichnis für den Signaturschlüssel anlegen
sudo install -m 0755 -d /etc/apt/keyrings
| Angabe | Bedeutung |
|---|---|
-m 0755 |
Rechte des angelegten Verzeichnisses |
-d |
Es wird ein Verzeichnis statt einer Datei angelegt |
- Signaturschlüssel holen
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.ascsudo chmod a+r /etc/apt/keyrings/docker.asc
| Angabe | Bedeutung |
|---|---|
-f |
Bei HTTP-Fehlern keine Fehlerseite speichern, sondern abbrechen |
-s |
Keine Fortschrittsanzeige |
-S |
Fehlermeldungen trotz -s anzeigen
|
-L |
Weiterleitungen folgen |
-o |
Zieldatei |
a+r |
Leserecht für alle, damit apt den Schlüssel lesen kann |
- Hinweis
apt-key gibt es nicht mehr. Schlüssel liegen heute als Datei unter /etc/apt/keyrings und werden in der Paketquelle über signed-by genau dieser einen Quelle zugeordnet.
- Paketquelle eintragen
vim /etc/apt/sources.list.d/docker.list
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable
| Angabe | Bedeutung |
|---|---|
arch=amd64 |
Nur die 64-Bit-Intel/AMD-Architektur wird geladen |
signed-by=… |
Nur mit diesem Schlüssel signierte Pakete werden aus dieser Quelle akzeptiert |
trixie |
Codename der Debian-Version; bei einer anderen Debian-Version hier anpassen |
stable |
Der stabile Zweig des Docker-Repositories |
Docker installieren
sudo apt updatesudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
| Paket | Inhalt |
|---|---|
docker-ce |
Der Daemon |
docker-ce-cli |
Das Kommando docker
|
containerd.io |
Die darunterliegende Container-Laufzeit |
docker-buildx-plugin |
Der aktuelle Build-Unterbau (BuildKit) |
docker-compose-plugin |
Das Unterkommando docker compose
|
Installation prüfen
- Version des Clients und des Daemons
docker version
- Dienst-Status
systemctl status docker
- Systemweite Informationen
docker info
- Funktionstest
sudo docker run --rm hello-world
| Angabe | Bedeutung |
|---|---|
--rm |
Der Container wird nach dem Ende automatisch gelöscht |
Docker ohne sudo benutzen
sudo usermod -aG docker $USERnewgrp docker
| Angabe | Bedeutung |
|---|---|
-a |
Gruppe hinzufügen, bestehende Gruppen bleiben erhalten |
-G |
Betrifft die Nebengruppen |
newgrp docker |
Die neue Gruppe gilt sofort in dieser Sitzung, ohne Neuanmeldung |
- Sicherheitshinweis
Mitglieder der Gruppe docker dürfen den Docker-Socket ansprechen und darüber Container mit vollen Hostrechten starten. Diese Gruppenmitgliedschaft ist gleichwertig mit Root-Rechten auf dem Host.
Aufbau der Kommandozeile
- Grundmuster
docker [Objekt] [Aktion] [Optionen] [Argumente]
- Beispiel
docker container ls -a
- Kurzformen
Häufige Kommandos gibt es zusätzlich ohne Objekt: docker ps statt docker container ls, docker images statt docker image ls.
Übung
- Installieren Sie Docker auf Ihrer Debian-VM über das offizielle Repository.
- Prüfen Sie mit
docker version, dass Client und Server dieselbe Version melden. - Nehmen Sie Ihren Benutzer in die Gruppe
dockerauf und starten Siedocker run --rm hello-worldohnesudo. - Lassen Sie sich mit
docker infoanzeigen, welcher Storage-Treiber verwendet wird.
Images
Was ist ein Image
Ein Image ist eine unveränderliche Vorlage. Es enthält ein fertiges Dateisystem und die Angabe, welches Kommando beim Start ausgeführt wird. Aus einem Image lassen sich beliebig viele Container erzeugen.
Beim Start eines Containers legt Docker über die schreibgeschützten Image-Schichten eine dünne beschreibbare Schicht. Alle Änderungen im laufenden Container landen dort und sind mit dem Container weg.
Layer
Ein Image besteht aus übereinanderliegenden Schichten. Jede Anweisung im Dockerfile erzeugt eine Schicht.
| Schicht | Inhalt |
|---|---|
| 1 | Debian-Basis |
| 2 | Systempakete nachinstalliert |
| 3 | Python-Abhängigkeiten installiert |
| 4 | Anwendungscode kopiert |
- Vorteile
- Beim Neubau werden nur die Schichten ab der geänderten Anweisung neu erzeugt, der Rest kommt aus dem Cache
- Mehrere Images teilen sich gemeinsame Basisschichten
- Jede Schicht liegt nur einmal auf der Platte
Overlay-Dateisystem
Docker legt die Schichten mit overlay2 übereinander. Sichtbar ist immer die oberste Fassung einer Datei. Wird eine Datei aus einer unteren Schicht geändert, kopiert der Kernel sie zuerst in die beschreibbare Schicht (Copy-on-Write) und ändert erst dann die Kopie.
Registries und Namensformat
- Vollständiger Name eines Images
[Registry/][Namensraum/]Repository[:Tag]
| Angabe | Beispiel | Bedeutung |
|---|---|---|
| Registry | docker.io |
Server; fehlt die Angabe, wird Docker Hub benutzt |
| Namensraum | library |
Konto oder Organisation; library steht für die offiziellen Images
|
| Repository | debian |
Name des Images |
| Tag | 13 |
Version; fehlt die Angabe, wird latest benutzt
|
- Gleichbedeutend
debian:13docker.io/library/debian:13
- Hinweis zu latest
latest ist kein Versionshinweis, sondern nur ein Tag-Name wie jeder andere. In Kursumgebungen und im Betrieb immer eine feste Version angeben.
Images herunterladen
docker pull debian:13docker pull caddy:2
Images auflisten
docker images
- Ausgabe
REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.29 3b4a1c2f9e70 2 weeks ago 192MB debian 13 7c9e5d1a4b83 3 weeks ago 117MB
| Spalte | Bedeutung |
|---|---|
| REPOSITORY | Name des Images |
| TAG | Version |
| IMAGE ID | Eindeutige Kennung |
| CREATED | Zeitpunkt, an dem das Image gebaut wurde |
| SIZE | Größe |
- Ausgabe auf bestimmte Spalten begrenzen
docker images --format "table Vorlage:.Repository\tVorlage:.Tag\tVorlage:.Size"
| Angabe | Bedeutung |
|---|---|
--format |
Eigene Ausgabevorlage; die Feldnamen in geschweiften Klammern werden ersetzt |
Images untersuchen
- Alle Metadaten als JSON
docker inspect debian:13
- Nur einzelne Felder
docker inspect --format 'Vorlage:.Os Vorlage:.Architecture' debian:13
- Bauschritte eines Images anzeigen
docker history caddy:2
Images löschen
- Ein bestimmtes Image
docker rmi debian:13
- Alle Images, die von keinem Container benutzt werden
docker image prune -a
| Angabe | Bedeutung |
|---|---|
-a |
Nicht nur namenlose Zwischenschichten, sondern alle unbenutzten Images |
Übung
- Laden Sie
debian:13undalpine:3herunter und vergleichen Sie die Größen. - Lassen Sie sich mit
docker historydie Schichten voncaddy:2anzeigen und bestimmen Sie die größte Schicht. - Geben Sie mit
docker inspect --formatnur das Kommando aus, das beim Start des Caddy-Images ausgeführt wird. - Löschen Sie
alpine:3wieder.
Container
Lifecycle
| Zustand | Bedeutung |
|---|---|
| created | Container angelegt, aber nicht gestartet |
| running | Prozess läuft |
| paused | Prozesse angehalten, Speicher bleibt belegt |
| exited | Prozess beendet, Container samt Daten noch vorhanden |
| removed | Container gelöscht |
- Wichtig
Ein Container lebt genau so lange wie sein Hauptprozess. Endet der Prozess, endet der Container.
Ersten Container starten
docker run --name web -d -p 8080:80 nginx:1.29
| Angabe | Bedeutung |
|---|---|
--name web |
Fester Name; ohne diese Angabe vergibt Docker einen Zufallsnamen |
-d |
Betrieb im Hintergrund, die Konsole bleibt frei |
-p 8080:80 |
Port 8080 des Hosts wird auf Port 80 im Container geleitet |
nginx:1.29 |
Zu verwendendes Image |
Interaktiver Betrieb
docker run -it --rm debian:13 bash
| Angabe | Bedeutung |
|---|---|
-i |
Standardeingabe bleibt offen |
-t |
Ein Terminal wird zugeteilt |
--rm |
Container wird nach dem Beenden gelöscht |
bash |
Ersetzt das im Image hinterlegte Startkommando |
Container auflisten
- Laufende Container
docker ps
- Ausgabe
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES 9f31c07ab2d4 nginx:1.29 "/docker-entrypoint.…" Up 4 minutes 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp web
- Alle Container, auch beendete
docker ps -a
- Nur die Kennungen
docker ps -q
| Angabe | Bedeutung |
|---|---|
-a |
Auch Container in anderen Zuständen als running |
-q |
Nur die Container-IDs, geeignet zur Weiterverarbeitung |
Starten, Stoppen, Neustarten
docker stop webdocker start webdocker restart web
- Ablauf beim Stoppen
Docker schickt SIGTERM, wartet standardmäßig zehn Sekunden und schickt danach SIGKILL.
- Wartezeit ändern
docker stop -t 30 web
| Angabe | Bedeutung |
|---|---|
-t 30 |
Wartezeit in Sekunden bis zum harten Abbruch |
- Prozesse anhalten und fortsetzen
docker pause webdocker unpause web
Beim Anhalten werden die Prozesse im Container eingefroren, der belegte Arbeitsspeicher bleibt bestehen. Im Unterschied zu docker stop läuft der Container danach an derselben Stelle weiter.
Restart-Policy
docker run -d --name web --restart unless-stopped -p 8080:80 nginx:1.29
| Wert | Verhalten |
|---|---|
no |
Kein automatischer Neustart (Vorgabe) |
on-failure |
Neustart nur bei Exit-Code ungleich 0 |
always |
Immer, auch nach einem docker stop mit anschließendem Neustart des Daemons
|
unless-stopped |
Wie always, aber ein manuell gestoppter Container bleibt gestoppt
|
Container löschen
- Beendeten Container löschen
docker rm web
- Laufenden Container löschen
docker rm -f web
- Alle beendeten Container löschen
docker container prune
| Angabe | Bedeutung |
|---|---|
-f |
Container wird zuerst hart beendet und dann gelöscht |
Logs
docker logs webdocker logs -f webdocker logs --tail 20 webdocker logs -t web
| Angabe | Bedeutung |
|---|---|
-f |
Ausgabe läuft weiter mit |
--tail 20 |
Nur die letzten 20 Zeilen |
-t |
Mit Zeitstempel |
- Woher die Logs kommen
Docker sammelt das, was der Hauptprozess auf die Standardausgabe und die Standardfehlerausgabe schreibt. Was ein Dienst in eine Datei im Container schreibt, taucht hier nicht auf.
Kommandos im laufenden Container
docker exec web ls -l /usr/share/nginx/htmldocker exec -it web bash
| Angabe | Bedeutung |
|---|---|
-i |
Standardeingabe offen halten |
-t |
Terminal zuteilen |
- Hinweis
Änderungen über docker exec liegen nur in der beschreibbaren Schicht dieses Containers und sind nach dem Löschen weg. Für dauerhafte Änderungen gehört die Anpassung ins Dockerfile.
Ressourcen begrenzen
docker run -d --name web --memory 256m --cpus 0.5 nginx:1.29
| Angabe | Bedeutung |
|---|---|
--memory 256m |
Obergrenze für den Arbeitsspeicher |
--cpus 0.5 |
Anteil an einem CPU-Kern |
- Laufende Auslastung anzeigen
docker stats
Übung
- Starten Sie einen Nginx-Container im Hintergrund mit dem Namen
webund Portweiterleitung 8080 auf 80. - Rufen Sie die Seite mit
curl http://localhost:8080ab und sehen Sie sich anschließend die Logs an. - Öffnen Sie mit
docker execeine Shell im Container und ersetzen Sie die Datei/usr/share/nginx/html/index.htmldurch einen eigenen Text. - Löschen Sie den Container, starten Sie ihn neu und prüfen Sie, was aus Ihrer Änderung geworden ist.
- Starten Sie den Container erneut mit
--restart unless-stopped, starten Sie die VM neu und prüfen Sie, ob der Container wieder läuft.
Ports veröffentlichen
Netzwerk-Namespace
Jeder Container bekommt einen eigenen Netzwerk-Namespace mit eigener IP-Adresse. Ein Dienst, der im Container auf Port 80 lauscht, ist damit zunächst nur innerhalb des Containers erreichbar.
Host-Port und Container-Port
| Host-Port | Richtung | Container-Port |
|---|---|---|
| 8080 | → | 80 |
Alles, was auf dem Host an Port 8080 ankommt, wird an Port 80 im Container weitergereicht.
Client ---> Host:8080 ---> Container:80 ---> Dienst
Port veröffentlichen
docker run -d -p 8080:80 nginx:1.29
- Aufbau der Angabe
-p Host-Port:Container-Port
Mehrere Ports
docker run -d -p 8080:80 -p 8443:443 nginx:1.29
Bindung an eine bestimmte Adresse
docker run -d -p 127.0.0.1:8080:80 nginx:1.29
- Wirkung
Der Dienst ist nur noch vom Host selbst erreichbar. Ohne Adressangabe bindet Docker an alle Adressen des Hosts.
- Wichtig für die Firewall
Veröffentlichte Ports werden von Docker über eigene nftables-Regeln freigeschaltet. Eine Regel in der INPUT-Kette des Hosts greift dabei nicht. Wer den Zugriff einschränken will, bindet an eine feste Adresse wie oben oder arbeitet mit der Kette DOCKER-USER.
Zufällige Ports
docker run -d -P nginx:1.29docker ps
| Angabe | Bedeutung |
|---|---|
-P |
Alle im Image mit EXPOSE angegebenen Ports werden auf freie hohe Host-Ports gelegt |
Die tatsächlich vergebenen Ports stehen in der Spalte PORTS der Ausgabe von docker ps.
PORTS 0.0.0.0:32768->80/tcp
Übung
- Starten Sie zwei Nginx-Container gleichzeitig, einen auf Host-Port 8080, einen auf 8081.
- Versuchen Sie, einen dritten Container ebenfalls auf 8080 zu starten, und lesen Sie die Fehlermeldung.
- Starten Sie einen Container mit
-p 127.0.0.1:8082:80und prüfen Sie von einem zweiten Rechner aus, ob er erreichbar ist.
Daten dauerhaft speichern
Das Problem
Die beschreibbare Schicht gehört zum Container. Wird der Container gelöscht, sind die Daten weg. Für Datenbanken, Konfigurationen und Nutzdaten braucht es Speicher außerhalb des Containers.
Speicherarten
| Art | Ablage | Einsatz |
|---|---|---|
| Bind Mount | Beliebiges Verzeichnis des Hosts | Konfigurationsdateien, Zertifikate, Entwicklung |
| Named Volume | Von Docker verwaltet unter /var/lib/docker/volumes |
Nutzdaten von Diensten und Datenbanken |
| Anonymes Volume | Wie Named Volume, aber ohne Namen | Entsteht automatisch, ungeeignet für geplante Datenhaltung |
Bind Mount
docker run -d -p 8080:80 -v /srv/web:/usr/share/nginx/html nginx:1.29
- Aufbau der Angabe
-v Host-Verzeichnis:Container-Verzeichnis
| Eigenschaft | Bewertung |
|---|---|
| Direkter Zugriff vom Host | Vorteil bei Konfiguration und Entwicklung |
| Abhängig von der Verzeichnisstruktur des Hosts | Nachteil bei Umzug auf ein anderes System |
| Rechte richten sich nach den Host-Rechten | Häufige Fehlerquelle bei "permission denied" |
Named Volume
- Volume anlegen
docker volume create webdata
- Volume verwenden
docker run -d -v webdata:/usr/share/nginx/html nginx:1.29
- Aufbau der Angabe
-v Volumename:Container-Verzeichnis
- Verhalten beim ersten Start
Ist das Volume leer, kopiert Docker den Inhalt des Container-Verzeichnisses aus dem Image hinein. Bei einem Bind Mount passiert das nicht, dort überdeckt das Host-Verzeichnis den Inhalt.
Anonymes Volume
Enthält ein Image die Anweisung VOLUME, legt Docker beim Start ein Volume ohne Namen an. Solche Volumes tauchen in docker volume ls nur mit einer langen Kennung auf und bleiben nach dem Löschen des Containers zurück.
vim Dockerfile
FROM debian:13
VOLUME /app/data
CMD ["bash"]
docker build -t volumetest .docker run -it --rm volumetestdocker volume ls
Volume in mehreren Containern
Ein Named Volume kann von mehreren Containern gleichzeitig eingebunden werden.
docker run -d --name schreiber -v gemeinsam:/app/data debian:13 sleep 3600docker run -d --name leser -v gemeinsam:/app/data debian:13 sleep 3600docker exec schreiber sh -c 'echo Testeintrag > /app/data/test.txt'docker exec leser cat /app/data/test.txt
| Angabe | Bedeutung |
|---|---|
sleep 3600 |
Hält den Container am Leben, damit er für docker exec zur Verfügung steht
|
sh -c |
Das Kommando wird in einer Shell ausgeführt, damit die Umleitung > im Container wirkt und nicht auf dem Host
|
- Hinweis
Docker regelt den gleichzeitigen Zugriff nicht. Schreiben mehrere Container in dieselben Dateien, muss die Anwendung selbst dafür sorgen, dass sich das verträgt.
Volumes verwalten
docker volume lsdocker volume inspect webdatadocker volume rm webdatadocker volume prune
| Kommando | Bedeutung |
|---|---|
ls |
Alle Volumes anzeigen |
inspect |
Ablageort und Optionen anzeigen |
rm |
Einzelnes Volume löschen |
prune |
Alle Volumes löschen, die kein Container benutzt |
Daten sichern und zurückspielen
- Sichern
docker run --rm -v webdata:/source -v /backup:/backup debian:13 tar czf /backup/webdata.tar.gz -C /source .
- Zurückspielen
docker run --rm -v webdata:/target -v /backup:/backup debian:13 tar xzf /backup/webdata.tar.gz -C /target
| Angabe | Bedeutung |
|---|---|
--rm |
Der Hilfscontainer wird nach der Sicherung gelöscht |
-v webdata:/source |
Das zu sichernde Volume wird eingehängt |
-v /backup:/backup |
Das Zielverzeichnis des Hosts wird eingehängt |
-c |
Archiv erzeugen |
-x |
Archiv entpacken |
-z |
Mit gzip komprimieren |
-f |
Name der Archivdatei |
-C |
Vor dem Packen bzw. Entpacken in dieses Verzeichnis wechseln |
Nur-Lese-Einbindung
docker run -d -v /srv/web:/usr/share/nginx/html:ro nginx:1.29
| Angabe | Bedeutung |
|---|---|
:ro |
Der Container darf nur lesen |
Zertifikate, Schlüssel und Konfigurationsdateien werden grundsätzlich mit :ro eingebunden.
Übung
- Legen Sie auf dem Host
/srv/webmit einer eigenenindex.htmlan und binden Sie das Verzeichnis in einen Nginx-Container ein. - Ändern Sie die Datei auf dem Host und laden Sie die Seite neu, ohne den Container anzufassen.
- Legen Sie ein Named Volume an, hängen Sie es unter
/usr/share/nginx/htmlein und prüfen Sie, was Docker beim ersten Start hineinkopiert hat. - Binden Sie ein Named Volume in zwei Container gleichzeitig ein, schreiben Sie aus dem einen eine Datei hinein und lesen Sie sie aus dem anderen.
- Sichern Sie das Volume nach
/backup, löschen Sie es und spielen Sie es zurück.
Eigene Images bauen
Dockerfile
Ein Dockerfile ist eine Textdatei mit den Bauanweisungen. Jede Anweisung erzeugt eine Schicht.
FROM
- Legt das Basis-Image fest, muss die erste Anweisung sein
FROM python:3.13-slim
| Variante | Bedeutung |
|---|---|
python:3.13 |
Vollständige Debian-Basis, groß, viele Werkzeuge enthalten |
python:3.13-slim |
Abgespeckte Debian-Basis, deutlich kleiner |
python:3.13-alpine |
Alpine-Basis, am kleinsten, andere C-Bibliothek, gelegentlich Kompatibilitätsprobleme |
WORKDIR
- Setzt das Arbeitsverzeichnis für alle folgenden Anweisungen, legt es bei Bedarf an
WORKDIR /app
COPY
- Kopiert Dateien vom Baukontext in das Image
COPY requirements.txt .COPY app.py .
- Baukontext
Der Baukontext ist das Verzeichnis, das beim Bauen als letztes Argument angegeben wird. Nur Dateien aus diesem Verzeichnis können kopiert werden.
RUN
- Führt ein Kommando beim Bauen aus
RUN pip install --no-cache-dir -r requirements.txt
| Angabe | Bedeutung |
|---|---|
--no-cache-dir |
pip legt keinen Paketcache im Image ab, das Image bleibt kleiner |
- Mehrere Kommandos zusammenfassen
Jede RUN-Anweisung erzeugt eine Schicht. Zusammengehörige Schritte gehören in eine Anweisung, das Aufräumen in dieselbe Zeile.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
| Angabe | Bedeutung |
|---|---|
--no-install-recommends |
Empfohlene, aber nicht zwingende Pakete werden weggelassen |
rm -rf /var/lib/apt/lists/* |
Die Paketlisten werden entfernt; in einer eigenen Schicht würde das nichts bringen, weil die Daten in der darunterliegenden Schicht bleiben |
ENV
- Setzt eine Umgebungsvariable im Image und in jedem daraus erzeugten Container
ENV PYTHONUNBUFFERED=1
USER
- Legt fest, unter welchem Benutzer der Hauptprozess läuft
RUN useradd -r -u 1001 appUSER app
| Angabe | Bedeutung |
|---|---|
-r |
Systembenutzer ohne Heimatverzeichnis und ohne Anmeldung |
-u 1001 |
Feste UID, damit die Rechte auf Volumes vorhersagbar bleiben |
Ohne diese Anweisung läuft der Prozess im Container als root.
EXPOSE
- Dokumentiert, auf welchem Port der Dienst lauscht
EXPOSE 5000
EXPOSE veröffentlicht keinen Port. Es dient als Hinweis und wird von -P ausgewertet.
CMD und ENTRYPOINT
- CMD legt das Startkommando fest und ist beim Start überschreibbar
CMD ["python", "app.py"]
- ENTRYPOINT legt das Startkommando fest, CMD liefert dann nur noch die Argumente
ENTRYPOINT ["python", "app.py"]
| Schreibweise | Bedeutung |
|---|---|
CMD ["python", "app.py"] |
Exec-Form; der Prozess wird direkt gestartet und erhält die Signale von docker stop
|
CMD python app.py |
Shell-Form; es wird eine Shell dazwischengeschaltet, Signale kommen beim Prozess nicht sauber an |
Es gilt immer nur die letzte CMD-Anweisung im Dockerfile. Die Exec-Form ist die einzusetzende Schreibweise.
.dockerignore
- Schließt Dateien vom Baukontext aus
vim .dockerignore
.git
__pycache__
*.log
Ohne diese Datei wird das gesamte Verzeichnis an den Daemon übertragen, auch das Git-Verzeichnis.
Image bauen
docker build -t webapp:1.0 .
| Angabe | Bedeutung |
|---|---|
-t webapp:1.0 |
Name und Tag des entstehenden Images |
. |
Der Baukontext, hier das aktuelle Verzeichnis |
- Reihenfolge im Dockerfile
Selten geänderte Anweisungen gehören nach oben, häufig geänderte nach unten. Deshalb wird zuerst requirements.txt kopiert und installiert und erst danach der Anwendungscode. Eine Codeänderung macht dann nur die letzten Schichten ungültig.
Tags
docker tag webapp:1.0 webapp:latestdocker tag webapp:1.0 registry.it2XX.int/webapp:1.0docker push registry.it2XX.int/webapp:1.0
- Aufbau
docker tag Quelle Ziel
Ein Tag ist nur ein zusätzlicher Name für dasselbe Image, es wird nichts kopiert.
Mehrstufiger Bau
Bei mehrstufigen Builds wird in einer ersten Stufe gebaut und in einer zweiten nur das Ergebnis übernommen. Compiler und Entwicklungspakete landen damit nicht im fertigen Image.
FROM golang:1.24 AS build
WORKDIR /src
COPY . .
RUN go build -o /out/server .
FROM debian:13-slim
COPY --from=build /out/server /usr/local/bin/server
CMD ["server"]
| Angabe | Bedeutung |
|---|---|
AS build |
Name der ersten Stufe |
--from=build |
Die Datei wird aus der genannten Stufe übernommen, nicht aus dem Baukontext |
Beispiel: eigene Anwendung
- Projektverzeichnis anlegen
mkdir -p ~/webappcd ~/webapp
- Anwendung
vim app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hallo aus dem Container\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
- Abhängigkeiten
vim requirements.txt
flask==3.1.0
- Bauanleitung
vim Dockerfile
FROM python:3.13-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
RUN useradd -r -u 1001 app
USER app
EXPOSE 5000
CMD ["python", "app.py"]
- Bauen und starten
docker build -t webapp:1.0 .docker run -d --name webapp -p 5000:5000 webapp:1.0curl http://localhost:5000
Übung
- Bauen Sie das Image
webapp:1.0und starten Sie es. - Ändern Sie den Text in
app.py, bauen Sie erneut und beobachten Sie, welche Schichten aus dem Cache kommen. - Tauschen Sie im Dockerfile die Reihenfolge, kopieren Sie also
app.pyvor der Installation der Abhängigkeiten, bauen Sie erneut und vergleichen Sie die Bauzeit. - Prüfen Sie mit
docker exec webapp id, unter welchem Benutzer der Prozess läuft. - Legen Sie eine
.dockerignorean und vergleichen Sie die Größe des übertragenen Baukontexts in der Ausgabe vondocker build.
Docker Compose
Was ist Compose
Compose beschreibt mehrere Container samt Netzwerken und Volumes in einer YAML-Datei und betreibt sie gemeinsam.
- Aktueller Stand
Compose ist ein Unterkommando von Docker und wird als docker compose aufgerufen. Das alte, separat installierte Python-Werkzeug docker-compose mit Bindestrich wird nicht mehr gepflegt. In Anleitungen im Netz steht häufig noch die alte Schreibweise.
Aufbau der Datei
- Dateiname
Der aktuelle Standardname ist compose.yaml. docker-compose.yml wird aus Kompatibilitätsgründen weiterhin gelesen.
- Keine Versionsangabe
Die früher übliche erste Zeile version: '3.8' ist entfallen. Sie wird ignoriert und erzeugt beim Start eine Warnung.
vim compose.yaml
services:
web:
image: nginx:1.29
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
app:
build: ./app
environment:
APP_ENV: production
depends_on:
- db
restart: unless-stopped
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: geheim
volumes:
- dbdata:/var/lib/postgresql/data
restart: unless-stopped
volumes:
dbdata:
| Schlüssel | Bedeutung |
|---|---|
services |
Die einzelnen Container |
image |
Fertiges Image aus einer Registry |
build |
Verzeichnis mit einem Dockerfile, aus dem gebaut wird |
ports |
Portweiterleitungen wie bei -p
|
volumes |
Einbindungen wie bei -v
|
environment |
Umgebungsvariablen |
depends_on |
Startreihenfolge |
restart |
Neustartverhalten |
volumes (unten) |
Deklaration der benannten Volumes |
Netzwerk
Compose legt für jedes Projekt ein eigenes Netzwerk an. Alle Dienste des Projekts hängen darin und erreichen sich über den Dienstnamen als Hostnamen. Der Dienst app erreicht die Datenbank also über den Namen db, ohne dass ein Port veröffentlicht werden muss.
- Wichtig
Nur Dienste, die von außen erreichbar sein müssen, bekommen einen Eintrag unter ports. Alles andere bleibt im internen Netz.
Projekt starten und stoppen
docker compose up -ddocker compose psdocker compose stopdocker compose downdocker compose down -v
| Kommando | Bedeutung |
|---|---|
up -d |
Erzeugt und startet alle Dienste im Hintergrund |
ps |
Zustand der Dienste des Projekts |
stop |
Hält die Container an, löscht sie aber nicht |
down |
Hält an und entfernt Container und Netzwerk |
down -v |
Zusätzlich werden die Volumes des Projekts gelöscht |
- Projektname
Der Projektname ist standardmäßig der Name des Verzeichnisses. Alle erzeugten Container, Netzwerke und Volumes tragen ihn als Präfix.
Weitere Kommandos
docker compose logs -fdocker compose logs -f webdocker compose up -d --builddocker compose exec web bashdocker compose run --rm app bashdocker compose up -d --scale app=3docker compose config
| Kommando | Bedeutung |
|---|---|
logs -f |
Logs aller Dienste mitlaufen lassen |
logs -f web |
Logs eines einzelnen Dienstes |
up -d --build |
Vor dem Start werden die selbst gebauten Images neu erzeugt |
exec web bash |
Shell in einem bereits laufenden Dienst |
run --rm app bash |
Startet einen zusätzlichen, einmaligen Container des Dienstes; --rm löscht ihn danach
|
up -d --scale app=3 |
Startet den Dienst in drei Instanzen |
config |
Zeigt die zusammengesetzte und geprüfte Konfiguration; erste Anlaufstelle bei YAML-Fehlern |
- Grenzen von scale
Ein Dienst mit einem festen Eintrag unter ports oder mit container_name lässt sich nicht vervielfältigen, weil Host-Port und Name nur einmal vergeben werden können. Sinnvoll ist --scale daher nur für Dienste hinter einem Reverse Proxy.
Healthcheck und depends_on
depends_on wartet nur darauf, dass der Container gestartet ist, nicht darauf, dass der Dienst darin bereit ist. Mit einem Healthcheck lässt sich das ändern.
services:
app:
image: webapp:1.0
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: geheim
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
| Schlüssel | Bedeutung |
|---|---|
test |
Kommando, das den Zustand prüft; Rückgabewert 0 bedeutet gesund |
CMD-SHELL |
Das Kommando wird in einer Shell ausgeführt |
interval |
Abstand zwischen zwei Prüfungen |
timeout |
Zeit, nach der eine Prüfung als fehlgeschlagen gilt |
retries |
Zahl der Fehlversuche, bis der Dienst als ungesund gilt |
condition: service_healthy |
Der abhängige Dienst startet erst, wenn der Healthcheck gesund meldet |
Übung
- Legen Sie ein Verzeichnis
~/stackmit einercompose.yamlan, die Nginx und Ihrewebapp:1.0enthält. - Starten Sie den Stack und prüfen Sie mit
docker compose psunddocker network ls, was Compose angelegt hat. - Prüfen Sie aus dem Nginx-Container heraus mit
docker compose exec web curl http://app:5000, ob die Namensauflösung über den Dienstnamen funktioniert. - Fügen Sie bewusst einen YAML-Fehler ein und lassen Sie ihn sich mit
docker compose configanzeigen.
Abschlussprojekt: Uptime Kuma hinter Caddy
Ziel
Uptime Kuma wird über HTTPS unter kuma.it2XX.int erreichbar gemacht. Caddy nimmt die verschlüsselte Verbindung an und reicht sie intern unverschlüsselt an Uptime Kuma weiter. Beide Dienste liegen in getrennten Compose-Projekten und werden über ein gemeinsames Netzwerk verbunden.
| Bestandteil | Aufgabe |
|---|---|
| Caddy | Nimmt Port 80 und 443 an, terminiert TLS, leitet weiter |
| Uptime Kuma | Überwachungsdienst, lauscht intern auf Port 3001 |
Netzwerk proxy |
Verbindet die beiden Projekte |
| Zertifikat | /etc/ssl/own.crt und /etc/ssl/own.key auf dem Host
|
- Warum zwei getrennte Projekte
Der Reverse Proxy ist ein eigenständiger Dienst, der später weitere Anwendungen bedienen soll. Jede Anwendung bekommt ihr eigenes Projektverzeichnis und kann unabhängig neu gestartet werden, ohne den Proxy anzufassen.
Namensauflösung
Der Name kuma.it2XX.int muss auf die Adresse des Docker-Hosts zeigen.
vim /etc/hosts
10.20.XX.10 kuma.it2XX.int kuma
Gemeinsames Netzwerk anlegen
docker network create proxy
Das Netzwerk wird außerhalb der Projekte angelegt, damit beide Compose-Dateien es als bestehendes Netzwerk einbinden können.
Zertifikat prüfen
ls -l /etc/ssl/own.crt /etc/ssl/own.keyopenssl x509 -in /etc/ssl/own.crt -noout -subject -dates
| Angabe | Bedeutung |
|---|---|
-in |
Zu prüfende Zertifikatsdatei |
-noout |
Das Zertifikat selbst wird nicht ausgegeben |
-subject |
Zeigt den Namen, auf den das Zertifikat ausgestellt ist |
-dates |
Zeigt Beginn und Ende der Gültigkeit |
Der im Zertifikat eingetragene Name muss kuma.it2XX.int lauten, sonst lehnt der Browser die Verbindung ab.
Projekt Caddy
- Verzeichnis anlegen
mkdir -p ~/proxycd ~/proxy
- Konfiguration des Proxys
vim Caddyfile
kuma.it2XX.int {
tls /etc/ssl/own.crt /etc/ssl/own.key
reverse_proxy kuma:3001
}
| Zeile | Bedeutung |
|---|---|
kuma.it2XX.int { } |
Der Block gilt für Anfragen mit diesem Hostnamen |
tls /etc/ssl/own.crt /etc/ssl/own.key |
Es wird das angegebene Zertifikat benutzt; damit fordert Caddy kein Zertifikat über ACME an |
reverse_proxy kuma:3001 |
Weiterleitung an den Container kuma auf Port 3001
|
- Compose-Datei
vim compose.yaml
services:
caddy:
image: caddy:2
container_name: caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- /etc/ssl/own.crt:/etc/ssl/own.crt:ro
- /etc/ssl/own.key:/etc/ssl/own.key:ro
- caddy_data:/data
- caddy_config:/config
networks:
- proxy
restart: unless-stopped
volumes:
caddy_data:
caddy_config:
networks:
proxy:
external: true
| Eintrag | Bedeutung |
|---|---|
container_name: caddy |
Fester Containername statt des Namens aus Projekt und Dienst |
./Caddyfile:/etc/caddy/Caddyfile:ro |
Die Konfiguration wird nur lesend eingebunden |
/etc/ssl/own.crt:…:ro |
Zertifikat und Schlüssel werden unter demselben Pfad wie auf dem Host eingebunden, nur lesend |
caddy_data |
Dauerhafter Zustand von Caddy |
caddy_config |
Von Caddy erzeugte Konfigurationsdateien |
networks: proxy |
Der Dienst hängt im gemeinsamen Netz |
external: true |
Das Netz wird nicht von Compose angelegt, es muss vorhanden sein |
Projekt Uptime Kuma
- Verzeichnis anlegen
mkdir -p ~/kumacd ~/kuma
- Compose-Datei
vim compose.yaml
services:
kuma:
image: louislam/uptime-kuma:1
container_name: kuma
volumes:
- kuma_data:/app/data
networks:
- proxy
restart: unless-stopped
volumes:
kuma_data:
networks:
proxy:
external: true
| Eintrag | Bedeutung |
|---|---|
container_name: kuma |
Unter diesem Namen erreicht Caddy den Dienst |
kein ports-Eintrag |
Der Dienst ist nur im internen Netz erreichbar, nicht direkt vom Host |
kuma_data:/app/data |
Datenbank und Konfiguration von Uptime Kuma bleiben erhalten |
Starten und prüfen
- Proxy starten
cd ~/proxydocker compose up -d
- Anwendung starten
cd ~/kumadocker compose up -d
- Zustand prüfen
docker compose psdocker compose logs -f caddy
- Verbindung testen
curl -I --cacert /etc/ssl/own.crt https://kuma.it2XX.int
| Angabe | Bedeutung |
|---|---|
-I |
Nur die Kopfzeilen abrufen |
--cacert |
Das eigene Zertifikat wird zur Prüfung herangezogen |
Danach die Seite im Browser aufrufen und den Ersteinrichtungsdialog von Uptime Kuma durchlaufen.
Typische Fehler
| Meldung | Ursache |
|---|---|
network proxy declared as external, but could not be found |
Das Netzwerk wurde nicht mit docker network create proxy angelegt
|
dial tcp: lookup kuma on …: no such host |
Der Kuma-Container läuft nicht oder hängt nicht im Netz proxy
|
bind: address already in use |
Auf dem Host belegt bereits ein Dienst Port 80 oder 443 |
| Browser meldet unbekannten Aussteller | Das eigene Wurzelzertifikat ist auf dem Client nicht eingetragen |
| Zertifikatsname passt nicht | Im Zertifikat steht ein anderer Name als kuma.it2XX.int
|
Übung
- Bauen Sie beide Projekte wie beschrieben auf und rufen Sie Uptime Kuma über HTTPS auf.
- Legen Sie in Uptime Kuma eine Überwachung für
https://kuma.it2XX.intselbst an. - Halten Sie mit
docker compose stopim Kuma-Projekt den Dienst an und sehen Sie sich an, welche Meldung Caddy im Log ausgibt. - Löschen Sie den Kuma-Container mit
docker compose downund starten Sie ihn neu. Prüfen Sie, ob Ihre Überwachung noch vorhanden ist. - Erweitern Sie das Caddy-Projekt um einen zweiten Namen, der auf Ihre
webapp:1.0zeigt.
Empfehlungen für den Betrieb
- Images
- Feste Versionen statt
latestverwenden - Zusammengehörige Schritte in einer RUN-Anweisung ausführen und im selben Schritt aufräumen
- Selten geänderte Anweisungen nach oben, häufig geänderte nach unten
- Mehrstufigen Bau nutzen, damit keine Bauwerkzeuge im fertigen Image landen
.dockerignoreanlegen
- Container
- Ein Dienst je Container
- Prozesse nicht als root laufen lassen
- Dauerhafte Daten in Named Volumes, Konfiguration und Zertifikate als Bind Mount mit
:ro - Nur die Ports veröffentlichen, die wirklich von außen erreichbar sein müssen
restart: unless-stoppedfür Dienste, die einen Neustart überstehen sollen
- Sicherheit
- Offizielle oder selbst gebaute Images bevorzugen
- Images regelmäßig neu bauen, damit Sicherheitsupdates der Basis einfließen
- Zugehörigkeit zur Gruppe
dockernur an Personen vergeben, die Root-Rechte auf dem Host haben dürfen - Passwörter nicht ins Image, sondern über Umgebungsvariablen oder Dateien einbringen
Kommandoübersicht
Images
| Kommando | Bedeutung |
|---|---|
docker pull image:tag |
Image herunterladen |
docker images |
Lokale Images anzeigen |
docker inspect image |
Metadaten anzeigen |
docker history image |
Schichten anzeigen |
docker build -t name:tag . |
Image bauen |
docker tag quelle ziel |
Weiteren Namen vergeben |
docker push image:tag |
Image in eine Registry laden |
docker rmi image |
Image löschen |
docker image prune -a |
Unbenutzte Images löschen |
Container
| Kommando | Bedeutung |
|---|---|
docker run image |
Container erzeugen und starten |
docker ps |
Laufende Container anzeigen |
docker ps -a |
Alle Container anzeigen |
docker stop name |
Container anhalten |
docker start name |
Container starten |
docker restart name |
Container neu starten |
docker pause name |
Prozesse im Container anhalten |
docker unpause name |
Angehaltene Prozesse fortsetzen |
docker rm name |
Container löschen |
docker rm -f name |
Laufenden Container löschen |
docker logs -f name |
Logs mitlaufen lassen |
docker exec -it name bash |
Shell im Container öffnen |
docker stats |
Ressourcenverbrauch anzeigen |
docker container prune |
Beendete Container löschen |
Volumes und Netzwerke
| Kommando | Bedeutung |
|---|---|
docker volume create name |
Volume anlegen |
docker volume ls |
Volumes anzeigen |
docker volume inspect name |
Ablageort anzeigen |
docker volume rm name |
Volume löschen |
docker network create name |
Netzwerk anlegen |
docker network ls |
Netzwerke anzeigen |
docker network inspect name |
Angeschlossene Container anzeigen |
Compose
| Kommando | Bedeutung |
|---|---|
docker compose up -d |
Projekt im Hintergrund starten |
docker compose up -d --build |
Vor dem Start neu bauen |
docker compose ps |
Dienste des Projekts anzeigen |
docker compose logs -f dienst |
Logs eines Dienstes |
docker compose exec dienst bash |
Shell in einem Dienst |
docker compose run --rm dienst bash |
Einmaligen Zusatzcontainer starten |
docker compose up -d --scale dienst=3 |
Dienst in mehreren Instanzen starten |
docker compose config |
Konfiguration prüfen |
docker compose stop |
Dienste anhalten |
docker compose down |
Container und Netzwerk entfernen |
docker compose down -v |
Zusätzlich Volumes entfernen |
System
| Kommando | Bedeutung |
|---|---|
docker info |
Systemweite Informationen |
docker version |
Versionen von Client und Server |
docker system df |
Belegten Plattenplatz anzeigen |
docker system prune |
Unbenutzte Objekte löschen |