Kubernetes Services

Ein Service in Kubernetes ist eine Abstraktion die eine logische Menge von Pods exponiert und Load Balancing bereitstellt.

Services haben eine stabile IP-Adresse und DNS-Namen, auch wenn Pods dynamisch erstellt/gelöscht werden.


Problem: Warum Services?

OHNE SERVICE (Problem):
┌─────┐ ┌─────┐ ┌─────┐
│Pod 1│ │Pod 2│ │Pod 3│
│10.0.│ │10.0.│ │10.0.│
│1.5  │ │1.8  │ │1.12 │
└─────┘ └─────┘ └─────┘
   ▲        ▲       ▲
   └────────┴───────┘
Welchen Pod ansprechen?
IPs ändern sich ständig!

MIT SERVICE (Lösung):
       ┌─────────────┐
       │   SERVICE   │
       │ 10.96.0.100 │ ← Stabile IP
       │  my-service │ ← Stabile DNS
       └──────┬──────┘
              │ Load Balancing
       ┌──────┼──────┐
       ▼      ▼      ▼
    ┌─────┐┌─────┐┌─────┐
    │Pod 1││Pod 2││Pod 3│
    └─────┘└─────┘└─────┘

Services lösen:


Service-Typen

SERVICE TYPEN:
┌──────────────────────────────────────────┐
│   ClusterIP (Standard)                   │
│   Nur innerhalb des Clusters            │
│   ┌─────┐     ┌──────────┐              │
│   │ Pod │────►│ Service  │              │
│   └─────┘     │10.96.0.1 │              │
│               └──────────┘              │
└──────────────────────────────────────────┘

┌──────────────────────────────────────────┐
│   NodePort                               │
│   Auf jedem Node ein Port                │
│   External:30080 → Pod:80                │
│                                          │
│   [Internet]                             │
│        ▼                                 │
│   Node:30080 ───► Service ───► Pod      │
└──────────────────────────────────────────┘

┌──────────────────────────────────────────┐
│   LoadBalancer                           │
│   Cloud Load Balancer                    │
│                                          │
│   [Internet]                             │
│        ▼                                 │
│   Cloud LB ───► Nodes ───► Pods         │
└──────────────────────────────────────────┘

ClusterIP Service

Standard-Typ. Nur innerhalb des Clusters erreichbar.

# clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP        # Standard, kann weggelassen werden
  selector:
    app: myapp           # Pods mit diesem Label
  ports:
  - protocol: TCP
    port: 80             # Service Port
    targetPort: 8080     # Pod Port
# Service erstellen
kubectl apply -f clusterip-service.yaml

# Services anzeigen
kubectl get services

# Output:
# NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# my-service   ClusterIP   10.96.100.50   <none>        80/TCP    10s

# Details
kubectl describe service my-service

# Testen (von innerhalb des Clusters)
kubectl run test --rm -it --image=busybox -- sh
# Im Container:
wget -qO- my-service:80

Use Cases:


NodePort Service

Exponiert Service auf jedem Node über einen statischen Port (30000-32767).

# nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
spec:
  type: NodePort
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80           # Service Port
    targetPort: 8080   # Pod Port
    nodePort: 30080    # Node Port (optional, auto-assigned wenn nicht angegeben)
# Service erstellen
kubectl apply -f nodeport-service.yaml

# Services anzeigen
kubectl get services

# Output:
# NAME                  TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# my-nodeport-service   NodePort   10.96.100.51   <none>        80:30080/TCP   10s

# Testen (von außerhalb)
# http://<any-node-ip>:30080
curl http://192.168.49.2:30080

# Mit Minikube
minikube service my-nodeport-service --url
NODEPORT TRAFFIC FLOW:
[Client]
   │
   ▼
Node1:30080 ──┐
Node2:30080 ──┼──► Service ──┐
Node3:30080 ──┘               │
                              ▼
                         ┌─────┬─────┬─────┐
                         │Pod 1│Pod 2│Pod 3│
                         └─────┴─────┴─────┘

Use Cases:


LoadBalancer Service

Erstellt externen Load Balancer (Cloud Provider erforderlich).

# loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
# Service erstellen
kubectl apply -f loadbalancer-service.yaml

# Services anzeigen
kubectl get services

# Output:
# NAME               TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
# my-loadbalancer    LoadBalancer   10.96.100.52   35.225.123.45   80:31234/TCP   2m

# Testen
curl http://35.225.123.45
LOADBALANCER TRAFFIC FLOW:
[Internet]
   │
   ▼
[Cloud Load Balancer]
 External IP: 35.225.123.45
   │
   ├──► Node1:31234 ──┐
   ├──► Node2:31234 ──┼──► Service ──┐
   └──► Node3:31234 ──┘               │
                                      ▼
                                 ┌─────┬─────┬─────┐
                                 │Pod 1│Pod 2│Pod 3│
                                 └─────┴─────┴─────┘

Requirements:

Use Cases:


ExternalName Service

Mappt Service zu externem DNS-Namen.

# externalname-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: external-database
spec:
  type: ExternalName
  externalName: db.example.com
# Service erstellen
kubectl apply -f externalname-service.yaml

# Von Pods aus:
# mysql -h external-database -u user -p
# → Verbindet zu db.example.com

Use Cases:


Service erstellen

Imperativ

# Deployment exponieren (ClusterIP)
kubectl expose deployment myapp --port=80 --target-port=8080

# NodePort
kubectl expose deployment myapp --type=NodePort --port=80

# LoadBalancer
kubectl expose deployment myapp --type=LoadBalancer --port=80

# YAML generieren
kubectl expose deployment myapp --port=80 --dry-run=client -o yaml > service.yaml

Deklarativ

# complete-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  labels:
    app: myapp
spec:
  type: LoadBalancer
  selector:
    app: myapp
    tier: backend
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080

  - name: https
    protocol: TCP
    port: 443
    targetPort: 8443

  # Session Affinity (Sticky Sessions)
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600
# Service erstellen/aktualisieren
kubectl apply -f complete-service.yaml

Service Discovery

Über DNS

SERVICE DNS FORMAT:
<service-name>.<namespace>.svc.cluster.local

Beispiele:
my-service.default.svc.cluster.local
database.production.svc.cluster.local
api.staging.svc.cluster.local
# Beispiel: Frontend → Backend Communication
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: frontend
    image: frontend:v1
    env:
    # Im gleichen Namespace
    - name: BACKEND_URL
      value: "http://backend-service:80"

    # In anderem Namespace
    - name: DATABASE_URL
      value: "http://database.production.svc.cluster.local:5432"
# DNS testen (von Pod aus)
kubectl run test --rm -it --image=busybox -- sh

# Im Container:
nslookup my-service
nslookup my-service.default
nslookup my-service.default.svc.cluster.local

# HTTP Request
wget -qO- my-service:80

Über Environment Variables

Kubernetes injiziert automatisch Env-Variablen für Services.

# Service existiert: my-service (Port 80)
# Pods bekommen automatisch:
MY_SERVICE_SERVICE_HOST=10.96.100.50
MY_SERVICE_SERVICE_PORT=80
MY_SERVICE_PORT=tcp://10.96.100.50:80
MY_SERVICE_PORT_80_TCP=tcp://10.96.100.50:80
MY_SERVICE_PORT_80_TCP_PROTO=tcp
MY_SERVICE_PORT_80_TCP_PORT=80
MY_SERVICE_PORT_80_TCP_ADDR=10.96.100.50

⚠️ Reihenfolge wichtig!


Endpoints

Endpoints sind die Pod-IPs hinter einem Service.

# Endpoints anzeigen
kubectl get endpoints my-service

# Output:
# NAME         ENDPOINTS                              AGE
# my-service   10.244.1.5:8080,10.244.2.3:8080,...   5m

# Details
kubectl describe endpoints my-service
# Service
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: myapp    # ← Wählt Pods aus
  ports:
  - port: 80
    targetPort: 8080

Automatische Endpoints:


Headless Service

Service ohne Cluster-IP für direkte Pod-Adressierung.

# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  clusterIP: None     # ← Headless!
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
# DNS Lookup gibt Pod-IPs zurück (nicht Service-IP)
kubectl run test --rm -it --image=busybox -- sh

# Im Container:
nslookup headless-service

# Output:
# Name:      headless-service.default.svc.cluster.local
# Address 1: 10.244.1.5 pod-1.headless-service.default.svc.cluster.local
# Address 2: 10.244.2.3 pod-2.headless-service.default.svc.cluster.local
# Address 3: 10.244.3.7 pod-3.headless-service.default.svc.cluster.local

Use Cases:


Multi-Port Service

# multi-port-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: multi-port-service
spec:
  selector:
    app: myapp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080

  - name: https
    protocol: TCP
    port: 443
    targetPort: 8443

  - name: metrics
    protocol: TCP
    port: 9090
    targetPort: 9090

⚠️ Bei Multi-Port MUSS name gesetzt sein!


Session Affinity

Sticky Sessions - Client bleibt bei gleichem Pod.

apiVersion: v1
kind: Service
metadata:
  name: sticky-service
spec:
  selector:
    app: myapp
  sessionAffinity: ClientIP    # ← Sticky Sessions
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600     # 1 Stunde
  ports:
  - port: 80
    targetPort: 8080

Optionen:

Use Cases:


Service mit Deployment

Vollständiges Beispiel:

# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp        # ← Service selektiert über dieses Label
    spec:
      containers:
      - name: myapp
        image: myapp:v1
        ports:
        - containerPort: 8080
---
# app-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  type: LoadBalancer
  selector:
    app: myapp          # ← Matcht Pods mit app=myapp
  ports:
  - protocol: TCP
    port: 80            # Service Port
    targetPort: 8080    # Container Port
# Beide erstellen
kubectl apply -f app-deployment.yaml

# Service exponiert automatisch alle Pods
kubectl get endpoints myapp-service

Service Testing

# Service testen (von innerhalb)
kubectl run test-pod --rm -it --image=busybox -- sh

# Im Container:
wget -qO- myapp-service:80
nslookup myapp-service

# Service Port-Forward (lokal testen)
kubectl port-forward service/myapp-service 8080:80

# Browser: http://localhost:8080

Troubleshooting

Service erreichbar, Pods nicht

# 1. Endpoints prüfen
kubectl get endpoints my-service

# Leer? → Selector passt nicht!
# 2. Labels prüfen
kubectl get pods --show-labels

# 3. Selector prüfen
kubectl get service my-service -o yaml | grep selector -A 5

# 4. Pods müssen Ready sein
kubectl get pods -l app=myapp

Service nicht erreichbar

# 1. Service existiert?
kubectl get services

# 2. Service Typ prüfen
kubectl describe service my-service

# 3. NodePort: Firewall?
# 4. LoadBalancer: External IP pending?
kubectl get service my-service

# 5. DNS testen
kubectl run test --rm -it --image=busybox -- nslookup my-service

LoadBalancer External IP pending

# Auf Cloud-Provider: Dauert 1-2 Minuten
kubectl get service my-service -w

# Minikube: Tunnel starten
minikube tunnel

# On-Premise: MetalLB installieren
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.0/config/manifests/metallb-native.yaml

Service vs. Ingress

SERVICE (L4 Load Balancing):
┌─────────────────────────────┐
│  [Internet]                 │
│       ▼                     │
│  LoadBalancer Service       │
│  (1 External IP)            │
│       ▼                     │
│  Pods                       │
└─────────────────────────────┘
→ 1 Service = 1 Load Balancer = 1 IP

INGRESS (L7 Load Balancing):
┌─────────────────────────────┐
│  [Internet]                 │
│       ▼                     │
│  Ingress Controller         │
│  (1 External IP)            │
│       ▼                     │
│  ┌──────────┬──────────┐    │
│  │ Service1 │ Service2 │    │
│  └──────────┴──────────┘    │
│       ▼          ▼          │
│  ┌──────────┐┌──────────┐   │
│  │  Pods    ││  Pods    │   │
│  └──────────┘└──────────┘   │
└─────────────────────────────┘
→ Mehrere Services = 1 IP + Routing

Best Practices

Service Best Practices

1. Verwende Labels konsistent

# Deployment
labels:
  app: myapp
  version: v1
  tier: backend

# Service
selector:
  app: myapp    # Nicht version/tier selektieren!

2. ClusterIP für interne Services

# ✅ Datenbank nur intern
type: ClusterIP

# ❌ Datenbank extern
type: LoadBalancer

3. LoadBalancer nur für Entry Points

# ✅ Frontend/API Gateway
type: LoadBalancer

# Besser: Ingress für Multiple Services

4. Verwende named Ports

# Deployment
ports:
- name: http
  containerPort: 8080

# Service
ports:
- name: http
  port: 80
  targetPort: http  # ← Port-Name statt Number

5. Health Checks in Pods

# Service routet nur zu Ready Pods
readinessProbe:
  httpGet:
    path: /ready
    port: 8080

Zusammenfassung

Kubernetes Services

  • Service Discovery: Stabile DNS-Namen für Pods
  • Load Balancing: Traffic auf Pods verteilen
  • ClusterIP: Intern (Standard)
  • NodePort: Extern über Node-Port
  • LoadBalancer: Cloud Load Balancer
  • Headless: Direkte Pod-Adressierung

Quick Reference

# Deployment exponieren
kubectl expose deployment myapp --port=80 --type=LoadBalancer

# Services anzeigen
kubectl get services

# Service testen
kubectl port-forward service/myapp 8080:80

# DNS testen
kubectl run test --rm -it --image=busybox -- nslookup myapp


Verwandte Konzepte