Episode praktik pertama: membuat cluster EKS, memasang CRD dan controller Karpenter lewat Helm, membangun IAM role berprinsip least privilege, lalu memverifikasi instalasi dengan contoh minimal NodePool dan EC2NodeClass.

Di episode 2 kalian sudah memahami arsitektur Karpenter: controller yang mendeteksi pod Unschedulable, memilih NodePool, lalu meluncurkan node lewat cloud provider. Sekarang waktunya praktik nyata. Episode ini memandu kalian dari nol — membuat cluster EKS, memasang CRD dan controller Karpenter lewat Helm, membangun IAM role minimal, sampai verifikasi dengan contoh NodePool dan EC2NodeClass pertama.
Pastikan tools dari episode 0 sudah terpasang dan kredensial AWS valid. Kita mulai dari pembuatan cluster.
Karpenter 1.14.0 mendukung Kubernetes 1.29 sampai 1.36. Buat cluster dengan satu managed nodegroup yang berperan sebagai vended nodegroups — istilah untuk nodegroup bawaan eksctl yang mengantarkan cluster sampai bisa dipakai sebelum Karpenter mengambil alih. Nodegroup ini sengaja dibuat kecil dan hanya menjalankan pod sistem.
eksctl create cluster \
--name learn-karpenter \
--version 1.32 \
--region ap-southeast-1 \
--nodegroup-name system \
--node-type m5.large \
--nodes 1 \
--nodes-min 1 \
--nodes-max 1 \
--managedProses memakan 10-15 menit. Setelah selesai, pastikan cluster sehat:
kubectl cluster-info
kubectl get nodesKarpenter memanggil AWS API untuk meluncurkan dan menghentikan instance. IAM role untuk controller dibuat dengan prinsip least privilege dan dipasang lewat IRSA (IAM Roles for Service Accounts).
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:RunInstances",
"ec2:CreateFleet",
"ec2:TerminateInstances",
"ec2:DescribeInstances",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstanceTypes"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["iam:PassRole"],
"Resource": "arn:aws:iam::ACCOUNT_ID:role/KarpenterNodeRole-*"
}
]
}Buat policy lewat aws CLI, lalu hubungkan ke service account:
aws iam create-policy \
--policy-name KarpenterControllerPolicy \
--policy-document file://policy.json
eksctl create iamserviceaccount \
--cluster learn-karpenter \
--namespace karpenter \
--name karpenter \
--role-name karpenter-irsa \
--attach-policy-arn arn:aws:iam::ACCOUNT_ID:policy/KarpenterControllerPolicy \
--approveWarning
Pastikan file policy.json memakai nomor akun yang benar, dan jangan pernah memasukkan access key AWS ke dalam cluster. IRSA memastikan kredensial dikelola oleh IAM, bukan disimpan sebagai secret di Kubernetes.
Node yang diluncurkan Karpenter juga butuh instance profile dengan role untuk berkomunikasi dengan EKS — role ini biasanya dinamai KarpenterNodeRole-... . Di episode 5 kita akan lihat bagaimana EC2NodeClass menunjuk ke instance profile ini lewat field role.
Chart Helm karpenter menginstal controller sekaligus CRD NodePool, NodeClaim, dan EC2NodeClass.
helm upgrade --install karpenter oci://ghcr.io/aws/karpenter/charts/karpenter \
--namespace karpenter \
--create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::ACCOUNT_ID:role/karpenter-irsa \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--version 1.14.0Verifikasi controller dan CRD:
kubectl get pods -n karpenter
kubectl get crd nodepools.karpenter.sh
kubectl get crd nodeclaims.karpenter.sh
kubectl get crd ec2nodeclasses.karpenter.k8s.awsController sehat ditandai dengan pod berstatus Running. Untuk memastikan versi, cek bagian image pada deployment kubectl get deployment -n karpenter karpenter -o yaml — seharusnya ber-tag 1.14.0.
Sebelum membuat resource Karpenter, pastikan subnet dan security group bisa "ditemukan" Karpenter. Metode paling umum adalah tag discovery:
aws ec2 create-tags \
--resources SUBNET_ID SECURITY_GROUP_ID \
--tags Key=karpenter.sh/discovery,Value=learn-karpenterGanti SUBNET_ID dan SECURITY_GROUP_ID dengan nilai di cluster kalian. Ini adalah cara Karpenter mengetahui subnet dan security group mana yang boleh dipakai — detail selector-nya kita pelajari di episode 5.
Sekarang definisikan dua resource pertama. EC2NodeClass dulu, karena NodePool menunjuk ke sana.
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2
role: "KarpenterNodeRole-eks-learn-karpenter"
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: learn-karpenter
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: learn-karpenterTerapkan keduanya:
kubectl apply -f ec2nodeclass.yaml
kubectl apply -f nodepool.yaml
kubectl get nodepoolsPerhatikan struktur YAML-nya: NodePool menunjuk EC2NodeClass lewat nodeClassRef, sementara requirement capacity-type diatur sebagai on-demand. Struktur lengkap NodePool akan kita bedah di episode 4.
Sekarang buktikan Karpenter bekerja. Deploy workload yang tidak muat di nodegroup sistem, lalu amati:
kubectl create deployment nginx --image=nginx --replicas=3
kubectl get pods -o wide
kubectl get nodeclaimsJika semuanya benar, Karpenter akan membuat NodeClaim, meluncurkan instance baru, dan pod-pod nginx akan dijadwalkan ke node tersebut. Label karpenter.sh/nodepool=default akan muncul pada node baru — cek dengan kubectl get nodes -l karpenter.sh/nodepool=default -o wide.
Important
Kalau pod tetap Pending dan tidak ada NodeClaim yang muncul, langkah debug pertama: lihat log controller dengan kubectl logs -n karpenter deployment/karpenter dan pastikan tag discovery subnet atau security group sudah benar. Dua hal ini penyebab paling umum kegagalan provisioning di AWS.
Episode 3 ini menuntun kalian dari nol sampai Karpenter benar-benar memprovisikan node. Kalian membuat cluster EKS, membangun IAM role minimal, memasang CRD dan controller lewat Helm, menyiapkan tag discovery, lalu menarapkan NodePool dan EC2NodeClass pertama yang langsung diuji dengan deployment nginx.
Inti yang harus dibawa pulang:
Di episode 4 kita membedah NodePool dan scheduling: struktur spec.template, requirements, disruption, weight, serta bagaimana Karpenter mencocokkan pod dengan pool — termasuk label dan anotasi karpenter.sh yang dipasang pada node. Sampai jumpa di episode berikutnya!