Zum Inhalt

Lab 03: Übungsaufgabe: Horizontal Pod Autoscaling mit HPA und KEDA auf AKS

Erzeuge dir für diese Übung einen Cluster wie in Aufgabe 1 und setze dir Umgebungsvariablen wie dort beschrieben.

Teil 1: Horizontal Pod Autoscaler (HPA) – Grundlagen

Aufgabe 1.1: Demo-Anwendung deployen

Erstelle eine CPU-intensive Anwendung für die Lasttest-Szenarien.

Datei: cpu-stress-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cpu-stress-app
  labels:
    app: cpu-stress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cpu-stress
  template:
    metadata:
      labels:
        app: cpu-stress
    spec:
      containers:
        - name: stress
          image: vish/stress
          resources:
            requests:
              cpu: "100m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"
          args:
            - -cpus
            - "1"
---
apiVersion: v1
kind: Service
metadata:
  name: cpu-stress-svc
spec:
  selector:
    app: cpu-stress
  ports:
    - port: 80
      targetPort: 8080
kubectl apply -f cpu-stress-deployment.yaml

Aufgabe 1.2: HPA mit CPU-Metrik erstellen

Erstelle einen HPA, der auf CPU-Auslastung reagiert.

Datei: hpa-cpu.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: cpu-stress-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: cpu-stress-app
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 50
          periodSeconds: 30
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 15
        - type: Pods
          value: 4
          periodSeconds: 15
      selectPolicy: Max
kubectl apply -f hpa-cpu.yaml

Aufgabe 1.3: HPA beobachten und testen

Beobachte das Verhalten des HPA. Es kann etwa eine Minute dauern, bis er seine Arbeit aufgenommen hat – solange ist die letztgemessene CPU-Auslastung noch <unknown>.

# HPA Status kontinuierlich beobachten
kubectl get hpa cpu-stress-hpa --watch

# In einem separaten Terminal: Details anzeigen
kubectl describe hpa cpu-stress-hpa

Fragen zur Reflexion:

  1. Welche Metriken werden aktuell vom HPA erfasst?
  2. Was bedeuten die Werte in der TARGETS-Spalte?
  3. Wie lange dauert es typischerweise, bis der HPA auf Lastveränderungen reagiert?

Du kannst nun übrigens auch das Cluster-Autoscaling testen, wenn es im Cluster aktiviert ist. Wenn du das maxReplicas-Feld der YAML auf einen sehr hohen Wert setzt, z.B. 100, so muss AKS bald neue Nodes starten. Denke aber bitte daran, um Anschluss das Deployment zu löschen, um den Cluster wieder zu entlasten.

Teil 2: KEDA auf AKS aktivieren

Aufgabe 2.1: KEDA Add-on aktivieren

KEDA ist als natives AKS-Add-on verfügbar und wird von Microsoft verwaltet. Dies ist die empfohlene Methode für AKS.

Bei bestehendem Cluster aktivieren:

# KEDA Add-on aktivieren
az aks update \
  --resource-group $RESOURCE_GROUP \
  --name $CLUSTER_NAME \
  --enable-keda

# Installation prüfen
kubectl get pods -n kube-system -l app=keda-operator

Hinweis: Bei der Cluster-Erstellung kann KEDA direkt mit --enable-keda aktiviert werden (siehe Voraussetzungen).

Aufgabe 2.2: KEDA Add-on Status prüfen

Als letzten Test prüfen wir nun noch, ob KEDA wirklich aktiviert ist:

# Add-on Status im Cluster anzeigen
az aks show \
  --resource-group $RESOURCE_GROUP \
  --name $CLUSTER_NAME \
  --query "workloadAutoScalerProfile.keda" -o json

Teil 3: Event-basiertes Autoscaling mit KEDA

Szenario A: Azure Service Bus Queue Scaler

Dieses Szenario zeigt, wie KEDA basierend auf der Nachrichtenlänge einer Azure Service Bus Queue skaliert.

Azure Service Bus ist ein Azure-Dienst, der Nachrichten zwischen Anwendungen transportiert. Dies entkoppelt Systeme voneinander, sodass sie unabhängig arbeiten können und keine Nachrichten verloren gehen, selbst wenn ein System kurz offline ist.

Aufgabe 3.1: Azure Service Bus einrichten

# Service Bus Namespace erstellen
az servicebus namespace create \
  --resource-group $RESOURCE_GROUP \
  --name sb-keda-demo-$RANDOM \
  --location germanywestcentral \
  --sku Standard

# Queue erstellen
SB_NAMESPACE=$(az servicebus namespace list -g $RESOURCE_GROUP --query "[0].name" -o tsv)
az servicebus queue create \
  --resource-group $RESOURCE_GROUP \
  --namespace-name $SB_NAMESPACE \
  --name orders-queue

# Connection String abrufen
SB_CONNECTION=$(az servicebus namespace authorization-rule keys list \
  --resource-group $RESOURCE_GROUP \
  --namespace-name $SB_NAMESPACE \
  --name RootManageSharedAccessKey \
  --query primaryConnectionString -o tsv)

echo "Connection String: $SB_CONNECTION"

Aufgabe 3.2: Queue-Consumer Deployment erstellen

In diesem Szenario simulieren wir einen Order-Processor, der Nachrichten aus einer Service Bus Queue verarbeitet. In der Praxis wäre dies eine Anwendung, die:

  • Nachrichten aus der Queue liest
  • Die Bestellung verarbeitet (z.B. Validierung, Datenbankschreibung)
  • Die Nachricht nach erfolgreicher Verarbeitung bestätigt

Für die Übung verwenden wir ein einfaches Demo-Image, das die Verarbeitung simuliert:

Datei: queue-consumer-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-processor
  labels:
    app: order-processor
spec:
  replicas: 0  # KEDA startet bei 0 und skaliert basierend auf Queue-Länge
  selector:
    matchLabels:
      app: order-processor
  template:
    metadata:
      labels:
        app: order-processor
    spec:
      containers:
        - name: processor
          # Demo-Image: Simuliert Nachrichtenverarbeitung durch Sleep
          # In Produktion: Ihre eigene Consumer-Anwendung (z.B. .NET, Java, Python)
          image: busybox:1.36
          command:
            - /bin/sh
            - -c
            - |
              echo "Order Processor gestartet"
              echo "Warte auf Nachrichten aus Queue: $QUEUE_NAME"
              # Simuliert kontinuierliche Verarbeitung
              while true; do
                echo "[$(date)] Verarbeite Bestellungen..."
                sleep 10
              done
          env:
            - name: QUEUE_NAME
              value: "orders-queue"
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "100m"
              memory: "128Mi"

Hinweis für die Praxis: Ein echter Service Bus Consumer würde z.B. so aussehen:

  • .NET: Azure.Messaging.ServiceBus-SDK mit ServiceBusProcessor
  • Java: azure-messaging-servicebus mit ServiceBusProcessorClient
  • Python: azure-servicebus mit ServiceBusReceiver
kubectl apply -f queue-consumer-deployment.yaml

# Prüfen, dass kein Pod läuft (replicas: 0)
kubectl get pods -l app=order-processor

Aufgabe 3.3: KEDA TriggerAuthentication und ScaledObject

KEDA muss sich mit Azure Service Bus verbinden, um zu prüfen, ob Nachrichten in der Queue vorhanden sind. Dazu müssen wir den Connection-String als Kubernetes-Secret hinterlegen:

# Secret mit echter Connection String erstellen
kubectl create secret generic servicebus-secret \
  --from-literal=connectionString="$SB_CONNECTION"

Weiterhin brauchen wir eine sogenannte TriggerAuthentication: Sie teilt KEDA mit, wie es sich mit der Auslöser-Quelle verbindet. In diesem Fall läuft dies über unser Secret ab, was wir gerade angelegt haben.

Datei: keda-servicebus-auth.yaml

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: azure-servicebus-auth
spec:
  secretTargetRef:
    - parameter: connection
      name: servicebus-secret
      key: connectionString

Außerdem müssen wir KEDA so konfigurieren, dass es unseren Order-Processor skaliert, den wir vorhin angelegt haben. Dies geht über ein sogenanntes ScaledObject. Es wird einen HorizontalPodAutoscaler für uns anlegen und entsprechend des Durchsatzes der Message Queue einstellen.

Datei: keda-servicebus-scaledobject.yaml

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaler
spec:
  scaleTargetRef:
    name: order-processor
  pollingInterval: 15
  cooldownPeriod: 300
  minReplicaCount: 0
  maxReplicaCount: 30
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 120
          policies:
            - type: Percent
              value: 25
              periodSeconds: 60
  triggers:
    - type: azure-servicebus
      metadata:
        queueName: orders-queue
        messageCount: "5"
        activationMessageCount: "1"
      authenticationRef:
        name: azure-servicebus-auth

Nun müssen wir die Dateien natürlich noch anwenden:

kubectl apply -f keda-servicebus-auth.yaml
kubectl apply -f keda-servicebus-scaledobject.yaml

Aufgabe 3.4: Skalierung testen

Die Azure CLI unterstützt kein direktes Senden von Service Bus Nachrichten (nur Management-Operationen). Daher werden wir die Last über das Azure-Portal simulieren.

  • Öffne den Service Bus Namespace im Azure Portal
  • Wähle unter "Entities" die Queue orders-queue
  • Klicke auf "Service Bus Explorer" im linken Menü
  • Wähle "Send messages"
  • Gebe eine Nachricht ein und klicken auf "Send"
  • Wiederhole dies mehrfach oder nutze "Repeat send"

Beobachte nebenbei das Verhalten des Deployments und des HPA.

kubectl get pods -l app=order-processor --watch
kubectl get scaledobject order-processor-scaler -o yaml
kubectl get hpa

Schau dir an:

  • Wie viele Sekunden nach dem Senden der Nachrichten beginnt die Skalierung?
  • Auf wie viele Pods wird skaliert?
  • Wie verhält sich das Scale-Down nach Verarbeitung aller Nachrichten?