Inhoudsopgave
- Wat je hebt aan het einde
- Vereisten
- Waarom Karpenter in plaats van Cluster Autoscaler
- Karpenter installeren op EKS
- Een NodePool en EC2NodeClass aanmaken
- Spot en on-demand met gewogen NodePools
- Disruption, consolidation en drift
- Migreren vanaf Cluster Autoscaler
- Karpenter monitoren met Prometheus en Grafana
- Production hardening checklist
- Veelvoorkomende valkuilen
- Wanneer escaleren
Wat je hebt aan het einde
Een draaiende Karpenter-installatie op EKS die nodes provisiont op basis van daadwerkelijke pod-requirements, onderbenutte capaciteit automatisch consolideert, Spot-interruptions afhandelt, en metrics naar je Prometheus-monitoringstack stuurt.
Vereisten
- Een EKS-cluster met Kubernetes 1.28+
kubectl,helmenawsCLI lokaal geinstalleerd- IAM-permissies om rollen, policies en EC2-tags aan te maken
- Subnets en security groups getagd voor Karpenter-discovery (komt aan bod bij de installatiestappen)
- Prometheus en Grafana geinstalleerd als je de monitoringsectie meteen werkend wilt hebben
Waarom Karpenter in plaats van Cluster Autoscaler
Cluster Autoscaler (CA) scant periodiek op pending pods en vraagt vervolgens een Auto Scaling Group om een node toe te voegen uit een voorgedefinieerde set instance types. Die round-trip duurt 3–5 minuten op een goede dag.
Karpenter slaat de ASG gewoon over. Het bekijkt elke unschedulable pod event-by-event, berekent welk instance type het best past uit maximaal 60 kandidaten, en roept de EC2 Fleet API direct aan. Het resultaat: nodes ready in 45–60 seconden.
| Dimensie | Cluster Autoscaler | Karpenter |
|---|---|---|
| Trigger | Periodieke scan (10+ s) | Event per pending pod |
| Nodeselectie | Voorgedefinieerde node groups | Alle instance types die aan NodePool voldoen |
| Provisioningtijd | 3–5 min (ASG-vertraging) | 45–60 s (EC2 Fleet direct) |
| Consolidation | Verwijdert alleen idle nodes | Empty, multi-node en single-node consolidation |
| Instance-flexibiliteit | Beperkt tot node group types | Elk instance dat aan requirements voldoet |
Teams die van CA naar Karpenter overstappen rapporteren doorgaans 20–40% kostenbesparing door betere bin-packing en automatische consolidation.
Even een belangrijk punt dat vaak verwarring geeft: Karpenter vervangt Cluster Autoscaler, niet HPA of VPA. HPA schaalt pods, VPA past resource requests aan, Karpenter provisiont de nodes waar die pods op draaien. Ze zijn complementaire lagen, geen alternatieven.
Karpenter installeren op EKS
Stap 1: omgevingsvariabelen instellen
export KARPENTER_NAMESPACE="kube-system"
export KARPENTER_VERSION="1.11.1" # laatste stabiele versie per april 2026
export K8S_VERSION="1.31" # moet overeenkomen met je EKS-clusterversie
export CLUSTER_NAME="production-main"
export AWS_DEFAULT_REGION="eu-west-1"
export AWS_ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
Stap 2: IAM-rollen aanmaken
Karpenter heeft twee IAM-rollen nodig:
- KarpenterNodeRole voor de EC2-instances die het lanceert. Koppel
AmazonEKSWorkerNodePolicy,AmazonEKS_CNI_Policy,AmazonEC2ContainerRegistryReadOnlyenAmazonSSMManagedInstanceCore. - KarpenterControllerRole voor de controller-pod zelf, via IRSA. Deze rol heeft scoped EC2-permissies nodig:
ec2:RunInstances,ec2:CreateFleet,ec2:TerminateInstances,ec2:DescribeInstances,ec2:DescribeSubnets,ec2:DescribeSecurityGroups,ec2:DescribePlacementGroups(verplicht sinds v1.11),ec2:CreateTags,ec2:DeleteTags,iam:PassRole,iam:ListInstanceProfiles,ssm:GetParameter,sqs:ReceiveMessage,sqs:DeleteMessage, en meer.
Beveiligingsopmerking: elke principal die de tags karpenter.sh/managed-by, karpenter.sh/nodepool en kubernetes.io/cluster/${CLUSTER_NAME} kan aanmaken of verwijderen, kan indirect beinvloeden wat Karpenter provisiont. Beperk tag-CRUD in je IAM-policies.
Stap 3: subnets en security groups taggen
# Karpenter vindt subnets en security groups via tags
aws ec2 create-tags \
--resources subnet-0abc1234 subnet-0def5678 sg-0aabb1122 \
--tags Key=karpenter.sh/discovery,Value=${CLUSTER_NAME}
Karpenter kiest het subnet met de meeste beschikbare IP's per availability zone.
Stap 4: installeren met Helm
helm registry logout public.ecr.aws # oude tokens opruimen
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version "${KARPENTER_VERSION}" \
--namespace "${KARPENTER_NAMESPACE}" \
--set "settings.clusterName=${CLUSTER_NAME}" \
--set "settings.interruptionQueue=${CLUSTER_NAME}" \
--wait
Stap 5: controleren of de controller draait
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter --tail=20
Verwachte output bevat regels als controller started en watching for pending pods. Geen ERROR-regels over IAM of STS.
Een NodePool en EC2NodeClass aanmaken
Een NodePool definieert wat voor soort nodes Karpenter mag provisionen (instance families, capacity types, architecturen, limieten). Een EC2NodeClass definieert hoe ze op AWS worden aangemaakt (AMI, subnets, security groups, IAM, disk).
# ec2nodeclass.yaml
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
role: "KarpenterNodeRole-production-main"
amiSelectorTerms:
- alias: "al2023@v20250301" # pin in productie; gebruik nooit @latest
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "production-main"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "production-main"
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 50Gi
volumeType: gp3
encrypted: true
metadataOptions:
httpEndpoint: enabled
httpTokens: required # alleen IMDSv2
httpPutResponseHopLimit: 1
# nodepool.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
metadata:
labels:
team: platform
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-family
operator: In
values: ["m", "c", "r"] # brede families voor bin-packing flexibiliteit
expireAfter: 720h # 30 dagen; dwingt node-refresh af
terminationGracePeriod: 48h # harde deadline voor draining
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
budgets:
- nodes: "10%"
limits:
cpu: "1000"
memory: "1000Gi"
weight: 50
Beide toepassen:
kubectl apply -f ec2nodeclass.yaml -f nodepool.yaml
Controleer of Karpenter de NodePool ziet:
kubectl get nodepools
Verwachte output:
NAME NODECLASS WEIGHT AGE
default default 50 12s
Spot en on-demand met gewogen NodePools
Voor workloads die interruption tolereren, splits je op in twee NodePools met weight-based priority:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot
spec:
weight: 100 # hoger gewicht = eerst geprobeerd
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.aws/instance-family
operator: In
values: ["m", "c", "r", "m6i", "c6i", "r6i"] # breed voor price-capacity-optimized
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "500"
---
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: on-demand-fallback
spec:
weight: 10
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "500"
Karpenter probeert eerst de weight-100 Spot-pool. Als er geen Spot-capaciteit beschikbaar is, valt het terug op de weight-10 on-demand pool. Voor Spot gebruikt Karpenter de price-capacity-optimized allocation strategy, die prijs en interruptiekans afweegt in plaats van blindelings de goedkoopste pool te kiezen. Houd instance families breed: minder dan 15 instance types blokkeert single-node Spot-consolidation.
Voor GPU-workloads gebruik je een aparte NodePool met taints om dure GPU-nodes te isoleren van gewone workloads.
Disruption, consolidation en drift
Het disruptionmodel van Karpenter kent twee categorieen:
Voluntary (beperkt door disruption budgets):
- Consolidation werkt in drie lagen: verwijder eerst lege nodes, probeer dan multi-node consolidation (verplaats workloads van meerdere nodes naar minder), en dan single-node consolidation (vervang een node door een kleinere).
WhenEmptyOrUnderutilizedschakelt alle drie in.WhenEmptydoet alleen lege nodes. - Drift detecteert wanneer een draaiende node niet meer overeenkomt met de gewenste NodePool- of EC2NodeClass-spec (gewijzigde AMI, aangepaste requirements, gewijzigde security groups). Karpenter vervangt gedrifte nodes geleidelijk.
Forceful (niet beperkt):
- Expiration draineert en termineert nodes wanneer
expireAfterverloopt (standaard 720h / 30 dagen). - Interruption handelt EC2-lifecycle-events af: Spot 2-minutenwaarschuwingen, gepland onderhoud, instance-stopsignalen. Karpenter provisiont alvast een vervanging tijdens het waarschuwingsvenster.
Disruption budgets per reden
Sinds v1.0 kun je budgets per disruptiereden scopen:
disruption:
budgets:
- nodes: "20%"
reasons: ["Drifted"]
- nodes: "10%"
reasons: ["Underutilized"]
- nodes: "0"
reasons: ["Empty"]
schedule: "0 9 * * mon-fri" # blokkeer verwijdering lege nodes tijdens kantooruren
duration: 8h
Specifieke pods beschermen
Voeg karpenter.sh/do-not-disrupt: "true" toe als pod-annotatie om voluntary disruption (consolidation, drift) op de node van die pod te blokkeren. Dit blokkeert geen expiration of Spot-interruption. Combineer het met PodDisruptionBudgets voor bredere beschikbaarheidsgaranties.
Migreren vanaf Cluster Autoscaler
Karpenter en Cluster Autoscaler kunnen naast elkaar draaien. Zero-downtime migratie volgt deze volgorde:
Stap 1: workloads voorbereiden
Voeg PodDisruptionBudgets toe aan elke productie-Deployment. Zonder PDB's worden bij het afschalen van oude node groups alle replica's in een keer ge-evict. Stel accurate resource requests in zodat Karpenter effectief kan bin-packen.
Stap 2: Karpenter deployen naast CA
Installeer Karpenter zoals hierboven beschreven. Gebruik nodeAffinity op het Karpenter-controller Deployment om het vast te pinnen op nodes in je bestaande managed node group. Karpenter mag niet draaien op nodes die het zelf beheert (circulaire dependency als het zijn eigen controller evict).
Stap 3: NodePool en EC2NodeClass aanmaken
Pas de manifests uit de vorige secties toe. Karpenter begint direct met het bekijken van unschedulable pods, maar raakt bestaande CA-managed nodes niet aan.
Stap 4: Cluster Autoscaler naar nul schalen
kubectl scale deployment cluster-autoscaler -n kube-system --replicas=0
Stap 5: node group-capaciteit geleidelijk verlagen
Verlaag de minSize en desiredCapacity van je ASG's stapsgewijs. Naarmate workloads op natuurlijke wijze churnen (deployments, scaling events, pod-restarts), landen pods op Karpenter-geprovisionneerde nodes. Oude nodes draineen door normale turnover.
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name eks-managed-ng-1 \
--min-size 2 \
--desired-capacity 2
Houd minimaal 2 nodes per AZ in de oorspronkelijke node group totdat je hebt bevestigd dat Karpenter alle workloads afhandelt.
Stap 6: verifieren en opruimen
kubectl get nodes -L karpenter.sh/nodepool
Nodes met een karpenter.sh/nodepool-label worden door Karpenter beheerd. Zodra er geen workloads meer op onbeheerde nodes draaien, kun je de oude ASG's verwijderen.
Salesforce migreerde meer dan 1.000 EKS-clusters naar Karpenter met precies deze gefaseerde aanpak.
Karpenter monitoren met Prometheus en Grafana
Karpenter stelt Prometheus-metrics beschikbaar op karpenter.kube-system.svc.cluster.local:8080/metrics. Als je kube-prometheus-stack draait, voeg dan een ServiceMonitor toe:
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
path: /metrics
Belangrijke metrics om te volgen
| Metric | Wat het je vertelt |
|---|---|
karpenter_nodes_created_total |
Hoeveel nodes Karpenter heeft geprovisioned |
karpenter_nodes_terminated_total |
Hoeveel nodes zijn verwijderd (consolidation, expiry, interruption) |
karpenter_pods_startup_duration_seconds |
Tijd van pod-creatie tot running state |
karpenter_scheduler_queue_depth |
Pending pod-batches die op nodes wachten |
karpenter_voluntary_disruption_decisions_total |
Consolidation- en drift-beslissingen |
karpenter_nodes_termination_duration_seconds |
Draintijd; hoge p95 signaleert vastgelopen PDB's |
karpenter_voluntary_disruption_eligible_nodes |
Nodes die in aanmerking komen voor consolidation maar niet worden aangepakt |
Volledige referentie: Karpenter metrics-documentatie.
Grafana-dashboards
Importeer deze van Grafana Labs:
- Karpenter Overview (ID 21699) voor NodePool-, node- en pod-aantallen
- Karpenter Performance (ID 22173) voor cloud provider-fouten en pod-opstarttijd
- Karpenter Activity (ID 18862) voor scale-up/down event-tijdlijnen
Alerts die de moeite waard zijn
- Langdurig hoge queue depth:
karpenter_scheduler_queue_depth > 5langer dan 2 minuten betekent dat Karpenter geen capaciteit kan vinden. Controleer NodePool-limieten, instance-beschikbaarheid en IAM-permissies. - Trage termination:
histogram_quantile(0.95, karpenter_nodes_termination_duration_seconds_bucket) > 600betekent dat draining langer dan 10 minuten duurt. Kijk naar blokkerende PDB's ofdo-not-disrupt-annotaties. - Provisioning-piek: een plotselinge stijging in
rate(karpenter_nodeclaims_created_total[5m])kan wijzen op een kapotte HPA-loop of een losgeslagen Deployment. - Consolidation geblokkeerd:
karpenter_voluntary_disruption_eligible_nodesblijft hoog terwijlkarpenter_voluntary_disruption_decisions_totalvlak blijft. Disruption budgets of PDB's voorkomen opruiming.
Production hardening checklist
- Pin AMI-versies. Gebruik
al2023@v20250301, niet@latest. Test AMI-updates in staging voordat je ze via drift uitrolt. - Draai Karpenter op Fargate of een dedicated managed node group. Nooit op Karpenter-managed nodes. Een circulaire dependency betekent dat Karpenter zijn eigen controller evict.
- Vereis IMDSv2. Stel
httpTokens: requiredin bij EC2NodeClassmetadataOptionsom SSRF-gebaseerde credential-diefstal te blokkeren. - Stel NodePool resource-limieten in. Definieer altijd
limits.cpuenlimits.memoryom uitgaven per NodePool te begrenzen. - Gebruik IRSA voor de controllerrol. Koppel IAM-permissies nooit via EC2-instance metadata.
- Houd instance families breed voor Spot. Minder dan 15 instance type-opties blokkeert single-node Spot-consolidation.
- Stel
terminationGracePeriodin wanneer jeexpireAftergebruikt. Zonder dat blokkeert een pod metdo-not-disruptde node-drain oneindig.
Veelvoorkomende valkuilen
Pods blijven in Pending ondanks beschikbare NodePools. De pod-requirements (resource requests, node selectors, tolerations) passen niet binnen de requirements van een NodePool. Voer kubectl describe pod <naam> uit en bekijk de Events-sectie voor scheduling-faalredenen. Karpenter kan alleen nodes provisionen die voldoen aan de doorsnede van NodePool-constraints en pod-constraints.
Nodes worden aangemaakt en meteen weer getermineerd. De EC2-instance start maar slaagt er niet in om het cluster te joinen. Veelvoorkomende oorzaken: ontbrekende VPC-endpoints voor STS of SSM in private clusters, verkeerde security group-regels die kubelet-communicatie blokkeren, of een verkeerd IAM-instance profile.
Consolidation vindt niet plaats. Check karpenter_voluntary_disruption_eligible_nodes. Als die hoog is maar decisions nul, blokkeren disruption budgets of PDB's. Controleer ook of consolidationPolicy op WhenEmptyOrUnderutilized staat, niet op WhenEmpty.
Trage Windows-nodes. Windows-nodes doen er ~6 minuten over om het cluster te joinen plus 15–20 minuten om de base image te pullen. Dit is een inherente platformbeperking, geen Karpenter-probleem. Verwacht geen sub-minuut provisioning voor Windows-workloads.
v1.0-migratiefouten. Als je upgradet van Karpenter v0.x, zijn de Provisioner- en AWSNodeTemplate-CRD's verwijderd in v1.0. Draai karpenter-convert -f provisioner.yaml > nodepool.yaml voordat je upgradet. v1.1 dropt v1beta1 volledig.
Wanneer escaleren
Verzamel deze informatie voordat je hulp vraagt:
- Karpenter-controllerlogs:
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter --tail=100 - NodePool- en EC2NodeClass-specs:
kubectl get nodepools -o yamlenkubectl get ec2nodeclasses -o yaml - Pending pods en hun events:
kubectl get pods --field-selector=status.phase=Pending -A - NodeClaim-status:
kubectl get nodeclaims -o wide - Karpenter-versie:
helm list -n kube-system | grep karpenter - EKS-clusterversie en platformversie
- IAM-rol ARN's voor zowel controller- als noderollen