Start / Cheat Sheets

Cheat Sheet

Container, Images und Lieferkette — Cheat Sheet

Stand: · Docker, Podman und Kubernetes für Entwickler

DockerPodmanContainerSupply Chain

Der Spickzettel zur ersten Hälfte des Seminars Docker, Podman und Kubernetes für Entwickler: Wie ein Image entsteht, warum der Cache greift oder nicht, was rootless anders macht, wie ein Compose-Aufbau verlässlich hochfährt und woran eine Lieferkette nachweisbar wird. Alles, was danach im Cluster passiert, steht auf dem zweiten Blatt: Kubernetes im Betrieb.

Maßgeblich für Flags und Versionsdetails ist die Dokumentation der jeweiligen Werkzeuge; dieses Blatt trifft eine Auswahl — die Entscheidungsraster, die einen Versionssprung überleben.

Dockerfile-Anweisungen

AnweisungWirkung
FROMwählt das Basis-Image und eröffnet eine Bau-Etappe
WORKDIRsetzt das Arbeitsverzeichnis für alle folgenden Zeilen
COPYkopiert Dateien aus dem Build-Kontext ins Image
ADDwie COPY, entpackt zusätzlich Archive und lädt URLs
RUNführt einen Befehl beim Bauen aus und schreibt das Ergebnis fest
EXPOSEdokumentiert den Port, öffnet ihn aber nicht
USERlegt fest, unter welcher Kennung der Prozess später läuft
ENTRYPOINTbestimmt das Programm, das beim Start ausgeführt wird
CMDliefert die Vorgabe-Argumente, die überschrieben werden dürfen

ADD nur für Archive, sonst COPY — das ist die vorhersagbarere Wahl. ENTRYPOINT bleibt beim Start stehen, CMD wird von jedem Argument überschrieben; in der Klammer-Schreibweise startet der Prozess direkt und empfängt Stopp-Signale, ohne Klammern hängt eine Shell davor, die das Signal verschluckt.

Modul: Container- und Image-Grundlagen

Was den Cache ungültig macht

ÄnderungFolge für den Cache
Basis-Image zeigt auf eine neue Fassungalles wird neu gebaut
Text einer RUN-Zeile geändertab dieser Zeile neu
Datei im COPY-Ziel geändertab diesem COPY neu
Datei geändert, die kein COPY erfasstkein Einfluss
Reihenfolge zweier Anweisungen getauschtab der ersten Abweichung neu

Der Zeitstempel einer Datei zählt nicht, nur ihr Inhalt. Daraus folgt die Sortierregel: Abhängigkeiten nach vorn, Quellcode nach hinten.

# selten geändert - liegt vorn und bleibt im Cache
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# oft geändert - liegt hinten
COPY src ./src

Der Build-Kontext wandert vollständig zur Bau-Maschine, bevor die erste Anweisung läuft — eine .dockerignore ist deshalb kein Kosmetikschritt.

Modul: Container- und Image-Grundlagen

Basis-Images für die letzte Etappe

BasisEinordnung
Vollbildvollständige Distribution, bequem, groß
Schlankdieselbe Distribution ohne Zusatzpakete
Alpinesehr klein, andere Standardbibliothek, gelegentlich Überraschungen
Distrolessnur Laufzeit, keine Shell, schwer zu untersuchen
Scratchleer, nur für Programme ohne jede Abhängigkeit

Ohne Shell lässt sich nichts mehr im laufenden Container nachsehen — das ist Absicht und Preis zugleich. Der Multi-Stage-Schnitt liefert das Ergebnis der ersten Etappe in eine schlanke zweite:

FROM node:22 AS build
RUN npm ci && npm run build
FROM node:22-alpine
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]

Modul: Container- und Image-Grundlagen

Tag oder Digest

EigenschaftTagDigest
Lesbarjanein
Beweglichjanein
Eignung zum Veröffentlichengutunhandlich
Eignung im Deploymentriskantrichtig
Prüfbar beim Herunterladenneinja

Faustregel: mit Tags veröffentlichen, mit Digests deployen. Der Digest kommt aus dem Push und gehört in die Pipeline-Variable, nicht in eine Notiz:

docker inspect --format '{{index .RepoDigests 0}}' \
  registry.example/api:1.4.0

Der OCI-Standard regelt das in drei Spezifikationen: Image (Aufbau aus Schichten, Konfiguration, Manifest), Runtime (wie daraus ein Prozess wird) und Distribution (wie Registries Images annehmen und ausliefern). Deshalb sind Werkzeuge und Registries einzeln austauschbar.

Modul: Container- und Image-Grundlagen

BuildKit: Cache-Mounts und Architekturen

RUN --mount=type=cache,target=/root/.npm \
    npm ci --omit=dev
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.example/api:1.4.0 --push .

Der Zwischenspeicher liegt neben dem Image, nicht darin — er vergrößert das Ergebnis nicht. Das Ergebnis eines Mehr-Architektur-Laufs ist eine Manifest-Liste; beim Herunterladen wählt die Maschine ihre Architektur selbst. Geheimnisse gehören dabei nie in ein Bau-Argument, wo sie in der Bau-Historie sichtbar bleiben.

Modul: Container- und Image-Grundlagen

Docker und Podman im Vergleich

MerkmalDockerPodman
Ausführendes Bauteilzentraler Dienstder Aufruf selbst
Rechte im StandardfallAdministratoreigene Kennung
Container im ProzessbaumKind des DienstesKind der Sitzung
Neustart des Dienstestrifft alle Containerbetrifft nichts
Sichtbarkeit für Anderealle sehen allesje Kennung getrennt
Sitzungsdiensteüber den Dienstüber die Sitzung

Beim Rootless-Betrieb kehren sich die Voreinstellungen um:

FrageDockerPodman
Standardeinstellungrootfulrootless
Einrichtung nötigja, eigenes Skriptnein
Ablage der Imagesje Dienst getrenntim Heimverzeichnis
Wechsel im Nachhineinzweite AblageAufruf mit Vorsatz
Zusätzliche KennungenPflichtPflicht

Rootless und rootful teilen nie eine Ablage — dasselbe Image liegt danach zweimal. Die zusätzlichen Kennungen sind in beiden Fällen Pflicht:

sudo usermod --add-subuids 100000-165535 mkoester
sudo usermod --add-subgids 100000-165535 mkoester
grep mkoester /etc/subuid /etc/subgid

Ein Leerzeichen am Zeilenende dieser beiden Dateien lässt den Eintrag stillschweigend ausfallen.

Modul: Lokale Entwicklung mit Docker und Podman

Netzwerk im rootless Betrieb

Eigenschaftrootfulrootless mit pasta
Netzgerät auf dem Rechnerechte Brückekeines
Adressumschreibungjaim Standardfall nein
Absenderadressewird ersetztbleibt erhalten
Ports unter 1024ohne Weitereserst nach Freigabe
Durchsatz im Messfallrund 18 Gbit/srund 8 bis 10 Gbit/s

Die Durchsatzwerte stammen aus einer einzelnen Messreihe — im eigenen Aufbau nachmessen. Für Ports unter 1024 ist der ruhigere Weg, hoch zu veröffentlichen (-p 8080:3000) statt net.ipv4.ip_unprivileged_port_start systemweit zu senken. Die Wahl des Hilfsprogramms steht an genau einer Stelle:

# ~/.config/containers/containers.conf
[network]
default_rootless_network_cmd = "pasta"

Modul: Lokale Entwicklung mit Docker und Podman

Compose, Sockets und lokale Cluster

WegUmsetzungEinordnung
docker composeErweiterung von Dockervollständigster Sprachumfang
podman composeHülle um ein fremdes WerkzeugVorrang für docker-compose
podman-composeeigenständig, in Pythondeckt weniger Schlüssel ab

Der Podman-Socket ist die Brücke für alles, was eine Docker-Schnittstelle erwartet — Compose, Testcontainers, k3d:

systemctl --user enable --now podman.socket
export DOCKER_HOST=unix:///run/user/1000/podman/podman.sock
MerkmalDocker DesktopPodman Desktop
Lizenzfür größere Firmen kostenpflichtigApache 2.0
Engine darunterDockerPodman, auch andere
Lokale Clustereigener SchalterKind, Minikube und weitere
Nach Kubernetes sendenüber UmwegePod aus der Oberfläche

Beide Oberflächen ändern nichts an den Images — sie bedienen nur, was ohnehin da ist. Für den Cluster auf dem Arbeitsplatz stehen vier Wege zur Wahl:

WerkzeugUnterbauEinordnung
kindKubernetes in ContainernMehrknoten und Pipelines
minikubeein Knoten, viele Treiberreich an Zusatzmodulen
k3dk3s in Containernsehr schnell und schlank
Rancher Desktopk3s mit OberflächeUmstieg ohne Kommandozeile

Für Podman gilt bei kind und k3d ausdrücklich der Vermerk „in Erprobung”. Ein lokaler Cluster sieht außerdem andere Images als die eigene Engine.

Modul: Lokale Entwicklung mit Docker und Podman

Compose: die tragenden Schlüssel

SchlüsselWirkung
imagenimmt ein fertiges Image aus einer Registry
buildbaut aus einem Bauplan im Projekt, statt zu ziehen
environmentsetzt Umgebungswerte für den Prozess im Container
portsveröffentlicht einen Port auf dem eigenen Rechner
volumesbindet Ablagen und Verzeichnisse ein
depends_onlegt Reihenfolge und Bedingung des Starts fest
profileshält einen Dienst zurück, bis er ausdrücklich gewählt wird

image und build schließen sich nicht aus — zusammen sind sie Bauplan mit Veröffentlichungsziel. Der Dienstname ist der Rechnername im internen Netz; deshalb steht er in der Datenbankadresse und nicht ein Verzeichnisname.

Modul: Containerisierte Entwicklungs-Workflows

Volumes und Bind-Mounts

ArtHerkunftTypischer Einsatz
Benannte Ablagevon der Engine verwaltetDatenbankdateien
Verzeichnis vom Rechnerein Pfad im ProjektQuellcode beim Entwickeln
Namenlose Ablagebei Bedarf erzeugtselten gewollt, oft versehen
Arbeitsspeichernichts, flüchtigZwischendateien und Geheimnisse

Namenlose Ablagen entstehen, wenn nur ein Ziel angegeben wird — sie sammeln sich unbemerkt an. Abhängigkeiten gehören ins Image, nicht in ein eingebundenes Verzeichnis: ein Mount über node_modules verdeckt genau das, was im Image installiert wurde.

Modul: Containerisierte Entwicklungs-Workflows

Healthchecks und Startreihenfolge

BedingungBedeutung
service_startedder Startbefehl ist abgesetzt, mehr nicht
service_healthyder Healthcheck des Dienstes meldet Bereitschaft
service_completed_successfullyder Dienst ist fehlerfrei zu Ende gelaufen
restartder abhängige Dienst wird nach einem Ersetzen mit erneuert
requiredohne diesen Schalter bricht ein fehlender Dienst den Start ab
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U dienst"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s

Die Anlaufzeit (start_period) zählt Fehlversuche nicht als krank — dafür ist sie da. Ohne Healthcheck im Ziel-Dienst kann condition: service_healthy nie erfüllt werden. Ein Prüfbefehl, der nur auf einen offenen Port sieht, meldet oft Bereitschaft, die es nicht gibt.

Modul: Containerisierte Entwicklungs-Workflows

Testcontainers und Devcontainers

EinstellungZweck
DOCKER_HOSTAdresse des Sockets, auch für Podman
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDEPfad für Container, die selbst Container starten
TESTCONTAINERS_HOST_OVERRIDERechner, auf dem die Ports erscheinen
TESTCONTAINERS_RYUK_DISABLEDschaltet das Aufräum-Bauteil ab

Der veröffentlichte Port wird bei jedem Lauf zufällig gewählt — er darf nirgends fest stehen. Für rootless Podman nennt die Dokumentation das Abschalten des Aufräum-Bauteils ausdrücklich; dann muss die Pipeline selbst aufräumen.

Die Lebenszyklus-Befehle eines Devcontainers in ihrer Reihenfolge:

BefehlLäuft
initializeCommandauf dem eigenen Rechner, vor dem Container
onCreateCommandeinmal, direkt nach dem Erzeugen
updateContentCommanddanach, wenn Inhalte aufgefrischt werden
postCreateCommandzuletzt beim Erzeugen, für die eigentliche Einrichtung
postStartCommandbei jedem Start des Containers
postAttachCommandsobald sich ein Werkzeug verbindet

Nur der erste läuft außerhalb des Containers. Abhängigkeiten beim Erzeugen installieren, nicht beim Verbinden — sonst verlängert sich jeder Start.

Modul: Containerisierte Entwicklungs-Workflows

Bauen in der Pipeline

ZielEinordnung
type=ghaSpeicher der Pipeline, ohne eigene Einrichtung
type=registryeigenes Ziel in der Registry, auch über Projekte hinweg
type=inlineim Image selbst, nur die letzte Schichtenfolge
type=localVerzeichnis auf der Bau-Maschine, für eigene Läufer

Mit dem Zusatz mode=max wandern auch die Zwischenetappen in den Speicher, nicht nur das Ergebnis. Auf einem fremden Bau-Dienst stehen drei Wege zur Wahl:

WegEinordnung
Dienst im Diensteigener Dienst je Auftrag, verlangt weitgehende Rechte
Socket einbindenohne Sonderrechte, gibt aber den Rechner preis
Werkzeug ohne Dienstbaut ohne Hintergrunddienst, empfohlener Ausweg

Die Dokumentation nennt das Einbinden des Sockets ausdrücklich als Weg zur Rechteausweitung.

Modul: Containerisierte Entwicklungs-Workflows

Das Image härten

EinstellungWirkung
runAsNonRootverweigert den Start unter der Kennung des Verwalters
runAsUserlegt die Kennung ausdrücklich fest
allowPrivilegeEscalationverhindert Rechtezuwachs im laufenden Prozess
capabilities drop ALLnimmt alle Sonderrechte des Kerns weg
readOnlyRootFilesystemmacht das Dateisystem des Containers unveränderlich
seccompProfilebeschränkt die erlaubten Aufrufe an den Kern

Genau diese sechs prüft das strenge Profil der Pod Security Standards. Eine numerische Kennung (USER 10001) wirkt auch dann, wenn im Image gar keine Benutzerdatenbank liegt. Ein gehärtetes Image lässt sich unsicher betreiben — erst Bauplan und Manifest zusammen tragen.

Modul: Container-Sicherheit und Supply Chain

Scannen, Stückliste, Signatur

PrüferFindet
vulnbekannte Schwachstellen in Paketen und Abhängigkeiten
secretversehentlich mitgelieferte Zugangsdaten und Schlüssel
misconfigriskante Einstellungen in Bauplänen und Manifesten
licenseLizenzen der enthaltenen Bestandteile

Voreingestellt laufen die ersten zwei. Ohne Abbruchcode ist ein Scan Dekoration:

trivy image --severity HIGH,CRITICAL --ignore-unfixed \
  --exit-code 1 registry.example/api@$DIGEST
FormatEinordnung
SPDXaus der Lizenzarbeit, breit anerkannt, auch als ISO-Norm
CycloneDXaus der Sicherheitsarbeit, nah an Schwachstellendaten
beide zugleichüblich, weil Abnehmer unterschiedliche Formate verlangen

Stückliste und Signatur entstehen immer über den Digest, nie über den Tag. Eine belastbare Prüfung braucht vier Bestandteile:

BestandteilBedeutung
Digestbenennt genau die Fassung, die geprüft wird
Identitätwer signiert hat, als Muster oder exakter Wert
Ausgeberwelcher Identitätsdienst das bestätigt hat
Protokolleintragbelegt, dass die Signatur zum Zeitpunkt existierte
cosign verify "$IMAGE" \
  --certificate-identity-regexp 'https://github.com/beispiel/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Eine Prüfung ohne Identität und Ausgeber beantwortet nur, dass irgendwer signiert hat.

Modul: Container-Sicherheit und Supply Chain

SLSA-Stufen

StufeVerlangt
Build L0keine Zusage, kein Nachweis
Build L1Provenance existiert und beschreibt den Bauvorgang
Build L2die Bauplattform erzeugt und signiert sie selbst
Build L3gehärtete Bauläufe, die einander nicht beeinflussen können

Eine vierte Stufe gibt es in der aktuellen Fassung nicht mehr — sie stammt aus der Entwurfsfassung. Provenance und Stückliste sind zweierlei: die eine beschreibt den Weg, die andere den Inhalt.

Modul: Container-Sicherheit und Supply Chain

Pod Security Standards und Kyverno

BetriebsartFolge bei einem Verstoß
enforceder Pod wird abgelehnt und startet nicht
auditder Pod läuft, der Verstoß landet im Prüfprotokoll
warnder Pod läuft, der Einreichende bekommt eine Warnung

Erzwingen greift nur bei Pods; Deployments erreicht man über die beiden anderen Arten. Bestehende Pods bleiben unberührt — erst der nächste Start wird geprüft.

SchalterWirkung
imageReferencesauf welche Images die Regel überhaupt zielt
failureActionerzwingen oder nur berichten
mutateDigestersetzt den Tag beim Zulassen durch den Digest
verifyDigestverlangt, dass überhaupt ein Digest angegeben ist
requiredverlangt für jedes passende Image eine Signatur

Die drei letzten stehen voreingestellt auf wahr. Seit Kyverno 1.13 sitzt der Schalter für Erzwingen in der Regel, nicht mehr oben in der Spezifikation. Ohne attestors prüft die Regel nur, dass signiert wurde — nicht von wem.

Modul: Container-Sicherheit und Supply Chain

Typische Fallen

  • EXPOSE für eine Freigabe halten — der Port wird erst beim Start veröffentlicht.
  • Zugangsdaten per COPY oder Bau-Argument ins Image — sie bleiben in jeder Schicht und in der Bau-Historie nachlesbar.
  • Den beweglichen Standard-Tag in die Produktion lassen — Fassungen tauschen unbemerkt; im Deployment gehört der Digest hin.
  • Zusätzliche Kennungen vergessen — der rootless Start scheitert mit einer unverständlichen Meldung.
  • Standardports im Compose-Aufbau festschreiben — ein fester Port 80 stoppt den rootless Lauf sofort.
  • Eine feste Wartezeit ins Startskript legen — auf der Bau-Maschine ist sie dann doch zu kurz; dafür gibt es Healthchecks.
  • Die benannte Ablage beim Aufräumen mitentfernen — der Datenstand der Entwicklung ist weg.
  • Den Port der Testdatenbank festschreiben — Testcontainers vergibt ihn bei jedem Lauf neu.
  • Das Dateisystem unveränderlich machen ohne Pfad für Zwischendateien — Zwischenspeicher und Protokollpfade klemmen zuerst.
  • Nachträglich vom Tag eine Stückliste erzeugen — sie bezieht sich dann auf eine andere Fassung.
  • Eine Signatur für eine Aussage über den Inhalt halten — sie belegt die Herkunft, nicht die Sicherheit.
  • Sofort erzwingen statt erst berichtend prüfen — der halbe Namensraum verschwindet beim nächsten Neustart, im schlimmsten Fall samt Systemdiensten.

Zum Seminar Docker, Podman und Kubernetes für Entwickler