Veröffentlicht am

Karpenter Autoscaling: 60% schnellere Nodes auf AWS

Teilen:
Authors

Kubernetes Karpenter: 60% schnellere Node-Provisionierung auf AWS

TL;DR

  • Karpenter provisioniert Nodes in unter 60 Sekunden -- der Cluster Autoscaler braucht 2-5 Minuten ueber Node Groups.
  • Statt vordefinierter Node Groups waehlt Karpenter dynamisch aus ueber 700 EC2-Instance-Typen die optimale Instanz.
  • NodePool und EC2NodeClass sind die zentralen Ressourcen seit Karpenter v1.0 (GA).
  • Consolidation packt Pods automatisch auf weniger Nodes und entfernt ungenutzte Kapazitaet.
  • Spot-Instance-Integration mit Interruption Handling spart 60-90% gegenueber On-Demand.

Warum der Cluster Autoscaler an seine Grenzen stoesst

Der Cluster Autoscaler skaliert ueber Managed Node Groups. Jede Node Group hat einen festen Instance-Typ, ein Minimum und Maximum. Braucht ein Team GPUs, muss vorab eine GPU-Node-Group existieren. In der Praxis entstehen 10-20 Node Groups, jede manuell konfiguriert.

Dazu kommen Timing-Probleme: CA prueft in Intervallen ob Pods pending sind, triggered die ASG, die EC2-Instanz muss booten und dem Cluster beitreten. Das dauert 2-5 Minuten. Mehr zu Autoscaling-Optionen unter Kubernetes Autoscaling und Kosten sparen.

Wie Karpenter anders arbeitet

Karpenter ueberspringt Node Groups komplett. Sobald ein Pod nicht schedulbar ist, berechnet Karpenter welcher EC2-Instance-Typ das beste Preis-Leistungs-Verhaeltnis bietet und startet die Instanz direkt ueber die AWS-API.

FeatureCluster AutoscalerKarpenter
Provisionierungszeit2-5 Minuten30-90 Sekunden
Instance-AuswahlFeste Node GroupsDynamisch aus allen Typen
Spot-IntegrationMixed Instances PolicyNative Fleet-API
Bin PackingBegrenztAktive Consolidation
GPU-SupportEigene Node Group noetigAutomatische Auswahl
Downscaling10+ Minuten CooldownAggressiv konfigurierbar

Installation auf EKS

IAM-Rollen und OIDC

export CLUSTER_NAME="production-cluster"
export AWS_REGION="eu-central-1"
export KARPENTER_VERSION="1.1.0"

# OIDC Provider erstellen
eksctl utils associate-iam-oidc-provider \
  --cluster ${CLUSTER_NAME} \
  --region ${AWS_REGION} --approve

# IAM-Rolle fuer Karpenter
eksctl create iamserviceaccount \
  --cluster ${CLUSTER_NAME} \
  --name karpenter --namespace kube-system \
  --role-name "KarpenterControllerRole-${CLUSTER_NAME}" \
  --attach-policy-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/KarpenterControllerPolicy" \
  --approve

Helm-Installation

helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version "${KARPENTER_VERSION}" \
  --namespace kube-system \
  --set "settings.clusterName=${CLUSTER_NAME}" \
  --set "settings.interruptionQueueName=karpenter-${CLUSTER_NAME}" \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --wait

kubectl get pods -n kube-system -l app.kubernetes.io/name=karpenter

Fuer Helm-Grundlagen: Helm Charts Einfuehrung.

NodePool-Konfiguration

Allgemeiner Production NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general-purpose
spec:
  template:
    metadata:
      labels:
        workload-type: general
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand", "spot"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge", "4xlarge"]
      expireAfter: 720h
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 60s
  limits:
    cpu: "200"
    memory: 800Gi
  weight: 50

Spot-optimierter NodePool fuer Batch-Jobs

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: batch-spot
spec:
  template:
    metadata:
      labels:
        workload-type: batch
      taints:
        - key: batch-only
          value: "true"
          effect: NoSchedule
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
      expireAfter: 168h
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s
  limits:
    cpu: "400"
    memory: 1600Gi
  weight: 80

EC2NodeClass: AWS-spezifische Einstellungen

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  role: "KarpenterNodeRole-production-cluster"
  amiSelectorTerms:
    - alias: al2023@latest
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "production-cluster"
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: "production-cluster"
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 100Gi
        volumeType: gp3
        iops: 3000
        throughput: 125
        encrypted: true
        deleteOnTermination: true
  metadataOptions:
    httpEndpoint: enabled
    httpPutResponseHopLimit: 2
    httpTokens: required
  tags:
    Environment: production
    ManagedBy: karpenter

Consolidation: Automatisches Bin Packing

Consolidation ist eines der staerksten Karpenter-Features. Im Modus WhenEmptyOrUnderutilized ersetzt Karpenter unterausgelastete Nodes durch kleinere Instanzen:

Vorher:
  Node 1 (m5.2xlarge - 8 vCPU): Pod A (2 CPU), Pod B (1 CPU)  = 37%
  Node 2 (m5.2xlarge - 8 vCPU): Pod C (1 CPU)                  = 12%
  Node 3 (m5.2xlarge - 8 vCPU): leer                            = 0%

Nachher:
  Node 1 (m5.xlarge - 4 vCPU): Pod A, Pod B, Pod C              = 100%
  Ersparnis: 2 Nodes weniger = ca. 66% Kostenreduktion

Disruption Budgets

disruption:
  consolidationPolicy: WhenEmptyOrUnderutilized
  consolidateAfter: 60s
  budgets:
    - nodes: "10%"
    - nodes: "0"
      schedule: "0 9 * * 1-5"
      duration: 8h

Maximal 10% der Nodes gleichzeitig. Zwischen 9 und 17 Uhr an Werktagen keine Disruption. Karpenter respektiert ausserdem PodDisruptionBudgets:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: webapp-pdb
  namespace: webapp
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: webapp

Spot-Instance-Handling

Interruption Queue einrichten

export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

aws sqs create-queue \
  --queue-name "karpenter-${CLUSTER_NAME}" \
  --region ${AWS_REGION}

aws events put-rule \
  --name "karpenter-spot-interruption" \
  --event-pattern '{"source":["aws.ec2"],"detail-type":["EC2 Spot Instance Interruption Warning"]}' \
  --region ${AWS_REGION}

aws events put-targets \
  --rule "karpenter-spot-interruption" \
  --targets "Id=karpenter-queue,Arn=arn:aws:sqs:${AWS_REGION}:${AWS_ACCOUNT_ID}:karpenter-${CLUSTER_NAME}" \
  --region ${AWS_REGION}

Bei einer Spot-Interruption empfaengt Karpenter die Nachricht, cordoned den Node, draint Pods unter Beachtung der PDBs und startet einen Ersatz-Node. Die Fleet-API waehlt automatisch den Instance-Typ mit der hoechsten Verfuegbarkeit.

Kostenbeispiel

Cluster Autoscaler (On-Demand, m5.2xlarge):
  Monatsdurchschnitt bei 60% Auslastung: ca. 1.800 EUR/Monat

Karpenter (Mix aus m5/m6i/m7i, Spot wo moeglich):
  Consolidation + Spot: ca. 650 EUR/Monat

Ersparnis: ca. 64%

Fuer weitergehende Kostenstrategien: Kubernetes Kosten-Optimierung Praxis-Guide.

Migration vom Cluster Autoscaler

Schritt 1: Karpenter parallel installieren

# Cluster Autoscaler NICHT sofort deinstallieren
kubectl apply -f nodepool-general.yaml
kubectl get nodepools
kubectl get ec2nodeclasses

Schritt 2: Workloads schrittweise migrieren

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  template:
    spec:
      nodeSelector:
        karpenter.sh/nodepool: general-purpose
      containers:
        - name: webapp
          image: webapp:latest
          resources:
            requests:
              cpu: "500m"
              memory: 512Mi

Schritt 3: Node Groups entfernen

# ASG auf 0 setzen, dann Cluster Autoscaler deinstallieren
aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name "eks-nodegroup-general" \
  --min-size 0 --desired-capacity 0 --max-size 0

helm uninstall cluster-autoscaler -n kube-system

Monitoring mit Prometheus

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: karpenter
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: karpenter
  endpoints:
    - port: http-metrics
      interval: 30s

Wichtige Metriken:

karpenter_nodes_created_total                          # Provisionierte Nodes
karpenter_pods_startup_duration_seconds                # Pod-Startzeit
karpenter_disruption_actions_performed_total            # Consolidation-Aktivitaet
karpenter_nodepools_usage{resource_type="cpu"}         # CPU-Auslastung
karpenter_nodeclaims_created_total{capacity_type="spot"} # Spot vs. On-Demand

Fuer die vollstaendige Monitoring-Strategie: Kubernetes Monitoring und Observability.

Troubleshooting

# Controller Logs
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter -f

# NodePool Limits erreicht?
kubectl get nodepool general-purpose -o jsonpath='{.status.resources}'

# Subnets/Security Groups pruefen
kubectl describe ec2nodeclass default

# Instance-Typ nicht verfuegbar?
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter | grep "InsufficientInstanceCapacity"

# Alle Karpenter-Nodes anzeigen
kubectl get nodes -l karpenter.sh/nodepool

Fazit

Karpenter ist fuer EKS-Cluster der klare Nachfolger des Cluster Autoscalers. Schnellere Provisionierung, automatische Instance-Auswahl und aggressive Consolidation sparen Kosten und reduzieren Komplexitaet. Die Migration kann schrittweise erfolgen. Fuer EKS-Best-Practices: AWS EKS Enterprise Kubernetes.


Verwandte Artikel

Braucht ihr Unterstuetzung bei der Karpenter-Einfuehrung oder der Migration vom Cluster Autoscaler? Kontaktiert uns fuer eine individuelle Beratung.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen