Kubernetes Grundlagen: Pod, Deployment und Service
1. Überblick
Kubernetes hat dutzende Resource-Typen, aber 80 % der täglichen Arbeit dreht sich um drei Konzepte: Pod, Deployment, Service. Dieser Artikel hilft dir, das Wesen jeder Resource zu verstehen, wie sie zusammenhängen, und wie du ein Bündel YAML-Manifeste schreibst, um eine echte Anwendung im Cluster zum Laufen zu bringen.
Danach kannst du selbst eine Spring-Boot-Anwendung auf Kubernetes deployen, sie auf mehrere Replicas skalieren und über Service nach außen exponieren – alles nur mit kubectl und ein paar YAML-Dateien.
Hinweis: Dieser Artikel setzt voraus, dass du bereits einen Cluster hast (Minikube, Kind oder Cloud) und
kubectlauf den richtigen Context zeigt.
2. Pod – kleinste Bereitstellungseinheit
2.1. Was ist ein Pod?
Ein Pod ist eine Gruppe von einem oder mehreren Containern, die sich einen Network-Namespace (gleiche IP, gleicher Port-Bereich) und Volumes teilen. In der Praxis enthalten die meisten Pods nur einen Application-Container.
apiVersion: v1
kind: Pod
metadata:
name: order-service-pod
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Erklärung:
metadata.labelsvergeben das Labelapp: order-service– so „erkennt” Kubernetes verschiedene Ressourcen wieder, und Service sowie Deployment nutzen dieses Label, um verwandte Objekte auszuwählen.resources.requestssind die minimalen Ressourcen, die der Scheduler haben muss, um den Pod auf einem Node zu platzieren;limitssind die Obergrenzen – bei Überschreitung werden Ressourcen gedrosselt (CPU) oder der Pod wird beendet (Memory – OOMKilled).containerPort: 8080ist nur eine Deklaration zu Informationszwecken; sie öffnet den Port nicht automatisch nach außen.
Anwenden und überprüfen:
kubectl apply -f order-service-pod.yaml
kubectl get pods
NAME READY STATUS RESTARTS AGE
order-service-pod 1/1 Running 0 5s
2.2. Warum sollte man keine Pods direkt erstellen?
Pods, die wie oben „nackt” erstellt werden, haben ein großes Problem: Wenn der Node, der den Pod enthält, abstürzt, oder wenn der Pod crasht, erstellt ihn niemand automatisch neu. Ein Pod hat keinen Self-Healing-Mechanismus und keine Skalierungsfunktion.
Hinweis: In Production-Umgebungen solltest du Pods fast nie direkt erstellen. Nutze immer einen Deployment (oder StatefulSet, Job), um Pods zu verwalten.
3. Deployment – Verwaltung des Pod-Lebenszyklus
3.1. Einen Deployment deklarieren
Ein Deployment erstellt und verwaltet eine Menge von Pods über einen ReplicaSet, stellt sicher, dass immer die richtige Anzahl von Replicas läuft, und unterstützt Rolling Updates ohne Downtime.
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Erklärung:
replicas: 3erfordert, dass der Deployment immer 3 Pods am Laufen hält – wenn ein Pod stirbt, erstellt der Deployment automatisch einen neuen Pod als Ersatz.selector.matchLabelsmuss mittemplate.metadata.labelsübereinstimmen. Das ist die obligatorische Verbindung zwischen Deployment und den von ihm verwalteten Pods.readinessProbebestimmt, ob ein Pod Traffic erhalten soll oder nicht (wird vom Service verwendet);livenessProbeentscheidet, ob ein Pod neu gestartet werden muss. Diese beiden Probes zu verwechseln ist ein sehr häufiger Fehler für Kubernetes-Anfänger.
Anwenden und beobachten:
kubectl apply -f order-service-deployment.yaml
kubectl get deployments
kubectl get pods -l app=order-service
NAME READY UP-TO-DATE AVAILABLE AGE
order-service 3/3 3 3 12s
NAME READY STATUS RESTARTS AGE
order-service-7d4f9c8b6d-2kx9p 1/1 Running 0 12s
order-service-7d4f9c8b6d-7h2mn 1/1 Running 0 12s
order-service-7d4f9c8b6d-xq1lw 1/1 Running 0 12s
3.2. Skalierung und Rolling Update
Scale erhöhen/reduzieren, ohne YAML zu bearbeiten:
kubectl scale deployment order-service --replicas=5
Rolling Update beim Ändern des Image – der Deployment ersetzt alte Pods automatisch durch neue gemäß einer Rollierungs-Strategie, ohne Downtime:
kubectl set image deployment/order-service order-service=nonladev/order-service:1.1
kubectl rollout status deployment/order-service
Waiting for deployment "order-service" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "order-service" successfully rolled out
Wenn die neue Version einen Bug hat, schnell auf die vorherige Version zurückrollen:
kubectl rollout undo deployment/order-service
3.3. Rolling-Update-Strategie konfigurieren
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
maxUnavailable: 1 erlaubt maximal 1 Pod, während des Updates „ausfallen”; maxSurge: 1 erlaubt, maximal 1 zusätzliche Pod über die deklarierten Replicas hinaus zu erstellen. Mit replicas: 3 wird der Cluster während der Umstellung 2–4 Pods ausführen, aber niemals weniger als 2.
4. Service – stabile Pod-Exposition
4.1. Warum ist ein Service nötig?
Pods haben ihre eigene IP, aber diese IP ändert sich, wenn Pods neu erstellt werden (Crash, Rolling Update, Skalierung). Ein Service bietet eine stabile Adresse (ClusterIP + DNS-Name), die vor einer Gruppe von Pods steht, und verteilt Traffic automatisch basierend auf Label-Selektoren – unabhängig davon, wie oft sich die Pods darunter ändern.
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Erklärung:
selector.app: order-servicepasst zu dem Label auf den Pods (das vom Deployment intemplate.metadata.labelsgesetzt wird) – der Service findet und verfolgt diese Pods automatisch über ein zwischengelagertes Objekt namens Endpoints.port: 80ist der Port, auf dem der Service innerhalb des Clusters lauscht;targetPort: 8080ist der Port, den der Container tatsächlich freigibt. Diese beiden Zahlen müssen nicht gleich sein.type: ClusterIP(Standardwert) ist nur von innerhalb des Clusters erreichbar – ideal für interne Services zwischen Microservices.
Service und Endpoints überprüfen:
kubectl apply -f order-service-service.yaml
kubectl get svc order-service
kubectl get endpoints order-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
order-service ClusterIP 10.96.142.201 <none> 80/TCP 4s
NAME ENDPOINTS AGE
order-service 10.244.1.5:8080,10.244.1.6:8080,+1 more 4s
4.2. Service-Typen
| Type | Zugriffsbereich | Verwendung |
|---|---|---|
ClusterIP |
Nur innerhalb des Clusters | Kommunikation zwischen internen Microservices |
NodePort |
Fester Port (30000–32767) auf allen Nodes | Schnelle Tests, Dev-/On-Prem-Umgebung |
LoadBalancer |
Public-IP vom Cloud-Provider | Service ins Internet auf der Cloud freilegen |
Beispiel: Schnelle Freigabe für Tests auf Minikube:
apiVersion: v1
kind: Service
metadata:
name: order-service-external
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
nodePort: 30080
type: NodePort
minikube service order-service-external --url
Hinweis:
NodePortfür Production zu verwenden, ist ein Anti-Pattern – der Port-Bereich ist begrenzt, es gibt keine TLS-Terminierung oder health-basiertes Routing wie bei Ingress. Für Production in der Cloud bevorzugeLoadBalanceroder einen Ingress-Controller.
5. Alle drei in einer echten Anwendung kombinieren
Die gesamte Pipeline: Client ruft den Service auf → Service verteilt zu Pods, die vom Deployment verwaltet werden.
Client
│
▼
Service (order-service, ClusterIP 10.96.142.201:80)
│ (label selector: app=order-service)
▼
┌─────────────┬─────────────┬─────────────┐
│ Pod #1 │ Pod #2 │ Pod #3 │
│ :8080 │ :8080 │ :8080 │
└─────────────┴─────────────┴─────────────┘
▲ verwaltet durch
Deployment (order-service, replicas: 3)
Zur Vereinfachung kannst du den Deployment und Service in einer Datei zusammenfassen und durch --- trennen:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
kubectl apply -f order-service-all.yaml
6. Tests für Readiness-Endpoint vor dem Deployment schreiben
Bevor die Probe von Kubernetes sich auf /actuator/health/readiness stützt, solltest du im Spring-Boot-Code sicherstellen, dass dieser Endpoint den korrekten Status zurückgibt.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.health.readiness-state.enabled=true
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderServiceHealthTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void readinessProbe_shouldReturnUp() {
ResponseEntity<String> response =
restTemplate.getForEntity("/actuator/health/readiness", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).contains("\"status\":\"UP\"");
}
}
Dieser Test ruft den echten Readiness-Endpoint über TestRestTemplate auf und bestätigt HTTP 200 sowie einen Body mit UP. Das ist genau der Endpoint, den die readinessProbe im Deployment regelmäßig aufruft – wenn der Test hier fehlschlägt, wird der Pod im Cluster nie „Ready”.
7. Häufige Fehler
- Selector passt nicht zu Labels: Service oder Deployment findet keine Pods (Endpoints leer), weil
selectorundlabelsfalsch geschrieben oder unterschiedlich sind. Immerkubectl get endpoints <svc-name>ausführen, um das zu überprüfen. - Fehlende readinessProbe: Der Service leitet Traffic an Pods, deren Anwendung noch nicht vollständig gestartet ist, verursacht 502/503-Fehler unmittelbar nach dem Deployment.
- Fehlende Resource-Anforderungen/Limits: Der Scheduler könnte den Pod auf dem falschen Node platzieren oder der Pod wird unerwartet OOMKilled bei erhöhtem Traffic.
- „Nackte” Pods für Production: kein Self-Healing, wenn ein Pod weg ist, ist er weg, du musst ihn manuell neu erstellen.
- Image-Tag
latest: Rolling Updates erkennen Änderungen nicht, Kubernetes denkt, das Image hat sich nicht geändert, und zieht es nicht erneut. Nutze immer ein spezifisches Version-Tag.
8. Fazit
Ein Pod ist die kleinste Container-Ausführungseinheit, ein Deployment verwaltet den Lebenszyklus und die Skalierung einer Menge von Pods, und ein Service bietet eine stabile Adresse, um auf sie zuzugreifen, obwohl sich die Pods darunter ständig ändern. Die Beherrschung dieser drei Bausteine ist die Grundlage, um später zu Ingress, ConfigMap/Secret oder Helm Charts fortzuschreiten. Nächste Schritte zum Ausprobieren: Deploye eine echte Spring-Boot-Anwendung, beobachte das Rolling Update und versuche, einen Pod manuell zu löschen, um zu sehen, wie sich der Deployment selbst wiederherstellt.