Di lingkungan cloud, ekspansi (scale-out) dan pengurangan (scale-in) worker node (instans VM) jauh lebih mudah dilakukan dibandingkan di lingkungan on-premise. Sama seperti saat sebelum menggunakan Kubernetes di mana lingkungan VM memanfaatkan Auto Scaling dari AWS untuk menggunakan sumber daya EC2 secara fleksibel sesuai beban sistem, di lingkungan Kubernetes pun worker node dapat diperluas dan diperkecil secara bebas.
Autoscaling node di lingkungan Kubernetes umumnya menggunakan Cluster Autoscaler (CAS) dan Karpenter. Dibandingkan dengan CAS konvensional, Karpenter memiliki struktur yang lebih ringkas sehingga jauh lebih cepat dan fleksibel. Biaya node juga dapat ditekan dibandingkan menggunakan CAS. Mari kita pelajari Karpenter secara mendalam melalui praktik langsung.
Topik Utama
- Perbandingan CAS (Cluster Autoscaler) vs Karpenter
- Keunggulan Karpenter
Tugas Praktik
- Instalasi Karpenter dan eks-node-viewer
- Pengujian Scale-Out/In Node EKS menggunakan Karpenter
Alamat repositori GitHub untuk sumber kode yang digunakan pada bab ini: https://github.com/junghoon2/k8s-class/tree/main/karpenter
1. Keunggulan Karpenter Dibandingkan Cluster Autoscaler
Karpenter adalah open-source tool yang dirancang untuk mengoptimalkan manajemen node dan penjadwalan (scheduling) di klaster Kubernetes. Sebelumnya, Cluster Autoscaler digunakan untuk melakukan scaling node Kubernetes secara dinamis. CAS memanfaatkan API terpisah yang disesuaikan untuk masing-masing penyedia layanan cloud (AWS, Azure, GCP).
Cluster Autoscaler mengelola node EKS dengan memanfaatkan Auto Scaling Group (ASG) milik AWS. Karena ASG merupakan resource AWS terpisah yang tidak berhubungan langsung dengan EKS, proses sinkronisasi data antara ASG dan node EKS membutuhkan waktu tambahan. Berdasarkan pengujian langsung, penambahan node dengan CAS memerlukan waktu sekitar 2 menit, sedangkan Karpenter hanya membutuhkan waktu sekitar 40 detik.

Node group pada ASG hanya dapat menggunakan tipe instans tunggal, sehingga pemborosan sumber daya (resource) dapat terjadi. Misalnya, hanya untuk menjalankan satu Pod yang membutuhkan 0.1 Core / 128Mi resource, node berkapasitas 4 Core / 32Gi tetap harus dialokasikan sehingga menyebabkan pemborosan biaya.
Karena CAS merupakan teknologi yang lebih lama dibandingkan Karpenter, tingkat kematangan teknologinya memang lebih tinggi. Namun, dari segi biaya dan kecepatan, Karpenter jauh lebih unggul sehingga saat ini banyak perusahaan beralih mengadopsi Karpenter. Penulis sendiri berhasil menghemat biaya lebih dari 30% setelah menerapkan Karpenter dibandingkan metode sebelumnya.

Karpenter menambah dan mengurangi node EKS secara langsung tanpa menggunakan Auto Scaling Group. Selain itu, Karpenter secara otomatis mengalokasikan tipe node paling hemat biaya berdasarkan kebutuhan resource Pod. Ketika Pod yang berjalan telah selesai/berhenti, Karpenter akan kembali memeriksa kebutuhan resource yang diperlukan dan secara otomatis menghapus node lama lalu mengalokasikan node baru yang paling optimal demi efisiensi biaya.
Namun, saat menggunakan Karpenter untuk optimasi node, node baru akan dijalankan dan node lama akan diterminasi sehingga Pod yang sedang berjalan akan dihentikan dan dijalankan kembali di node baru. Oleh karena itu, konfigurasi dasar untuk stabilitas layanan seperti replikasi Pod (redundansi), PodDisruptionBudget, dan Graceful Shutdown mutlak diperlukan. Langkah-langkah ini sebenarnya bukan hanya ditujukan khusus untuk Karpenter saja, melainkan praktik dasar yang memang wajib dilakukan demi meningkatkan stabilitas sistem secara keseluruhan.
2. Instalasi Karpenter Menggunakan Helm
Mari kita pasang Karpenter secara langsung untuk mempelajari fitur-fiturnya secara detail. Karena Karpenter mengontrol instans EC2 di luar EKS, maka diperlukan IRSA (IAM Roles for Service Accounts). Pemasangan akan jauh lebih mudah jika menggunakan modul Karpenter pada Terraform. Tambahkan modul Karpenter ke dalam kode Terraform EKS yang sebelumnya telah digunakan seperti berikut ini:
Modul Terraform karpenter
module "karpenter" {
source = "terraform-aws-modules/eks/aws//modules/karpenter"
cluster_name = module.eks.cluster_name
irsa_oidc_provider_arn = module.eks.oidc_provider_arn
policies = {
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
tags = local.tags
}
Lakukan proses deployment menggunakan Terraform:
$ (⎈ |switch-singapore-test:default) tf init $ (⎈ |switch-singapore-test:default) tf plan -out planfile $ (⎈ |switch-singapore-test:default) tf apply planfile
Hal penting yang perlu diperhatikan adalah mencatat bagian output Karpenter yang dihasilkan setelah mengeksekusi kode Terraform. Catat informasi karpenter_instance_profile_name dan karpenter_queue_name yang dibutuhkan untuk instalasi Helm Karpenter:
karpenter_instance_profile_name = "Karpenter-jerry-test-2023082121003044240000001d" karpenter_queue_name = "Karpenter-jerry-test"
Setelah konfigurasi IRSA selesai, sekarang saatnya memasang Karpenter. Kita akan menggunakan Helm:
$ (⎈ |sent-seoul-stage:mongodb) helm pull oci://public.ecr.aws/karpenter/karpenter --version v0.27.5 Pulled: public.ecr.aws/karpenter/karpenter:v0.27.5 Digest: sha256:9491ba645592ab9485ca8ce13f53193826044522981d75975897d229b877d4c2 $ (⎈ |sent-seoul-stage:mongodb) rm -rf karpenter-v0.27.5.tgz $ (⎈ |sent-seoul-stage:mongodb) mv karpenter karpenter-v0.27.5 $ (⎈ |switch-singapore-test:echoserver) cd karpenter-v0.27.5 $ (⎈ |switch-singapore-test:echoserver) cp values.yaml my-values.yaml
Ubah file default Helm Value (my-values.yaml) seperti berikut:
my-values.yaml
replicas: 2
controller:
resources:
limits:
memory: 1Gi
requests:
cpu: 100m
memory: 1Gi
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: $ROLE_ARN
settings:
aws:
clusterEndpoint: $Endpoint
clusterName: $Name
defaultInstanceProfile: Karpenter-jerry-test-2023082121003044240000001d
interruptionQueueName: Karpenter-jerry-test
. [serviceAccount.annotations.eks.amazonaws.com/role-arn](https://serviceAccount.annotations.eks.amazonaws.com/role-arn):$ROLE_ARN
Masukkan informasi ROLE_ARN Karpenter masing-masing yang telah dibuat pada modul Terraform Karpenter sebelumnya.
. settings.aws.clusterEndpoint
Masukkan informasi Endpoint API Server klaster Anda. Informasi EndPoint dapat dilihat melalui AWS EKS Console.

. settings.aws.defaultInstanceProfile, interruptionQueueName
Masukkan nilai dari hasil Output saat pemasangan modul Terraform Karpenter sebelumnya.
Lakukan instalasi ke dalam namespace karpenter:
$ (⎈ |switch-singapore-test:karpenter) helm install karpenter --namespace karpenter --create-namespace -f my-values.yaml . NAME: karpenter LAST DEPLOYED: Fri Sep 22 05:51:10 2023 NAMESPACE: karpenter STATUS: deployed REVISION: 1 TEST SUITE: None
Setelah instalasi selesai, Anda dapat memverifikasi pod Karpenter Controller di dalam namespace karpenter:
$ (⎈ |switch-singapore-test:karpenter) k get pod NAME READY STATUS RESTARTS AGE karpenter-f654d4b8d-v65dk 1/1 Running 0 7d13h
3. Instalasi Karpenter Provisioner & AWS Node Template
Agar Karpenter dapat men-deploy node baru, konfigurasi detail mengenai node tersebut sangat diperlukan. Karpenter menggunakan dua CRD (Custom Resource Definition) yaitu Provisioner dan AWSNodeTemplate untuk mendefinisikan spesifikasi detailnya.
Istilah Provisioning di lingkungan IT pada dasarnya mengacu pada proses pengalokasian, konfigurasi, dan pengelolaan sumber daya komputasi virtual. Pada Karpenter, hal ini juga mencakup penentuan kebutuhan fisik worker node Kubernetes; termasuk kebijakan resource apa yang harus dipenuhi dan batasan (constraints) apa yang harus dipertimbangkan.
Mari kita pelajari melalui contoh file manifes berikut:
provisioner.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: spot
namespace: karpenter
spec:
# References cloud provider-specific custom resource, see your cloud provider specific documentation
providerRef:
name: default
# Labels are arbitrary key-values that are applied to all nodes
labels:
env: test
# Requirements that constrain the parameters of provisioned nodes.
# These requirements are combined with pod.spec.affinity.nodeAffinity rules.
# Operators { In, NotIn } are supported to enable including or excluding values
requirements:
- key: "karpenter.k8s.aws/instance-family"
operator: In
values: ["t3"]
- key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand
operator: In
values: ["spot"]
limits:
resources:
cpu: "100"
memory: "200Gi"
# Enables consolidation which attempts to reduce cluster cost by both removing un-needed nodes and down-sizing those
# that can't be removed. Mutually exclusive with the ttlSecondsAfterEmpty parameter.
consolidation:
enabled: true
. metadata.name: spot
Tipe node dapat dibedakan berdasarkan penamaan. Sebagai contoh, Anda dapat memisahkan jenis node menjadi Spot node dan On-Demand node. Pod yang dialokasikan untuk berjalan di Spot node dapat dijadwalkan agar di-deploy khusus ke kelompok Spot node tersebut.
. spec.requirements.key: “karpenter.k8s.aws/instance-family”
Menentukan kelompok tipe instans (instance family) dari node yang akan disediakan oleh Karpenter. Tergantung pada karakteristik Pod, Anda dapat memilih tipe c6i (fokus komputasi), g5 (node GPU), r6i (fokus memori), atau multi-tipe instans lainnya. Karena ini merupakan lingkungan pengujian, penulis menentukan tipe t3 yang biayanya murah.Sebagai informasi tambahan, penggunaan instans kelompok t4g yang menggunakan prosesor berbasis Arm dapat meningkatkan efisiensi rasio harga terhadap performa (price-performance) sekitar 20%.
. spec.requirements.key: “karpenter.sh/capacity-type”
Menentukan instans On-Demand atau Spot. Nilai bawaannya adalah on-demand, dan dapat diubah menjadi spot. Pada aplikasi umum berbasis web atau stateless Pod yang tidak menggunakan persistent volume besar dan dapat membuat koneksi ulang secara mandiri ketika node dimatikan kapan saja, penggunaan Spot instance sangat disarankan. Kubernetes dirancang dengan arsitektur berorientasi ketersediaan tinggi (High Availability), sehingga sangat cocok menggunakan Spot instance. Banyak perusahaan kini telah aktif menerapkan Spot instance pada lingkungan operasional produksi mereka. Secara pribadi, fleksibilitas dalam memaksimalkan Spot instance ini merupakan salah satu keunggulan terbesar dari EKS.
. spec.requirements.consolidation.enabled: true
Ketika jumlah Pod yang berjalan di suatu node berkurang, Karpenter secara otomatis melakukan konsolidasi (consolidation) atau memindahkan Pod ke node lain untuk memangkas biaya. Berbeda dengan CAS, Karpenter bahkan dapat menyesuaikan tipe instans yang baru diluncurkan dan menempatkan Pod baru ke node yang paling hemat biaya.
Selanjutnya adalah manifes AWSNodeTemplate. Bagian ini menetapkan konfigurasi AWS yang diperlukan untuk menjalankan node di luar tipe hardware, seperti konfigurasi volume storage, Security Group, dan Subnet.
AWSNodeTemplate
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
name: default
spec:
subnetSelector:
karpenter.sh/discovery: jerry-test
securityGroupSelector:
karpenter.sh/discovery: jerry-test
tags:
karpenter.sh/discovery: jerry-test
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
encrypted: true
deleteOnTermination: true
. spec.subnetSelector, securityGroupSelector
AWSNodeTemplate mewajibkan penentuan informasi Subnet dan Security Group. Ini mendefinisikan informasi dasar di lingkungan mana node akan diluncurkan.
Karpenter mengalokasikan resource berdasarkan informasi tag pada Subnet dan Security Group. Saat modul Karpenter diinstal, tag akan secara otomatis ditambahkan ke Subnet dan Security Group sesuai nama klaster.
Informasi tag ini dapat dipastikan melalui AWS Console seperti di bawah ini:

Ketik “karpenter” pada kolom pencarian Security Group seperti gambar di atas untuk melihat resource terkait. Saat memeriksa tag, datanya sama persis dengan yang diisikan pada AWSNodeTemplate di atas.

Sama halnya dengan Security Group, Anda juga dapat menemukan tag identik pada Subnet.

Node yang diluncurkan oleh Karpenter akan diberikan alokasi Security Group dan Subnet yang memiliki tag karpenter seperti di atas.
. spec.subnetSelector.Name: “jerry-test-private-ap-northeast-2*”
Node EKS sangat disarankan untuk di-deploy ke dalam Private Subnet demi alasan keamanan dan biaya. Anda dapat menentukan nama tag Private agar node berjalan di Private Subnet. Sintaks wildcard * juga didukung, sehingga Anda tidak perlu menuliskan ap-northeast-2a/2b/2c satu per satu secara manual.
. spec.blockDeviceMappings
Anda dapat memodifikasi konfigurasi disk node. Penulis menetapkan ukuran disk default sebesar 100Gi guna menjaga kestabilan operasional.
Buat CRD Karpenter berdasarkan manifes yang telah disiapkan:
$ (⎈ |switch-singapore-test:karpenter) k apply -f provisioner.yaml -f awsNodeTemplate.yaml provisioner.karpenter.sh/spot created awsnodetemplate.karpenter.k8s.aws/default created $ (⎈ |switch-singapore-test:karpenter) k get provisioners.karpenter.sh NAME AGE spot 85s $ (⎈ |switch-singapore-test:karpenter) k get awsnodetemplates.karpenter.k8s.aws NAME AGE default 94s
Dua CRD baru, yaitu provisioner dan awsnodetemplate, telah berhasil dibuat dan diverifikasi.
4. Praktik Node Autoscaling Menggunakan Karpenter
Untuk memantau node yang diluncurkan oleh Karpenter secara mudah, kita akan memasang eks-node-viewer. eks-node-viewer adalah tool berbasis CLI yang dikembangkan oleh AWS untuk memvisualisasikan perubahan dan alokasi resource node secara dinamis. Tool ini dapat diinstal dengan mudah menggunakan brew:
brew tap aws/tap brew install eks-node-viewer
Selanjutnya, jalankan eks-node-viewer.

Seperti terlihat pada tangkapan layar, eks-node-viewer menampilkan status node klaster secara intuitif. Informasi tipe instans, status spot/on-demand, hingga estimasi biaya dapat dipantau langsung. Selain itu, rasio kapasitas alokasi node (Capacity) dibandingkan dengan total permintaan resource Pod yang sedang berjalan (Request) disajikan dalam persentase (%). Dengan begitu, efektivitas alokasi resource dapat dinilai dengan cepat.
Satu hal yang perlu diperhatikan adalah adanya tiga metrik resource yang dihitung berdasarkan alokasi request Pod, bukan pemakaian riil (Usage). Pertama, Capacity adalah total kapasitas fisik resource yang dapat digunakan oleh node. Kedua, Request adalah total jumlah resource request dari seluruh Pod yang dialokasikan di node tersebut. Ketiga, Usage adalah konsumsi riil resource yang sedang digunakan oleh Pod saat itu, yang bisa saja lebih rendah atau lebih tinggi daripada nilai request (meski tetap tidak bisa melampaui limit). Meningkatkan rasio request terhadap capacity berarti memanfaatkan resource node secara jauh lebih efisien.
Selain itu, eks-node-viewer juga memungkinkan Anda melihat waktu yang dibutuhkan saat proses provisi node berlangsung secara aktual.
Sekarang, mari kita deploy node baru menggunakan Karpenter dan pantau perubahannya melalui eks-node-viewer. Praktik ini bertujuan untuk memverifikasi bagaimana Karpenter secara otomatis melakukan penambahan node berdasarkan resource request dari Pod. Berikut adalah manifes Pod yang digunakan:
busybox deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: default
spec:
replicas: 4
selector:
matchLabels:
app: busybox # Cocokkan dengan label POD
template:
metadata:
labels:
app: busybox # Cocokkan dengan selector label
spec:
containers:
- name: busybox
image: busybox
command: ["sh"]
args: ["-c", "sleep inf"]
resources:
requests:
cpu: 400m
memory: 128Mi
. spec.template.spec.resources.requests
Karpenter menjalankan node baru dengan mengacu pada batas permintaan (requests) CPU dan Memori dari Pod. Karpenter akan memilih instans dengan biaya termurah di antara opsi node yang mampu memenuhi kebutuhan request tersebut. Pada contoh di atas, node yang diluncurkan harus mencukupi total resource (400m * 4 = 1600m CPU, 128Mi * 4 = 512Mi Memori).
Bagi jendela terminal menjadi dua: jalankan deploy Pod busybox di terminal atas, dan jalankan eks-node-viewer di terminal bawah.
$ (⎈ |switch-singapore-test:default) k apply -f busybox-deploy.yaml deployment.apps/busybox created
Anda dapat melihat durasi kesiapan node secara real-time seperti yang tampak di bawah ini. Waktu yang dibutuhkan hingga node baru berhasil ditambahkan adalah 40 detik. Jika menggunakan Cluster Autoscaler (CAS) konvensional, proses ini memakan waktu lebih dari 2 menit; ini berarti Karpenter sekitar 70% lebih cepat.

Ketika tipe node diperiksa, tipe yang dipilih adalah t3.small. Karena kebutuhan resource Pod adalah gabungan dari CPU 400m * 4 dan Memori 128Mi * 4, Karpenter secara otomatis memilih tipe EC2 paling hemat biaya dari daftar node group yang ditentukan pada Karpenter Provisioner.
Jika menggunakan Cluster Autoscaler versi lama, sistem akan mengalokasikan tipe instans tunggal tetap yang sudah ditentukan pada node group (misalnya c6i.large) tanpa memedulikan seberapa kecil resource request dari Pod, yang akhirnya berujung pada pemborosan biaya.
Selanjutnya, kita lakukan pengujian sebaliknya yaitu pengurangan jumlah node (Scale-In). Buka dua jendela terminal dan hapus Pod deployment pada salah satu jendela:
$ (⎈ |switch-singapore-test:default) k delete deployments.apps busybox

Ketika Pod dihapus, node ikut terhapus dalam waktu sekitar 10 detik seperti yang terlihat pada tangkapan layar. Kecepatan ini pun jauh melampaui performa CAS. Karpenter beroperasi sangat cepat karena mengelola node secara langsung tanpa melalui perantara Auto Scaling Group (ASG).
Log penambahan dan pengurangan node dapat dipantau langsung pada pod Karpenter:
$ (⎈ |switch-singapore-test:default) k logs -n karpenter karpenter-f654d4b8d-v65dk
2023-05-24T00:50:00.733Z INFO controller.provisioner.cloudprovider launched instance {"commit": "698f22f-dirty", "provisioner": "default", "id": "i-0f7c4e1e1e28aea85", "hostname": "ip-10-110-25-141.ap-southeast-1.compute.internal", "instance-type": "t3.micro", "zone": "ap-southeast-1b", "capacity-type": "spot", "capacity": {"cpu":"2","ephemeral-storage":"100Gi","memory":"947Mi","pods":"4"}}
(dilewati / disingkat)
2023-05-24T01:00:13.982Z INFO controller.deprovisioning deprovisioning via consolidation delete, terminating 8 machines ip-10-110-24-178.ap-southeast-1.compute.internal/t3.micro/spot, ip-10-110-27-116.ap-southeast-1.compute.internal/t3.2xlarge/spot, ip-10-110-1-0.ap-southeast-1.compute.internal/t3.2xlarge/spot, ip-10-110-22-211.ap-southeast-1.compute.internal/t3.micro/spot, ip-10-110-17-156.ap-southeast-1.compute.internal/t3.micro/spot, ip-10-110-25-158.ap-southeast-1.compute.internal/t3.micro/spot, ip-10-110-12-187.ap-southeast-1.compute.internal/t3.2xlarge/spot, ip-10-110-25-141.ap-southeast-1.compute.internal/t3.micro/spot {"commit": "698f22f-dirty"}
2023-05-24T01:00:14.052Z INFO controller.termination cordoned node {"commit": "698f22f-dirty", "node": "ip-10-110-24-178.ap-southeast-1.compute.internal"}
(dilewati / disingkat)
Proses penambahan dan pengurangan node dapat dipantau secara transparan melalui log. Jika menemui kendala terkait penambahan atau pengurangan node di lapangan, Anda dapat meninjau log pada pod Karpenter untuk mengidentifikasi akar penyebabnya.
Demikian pembahasan mengenai Karpenter. Pada artikel berikutnya, kita akan membahas opsi terminationGracePeriodSeconds dan PodDisruptionBudget agar deployment Pod menggunakan Karpenter dapat berlangsung dengan lebih aman.
