<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://xinux.net/index.php?action=history&amp;feed=atom&amp;title=Kubernetes_Compose_App_Uebersetzen</id>
	<title>Kubernetes Compose App Uebersetzen - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://xinux.net/index.php?action=history&amp;feed=atom&amp;title=Kubernetes_Compose_App_Uebersetzen"/>
	<link rel="alternate" type="text/html" href="https://xinux.net/index.php?title=Kubernetes_Compose_App_Uebersetzen&amp;action=history"/>
	<updated>2026-08-23T16:05:43Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Xinux Wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>https://xinux.net/index.php?title=Kubernetes_Compose_App_Uebersetzen&amp;diff=72554&amp;oldid=prev</id>
		<title>Thomas.will: Die Seite wurde neu angelegt: „Dieser Artikel übersetzt eine bekannte Docker-Compose-Anwendung Schritt für Schritt nach Kubernetes. Als Beispiel dient eine einfache Web-Anwendung mit Daten…“</title>
		<link rel="alternate" type="text/html" href="https://xinux.net/index.php?title=Kubernetes_Compose_App_Uebersetzen&amp;diff=72554&amp;oldid=prev"/>
		<updated>2026-08-09T08:11:13Z</updated>

		<summary type="html">&lt;p&gt;Die Seite wurde neu angelegt: „Dieser Artikel übersetzt eine bekannte Docker-Compose-Anwendung Schritt für Schritt nach Kubernetes. Als Beispiel dient eine einfache Web-Anwendung mit Daten…“&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;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 &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;- und einem &amp;lt;code&amp;gt;db&amp;lt;/code&amp;gt;-Service besteht.&lt;br /&gt;
&lt;br /&gt;
==Ausgangspunkt: die Compose-Datei==&lt;br /&gt;
&lt;br /&gt;
Als Referenz die Compose-Anwendung, die übersetzt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
services:&lt;br /&gt;
  web:&lt;br /&gt;
    image: nginx:1.25&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;8080:80&amp;quot;&lt;br /&gt;
    environment:&lt;br /&gt;
      - APP_MODE=production&lt;br /&gt;
    depends_on:&lt;br /&gt;
      - db&lt;br /&gt;
&lt;br /&gt;
  db:&lt;br /&gt;
    image: postgres:16&lt;br /&gt;
    environment:&lt;br /&gt;
      - POSTGRES_PASSWORD=geheim&lt;br /&gt;
      - POSTGRES_DB=appdb&lt;br /&gt;
    volumes:&lt;br /&gt;
      - db_data:/var/lib/postgresql/data&lt;br /&gt;
&lt;br /&gt;
volumes:&lt;br /&gt;
  db_data:&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei wird in den folgenden Abschnitten Stück für Stück in Kubernetes-Objekte übersetzt.&lt;br /&gt;
&lt;br /&gt;
==Von Container zu Pod und Deployment==&lt;br /&gt;
&lt;br /&gt;
; Der Pod&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
; Warum nicht direkt Pods erstellen&lt;br /&gt;
* Ein einzeln erstellter Pod wird bei einem Absturz '''nicht''' automatisch neu gestartet. Dafür braucht es eine übergeordnete Instanz.&lt;br /&gt;
&lt;br /&gt;
; Das Deployment&lt;br /&gt;
* Ein '''Deployment''' beschreibt den Zielzustand (&amp;quot;es sollen N Instanzen dieses Pods laufen&amp;quot;) und sorgt dafür, dass er eingehalten wird – inklusive Neustart bei Absturz und kontrollierten Updates. Das Deployment entspricht am ehesten dem &amp;lt;code&amp;gt;web:&amp;lt;/code&amp;gt;-Service aus der Compose-Datei, nur mit eingebauter Selbstheilung.&lt;br /&gt;
&lt;br /&gt;
===Das web-Deployment===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
apiVersion: apps/v1&lt;br /&gt;
kind: Deployment&lt;br /&gt;
metadata:&lt;br /&gt;
  name: web&lt;br /&gt;
spec:&lt;br /&gt;
  replicas: 1&lt;br /&gt;
  selector:&lt;br /&gt;
    matchLabels:&lt;br /&gt;
      app: web&lt;br /&gt;
  template:&lt;br /&gt;
    metadata:&lt;br /&gt;
      labels:&lt;br /&gt;
        app: web&lt;br /&gt;
    spec:&lt;br /&gt;
      containers:&lt;br /&gt;
        - name: web&lt;br /&gt;
          image: nginx:1.25&lt;br /&gt;
          ports:&lt;br /&gt;
            - containerPort: 80&lt;br /&gt;
          env:&lt;br /&gt;
            - name: APP_MODE&lt;br /&gt;
              value: &amp;quot;production&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Anwenden&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl apply -f web-deployment.yaml&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Status prüfen&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl get deployments&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl get pods -o wide&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Von ports zu Service==&lt;br /&gt;
&lt;br /&gt;
Das &amp;lt;code&amp;gt;ports: &amp;quot;8080:80&amp;quot;&amp;lt;/code&amp;gt; aus Compose hat in Kubernetes kein direktes Gegenstück auf Container-Ebene. Stattdessen übernimmt ein eigenes Objekt die Netzwerk-Erreichbarkeit.&lt;br /&gt;
&lt;br /&gt;
; Der Service&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
; ClusterIP als Standard&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: web&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    app: web&lt;br /&gt;
  ports:&lt;br /&gt;
    - port: 80&lt;br /&gt;
      targetPort: 80&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Anwenden&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl apply -f web-service.yaml&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Erreichbarkeit von innerhalb des Clusters testen&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl run test-curl --rm -it --image=curlimages/curl -- curl -s http://web&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Erreichbarkeit von außen testen (ohne Ingress)&lt;br /&gt;
* Solange noch kein Ingress existiert, kann mit Port-Forwarding von außen zugegriffen werden – vergleichbar mit dem &amp;lt;code&amp;gt;8080:80&amp;lt;/code&amp;gt; aus Compose, nur manuell für die Dauer der Sitzung.&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl port-forward service/web 8080:80&amp;lt;/code&amp;gt;&lt;br /&gt;
* Anschließend ist die Anwendung unter &amp;lt;code&amp;gt;http://localhost:8080&amp;lt;/code&amp;gt; erreichbar, solange der Befehl im Vordergrund läuft.&lt;br /&gt;
&lt;br /&gt;
==Von environment zu ConfigMap und Secret==&lt;br /&gt;
&lt;br /&gt;
In der Compose-Datei stehen &amp;lt;code&amp;gt;APP_MODE=production&amp;lt;/code&amp;gt; (unkritisch) und &amp;lt;code&amp;gt;POSTGRES_PASSWORD=geheim&amp;lt;/code&amp;gt; (sensibel) beide gleich als &amp;lt;code&amp;gt;environment:&amp;lt;/code&amp;gt;. Kubernetes trennt das bewusst.&lt;br /&gt;
&lt;br /&gt;
; ConfigMap für unkritische Werte&lt;br /&gt;
* Für Konfiguration ohne Geheimhaltungsbedarf.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: ConfigMap&lt;br /&gt;
metadata:&lt;br /&gt;
  name: web-config&lt;br /&gt;
data:&lt;br /&gt;
  APP_MODE: &amp;quot;production&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Secret für sensible Werte&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Secret&lt;br /&gt;
metadata:&lt;br /&gt;
  name: db-secret&lt;br /&gt;
type: Opaque&lt;br /&gt;
stringData:&lt;br /&gt;
  POSTGRES_PASSWORD: geheim&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Beide anwenden&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl apply -f web-config.yaml&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl apply -f db-secret.yaml&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Im Deployment referenzieren&lt;br /&gt;
* Der &amp;lt;code&amp;gt;env:&amp;lt;/code&amp;gt;-Abschnitt im Deployment wird angepasst, um aus ConfigMap und Secret zu lesen statt den Wert direkt einzutragen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
env:&lt;br /&gt;
  - name: APP_MODE&lt;br /&gt;
    valueFrom:&lt;br /&gt;
      configMapKeyRef:&lt;br /&gt;
        name: web-config&lt;br /&gt;
        key: APP_MODE&lt;br /&gt;
  - name: POSTGRES_PASSWORD&lt;br /&gt;
    valueFrom:&lt;br /&gt;
      secretKeyRef:&lt;br /&gt;
        name: db-secret&lt;br /&gt;
        key: POSTGRES_PASSWORD&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Das db-Deployment und der Datenbank-Service==&lt;br /&gt;
&lt;br /&gt;
Die Datenbank wird nach demselben Muster übersetzt wie &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
apiVersion: apps/v1&lt;br /&gt;
kind: Deployment&lt;br /&gt;
metadata:&lt;br /&gt;
  name: db&lt;br /&gt;
spec:&lt;br /&gt;
  replicas: 1&lt;br /&gt;
  selector:&lt;br /&gt;
    matchLabels:&lt;br /&gt;
      app: db&lt;br /&gt;
  template:&lt;br /&gt;
    metadata:&lt;br /&gt;
      labels:&lt;br /&gt;
        app: db&lt;br /&gt;
    spec:&lt;br /&gt;
      containers:&lt;br /&gt;
        - name: db&lt;br /&gt;
          image: postgres:16&lt;br /&gt;
          ports:&lt;br /&gt;
            - containerPort: 5432&lt;br /&gt;
          env:&lt;br /&gt;
            - name: POSTGRES_DB&lt;br /&gt;
              value: &amp;quot;appdb&amp;quot;&lt;br /&gt;
            - name: POSTGRES_PASSWORD&lt;br /&gt;
              valueFrom:&lt;br /&gt;
                secretKeyRef:&lt;br /&gt;
                  name: db-secret&lt;br /&gt;
                  key: POSTGRES_PASSWORD&lt;br /&gt;
---&lt;br /&gt;
apiVersion: v1&lt;br /&gt;
kind: Service&lt;br /&gt;
metadata:&lt;br /&gt;
  name: db&lt;br /&gt;
spec:&lt;br /&gt;
  selector:&lt;br /&gt;
    app: db&lt;br /&gt;
  ports:&lt;br /&gt;
    - port: 5432&lt;br /&gt;
      targetPort: 5432&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Wichtig zum Namen des Service&lt;br /&gt;
* Der Service heißt &amp;lt;code&amp;gt;db&amp;lt;/code&amp;gt;. Innerhalb des Clusters ist die Datenbank damit unter genau diesem Namen erreichbar – das entspricht exakt dem Verhalten von &amp;lt;code&amp;gt;depends_on: db&amp;lt;/code&amp;gt; in Compose, wo der Service-Name ebenfalls als Hostname funktioniert. Der &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;-Container kann sich also mit &amp;lt;code&amp;gt;db:5432&amp;lt;/code&amp;gt; verbinden, ohne dass eine IP-Adresse bekannt sein muss.&lt;br /&gt;
&lt;br /&gt;
; Anwenden&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl apply -f db-deployment.yaml&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==depends_on gibt es nicht – was das bedeutet==&lt;br /&gt;
&lt;br /&gt;
; Kein eingebautes Warten&lt;br /&gt;
* Kubernetes startet &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;db&amp;lt;/code&amp;gt; unabhängig voneinander. Es gibt kein automatisches &amp;quot;warte, bis die Datenbank bereit ist&amp;quot; wie es &amp;lt;code&amp;gt;depends_on&amp;lt;/code&amp;gt; in Compose suggeriert (selbst dort wartet es ohne Healthcheck-Angabe nur auf den Containerstart, nicht auf Bereitschaft).&lt;br /&gt;
&lt;br /&gt;
; Die Kubernetes-Lösung: Probes&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
==Alltagswerkzeuge zum Debuggen==&lt;br /&gt;
&lt;br /&gt;
Diese Befehle ersetzen im Kubernetes-Alltag das, was &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; bei Compose waren.&lt;br /&gt;
&lt;br /&gt;
; Logs eines Pods ansehen&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl logs &amp;amp;lt;pod-name&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Logs fortlaufend verfolgen&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl logs -f &amp;amp;lt;pod-name&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In einen laufenden Container wechseln&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl exec -it &amp;amp;lt;pod-name&amp;amp;gt; -- /bin/bash&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Detaillierten Zustand und Ereignisse eines Pods ansehen&lt;br /&gt;
* Der wichtigste Befehl bei Problemen – zeigt unter anderem, warum ein Pod nicht startet.&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl describe pod &amp;amp;lt;pod-name&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; Alle Ressourcen eines Namespace auf einen Blick&lt;br /&gt;
* &amp;lt;code&amp;gt;kubectl get all&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Übung: Vollständige Übersetzung selbst durchführen==&lt;br /&gt;
&lt;br /&gt;
# 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.&lt;br /&gt;
# Prüft mit &amp;lt;code&amp;gt;kubectl get pods&amp;lt;/code&amp;gt;, dass beide Pods im Status &amp;lt;code&amp;gt;Running&amp;lt;/code&amp;gt; sind.&lt;br /&gt;
# Wechselt mit &amp;lt;code&amp;gt;kubectl exec -it&amp;lt;/code&amp;gt; in den &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;-Pod und prüft von dort mit &amp;lt;code&amp;gt;curl db:5432&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;nc -zv db 5432&amp;lt;/code&amp;gt;), ob die Datenbank über den Service-Namen erreichbar ist.&lt;br /&gt;
# Löscht den &amp;lt;code&amp;gt;db&amp;lt;/code&amp;gt;-Pod gezielt mit &amp;lt;code&amp;gt;kubectl delete pod&amp;lt;/code&amp;gt; und beobachtet, ob der &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;-Pod danach noch funktioniert – vergleicht das Verhalten mit dem, was ihr in Compose erwarten würdet.&lt;br /&gt;
# Öffnet mit &amp;lt;code&amp;gt;kubectl port-forward service/web 8080:80&amp;lt;/code&amp;gt; die Anwendung im Browser und bestätigt den Zugriff.&lt;br /&gt;
&lt;br /&gt;
==Übung: Fehler bewusst provozieren==&lt;br /&gt;
&lt;br /&gt;
# Ändert im &amp;lt;code&amp;gt;web&amp;lt;/code&amp;gt;-Deployment den Image-Namen versehentlich auf &amp;lt;code&amp;gt;nginx:1.25.9999&amp;lt;/code&amp;gt; (nicht existierendes Tag) und wendet es erneut an.&lt;br /&gt;
# Beobachtet mit &amp;lt;code&amp;gt;kubectl get pods&amp;lt;/code&amp;gt;, welcher Status auftritt.&lt;br /&gt;
# Findet mit &amp;lt;code&amp;gt;kubectl describe pod &amp;amp;lt;pod-name&amp;amp;gt;&amp;lt;/code&amp;gt; im Abschnitt &amp;lt;code&amp;gt;Events&amp;lt;/code&amp;gt; die genaue Fehlerursache.&lt;br /&gt;
# Korrigiert das Image wieder auf &amp;lt;code&amp;gt;nginx:1.25&amp;lt;/code&amp;gt; und bestätigt, dass der Pod danach wieder normal startet.&lt;/div&gt;</summary>
		<author><name>Thomas.will</name></author>
	</entry>
</feed>