🟡 Intermediate

Karpenter — moderný Kubernetes node autoscaler

Karpenter je open-source Kubernetes node autoscaler, ktorý vznikol v AWS v roku 2021 ako odpoveď na dlhoročné frustrujúce limity Cluster Autoscalera. Vo februári 2023 bol darovaný CNCF a aktuálne je v stave CNCF Incubating. V októbri 2024 vyšiel v1 GA a od vtedy beží oficiálna pracovná skupina pre multi-cloud. AWS provider je plne production-ready, Azure provider je v public preview (AKS integrácia), GCP v experimentálnom stave.

Základná myšlienka je jednoduchá a radikálna zároveň: namiesto toho, aby ste vopred definovali, aké skupiny nodov (node groups, ASG) váš klaster má, nechajte Karpenter nech sa pozrie na nevyschedulované pody, rozhodne, aký node treba (instance type, veľkosť, architektúra, spot vs on-demand), a ten node provisoní priamo — bez prostredníka v podobe Auto Scaling Group.

Výsledok: pody, ktoré čakajú na node, sú v produkčnom EKS klastri typicky spustené do 60–90 sekúnd od schedulingu. Cluster Autoscaler na rovnakej úlohe beží 3–5 minút, niekedy dlhšie.


Prečo Cluster Autoscaler nestačí

Cluster Autoscaler (CA) bol navrhnutý pre svet, kde node skupiny existujú vopred a autoscaler iba rozhoduje, koľko nodov z každej skupiny má byť active. Tento model má niekoľko fundamentálnych problémov.

Statická konfigurácia node groups

Aby CA vedel škálovať, musíte mať predefinované Node Groups / ASG — napr. m5.large-spot, m5.xlarge-on-demand, c5.2xlarge-gpu. Každá skupina je iná ASG v AWS, iný MIG v GCP. Keď príde pod s požiadavkami, ktoré žiadna existujúca skupina nesplňa, CA nevie pomôcť.

Praktický dôsledok: tímy definujú desiatky node groups "pro forma", aby pokryli potenciálne kombinácie. Väčšina z nich je prázdna, ale musíte ich udržiavať, aktualizovať AMI, spravovať.

Pomalý loop

CA funguje na polling modeli — každých 10 sekúnd skontroluje stav klastru. Keď nájde unschedulable pody, zavolá cloud API pre scale-up. Potom čaká, kým sa nový node prihlási. Celý proces trvá:

CA detects pending pod:        ~10s (polling interval)
CA calls ASG scale-up API:     ~5s
AWS spustí EC2 instance:       ~60–120s
kubelet sa zaregistruje:       ~30s
Node prejde na Ready:          ~30s
──────────────────────────────────────
Celkom:                        2.5 – 5 minút

Bin-packing je suboptimálny

CA škáluje node group, ktorá "najlepšie" vyhovuje nevyschedulovaným podom — ale "najlepšie" je obmedzené na dostupné skupiny. Ak máte m5.large skupinu a pod potrebuje 3 CPU, CA pridá m5.large (2 CPU) — nie, ten nestačí — a prejde na ďalšiu skupinu. Nikdy nenavrhne, že by bol c5.xlarge lacnejší a lepšie padol.

Karpenter rieši všetky tri problémy

Karpenter detects pending pod:   ~5s (watch, nie polling)
Karpenter calls EC2 API:         ~5s
AWS spustí EC2 instance:         ~60–120s
kubelet sa zaregistruje:         ~20s
Node prejde na Ready:            ~15s
──────────────────────────────────────
Celkom:                          ~90s (typicky)

Karpenter si sám vyberie najvhodnejší (a zvyčajne najlacnejší) instance type z celého dostupného katalógu EC2 — bez predkonfigurovaných skupín.


Architektúra — ako Karpenter funguje

Karpenter beží ako Deployment v Kubernetes klastri (typicky v namespace karpenter). Pozostáva z jedného hlavného procesu — karpenter-controller — ktorý implementuje niekoľko kontrolerov.

┌─────────────────────────────────────────────────────────┐
│  Kubernetes Control Plane                               │
│  ┌──────────────────────────────────────────────────┐  │
│  │  karpenter-controller (Deployment, 2 repliky)    │  │
│  │  ├── Provisioner Loop    (pending pods → nodes)  │  │
│  │  ├── Disruption Loop     (consolidation, drain)  │  │
│  │  ├── NodeClaim Controller (node lifecycle)       │  │
│  │  └── NodePool Controller  (CRD reconciliation)   │  │
│  └───────────────┬──────────────────────────────────┘  │
└──────────────────┼──────────────────────────────────────┘
                   │  priame volanie cloud API
          ┌────────▼────────┐
          │  AWS EC2 API    │  (alebo Azure VMSS API, GCP GCE API)
          │  (bez ASG)      │
          └────────┬────────┘
                   │
         ┌─────────▼──────────┐
         │  EC2 Node (nový)   │
         │  s bootstrap script│
         │  → kubelet → Ready │
         └────────────────────┘

Kľúčové komponenty

NodePool CRD — definuje "pravidlá hry": aké instance types sú povolené, aké limity (CPU/memory celkovo), aká disruption policy. Nahradil starý Provisioner CRD z v1alpha5.

NodeClass CRD — cloud-špecifická konfigurácia: pre AWS je to EC2NodeClass (AMI, subnets, security groups, IAM role), pre Azure AKSNodeClass, pre GCP GCPNodeClass. NodePool odkazuje na NodeClass cez nodeClassRef.

NodeClaim CRD — interný objekt, ktorý Karpenter vytvára pre každý node, ktorý má byť provisonovaný. Predstavuje "objednávku" na node voči cloudu. Keď node vznikne a zaregistruje sa, NodeClaim sa stane viazaným na konkrétny Kubernetes Node objekt.

SQS Interruption Queue (AWS) — pre Spot instance handling. Karpenter počúva na SQS frontu, kam AWS posiela 2-minútové upozornenia pred odobratím spot inštancie. Karpenter ihneď začne drainovať node a proaktívne provisoní náhradu.

Karpenter vs kube-scheduler

Dôležité: Karpenter nenahradza kube-scheduler. kube-scheduler stále rozmiestňuje pody na existujúce nody. Karpenter reaguje na situáciu, keď kube-scheduler označí pod ako Unschedulable — vtedy Karpenter príde s novým nodom.


NodePool CRD — od v1beta1 k v1 GA

V roku 2024 Karpenter vydal v1 API (GA) — NodePool nahradil Provisioner (v1alpha5) a neskôr NodePool z v1beta1. Ak ste mali starý klaster s Provisioner CRD, musíte migrovať.

Základná štruktúra NodePool:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general-purpose
spec:
  # Šablóna pre každý node, ktorý Karpenter vytvorí
  template:
    metadata:
      labels:
        nodepool: general-purpose
      # Vlastné labels — dostupné pre node selektory
    spec:
      # Odkaz na cloud-špecifickú konfiguráciu
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default

      # Požiadavky na node (filtre instance types)
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]   # minimálne 5. generácia
        - key: node.kubernetes.io/instance-type
          operator: NotIn
          values: ["m5.nano", "t3.micro"]   # explicitné vylúčenia

      # Expiry — nody sa po 720h (30d) nahradí novšou verziou AMI
      expireAfter: 720h

  # Disruption policy — kedy a ako Karpenter smie nody odstraňovať
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
    # budgets = brzdí disruption, max 10% nodov naraz
    budgets:
      - nodes: "10%"
      # počas business hodín nerobíme žiadne disruption
      - nodes: "0"
        schedule: "0 9 * * MON-FRI"
        duration: 8h

  # Globálny strop pre tento NodePool
  limits:
    cpu: "1000"         # max 1000 CPU jadier celkovo
    memory: "4000Gi"    # max 4 TB RAM

Dôležité polia requirements

Karpenter pozná desiatky well-known labels pre filtrovanie instance types. Najpoužívanejšie:

Label Popis
karpenter.sh/capacity-type on-demand, spot
kubernetes.io/arch amd64, arm64
karpenter.k8s.aws/instance-category c (compute), m (general), r (memory), g (GPU)...
karpenter.k8s.aws/instance-generation číslo generácie (Gt: "5" = len 6. gen a novšie)
node.kubernetes.io/instance-type konkrétne instance types (whitelist / blacklist)
topology.kubernetes.io/zone AZ selector

EC2NodeClass — cloud-špecifická konfigurácia

EC2NodeClass definuje, ako vyzerá node na infraštruktúrnej úrovni — AMI, subnety, security groups, IAM profil, userData, storage.

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  # AMI výber — Karpenter automaticky vyberie najnovšiu AMI
  # ktorá pasuje k verzii Kubernetes v klastri
  amiSelectorTerms:
    - alias: al2023@latest   # Amazon Linux 2023, najnovšia kompatibilná
    # Alternatíva — vlastná AMI s custom ID:
    # - id: ami-0123456789abcdef0
    # Alebo tag-based:
    # - tags:
    #     karpenter.sh/discovery: my-cluster
    #     kubernetes.io/role/node: "1"

  # Subnet výber — Karpenter môže použiť viacero subnetov
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-eks-cluster
        kubernetes.io/role/internal-elb: "1"

  # Security groups — pridané ku každému nodu
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-eks-cluster

  # IAM Instance Profile — node musí mať oprávnenia pre kubelet
  instanceProfile: "KarpenterNodeInstanceProfile"

  # EBS root volume
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 50Gi
        volumeType: gp3
        iops: 3000
        throughput: 125
        deleteOnTermination: true
        encrypted: true
        kmsKeyID: "arn:aws:kms:eu-west-1:123456789:key/my-key"

  # Vlastný userData — bash script spustený pri bootstrap
  # Pozor: Karpenter pridáva vlastné bootstrap príkazy, tu len doplnky
  userData: |
    #!/bin/bash
    echo "vm.max_map_count=262144" >> /etc/sysctl.conf
    sysctl -p

  # IMDS (Instance Metadata Service) — vždy použite v2
  metadataOptions:
    httpEndpoint: enabled
    httpProtocolIPv6: disabled
    httpPutResponseHopLimit: 1   # 1 = iba z node, nie z podov
    httpTokens: required         # IMDSv2 vynútený

  # Tags aplikované na EC2 inštanciu
  tags:
    Environment: production
    Team: platform
    ManagedBy: karpenter

EC2NodeClass pre Azure (AKSNodeClass)

Pre Azure je ekvivalentom AKSNodeClass:

apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
  name: default
spec:
  imageFamily: AzureLinux   # alebo Ubuntu2204
  osDiskSizeGB: 128

Inštalácia na AWS EKS

1. Príprava — IAM Role pre Karpenter controller

Karpenter musí mať IAM oprávnenia pre volanie EC2 API. Odporúčaný prístup je IRSA (IAM Roles for Service Accounts).

# Premenné
CLUSTER_NAME="my-eks-cluster"
AWS_REGION="eu-west-1"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Vytvorenie IAM policy pre Karpenter controller
cat <<EOF > karpenter-controller-policy.json
{
  "Statement": [
    {
      "Action": [
        "ssm:GetParameter",
        "ec2:DescribeImages",
        "ec2:RunInstances",
        "ec2:DescribeLaunchTemplates",
        "ec2:CreateLaunchTemplate",
        "ec2:DeleteLaunchTemplate",
        "ec2:DescribeInstances",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeInstanceTypes",
        "ec2:DescribeInstanceTypeOfferings",
        "ec2:DescribeAvailabilityZones",
        "ec2:DescribeSpotPriceHistory",
        "ec2:TerminateInstances",
        "ec2:CreateTags",
        "pricing:GetProducts",
        "sqs:DeleteMessage",
        "sqs:GetQueueAttributes",
        "sqs:GetQueueUrl",
        "sqs:ReceiveMessage"
      ],
      "Effect": "Allow",
      "Resource": "*",
      "Sid": "Karpenter"
    },
    {
      "Action": "iam:PassRole",
      "Effect": "Allow",
      "Resource": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole",
      "Sid": "PassNodeIAMRole"
    },
    {
      "Action": "eks:DescribeCluster",
      "Effect": "Allow",
      "Resource": "arn:aws:eks:${AWS_REGION}:${AWS_ACCOUNT_ID}:cluster/${CLUSTER_NAME}",
      "Sid": "EKSClusterEndpointLookup"
    }
  ],
  "Version": "2012-10-17"
}
EOF

aws iam create-policy \
  --policy-name KarpenterControllerPolicy \
  --policy-document file://karpenter-controller-policy.json

2. IRSA — viazanie IAM Role na Kubernetes ServiceAccount

# Vytvorenie IRSA pre Karpenter
eksctl create iamserviceaccount \
  --cluster "$CLUSTER_NAME" \
  --namespace karpenter \
  --name karpenter \
  --role-name KarpenterControllerRole \
  --attach-policy-arn "arn:aws:iam::${AWS_ACCOUNT_ID}:policy/KarpenterControllerPolicy" \
  --approve

# IAM Role pre nody (EC2 Instance Profile)
aws iam create-role \
  --role-name KarpenterNodeRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]
  }'

# Pripojiť potrebné policies k node role
for policy in \
  AmazonEKSWorkerNodePolicy \
  AmazonEKS_CNI_Policy \
  AmazonEC2ContainerRegistryReadOnly \
  AmazonSSMManagedInstanceCore; do
  aws iam attach-role-policy \
    --role-name KarpenterNodeRole \
    --policy-arn "arn:aws:iam::aws:policy/${policy}"
done

aws iam create-instance-profile --instance-profile-name KarpenterNodeInstanceProfile
aws iam add-role-to-instance-profile \
  --instance-profile-name KarpenterNodeInstanceProfile \
  --role-name KarpenterNodeRole

3. aws-auth — nody musia byť v klastri oprávnené

# Pridanie node role do aws-auth ConfigMap (EKS)
eksctl create iamidentitymapping \
  --cluster "$CLUSTER_NAME" \
  --arn "arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole" \
  --username system:node:{{EC2PrivateDNSName}} \
  --group system:bootstrappers \
  --group system:nodes

4. SQS fronta pre Spot Interruption Handling

# Vytvorenie SQS fronty
QUEUE_NAME="karpenter-${CLUSTER_NAME}"
aws sqs create-queue \
  --queue-name "${QUEUE_NAME}" \
  --attributes MessageRetentionPeriod=300

QUEUE_URL=$(aws sqs get-queue-url --queue-name "${QUEUE_NAME}" --query QueueUrl --output text)
QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url "${QUEUE_URL}" \
  --attribute-names QueueArn --query Attributes.QueueArn --output text)

# EventBridge pravidlá — AWS posiela Spot Interruption events do SQS
for event_type in \
  "aws.ec2" \
  "aws.health"; do
  # (skrátené — v produkcii použite Terraform module karpenter od aws-ia)
  echo "Configuring EventBridge for ${event_type}"
done

Pre produkciu odporúčam použiť Terraform module terraform-aws-modules/eks/aws//modules/karpenter — nastavuje IAM, SQS, EventBridge pravidlá automaticky.

5. Helm inštalácia Karpenter

# Nastavenie verzie — vždy fixovať na konkrétnu verziu!
KARPENTER_VERSION="1.3.3"
KARPENTER_NAMESPACE="kube-system"   # alebo vlastný namespace "karpenter"

# Login do ECR (Karpenter image je v ECR Public)
aws ecr-public get-login-password --region us-east-1 \
  | helm registry login --username AWS --password-stdin public.ecr.aws

# Inštalácia
helm install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version "${KARPENTER_VERSION}" \
  --namespace "${KARPENTER_NAMESPACE}" \
  --create-namespace \
  --set "settings.clusterName=${CLUSTER_NAME}" \
  --set "settings.interruptionQueue=${QUEUE_NAME}" \
  --set "serviceAccount.annotations.eks\.amazonaws\.com/role-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterControllerRole" \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi \
  --wait

# Overenie
kubectl get pods -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter
kubectl logs -n "${KARPENTER_NAMESPACE}" -l app.kubernetes.io/name=karpenter --follow

NodePool pre mix workloadov — production príklad

Reálny klaster má typicky viacero NodePoolov — jeden pre stabilné on-demand workloady, jeden pre spot, prípadne separátny pre GPU alebo arm64.

General-purpose on-demand NodePool

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general-ondemand
  annotations:
    karpenter.sh/description: "Stabilné workloady, critické služby"
spec:
  template:
    metadata:
      labels:
        nodepool: general-ondemand
        capacity-type: on-demand
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["5"]
        # Vylúčiť nano/micro — príliš malé pre produkciu
        - key: karpenter.k8s.aws/instance-size
          operator: NotIn
          values: ["nano", "micro", "small"]
      expireAfter: 720h   # 30 dní — rotácia AMI
      taints: []          # žiadne tainty = všetky pody sem môžu

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 5m
    budgets:
      - nodes: "20%"     # max 20% nodov naraz
      - nodes: "0"
        schedule: "0 9 * * MON-FRI"
        duration: 8h     # business hours = žiadna disruption

  limits:
    cpu: "500"
    memory: "2000Gi"
---
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-workers
  annotations:
    karpenter.sh/description: "Batch/stateless workloady na Spot"
spec:
  template:
    metadata:
      labels:
        nodepool: spot-workers
        capacity-type: spot
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64", "arm64"]   # arm64 bežne lacnejší spot
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r", "t"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["4"]
      expireAfter: 168h   # 7 dní pre spot

      # Taint = len pody s toleranciou spot sem prídu
      taints:
        - key: spot
          value: "true"
          effect: NoSchedule

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m   # spot je lacný, agresívne konsolidujeme
    budgets:
      - nodes: "50%"       # spot môžeme agresívne konsolidovať

  limits:
    cpu: "1000"
    memory: "4000Gi"

Pod s preferenciou spot + fallback na on-demand

apiVersion: apps/v1
kind: Deployment
metadata:
  name: data-processor
spec:
  replicas: 10
  template:
    spec:
      # Tolerancia pre spot taint
      tolerations:
        - key: spot
          operator: Equal
          value: "true"
          effect: NoSchedule

      # Preferujeme spot, ale môžeme aj on-demand
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: karpenter.sh/capacity-type
                    operator: In
                    values: ["spot"]

      containers:
        - name: processor
          image: my-registry/data-processor:1.0.0
          resources:
            requests:
              cpu: "2"
              memory: "4Gi"
            limits:
              cpu: "4"
              memory: "8Gi"

Consolidation — automatická optimalizácia clustra

Consolidation je jedna z najcennejších vlastností Karpenteru. Automaticky zistí, že niektoré nody sú underutilized, presunie pody na iné nody a odstraní prázdne/podvyužité nody.

Politiky consolidácie

WhenEmpty — node sa odstráni iba ak nemá žiadne workload pody (iba DaemonSety). Najbezpečnejší, minimálna disruption.

WhenEmptyOrUnderutilized — kombinovaná politika: Karpenter okrem prázdnych nodov aktívne hľadá príležitosti zmenšiť klaster. Ak vie presunúť pody na menší/lacnejší node a ušetriť, urobí to — drained starý node, nový (lacnejší) provisoní. Táto operácia sa volá replacement consolidation. (V predchádzajúcich verziách existovali samostatné WhenUnderutilized a WhenEmpty — v1 API ich zjednotilo.)

Ako funguje replacement consolidation

Stav pred consolidáciou:
  Node A: m5.2xlarge (8 CPU, 32GB RAM) — využitie: 30% CPU, 25% RAM
  Node B: m5.xlarge  (4 CPU, 16GB RAM) — využitie: 40% CPU, 35% RAM

Karpenter: "Viem to dať na jeden m5.xlarge a ušetriť ~60 USD/deň?"
  → Simuluje bin-packing
  → Áno — presunie pody z Node A na nový m5.xlarge
  → Drainuje Node A
  → Terminuje Node A

Stav po consolidácii:
  Node B: m5.xlarge  — 40% CPU
  Node C: m5.xlarge  — 35% CPU (nový, prijal pody z A)

Ochrana pred nechcenou disruption

Nie každý pod chcete nechať Karpenterom presúvať. Anotácie pre ochranu:

# Pod nesmie byť evicted / disrupted
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

# Node nesmie byť konsolidovaný/terminovaný
# (napr. počas kritickej dávkovej operácie)
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

PodDisruptionBudget — Karpenter rešpektuje PDB. Ak by presun podu porušil PDB, Karpenter počká alebo zvolí iný node.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-server-pdb
spec:
  minAvailable: "90%"   # min 90% replík musí byť vždy up
  selector:
    matchLabels:
      app: api-server

Spot Instance Support — interruption handling

Spot inštancie sú EC2 kapacita za trhovú cenu — typicky 60–90% lacnejšie ako on-demand, ale AWS ich môže odobrať s 2-minútovým varovaním. Karpenter to elegantne rieši.

Interruption handling workflow

AWS → SQS: "SpotInterruptionWarning pre i-0abc123"
Karpenter: prijme správu zo SQS fronty
  → nájde NodeClaim viazaný na i-0abc123
  → okamžite cordon node (nové pody neprídu)
  → začne drain (evict pody s graceful period)
  → simultánne provisoní náhradný node
  → po drain → terminuje starý node
  → pody sa rescheduleujú na nový node
Celý proces: ~90s — pred AWS termináciou (ktorá je za 120s)

Diverzifikácia spot inštancií

Najdôležitejšie pravidlo pre spot: nikdy nepoužívajte len jeden instance type. Karpenter sám diverzifikuje — keď NodePool dovoľuje viacero instance types, Karpenter vyberie z dostupných spot poolov.

# Dobré — Karpenter si vyberie z mnohých instance types
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-cpu
    operator: In
    values: ["4", "8", "16"]   # viacero veľkostí = väčší pool

# Zlé — príliš obmedzené, vysoké riziko interruption
requirements:
  - key: node.kubernetes.io/instance-type
    operator: In
    values: ["m5.xlarge"]   # JEDINÝ type = veľké riziko

Spot a stavové workloady

Spot je vhodný pre:

  • Stateless web/API servery (viacero replík s PDB)
  • Batch job procesory
  • CI/CD runners
  • Dátové pipeline workery

Spot je nevhodný pre:

  • Databázové primárne inštancie (MySQL, PostgreSQL primary)
  • Kafka brokeri (ak nemáte dostatočnú replikáciu)
  • etcd nody
  • Akýkoľvek singleton stateful workload bez rýchlej automatickej obnovy

Multi-architecture — arm64 Graviton

AWS Graviton (arm64) inštancie sú 20–40% lacnejšie pri ekvivalentnom výkone oproti x86 ekvivalentom. Karpenter natívne podporuje multi-arch klastre.

NodePool s arm64

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: graviton-workers
spec:
  template:
    metadata:
      labels:
        kubernetes.io/arch: arm64
        nodepool: graviton
    spec:
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default-arm64   # AMI musí byť arm64!
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["arm64"]
        - key: karpenter.k8s.aws/instance-family
          operator: In
          values: ["m7g", "c7g", "r7g"]   # Graviton 3. generácia
      taints:
        - key: kubernetes.io/arch
          value: arm64
          effect: NoSchedule

  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 2m
  limits:
    cpu: "500"

EC2NodeClass pre arm64

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default-arm64
spec:
  amiSelectorTerms:
    - alias: al2023@latest   # AL2023 existuje pre arm64 natívne
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-eks-cluster
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-eks-cluster
  instanceProfile: "KarpenterNodeInstanceProfile"
  blockDeviceMappings:
    - deviceName: /dev/xvda
      ebs:
        volumeSize: 50Gi
        volumeType: gp3
        deleteOnTermination: true
        encrypted: true

Pod — tolerancia a node selector pre arm64

spec:
  tolerations:
    - key: kubernetes.io/arch
      operator: Equal
      value: arm64
      effect: NoSchedule
  nodeSelector:
    kubernetes.io/arch: arm64
  containers:
    - name: app
      image: my-registry/app:1.0.0   # obraz musí byť multi-arch alebo arm64!

Upozornenie: Obraz musí mať arm64 variantu. Multi-arch imidže (buildované cez docker buildx s --platform linux/amd64,linux/arm64) fungujú automaticky — Docker/containerd stiahne správnu vrstvu podľa architektúry nodu.


Price Optimization — Karpenter ako finančný nástroj

Karpenter pri každom provisioning rozhodnutí vyberá najlacnejší instance type, ktorý spĺňa požiadavky nevyschedulovaných podov. Toto nie je len bin-packing — je to aktívna cenovná optimalizácia.

Ako Karpenter vyberá instance type

  1. Zozbiera requirements z NodePool (whitelistované kategórie, architektúry, capacity types).
  2. Stiahne aktuálny cenník EC2 (cez pricing:GetProducts API) a spot prices (cez ec2:DescribeSpotPriceHistory).
  3. Simuluje bin-packing: aký najmenší (= najlacnejší) node pokryje čakajúce pody?
  4. Z vyhovujúcich instance types vyberie najlacnejší dostupný v cieľovej AZ.

Reálna úspora — príklad

Situácia: 5 podov čakajúcich, každý potrebuje 1.5 CPU a 3Gi RAM

Cluster Autoscaler (s predkonfigurovanou m5.large skupinou):
  → m5.large = 2 CPU, 8GB RAM, ~0.096 USD/h
  → Potrebujem 5 nodov (každý hostí 1 pod, 0.5 CPU wasted)
  → Náklady: 5 × 0.096 = 0.480 USD/h

Karpenter (otvorený katalóg):
  → Vidí: m5.xlarge = 4 CPU, 16GB RAM, ~0.192 USD/h
  → Bin-pack: 2 pody na node (3 CPU použité, 1 wasted)
  → Potrebujem 3 nody (+ 1 pod čaká na consolidation s ďalším)
  → Náklady: 3 × 0.192 = 0.576 USD/h... ale:
  → Ak dovolíme Spot: c5.2xlarge spot = 4× 1.5 CPU, ~0.08 USD/h
  → Potrebujem 2 nody
  → Náklady: 2 × 0.08 = 0.160 USD/h — 67% úspora

V reálnom produkčnom klastri s Karpenterom a správne nastavenými NodePoolmi (mix on-demand + spot, ARM Graviton kde možno) typicky vidíme 30–60% zníženie nákladov na compute oproti statickým node groups s CA.


Integrácia s Cluster Autoscaler — kedy použiť oba

Karpenter a Cluster Autoscaler sa nevylučujú — v niektorých produkčných scenároch má zmysel použiť oba, ale na rôzne node skupiny.

Typická architektúra s oboma

┌─────────────────────────────────────────────────────────┐
│  EKS Klaster                                            │
│                                                         │
│  Managed Node Group "system" (CA)                       │
│  ├── 3× m5.large on-demand                              │
│  ├── kube-system: CoreDNS, kube-proxy, aws-node         │
│  ├── monitoring: Prometheus, Grafana                    │
│  └── Karpenter controller sám!                          │
│                                                         │
│  Karpenter NodePools (workload)                         │
│  ├── general-ondemand: web servery, API                 │
│  ├── spot-workers: batch, CI runners                    │
│  └── graviton: stateless compute                        │
└─────────────────────────────────────────────────────────┘

Prečo Karpenter neodporúča spúšťať sám seba na Karpenter nodoch: Ak Karpenter controller beží na node, ktorý sa Karpenter rozhodne terminovať (consolidation), nastane krátky výpadok. Pre systémové workloady (Karpenter, CoreDNS, monitoring) sú lepšie statické Managed Node Groups s CA — predvídateľné, stabilné.

Segregácia pomocou Node Selectors

# Karpenter controller — systémový namespace, systémový node
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: eks.amazonaws.com/nodegroup
              operator: In
              values: ["system"]   # managed node group, nie Karpenter

Azure a GCP support v 2026

Azure — public preview

Karpenter provider pre Azure (karpenter-provider-azure) je k aprílu 2026 v public preview (AKS integration preview). Integruje sa s AKS a priamo provisioní VM cez Azure VMSS API — analogicky ako AWS provider cez EC2. CRD majú v1alpha2 API version, čo indikuje ešte neustálenú API stabilitu. GA sa očakáva neskôr v 2026.

Kľúčové vlastnosti:

  • Podpora Azure Spot VMs (ekvivalent AWS Spot) s interruption handlingom cez Azure Scheduled Events API
  • AKSNodeClass CRD pre konfiguráciu OS image (AzureLinux, Ubuntu), disk size, network config
  • Integrácia s Azure Managed Identity (IRSA ekvivalent cez Workload Identity)
  • Podporovaný v AKS --enable-karpenter flagom (az aks create/update)
# AKS s Karpenterom
az aks create \
  --resource-group my-rg \
  --name my-aks \
  --enable-karpenter \
  --node-provisioning-mode Auto \
  --network-plugin azure \
  --network-plugin-mode overlay

GCP — beta od 2025

Karpenter provider pre GCP je v beta stave. Provisioní GCE VM instance priamo (bez MIG/autoscaling groups). Podporuje preemptible/spot VMs, custom machine types a Autopilot-incompatible (funguje iba s GKE Standard).

Status k aprílu 2026: GCP provider nemá GA, niektoré features (GPU provisioning, Windows nodes) stále chýbajú. Pre produkčné GKE klastre je zatiaľ bezpečnejšie čakať na GA alebo kombinovať s Node Auto Provisioning (GKE vlastný autoscaler).


Monitoring — metriky a dashboardy

Karpenter controller exponuje Prometheus metriky na porte 8000 (cesta /metrics). Pri Helm inštalácii s serviceMonitor.enabled=true sa automaticky vytvorí ServiceMonitor.

Kľúčové metriky

# Využitie NodePool (gauge) — allocatable a usage per resource
karpenter_nodepools_usage{nodepool="general-ondemand", resource_type="cpu"}
karpenter_nodepools_limit{nodepool="general-ondemand", resource_type="memory"}

# Stav NodeClaims (gauge) — počet per fáza
karpenter_nodeclaims_instance_termination_duration_seconds_bucket
karpenter_nodeclaims_launched_total{nodepool="general-ondemand"}
karpenter_nodeclaims_registered_total{nodepool="general-ondemand"}
karpenter_nodeclaims_terminated_total{nodepool="spot-workers", reason="expired"}

# Stav podov v scheduler (gauge podľa phase)
karpenter_pods_state{phase="pending", nodepool=""}

# Scheduling duration (histogram — ako dlho trval simulation)
karpenter_scheduler_scheduling_duration_seconds_bucket

# Disruption rozhodnutia (counter podľa reason a action)
karpenter_voluntary_disruption_decisions_total{reason="underutilized", consolidation_type="multi"}
karpenter_voluntary_disruption_decisions_total{reason="empty"}
karpenter_voluntary_disruption_decisions_total{reason="drifted"}

# Cloud API volania a chyby
karpenter_cloudprovider_duration_seconds_bucket{controller="nodeclaim_lifecycle", method="Create"}
karpenter_cloudprovider_errors_total{controller="nodeclaim_lifecycle", method="Create"}

# Interruption handling (AWS Spot — správy zo SQS)
karpenter_interruption_received_messages_total{message_type="SpotInterruption"}
karpenter_interruption_actions_performed_total{action_type="CordonAndDrain"}

Poznámka: názvy metrík v Karpenter v1 API boli prepracované oproti v1beta1. Vyššie uvedené sú pre karpenter controller v stabilnej verzii ≥v1.0.0 (koniec 2024+). Autoritatívny zoznam je v pkg/metrics upstream repozitára.

Prometheus alert pravidlá

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: karpenter-alerts
  namespace: monitoring
  labels:
    prometheus: kube-prometheus
    role: alert-rules
spec:
  groups:
    - name: karpenter
      interval: 30s
      rules:
        # Pody dlhšie ako 5 minút v pending state — Karpenter nevie pomôcť
        - alert: KarpenterPodsUnschedulable
          expr: |
            karpenter_pods_state{phase="pending"} > 0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Karpenter nemôže vyschedulovať pody"
            description: >
              {{ $value }} podov čaká viac ako 5 minút. Možné príčiny:
              NodePool limity, instance type exhaustion, IAM problém.

        # Karpenter cloud API chyby
        - alert: KarpenterCloudProviderErrors
          expr: |
            increase(karpenter_cloudprovider_errors_total[5m]) > 5
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Karpenter cloud API chyby"
            description: >
              {{ $value }} cloud API chýb za posledných 5 minút.
              Controller: {{ $labels.controller }}, metóda: {{ $labels.method }}.

        # Scheduling simulation trvá dlho
        - alert: KarpenterSchedulingSlow
          expr: |
            histogram_quantile(0.95,
              rate(karpenter_scheduler_scheduling_duration_seconds_bucket[10m])
            ) > 5
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Karpenter scheduling je pomalý"
            description: "p95 čas simulácie schedulera je {{ $value }}s (threshold: 5s)."

        # Príliš veľa disruption rozhodnutí — "disruption storm"
        - alert: KarpenterDisruptionStorm
          expr: |
            increase(karpenter_voluntary_disruption_decisions_total[10m]) > 10
          for: 0m
          labels:
            severity: warning
          annotations:
            summary: "Karpenter disruption storm"
            description: >
              {{ $value }} nodeov terminovaných za posledných 10 minút.
              Skontrolujte NodePool disruption budgets.

Grafana dashboard

Importujte oficálny Karpenter Grafana dashboard (ID 20347 — "Karpenter" od aws-observability) alebo komunintý 18566. Zobrazuje:

  • Node provisioning rate a latency
  • Disruption operácie v čase
  • NodePool utilization (CPU/RAM requests vs limits)
  • Spot vs on-demand pomer
  • Cloud API error rate

Troubleshooting — najčastejšie problémy

1. Pody zostávajú Pending — Karpenter nie je schopný provisonit

Symptómy: kubectl get pods ukazuje Pending dlhšie ako 2 minúty.

Diagnostika:

# 1. Pozrite Karpenter logy
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter \
  --since=5m | grep -i "cannot|error|failed|unschedulable"

# 2. Pozrite NodeClaim stav
kubectl get nodeclaims
kubectl describe nodeclaim <name>

# 3. Pozrite events na pod
kubectl describe pod <pending-pod-name> -n <namespace>
# Hľadajte: "didn't match NodePool requirements"

# 4. Aké NodePooly sú dostupné a aké sú ich limity?
kubectl get nodepool -o wide
kubectl describe nodepool general-ondemand | grep -A5 Limits

Bežné príčiny:

  • NodePool limit dosiahnutýlimits.cpu alebo limits.memory je plné. Riešenie: zvýšiť limity alebo pridať nový NodePool.
  • Žiadny vyhovujúci instance type — pod má nodeSelector alebo affinity ktoré nezodpovedajú žiadnemu NodePool. Riešenie: zosúladiť requirements.
  • IAM problém — Karpenter nemôže zavolať EC2 API. Riešenie: skontrolovať IRSA anotáciu a IAM policy.
  • Subnet nemá dostatok IP adries — VPC/subnet je vyčerpaný. Riešenie: pridať subnet, použiť VPC CNI prefix delegation.

2. Instance type exhaustion (InsufficientInstanceCapacity)

Symptómy: AWS vracia chybu InsufficientInstanceCapacity — žiadané instance types nie sú dostupné v danej AZ.

# Karpenter logy
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter \
  | grep "InsufficientInstanceCapacity"

Riešenie: Rozšíriť requirements v NodePool — viac instance categories, viac AZ. Karpenter automaticky skúsi ďalší instance type, ak prvý nie je dostupný.

# Úzky NodePool — problém
requirements:
  - key: node.kubernetes.io/instance-type
    operator: In
    values: ["m5.2xlarge"]   # jediný type = problém pri exhaustion

# Lepšie — viacero options
requirements:
  - key: karpenter.k8s.aws/instance-category
    operator: In
    values: ["c", "m", "r"]
  - key: karpenter.k8s.aws/instance-cpu
    operator: In
    values: ["8", "16", "32"]

3. Disruption storm — Karpenter terminuje príliš veľa nodov naraz

Symptómy: Klaster stráca veľa nodov v krátkom čase, pody sú masívne rescheduleované, výkon aplikácií degraduje.

Príčina: Slabo nastavené disruption.budgets alebo absencia PDB.

# Zistiť koľko nodov sa terminuje
kubectl get nodeclaims | grep Deleting

# Dočasne zastaviť všetku disruption — cez budget (emergency brake)
# Anotácia karpenter.sh/do-not-disrupt sa aplikuje na Pod/Node, nie NodePool.
# Pre NodePool použite disruption budget s nodes="0":
kubectl patch nodepool general-ondemand --type merge -p '
spec:
  disruption:
    budgets:
    - nodes: "0"
'

Dlhodobé riešenie:

disruption:
  budgets:
    - nodes: "10%"           # max 10% nodov naraz
    - nodes: "2"             # alebo absolútne číslo

4. AMI drift — nody sa nečakane terminujú

Symptómy: Nody s reason=drifted sa terminujú — Karpenter detekoval, že AMI na node je staršia ako aktuálna (napr. po vydaní novej AL2023 AMI).

Toto je zámerné správanie — Karpenter udržuje nody aktuálne. Ak to chcete spomaliť alebo kontrolovať:

spec:
  template:
    spec:
      expireAfter: 720h   # node žije max 30 dní (aj bez drift)

# Ak chcete fixnú AMI (nie odporúčané pre produkciu):
amiSelectorTerms:
  - id: ami-0123456789abcdef0   # konkrétna AMI verzia

5. Karpenter controller crashuje alebo je OOMKilled

Karpenter controller pre veľké klastre (1000+ nodov) môže potrebovať viac pamäte.

helm upgrade karpenter oci://public.ecr.aws/karpenter/karpenter \
  --namespace kube-system \
  --reuse-values \
  --set controller.resources.requests.memory=2Gi \
  --set controller.resources.limits.memory=2Gi

Produkčné odporúčania

Disruption budgets — brzdí Karpenter

V produkcii je budgets kritické. Bez nich môže Karpenter v horšom scenári (napr. veľký pricing shift na Spot) terminovať desiatky nodov naraz.

disruption:
  budgets:
    # Normálna prevádzka: max 10% nodov
    - nodes: "10%"
    # Business hours: žiadna disruption
    - nodes: "0"
      schedule: "0 8 * * MON-FRI"
      duration: 10h
    # Nočné okno: agresívna konsolidácia
    - nodes: "30%"
      schedule: "0 2 * * *"
      duration: 4h

do-not-disrupt anotácia pre kritické workloady

# Pre kritické singleton workloady
metadata:
  annotations:
    karpenter.sh/do-not-disrupt: "true"

Táto anotácia na pode alebo node zaistí, že Karpenter daný objekt nebude evictovať ani terminovať. Používajte pre:

  • Databázové primárne pody
  • Long-running batch joby, kde prerušenie = strata hodín práce
  • Kritické monitorovacie agenty

PDB pre každú produkčnú aplikáciu

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
  namespace: production
spec:
  maxUnavailable: 1     # alebo minAvailable: "90%"
  selector:
    matchLabels:
      app: api

expireAfter — rotácia AMI

Nastavte expireAfter na NodePool — zabezpečí, že nody sa pravidelne obnovia na novú AMI. Pre produkciu odporúčam 720h (30 dní). Bez tohto flagou by nody mohli bežať s 6 mesiacov starou AMI.

Vždy fixovať verziu Karpenter

# Zistiť aktuálnu najnovšiu stabilnú verziu
helm search repo karpenter --versions | head -5

# Inštalovať s fixnou verziou, nie "latest"
helm install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version "1.3.3" ...

Karpenter má agresívnejší release cyklus ako väčšina CNCF projektov. Vždy testujte upgrade v staging pred produkciou, a čítajte CHANGELOG.md — breaking changes v CRD API sú možné medzi minormi.

Tagging EC2 resources pre cost visibility

# EC2NodeClass — tags prenášané na EC2 inštancie
tags:
  Environment: production
  Team: platform
  Application: web-api
  ManagedBy: karpenter
  CostCenter: "1234"

S tagmi môžete v AWS Cost Explorer filtrovať náklady per tím, per aplikáciu — karpenter-provisionované nody sú plne "taggable" rovnako ako manuálne.


Zhrnutie

Karpenter zmenil spôsob, akým Kubernetes klastre spravujú compute kapacitu. Namiesto statických node groups a reaktívneho CA dostanete inteligentný systém, ktorý:

  • Provisioní nový node do ~90 sekúnd od vzniku nevyschedulovateľného podu.
  • Sám si vyberie najlacnejší vyhovujúci instance type z celého EC2 katalógu.
  • Kontinuálne konsoliduje klaster — odstraňuje prázdne a podvyužité nody.
  • Zvláda Spot interruptions proaktívne — drainuje pred termináciou a pripravuje náhradu.
  • Rotuje AMI automaticky — nody sú vždy na aktuálnom bezpečnostnom patchi.

Pre tímy na AWS EKS je Karpenter dnes de-facto štandardom pre workload node management. Cluster Autoscaler si ponechajte pre systémové managed node groups. Pre Azure je Karpenter provider v public preview a integruje sa natívne do AKS (GA sa očakáva v 2026). GCP zostáva v experimentálnom stave — pre produkciu ešte nie je pripravený.

Najdôležitejšia produkčná lekcia: nastavte disruption.budgets a PDB skôr, ako dostanete Karpenter do produkcie. Bez nich sa consolidation môže správať agresívnejšie, ako čakáte.


Ďalšie čítanie