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
- Zozbiera requirements z
NodePool(whitelistované kategórie, architektúry, capacity types). - Stiahne aktuálny cenník EC2 (cez
pricing:GetProductsAPI) a spot prices (cezec2:DescribeSpotPriceHistory). - Simuluje bin-packing: aký najmenší (= najlacnejší) node pokryje čakajúce pody?
- 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
AKSNodeClassCRD 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-karpenterflagom (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
karpentercontroller v stabilnej verzii ≥v1.0.0 (koniec 2024+). Autoritatívny zoznam je vpkg/metricsupstream 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.cpualebolimits.memoryje plné. Riešenie: zvýšiť limity alebo pridať nový NodePool. - Žiadny vyhovujúci instance type — pod má
nodeSelectoraleboaffinityktoré 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.