EKS with Karpenter Autoscaling (v1)

Karpenter is a high-performance, open-source Kubernetes node autoscaler built by AWS. Unlike the legacy Kubernetes Cluster Autoscaler — which is bound to EC2 Auto Scaling Groups — Karpenter evaluates unscheduled pod resource requirements directly and provisions optimal EC2 instances in seconds.
Key Benefits of Karpenter v1
- Cost-Optimized Provisioning: Automatically selects the most cost-effective instance types, including Spot instances.
- Sub-Minute Scaling: Boots up right-sized instances directly via EC2 Fleet API without ASG latency.
- Automated Node Consolidation: Continuously packs workloads and terminates underutilized nodes (
WhenEmptyOrUnderutilized). - Drift & Lifecycle Management: Automatically recycles nodes when AMIs change or max expiration thresholds (
expireAfter) are reached.
📁 Source Code: aws-eks-terraform / EKS-Cluster-karpenter-V1
📺 Video Walkthrough
Architecture & Karpenter v1 Constructs
Karpenter v1 uses two decoupled Custom Resource Definitions (CRDs):
graph LR
NP["NodePool (karpenter.sh/v1)<br/>Limits, Constraints, Disruption"] --> |nodeClassRef| NC["EC2NodeClass (karpenter.k8s.aws/v1)<br/>AMI, Subnets, Security Groups, IAM Role"]
EC2NodeClass: Controls AWS-specific infrastructure settings (AMI family, subnets, security groups, instance profile/IAM role, storage, block device mappings).NodePool: Defines scheduling constraints, resource limits (CPU/Memory), instance requirements (arch, capacity type, generation), and disruption/consolidation policies.
Karpenter Configuration
1. Helm Values (karpenter-values.yaml)
settings:
clusterName: eks-demo
replicas: 2
serviceAccount:
name: "karpenter-controller"
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::<AWS-ACCOUNT-ID>:role/karpenter-controller-eks-demo"
2. EC2NodeClass (ec2nodeclass.yaml)
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: karpenter-default
spec:
amiFamily: AL2023 # Alternatively: Bottlerocket
amiSelectorTerms:
- alias: al2023@latest
role: "karpenter-node-eks-demo"
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "eks-demo" # Subnet must have this tag
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "eks-demo" # Node security group tag
tags:
Name: karpenter-provisioned-node
Environment: dev
3. NodePool (nodepool.yaml)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: karpenter-default
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: karpenter-default
requirements:
- key: "karpenter.k8s.aws/instance-hypervisor"
operator: In
values: ["nitro"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["5"]
limits:
cpu: 50
memory: 100Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
expireAfter: 336h # 14 days
Installation & Deployment
# 1. Apply Terraform to create VPC, EKS cluster, and Karpenter IAM roles
terraform init -upgrade
terraform apply
# 2. Update kubeconfig
aws eks --profile labs --region eu-west-1 update-kubeconfig --name eks-demo
# 3. Install Karpenter controller Helm chart
helm install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version 1.0.6 \
--namespace karpenter \
--create-namespace \
-f Karpenter-app/karpenter-values.yaml
# 4. Apply EC2NodeClass and NodePool
kubectl apply -f Karpenter-app/karpenter-nodeclass.yaml
kubectl apply -f Karpenter-app/karpenter-nodepool.yaml
# 5. Verify custom resources
kubectl get ec2nodeclass,nodepool
Testing Karpenter Autoscaling
Deploy a test workload requesting significant CPU to trigger Karpenter node provisioning:
scale-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: "1"
memory: "1Gi"