Kubernetes Compose App Uebersetzen
Dieser Artikel übersetzt eine bekannte Docker-Compose-Anwendung Schritt für Schritt nach Kubernetes. Als Beispiel dient eine einfache Web-Anwendung mit Datenbank – die gleiche Art von Setup, die in Compose aus einem web- und einem db-Service besteht.
Ausgangspunkt: die Compose-Datei
Als Referenz die Compose-Anwendung, die übersetzt wird:
services:
web:
image: nginx:1.25
ports:
- "8080:80"
environment:
- APP_MODE=production
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=geheim
- POSTGRES_DB=appdb
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Diese Datei wird in den folgenden Abschnitten Stück für Stück in Kubernetes-Objekte übersetzt.
Von Container zu Pod und Deployment
- Der Pod
- Die kleinste ausführbare Einheit in Kubernetes ist der Pod, kein einzelner Container. Ein Pod kann einen oder mehrere Container enthalten, die sich Netzwerk und Storage teilen. In den meisten Fällen – wie hier – enthält ein Pod genau einen Container.
- Warum nicht direkt Pods erstellen
- Ein einzeln erstellter Pod wird bei einem Absturz nicht automatisch neu gestartet. Dafür braucht es eine übergeordnete Instanz.
- Das Deployment
- Ein Deployment beschreibt den Zielzustand ("es sollen N Instanzen dieses Pods laufen") und sorgt dafür, dass er eingehalten wird – inklusive Neustart bei Absturz und kontrollierten Updates. Das Deployment entspricht am ehesten dem
web:-Service aus der Compose-Datei, nur mit eingebauter Selbstheilung.
Das web-Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
env:
- name: APP_MODE
value: "production"
- Anwenden
kubectl apply -f web-deployment.yaml
- Status prüfen
kubectl get deploymentskubectl get pods -o wide
Von ports zu Service
Das ports: "8080:80" aus Compose hat in Kubernetes kein direktes Gegenstück auf Container-Ebene. Stattdessen übernimmt ein eigenes Objekt die Netzwerk-Erreichbarkeit.
- Der Service
- Ein Service gibt einer Gruppe von Pods (ausgewählt über Labels) eine stabile, gleichbleibende Adresse. Das ist notwendig, weil Pods jederzeit neu erstellt werden und dabei eine neue IP-Adresse bekommen – ein Service bleibt davon unberührt erreichbar.
- ClusterIP als Standard
- Ohne weitere Angabe ist ein Service nur innerhalb des Clusters erreichbar. Das entspricht noch nicht dem externen Zugriff aus der Compose-Datei, ist aber der erste, notwendige Schritt.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
- Anwenden
kubectl apply -f web-service.yaml
- Erreichbarkeit von innerhalb des Clusters testen
kubectl run test-curl --rm -it --image=curlimages/curl -- curl -s http://web
- Erreichbarkeit von außen testen (ohne Ingress)
- Solange noch kein Ingress existiert, kann mit Port-Forwarding von außen zugegriffen werden – vergleichbar mit dem
8080:80aus Compose, nur manuell für die Dauer der Sitzung. kubectl port-forward service/web 8080:80- Anschließend ist die Anwendung unter
http://localhost:8080erreichbar, solange der Befehl im Vordergrund läuft.
Von environment zu ConfigMap und Secret
In der Compose-Datei stehen APP_MODE=production (unkritisch) und POSTGRES_PASSWORD=geheim (sensibel) beide gleich als environment:. Kubernetes trennt das bewusst.
- ConfigMap für unkritische Werte
- Für Konfiguration ohne Geheimhaltungsbedarf.
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_MODE: "production"
- Secret für sensible Werte
- Inhalt wird base64-kodiert gespeichert – das ist keine Verschlüsselung, nur Verschleierung. Für echte Vertraulichkeit braucht ein Cluster zusätzliche Maßnahmen (Encryption at Rest, externe Secret-Stores), was über diesen Kurs hinausgeht.
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
POSTGRES_PASSWORD: geheim
- Beide anwenden
kubectl apply -f web-config.yamlkubectl apply -f db-secret.yaml
- Im Deployment referenzieren
- Der
env:-Abschnitt im Deployment wird angepasst, um aus ConfigMap und Secret zu lesen statt den Wert direkt einzutragen.
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: web-config
key: APP_MODE
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: POSTGRES_PASSWORD
Das db-Deployment und der Datenbank-Service
Die Datenbank wird nach demselben Muster übersetzt wie web.
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
spec:
replicas: 1
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
spec:
containers:
- name: db
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: "appdb"
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: POSTGRES_PASSWORD
---
apiVersion: v1
kind: Service
metadata:
name: db
spec:
selector:
app: db
ports:
- port: 5432
targetPort: 5432
- Wichtig zum Namen des Service
- Der Service heißt
db. Innerhalb des Clusters ist die Datenbank damit unter genau diesem Namen erreichbar – das entspricht exakt dem Verhalten vondepends_on: dbin Compose, wo der Service-Name ebenfalls als Hostname funktioniert. Derweb-Container kann sich also mitdb:5432verbinden, ohne dass eine IP-Adresse bekannt sein muss.
- Anwenden
kubectl apply -f db-deployment.yaml
depends_on gibt es nicht – was das bedeutet
- Kein eingebautes Warten
- Kubernetes startet
webunddbunabhängig voneinander. Es gibt kein automatisches "warte, bis die Datenbank bereit ist" wie esdepends_onin Compose suggeriert (selbst dort wartet es ohne Healthcheck-Angabe nur auf den Containerstart, nicht auf Bereitschaft).
- Die Kubernetes-Lösung
- Probes
- Statt einer Startreihenfolge prüft Kubernetes fortlaufend, ob ein Container tatsächlich bereit ist, Traffic zu empfangen. Das wird im nächsten Artikel beim Thema Health-Checks und Rolling Updates vertieft.
Alltagswerkzeuge zum Debuggen
Diese Befehle ersetzen im Kubernetes-Alltag das, was docker logs und docker exec bei Compose waren.
- Logs eines Pods ansehen
kubectl logs <pod-name>
- Logs fortlaufend verfolgen
kubectl logs -f <pod-name>
- In einen laufenden Container wechseln
kubectl exec -it <pod-name> -- /bin/bash
- Detaillierten Zustand und Ereignisse eines Pods ansehen
- Der wichtigste Befehl bei Problemen – zeigt unter anderem, warum ein Pod nicht startet.
kubectl describe pod <pod-name>
- Alle Ressourcen eines Namespace auf einen Blick
kubectl get all
Übung: Vollständige Übersetzung selbst durchführen
- Legt für jedes der oben gezeigten Manifeste eine eigene YAML-Datei an und wendet sie in dieser Reihenfolge an: ConfigMap, Secret, db-Deployment inkl. Service, web-Deployment inkl. Service.
- Prüft mit
kubectl get pods, dass beide Pods im StatusRunningsind. - Wechselt mit
kubectl exec -itin denweb-Pod und prüft von dort mitcurl db:5432(odernc -zv db 5432), ob die Datenbank über den Service-Namen erreichbar ist. - Löscht den
db-Pod gezielt mitkubectl delete podund beobachtet, ob derweb-Pod danach noch funktioniert – vergleicht das Verhalten mit dem, was ihr in Compose erwarten würdet. - Öffnet mit
kubectl port-forward service/web 8080:80die Anwendung im Browser und bestätigt den Zugriff.
Übung: Fehler bewusst provozieren
- Ändert im
web-Deployment den Image-Namen versehentlich aufnginx:1.25.9999(nicht existierendes Tag) und wendet es erneut an. - Beobachtet mit
kubectl get pods, welcher Status auftritt. - Findet mit
kubectl describe pod <pod-name>im AbschnittEventsdie genaue Fehlerursache. - Korrigiert das Image wieder auf
nginx:1.25und bestätigt, dass der Pod danach wieder normal startet.