Amazon EKS — Karpenter
Karpenter is AWS’s next-generation node provisioner. Unlike Cluster Autoscaler (which works through ASGs), Karpenter provisions EC2 instances directly — choosing the right instance type, size, and purchase option for each workload in real time.
Why Karpenter over Cluster Autoscaler?
| Feature | Cluster Autoscaler | Karpenter |
|---|---|---|
| Scales via | ASG desired count | EC2 directly (RunInstances) |
| Instance type selection | Fixed per ASG | Dynamic — picks optimal per workload |
| Scale-up speed | 2-5 minutes | 30-90 seconds |
| Bin-packing | No | Yes — consolidation and right-sizing |
| Spot handling | ASG mixed instances | Native diversification |
| Node lifecycle | ASG manages | Karpenter manages directly |
| Configuration | Per-ASG tags + CA flags | NodePool + EC2NodeClass CRDs |
Installation
Prerequisites
export KARPENTER_VERSION="1.0.0"
export CLUSTER_NAME="my-cluster"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
Helm Install
helm registry logout public.ecr.aws || true
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version "${KARPENTER_VERSION}" \
--namespace kube-system \
--set "settings.clusterName=${CLUSTER_NAME}" \
--set "settings.interruptionQueue=${CLUSTER_NAME}" \
--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
NodePool
NodePool defines what Karpenter can provision:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
metadata:
labels:
team: platform
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand", "spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["5"]
- key: karpenter.k8s.aws/instance-size
operator: In
values: ["medium", "large", "xlarge", "2xlarge"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
expireAfter: 720h # Recycle nodes every 30 days
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
limits:
cpu: "100"
memory: 400Gi
weight: 50
EC2NodeClass
EC2NodeClass defines AWS-specific configuration:
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
role: KarpenterNodeRole-my-cluster
amiSelectorTerms:
- alias: al2023@latest # Amazon Linux 2023
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
iops: 3000
throughput: 125
encrypted: true
tags:
Environment: production
ManagedBy: karpenter
Consolidation
Karpenter actively optimizes node utilization:
- WhenEmpty: Remove nodes with no running pods
- WhenEmptyOrUnderutilized: Replace nodes when workloads can fit on fewer/smaller nodes
Example consolidation flow:
- You have 3x
m5.xlargenodes at 30% utilization - Karpenter calculates workloads fit on 2x
m5.large - It drains one node, then replaces the others with smaller instances
- Result: fewer nodes, right-sized, lower cost
Spot Instance Handling
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
Karpenter automatically:
- Diversifies across instance types for Spot
- Handles Spot interruption notices
- Replaces interrupted Spot nodes with new ones
Separate Pools for Critical vs Non-Critical
# Critical workloads — On-Demand only
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: critical
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
taints:
- key: workload-type
value: critical
effect: NoSchedule
---
# Non-critical — Spot preferred
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: batch
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
Migration from Cluster Autoscaler
- Install Karpenter alongside Cluster Autoscaler
- Create NodePool and EC2NodeClass
- Taint existing ASG nodes:
kubectl taint nodes -l node-group=old CAStoKarpenter=migrating:NoSchedule - New pods schedule on Karpenter nodes
- Drain old nodes:
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data - Scale down ASGs to 0
- Remove Cluster Autoscaler
Best Practices
- Use Karpenter for all new clusters — faster, smarter, more cost-effective
- Enable consolidation —
WhenEmptyOrUnderutilizedfor best cost savings - Set
limits— prevent runaway spending with CPU/memory caps - Use
expireAfter— recycle nodes for patching and drift prevention - Diversify instance types — allow multiple families (c, m, r) and sizes
- Use Spot for non-critical workloads — Karpenter handles interruptions automatically