# Autoscaling HPA Kubernetes: Bertahan dari Traffic Spike

> Autoscaling menyala, dan detik-detik awal spike tetap dilayani pod yang sudah ada. Timeline Kubernetes HPA yang sebenarnya di managed PaaS, default demi default.

URL: https://www.matthewswong.com/id/blog/helipod-hpa-autoscaling-traffic-spike/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-09-03
- Updated: 2026-09-03
- Category: Cloud

> **Key takeaway:** Autoscaling HPA Kubernetes di managed PaaS tidak melayani awal sebuah traffic spike. Control loop-nya berjalan setiap lima belas detik secara default, scale-up masih menunggu pod scheduling dan readiness probe, dan CPU utilisation diukur terhadap resources.requests, jadi pod yang sudah berjalanlah yang menyerap ledakan pertama.

Panel autoscaling Helipod hanya tiga field: jumlah replica minimum, maksimum, dan target CPU. Screenshot di halaman depan mereka sendiri menunjukkan min 1, max 10, dan target 70 persen, dan janji di sebelahnya akurat sejauh yang dijanjikan — kamu menentukan batasnya, HPA mengurus sisanya. Yang disembunyikan tiga field itu adalah bahwa semua yang terjadi antara saat traffic datang dan saat pod baru melayani request pertamanya ditentukan oleh default Kubernetes yang tidak pernah dipilih siapa pun di panel itu.

Jadi saya membaca dokumentasi Kubernetes Horizontal Pod Autoscaler sambil membayangkan stopwatch, lalu mencatat setiap default yang aktif selama sebuah spike: sync period, scale-up policy, tolerance, dan dua delay readiness. Tulisan ini adalah timeline itu, ditambah empat kenyataan di level aplikasi yang tidak bisa diperbaiki platform mana pun, sebagus apa pun dashboard-nya.

## Autoscaling menyala bukan berarti spike-nya terlayani

Tidak ada autoscaler yang menambah kapasitas sebelum beban yang membenarkannya muncul. Horizontal Pod Autoscaler adalah sebuah control loop: ia membaca metric, membandingkannya dengan target, lalu meminta Deployment mengubah jumlah replica. Setiap langkah itu terjadi setelah traffic kamu datang dan sudah dilayani — atau sudah menumpuk di queue — oleh pod yang sudah ada. Tidak ada jalur prediktif di konfigurasi berbasis CPU yang dibuka sebuah PaaS.

Dokumentasi Kubernetes menyebutnya terang-terangan: horizontal pod autoscaling diimplementasikan sebagai control loop yang berjalan intermittent, bukan proses yang kontinu. Menganggapnya asuransi terhadap traffic spike adalah kesalahan kategori. Ia adalah asuransi terhadap spike yang berlangsung lebih dari beberapa menit. Ledakan di detik-detik awal adalah persoalan capacity planning, dan satu-satunya setting yang menyentuhnya adalah jumlah replica minimum yang kamu pilih sebelum apa pun terjadi.

## Tiga field di dashboard, belasan default di bawahnya

Tiga field itu dipetakan ke sebuah object HorizontalPodAutoscaler, dan object itu punya bagian behavior yang tidak pernah ditampilkan dashboard. Kubernetes mendokumentasikan default-nya secara eksplisit, jadi kamu bisa menuliskan persis apa yang kamu setujui saat menyalakan switch itu.

Bagian yang layak dihafal adalah asimetrinya. Scale-up punya stabilization window nol detik, jadi controller bertindak pada pembacaan pertama yang melewati target. Scale-down punya window 300 detik dan mengambil rekomendasi tertinggi dari mana pun di dalamnya — sebuah rolling maximum, dirancang supaya controller tidak menghapus pod yang sebentar lagi dibutuhkan lagi. Platformnya berpihak pada mempertahankan kapasitas, dan itu memang pilihan yang benar, tapi konsekuensinya aplikasi yang over-scaled tetap over-scaled selama lima menit setelah spike berakhir. Pada billing harian per resource, itu masuk tagihan, bukan pembulatan.

## Rumusnya, dijalankan pada sebuah spike

Seluruh keputusan controller adalah satu rasio: desiredReplicas adalah currentReplicas dikali current metric value dibagi desired metric value, dibulatkan ke atas. Ia melewatkan perubahan sama sekali ketika rasionya cukup dekat ke 1.0, di dalam tolerance yang default-nya 0.1. Dengan target CPU 70 persen, artinya apa pun di antara sekitar 63 dan 77 persen average utilisation dianggap steady state dan diabaikan.

Dua hal dalam aritmetika itu mengejutkan saya. Pertama, yang membatasi kecepatan ramp biasanya metric-nya, bukan policy-nya: default scale-up policy sebenarnya mengizinkan penambahan empat pod atau penggandaan setiap lima belas detik, tapi pod yang CPU limit-nya sama dengan CPU request-nya tidak pernah bisa melaporkan lebih dari 100 persen utilisation, jadi rasionya tertutup dan controller meminta lebih sedikit dari yang sebenarnya diizinkan. Kedua, efek dampening-nya. Ketika ada pod yang belum Ready, controller menghitung ulang dengan asumsi pod itu tidak mengonsumsi apa pun, justru supaya tidak overshoot — jadi saat kamu paling ingin ia agresif adalah saat ia sengaja konservatif.

## Jamnya berjalan dalam tick lima belas detik

Interval loop-nya ditentukan flag sync period di kube-controller-manager, dan default-nya lima belas detik. Di platform managed itu setting cluster-wide, artinya bukan milik kamu. Spike yang mulai satu detik setelah sebuah tick harus menunggu hampir satu periode penuh hanya untuk disadari, dan tidak ada apa pun di aplikasi atau konfigurasi kamu yang mengubahnya.

Setelah keputusan diambil, jumlah replica di Deployment berubah seketika dan kerja yang berguna belum dimulai. Scheduler masih harus menemukan node yang punya ruang untuk CPU request pod itu, container image masih harus tersedia atau di-pull, prosesnya masih harus boot, dan readiness probe masih harus lulus sebelum Service mau mengarahkan satu request pun ke sana. Tidak satu pun dari empat langkah itu punya anggaran waktu yang terdokumentasi, karena keempatnya bergantung pada image, node pool, dan aplikasi kamu. Empat langkah itu juga satu-satunya bagian timeline yang benar-benar bisa kamu perpendek.

*Hanya band paling bawah yang benar-benar dirasakan pengguna; semua band di atasnya adalah mesin platform yang melaporkan dirinya sehat sementara queue di replica awal kamu terus bertambah. Perhatikan bahwa dua band yang memuat angka nyata justru dua band yang tidak bisa diatur dari dashboard PaaS.*

> Satu-satunya setting yang mengubah apa yang terjadi di lima belas detik pertama adalah minReplicas. Tentukan dari ledakan awal yang kamu perkirakan, bukan dari beban rata-rata, terima bahwa kamu membayar pod idle di antara spike, dan anggap itu harga dari jendela waktu yang tidak bisa ditutup autoscaler mana pun. Knob lainnya hanya memengaruhi apa yang terjadi setelah kerusakannya terjadi.

## resources.requests adalah penyebut setiap keputusan

Target CPU bukan persentase dari node atau dari plan kamu. Controller menghitung utilisation sebagai persentase dari resource request yang setara pada container di dalam pod, jadi resources.requests.cpu adalah penyebut dari setiap keputusan scaling yang akan pernah diambil platform tentang aplikasi kamu. Kubernetes mendefinisikan satu CPU unit sebagai satu core fisik dan 100m sebagai seratus millicpu, jadi ini kuantitas absolut: angka yang salah tidak menggeser target sedikit, ia mengubah arti target itu.

Kegagalan yang tidak saya duga adalah kegagalan yang sunyi. Request yang jauh lebih besar dari yang bisa dikonsumsi aplikasi membuat targetnya tidak pernah tercapai, jadi HPA melaporkan utilisation yang nyaman sementara request pengguna timeout, dan semua dashboard tetap hijau. Request yang tidak diset sama sekali lebih buruk: dokumentasinya menyatakan bahwa jika sebuah container tidak punya resource request yang relevan, CPU utilisation untuk pod itu tidak terdefinisi dan autoscaler tidak mengambil tindakan apa pun untuk metric tersebut. Autoscaling menyala, terkonfigurasi, tampil di layar, dan tidak melakukan apa-apa.

> Menyetel requests sama dengan limits adalah default yang aman untuk scheduling sekaligus musuh diam-diam dari scaling yang cepat. CPU limit ditegakkan lewat throttling, jadi pod yang mentok di limit-nya tidak pernah bisa melaporkan lebih dari 100 persen utilisation, dan terhadap target 70 persen rasionya tidak bisa melewati sekitar 1.43 — kira-kira satu pod tambahan per pod yang ada, per tick. Sisakan headroom antara request dan limit kalau kamu ingin controller bisa meminta lompatan besar.

## Pod baru bukan pod yang sudah panas

Yang memasukkan sebuah pod ke dalam layanan adalah readiness, bukan keberadaannya. Kalau readiness probe mengembalikan status gagal, EndpointSlice controller mencabut IP address pod itu dari EndpointSlices setiap Service yang cocok dengannya, dan probe itu terus berjalan sepanjang siklus hidup container, bukan hanya saat startup. Jadi probe itulah gerbang masuk bagi seluruh kapasitas baru kamu, dan sejujur apa isinya menentukan apakah spike benar-benar terlayani atau hanya tercatat.

HPA punya pendapatnya sendiri soal pod yang benar-benar baru, dan pendapatnya konservatif. Pod yang belum ready sesaat setelah start dianggap masih initialising selama initial readiness delay yang default-nya tiga puluh detik, dan CPU-nya diabaikan selama cpu initialization period yang default-nya lima menit kecuali sample-nya diambil sepenuhnya saat pod itu Ready. Di atas itu, pod Next.js yang baru dingin dengan cara yang tidak bisa dilihat Kubernetes: panduan self-hosting Next.js mencatat bahwa cache asset yang dihasilkan disimpan di memory dan di disk, dan bahwa di platform container orchestration seperti Kubernetes setiap pod memegang copy cache-nya sendiri. Naik dari satu pod ke sepuluh mengalikan kerja origin kamu sepuluh kali sampai cache itu terisi.

> Log streaming real-time Helipod menampilkan semua pod sekaligus di browser, dan browser terminal-nya membuka shell ke pod production yang sedang berjalan tanpa SSH key atau VPN. Kombinasi itulah yang mengubah timeline ini dari teori menjadi sesuatu yang bisa diamati: kamu melihat baris log pertama pod baru, readiness check pertamanya yang gagal, dan slow query pertamanya, dalam urutan yang benar-benar terjadi.

## Target CPU buta terhadap connection pool

Bottleneck yang tidak bisa dilihat autoscaler berbasis CPU adalah bottleneck di mana aplikasi kamu menunggu, bukan bekerja. Pod yang tertahan menunggu koneksi database hampir tidak memakai CPU, jadi utilisation justru turun tepat ketika pengalaman pengguna memburuk, dan bacaan jujur dari metric itu adalah bahwa aplikasi butuh lebih sedikit pod. Sementara itu konfigurasi pool yang kamu tulis saat aplikasi masih satu pod sekarang dikalikan jumlah replica.

| Yang kamu lihat saat spike | Yang dilihat target CPU | Yang benar-benar menolong |
| --- | --- | --- |
| Request menumpuk padahal CPU stabil di sekitar target | Rasio di dalam tolerance 0.1, jadi tidak ada tindakan sama sekali | Batasnya concurrency, bukan CPU — scale pada queue depth atau request in-flight |
| Latency naik sementara CPU turun | Kandidat untuk scale down | Periksa I/O: pod yang menunggu database tidak memakai CPU selama menunggu |
| Setiap pod baru error di query pertamanya | Pod belum Ready, diasumsikan tidak mengonsumsi apa pun, jadi scale-up justru teredam | Bagi connection pool dengan maxReplicas sebelum spike, bukan sesudahnya |
| Request pertama ke setiap pod baru terasa lambat | Tidak ada — cache miss bukan metric yang ia kumpulkan | Cache handler bersama, atau keputusan sadar menerima cold tail di setiap scale-up |
| Replica bertahan di batas atas lama setelah traffic turun | Rekomendasi tertinggi dari 300 detik terakhir | Tidak ada, dan itu benar — perpendek stabilization window hanya kalau kamu bisa membuktikan spike sudah selesai |

Semua baris punya bentuk yang sama: metric-nya berperilaku benar dan menjawab pertanyaan yang berbeda dari yang kamu ajukan. Itu bukan argumen menentang autoscaling berbasis CPU, yang murah, tidak butuh komponen tambahan, dan menangani kasus umum dengan baik. Itu argumen supaya kamu tahu kegagalan mana saja yang bisa ia lihat.

## Yang saya periksa sebelum launch sekarang

Tidak ada satu pun dari ini yang butuh platform lain. Yang dibutuhkan adalah lima keputusan yang diambil sebelum traffic datang, bukan saat traffic sudah datang.

1. Ukur CPU nyata aplikasi di bawah beban dan set resources.requests.cpu ke angka itu, karena angka itu penyebut dari setiap keputusan scaling berikutnya.
2. Tentukan minReplicas dari ledakan awal, bukan dari rata-rata, karena tidak ada hal lain yang memengaruhi lima belas detik pertama.
3. Arahkan readiness probe ke endpoint yang gagal sampai aplikasi benar-benar sanggup melayani request, dan tambahkan startup probe alih-alih initialDelaySeconds kalau warm-up-nya mahal.
4. Bagi connection pool database dengan maxReplicas, supaya batas atas autoscaler juga menjadi batas atas jumlah koneksi.
5. Pantau log semua pod sekaligus saat spike nyata pertama, supaya timeline-nya diamati, bukan diasumsikan.

Autoscaling berhenti jadi sebuah switch bagi saya dan berubah menjadi empat angka yang harus bisa saya pertahankan: request, target, minimum, dan maksimum. Managed PaaS menjalankan loop-nya dengan andal, dan itu memang bagian terbesar dari pekerjaannya. Menentukan apa yang diukur loop itu, dan memastikan sudah ada cukup kapasitas berjalan untuk bertahan lima belas detik sebelum ia menyadari apa pun, tetap tugas saya.

## Sumber

- Dokumentasi Kubernetes — Horizontal Pod Autoscaling: control loop, sync period, rumus scaling, tolerance, dan default behavior
- Dokumentasi Kubernetes — Resource Management for Pods and Containers: CPU unit, requests, dan limit yang ditegakkan lewat throttling
- Dokumentasi Kubernetes — Pod Lifecycle: container probe, dan bagaimana readiness mengatur endpoint sebuah Service
- Dokumentasi Next.js — Self-Hosting: caching dan ISR, serta kenapa setiap pod memegang copy cache-nya sendiri
- Helipod — PaaS Indonesia yang panel autoscaling-nya hanya membuka minimum, maksimum, dan target CPU

*Di kolom kiri dan kanan, CPU tetap rendah sementara latency naik, sehingga autoscaler dengan benar memutuskan untuk tidak melakukan apa pun terhadap gangguan yang nyata.*

## Source links

- https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
- https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
- https://nextjs.org/docs/app/guides/self-hosting
- https://helipod.io/en

## FAQ

### Apakah menyalakan autoscaling melindungi aplikasi saya dari traffic spike?

Tidak untuk bagian awalnya. Kubernetes Horizontal Pod Autoscaler adalah control loop yang bereaksi terhadap metric yang sudah ia ukur, jadi detik-detik pertama sebuah spike tetap dilayani replica yang sudah berjalan. Autoscaling melindungi kamu dari spike yang berlangsung lebih dari beberapa menit; yang melindungi kamu dari ledakan pertama adalah minReplicas.

### Seberapa cepat Kubernetes HPA menambah pod saat traffic spike?

Control loop-nya berjalan intermittent, dengan interval yang default-nya lima belas detik, jadi sebuah spike bisa menunggu hampir satu periode penuh hanya untuk disadari. Scale-up tidak punya stabilization window secara default, jadi keputusannya sendiri langsung diambil begitu metric terbaca. Setelah itu tidak ada anggaran waktu yang terdokumentasi: scheduling, image pull, boot proses, dan readiness probe semuanya bergantung pada image dan cluster kamu.

### Kenapa HPA saya tidak scaling padahal aplikasinya jelas kelebihan beban?

Penyebab yang paling umum adalah resources.requests. Utilisation dihitung sebagai persentase dari CPU request container, jadi request yang tidak mungkin dipenuhi aplikasi membuat persentase terukurnya rendah selamanya. Kalau CPU request tidak diset sama sekali, dokumentasi Kubernetes menyatakan CPU utilisation untuk pod itu tidak terdefinisi dan autoscaler tidak mengambil tindakan apa pun untuk metric tersebut. Penyebab umum lainnya adalah bottleneck I/O, saat aplikasi menunggu, bukan memakai CPU.

### resources.requests.cpu sebaiknya diset berapa kalau scaling pakai target CPU?

Set ke CPU yang benar-benar dipakai satu pod di bawah beban, diukur dan bukan dikira-kira, karena angka itu penyebut dari setiap keputusan scaling. Jaga limit tetap di atas request supaya utilisation terukur bisa melewati 100 persen. Kalau limit sama dengan request, throttling menahan utilisation di 100 persen, dan terhadap target 70 persen itu menahan rasio scale-up di sekitar 1.43 per tick.

### Kenapa jumlah replica saya tetap tinggi lama setelah spike selesai?

Itu stabilization window untuk scale-down, yang default-nya 300 detik. Controller mengambil rekomendasi tertinggi dari mana pun di dalam window itu, sebuah rolling maximum yang mencegahnya menghapus pod yang sebentar lagi dibutuhkan lagi. Perilaku itu memang disengaja, dan memperpendeknya berarti menukar biaya dengan risiko thrashing saat spike sebenarnya belum berakhir.

---

- HTML version: https://www.matthewswong.com/id/blog/helipod-hpa-autoscaling-traffic-spike/
- English: https://www.matthewswong.com/en/blog/helipod-hpa-autoscaling-traffic-spike/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
