Skip to main content
BlogDevOps

Kubernetes HPA Scale to Zero untuk GPU Queue Worker (1.37)

HPA scale to zero Kubernetes kini Beta di 1.37: atur minReplicas 0 untuk queue dan GPU worker, lengkap dengan config, jebakan ScaledToZero, dan KEDA.

Matthews Wong

Software Engineer

Oktober 202612 menit baca
Kubernetes HPA Scale to Zero untuk GPU Queue Worker (1.37)

Ringkasan Utama

Kubernetes 1.37 menjadikan HorizontalPodAutoscaler scale to zero sebagai fitur Beta yang aktif secara default. Dengan spec.minReplicas 0, queue consumer atau GPU worker bisa idle saat metric external atau object seperti queue lag bernilai nol. Fitur ini tidak bisa memakai CPU atau memori, tidak mem-buffer request HTTP, dan butuh metrics pipeline yang sehat untuk bangun kembali.

Queue worker yang cepat atau lambat dibuat tiap tim menghabiskan sebagian besar hidupnya untuk menunggu. Job datang bergelombang, Deployment diam di satu replica sepanjang hari, dan kalau tiap replica menahan satu GPU, jam-jam idle itulah yang paling mahal. Sampai rilis ini, HorizontalPodAutoscaler bawaan tidak bisa menyelesaikannya karena batas bawahnya satu replica.

Kubernetes v1.37 memindahkan batas bawah itu. Tulisan ini adalah pembacaan teliti atas apa yang dikatakan release post, dokumentasi HPA, blog post fitur ini, dan KEP-2021 tentang scale to zero: config yang bekerja, condition yang menjaganya tetap jujur, delay yang harus dibayar, dan di mana fitur ini berhenti. Saya belum menjalankan 1.37 di production, jadi semua angka di bawah dikutip dari sumber-sumber itu, dan ketika saya menalar melampaui sumber, saya katakan terus terang.

Apa yang berubah di 1.37, dan apa yang tidak

Release notes v1.37 menyebut HPA scale to zero pertama kali diperkenalkan di Kubernetes v1.16 dan naik ke Beta di v1.37, yang kini aktif secara default. Feature gate-nya HPAScaleToZero, dan dokumentasi HPA menyatakan gate itu harus aktif di kube-apiserver, yang mengizinkan minReplicas 0, dan di kube-controller-manager, yang melakukan scaling. Defaultnya adalah perubahan sesungguhnya: blog post fitur ini menyebut bahwa sebelum 1.37, scale from zero membutuhkan add-on, komponen eksternal, atau gate Alpha.

RilisYang masukDisebut di mana
v1.16Pertama kali diperkenalkan. Timeline di blog post menyebutnya implementasi Alpha pertama.Release notes v1.37 dan blog post HPA
v1.36Condition ScaledToZero, dan perilaku controller yang membedakan scale-down otomatis dari pause manual.Blog post HPA, bagian timeline
v1.37Beta, aktif secara default. HPAScaleToZero menyala di kube-apiserver dan kube-controller-manager.Dokumentasi HPA
GABelum ada versi. Langkah berikutnya yang disebut adalah mengumpulkan operational feedback.Blog post HPA dan tracking issue KEP

Perubahan yang kurang kentara hadir satu rilis lebih awal. Timeline di blog post menyebut v1.36 menambahkan condition ScaledToZero dan perilaku controller yang membedakan scale-down otomatis dari pause manual. Condition itu, bukan jumlah replica, yang membuat fitur ini aman, dan bagian keempat menjelaskan alasannya. Beta juga belum GA: post tersebut menyebut langkah berikutnya adalah mengumpulkan operational feedback sebelum mempertimbangkan GA, dan tracking issue KEP tidak mencantumkan versi stable.

Kenapa hanya metric object dan external yang memenuhi syarat

Scale to zero berdasarkan CPU atau memori tidak didukung, karena metric itu bergantung pada Pod yang aktif. Dokumentasi HPA mengatakan hal yang sama: resource metrics hanya bisa diukur pada Pod yang berjalan, sehingga pada nol replica tidak ada yang bisa diukur dan tidak ada yang bisa meminta replica pertama kembali. Mengatur minReplicas 0 mewajibkan minimal satu metric object atau external, dan API server menolak HPA yang tidak memilikinya.

Metric object dan external tidak punya masalah itu. Dokumentasi menggambarkannya sebagai satu metric yang diambil untuk object yang dimaksud lalu dibandingkan dengan target. Panjang queue hidup di broker, bukan di worker, sehingga ia tetap melapor setelah worker hilang. Itulah inti desainnya: sinyal yang membangunkan workload harus hidup lebih lama dari workload itu. KEP-2021 merumuskan tujuannya secara sempit, scale to dan from zero untuk metric object dan external, dan mencantumkan resource metrics serta request buffering di level Service sebagai non-goal.

Config yang bekerja: queue lag, Prometheus Adapter, satu HPA

Contoh di blog Kubernetes mengasumsikan Prometheus sudah mengumpulkan series bernama queue_consumer_lag dengan label namespace dan name, lalu mengeksposnya lewat externalRules milik Prometheus Adapter. Dokumentasi adapter menjelaskan detail yang sering menjebak: HPA bersifat namespaced, sehingga adapter menambahkan label namespace ke query, dan path External Metrics API membawa namespace. Periksa metric lewat API server sebelum menulis HPA.

# 1. prometheus-adapter values: expose queue_consumer_lag as an EXTERNAL metric.
#    Shape taken from the Kubernetes v1.37 HPA post and the adapter's
#    external-metrics doc (v0.12.0).
externalRules:
- seriesQuery: '{__name__="queue_consumer_lag",name!=""}'
  metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (name)
  resources:
    overrides:
      namespace:
        resource: namespace   # HPAs are namespaced, so the adapter scopes by namespace

# 2. Prove the metric through the API server before any HPA exists.
#    If this does not print the current value, an HPA cannot wake from zero.
kubectl get --raw \
  '/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_consumer_lag?labelSelector=name%3Dworker_tasks'

HPA di bawah mengikuti contoh blog: Deployment bernama queue-worker, nol sampai sepuluh replica, satu replica diminta per 30 task dalam antrean. Deployment, limit GPU, dan grace period adalah tambahan saya untuk menunjukkan kasus yang menjadi alasan fitur ini dijual. Ganti baris GPU dengan extended resource apa pun yang diiklankan device plugin Anda.

# Deployment: leave spec.replicas OUT. It defaults to 1, which gives the HPA
# a workload to scale down, and a later kubectl apply will not reset the count.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: queue-worker
spec:
  selector:
    matchLabels:
      app: queue-worker
  template:
    metadata:
      labels:
        app: queue-worker
    spec:
      terminationGracePeriodSeconds: 600   # my choice: long enough to finish a task
      containers:
      - name: worker
        image: registry.example.com/queue-worker:1.0.0
        resources:
          limits:
            nvidia.com/gpu: 1   # swap for whatever extended resource your device plugin advertises
---
# HPA: the shape of the Kubernetes v1.37 blog example. One replica per 30
# queued tasks, from 0 up to 10.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: queue-worker
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: queue-worker
  minReplicas: 0          # needs at least one object or external metric below
  maxReplicas: 10
  metrics:
  - type: External
    external:
      metric:
        name: queue_consumer_lag
        selector:
          matchLabels:
            name: worker_tasks
      target:
        type: Value
        value: "30"

Tiga detail di blog post lebih penting daripada YAML-nya. Mulai Deployment dengan minimal satu replica, karena HPA tidak akan membangunkan workload yang tidak ia turunkan sendiri. Downscale stabilization window default-nya lima menit. Dan API server menolak minReplicas 0 pada HPA yang hanya memakai resource metrics. Dokumentasi juga menyarankan menghapus spec.replicas dari manifest begitu HPA memegang Deployment, karena kubectl apply berikutnya akan mengembalikan jumlah ke nilai itu dan menyebabkan thrashing.

Uji jalur metric secara terpisah lebih dulu. Blog menyebut bahwa jika query raw external metrics tidak mengembalikan nilai saat ini, perbaiki metrics pipeline sebelum membuat HPA, karena HPA tidak bisa scale from zero ketika metric-nya tidak tersedia.

Nol versus paused: condition ScaledToZero

Sebelum fitur ini, replicas 0 hanya berarti satu hal: seseorang mem-pause autoscaling. KEP menyebut cara itu bekerja karena HPA tidak pernah bisa mencapai nol sendiri. Begitu minReplicas 0 diizinkan, Deployment di nol menjadi ambigu, sehingga HPA mencatat condition ScaledToZero. Nilainya True ketika HPA memindahkan workload dari satu replica ke nol, dan False dengan reason NotScaledToZero setelah ia naik lagi. Workload di nol tanpa ScaledToZero True tetap dianggap paused.

# Who put this workload at zero: the HPA, or a human?
kubectl get hpa queue-worker \
  -o jsonpath='{range .status.conditions[*]}{.type}={.status} {.reason}{"\n"}{end}'

# Reading it, per the HPA docs and the v1.37 blog post:
#   ScaledToZero=True                   the HPA scaled it down and will wake it
#   ScaledToZero=False NotScaledToZero  it was scaled back up
#   no ScaledToZero=True, replicas 0    a manual pause: the HPA will NOT wake it
#   ScalingActive=False FailedGetExternalMetric
#                                       the metric is gone; nothing can scale up

kubectl describe hpa queue-worker      # same conditions, plus recent events

KEP jujur soal sisi tajam yang tersisa. Menerapkan Deployment baru dengan replicas 0 di samping HPA dengan minReplicas 0 tidak akan pernah scale, karena HPA bukan yang menaruhnya di sana. KEP menyebut ini bisa dideteksi lewat condition ScalingActive yang sudah ada, dan bahwa fitur pause eksplisit bisa menggantikan yang implisit nanti. Saya akan menjadikannya pemeriksaan pertama setiap kali worker macet di nol padahal ada pekerjaan menunggu.

Anggaran cold start: dari mana delay datang

Release blog menyebut waktu cold start sebagai trade-off: HPA harus mengamati metric, men-schedule Pod, dan menjalankan aplikasi. Sumber Kubernetes hanya memberi angka untuk bagian HPA sendiri, dan saya tidak akan mengarang sisanya. Tabel ini memuat apa yang memang mereka nyatakan.

TahapDefaultDampak pada queue consumer
Kesegaran metrics pipelineTidak ada angka; tergantung adapter dan setup scrape AndaReplica pertama baru muncul setelah metric yang Anda sajikan melewati threshold.
Reconcile loop HPA15 detik (--horizontal-pod-autoscaler-sync-period)KEP-2021 membatasi langkah dari nol ke satu oleh sync period ini ditambah kesegaran pipeline.
Stabilization scale-upTidak adaDokumentasi menyebut scale-up terjadi langsung ketika metric menuntutnya.
Stabilization scale-down300 detikNilai desired tertinggi dalam window yang menang, jadi nol baru tercapai setelah lima menit tenang kecuali Anda mengubahnya.
Scheduling Pod dan startup aplikasiTidak ada; Anda yang mengukurRelease blog menyebut ini, bersama pengamatan metric, sebagai trade-off cold start.

Ada dua pembacaan. Pertama, catatan SLO di KEP-2021 menyebut replica pertama muncul pada reconcile berikutnya setelah metric melewati threshold, ditambah kesegaran metrics pipeline, tanpa delay scale-up tambahan. Kedua, semua setelah itu berada di luar HPA: image pull, ketersediaan node GPU, model load. Dokumentasi menunjuk node autoscaling untuk jumlah node yang tepat; inferensi saya, node GPU yang ikut menyusut menambah waktu tunggunya sendiri, yang tidak dikuantifikasi sumber-sumber ini. Ukur seluruh jalurnya di cluster Anda sendiri sebelum menjanjikan latency kepada siapa pun.

# Added to the HPA spec above. The default scaleDown window is 300 s and the
# docs describe it as a rolling maximum of past desired states. 900 is MY
# number for bursty queues, not a recommendation from the Kubernetes docs:
# it trades a longer idle tail for fewer cold starts between bursts.
behavior:
  scaleDown:
    stabilizationWindowSeconds: 900
  scaleUp:
    stabilizationWindowSeconds: 0   # the documented default: scale up at once

HPA native atau KEDA: apa yang dilepas dan apa yang tidak perlu dipasang lagi

KEDA sudah menawarkan scale to zero, jadi pertanyaan jujurnya adalah apa yang diubah fitur native ini. Dokumentasi KEDA menjelaskan dua fase. Pada fase activation, operator KEDA memutuskan apakah workload naik dari atau turun ke nol, dan itu hanya berlaku untuk 0 ke 1. Pada fase scaling, HPA mengambil alih untuk 1 ke N berdasarkan metric yang diekspos KEDA. Fitur native menaruh keputusan 0 ke 1 di dalam controller HPA sendiri, tetapi hanya untuk metric yang sudah Anda sajikan.

AspekHPA native (1.37 Beta)KEDA ScaledObject (docs 2.19)
Siapa yang memutuskan 0 ke 1Controller HPA, memakai condition ScaledToZero untuk tahu bahwa ia sendiri yang menyebabkan nolOperator KEDA, dari hasil activation scaler; HPA menangani 1 ke N
Cara mengaktifkanspec.minReplicas: 0 pada HPA, dengan gate aktif secara default di 1.37Sebuah ScaledObject, dengan minReplicaCount default 0
Sumber metricAPI metric external atau object yang sudah Anda sajikan, misalnya lewat Prometheus AdapterKEDA memantau event source dan menyuplai datanya ke HPA
Threshold bangun terpisahTidak ada di docs yang saya baca: satu target menggerakkan semuanyaactivationThreshold, harus lebih besar dari nilainya, default 0, dan menang atas nilai scaling
Floor idle dan aktif terpisahTidak terdokumentasi. Ditanyakan di issue KEP dan diarahkan ke KEP baruidleReplicaCount, yang harus lebih kecil dari minReplicaCount
Pengatur waktuDownscale stabilization window, default 300 detikpollingInterval 30 detik dan cooldownPeriod 300 detik secara default

Pembacaan saya: jika Anda sudah menjalankan metrics adapter dan punya satu queue yang dipahami baik, fitur native mengurangi satu komponen. Jika Anda butuh threshold bangun yang berbeda dari target scaling, atau idle floor, docs KEDA menampilkan keduanya sedangkan docs HPA yang saya baca tidak menampilkan satu pun. Seorang assignee di issue KEP menjawab pertanyaan tentang floor idle dan aktif terpisah dengan mengatakan hal itu lebih baik dilacak sebagai KEP baru.

Di mana fitur ini berhenti bekerja

Lima failure mode, masing-masing diambil dari sumber, bukan dari insiden saya:

  • Metric hilang. HPA melaporkan ScalingActive False dengan reason seperti FailedGetExternalMetric, dan workload di nol tetap di sana sampai Anda memulihkan metric atau menaikkannya manual.
  • Seseorang atau pipeline mengatur replicas ke 0. Tanpa ScaledToZero True, HPA membacanya sebagai pause dan tidak akan membangunkannya, dan Deployment baru di nol di samping minReplicas 0 tidak akan pernah scale.
  • Control plane baru setengah di-upgrade. Controller manager dengan gate mati menganggap replicas 0 sebagai pause manual, jadi blog menyarankan menunggu sampai kedua komponen mendukung fitur ini sebelum membuat HPA dengan minReplicas 0.
  • Workload menerima traffic HTTP. Service tidak mem-buffer request, dan request buffering adalah non-goal eksplisit KEP, sehingga workload berbasis request butuh lapisan buffering terpisah.
  • Anda melakukan rollback. Atur HPA terkait ke minReplicas 1 atau lebih dan naikkan workload yang di nol ke minimal satu replica sebelum mematikan gate atau downgrade, atau workload bisa terjebak di nol.

Ada satu kegagalan lagi yang tidak disebut sumber, dan ia muncul dari cara bagian-bagian yang terdokumentasi saling bertemu. Metric di contoh adalah queue lag, yaitu task yang masih menunggu. Task yang sudah diambil worker tidak lagi menunggu. Dokumentasi menyebut stabilization window memakai nilai desired tertinggi selama window sebelumnya, lima menit secara default, sehingga job panjang bisa melewati window itu saat queue kosong dan HPA bebas menghapus Pod yang sedang mengerjakannya. Ini penalaran saya dari perilaku yang terdokumentasi, bukan sesuatu yang saya ukur.

Jika job GPU bisa berjalan lebih lama dari downscale window Anda, ekspos metric yang menghitung task queued ditambah in-flight, dan beri worker termination grace period yang cukup untuk menyelesaikan atau mengembalikan task-nya. Kalau tidak, scale to zero bisa membunuh pekerjaan yang tidak terlihat oleh metric queue mana pun.

Yang dikerjakan hari Senin

Jika Anda sudah di 1.37, fitur ini sudah menyala, jadi mulai dengan audit read-only baru kemudian pilih satu workload. Lima langkah, berurutan:

  1. Cari setiap HPA yang sudah mengatur minReplicas 0 dan setiap Deployment yang berada di nol replica. Yang pertama kini bisa scale sendiri; yang kedua mungkin paused atau mungkin sedang menunggu.
  2. Pilih satu queue consumer dengan idle panjang dan Pod mahal, dan buktikan metric-nya lewat External Metrics API sebelum menyentuh HPA apa pun.
  3. Terapkan HPA dengan minReplicas 0 di staging, kosongkan queue, perhatikan ScaledToZero menjadi True, lalu masukkan satu task dan ukur jeda dari pesan sampai replica Ready pertama.
  4. Periksa skew dan rollback. Pada control plane campuran, tunggu sampai kedua komponen mendukung fitur ini. Sebelum downgrade apa pun, atur minReplicas ke 1 dan naikkan workload yang berada di nol replica.
  5. Tambahkan alert di bawah: queue punya pekerjaan, tetapi HPA tidak bisa bertindak. Ia menangkap adapter yang rusak sebelum pengguna menyadari job yang macet.
# Step 1: every HPA that may already scale to zero, and every Deployment at zero.
kubectl get hpa -A -o json \
  | jq -r '.items[] | select(.spec.minReplicas == 0) | .metadata.namespace + "/" + .metadata.name'
kubectl get deploy -A -o json \
  | jq -r '.items[] | select(.spec.replicas == 0) | .metadata.namespace + "/" + .metadata.name'

# Step 5: page when the queue has work but the HPA cannot wake the workers.
# Metric names are the ones KEP-2021 points at (kube-state-metrics); the label
# names are kube-state-metrics' own, so confirm them on your version.
groups:
- name: scale-to-zero
  rules:
  - alert: QueueHasWorkButWorkersCannotWake
    expr: |
      max(queue_consumer_lag{name="worker_tasks"}) > 0
      and on()
      max(kube_horizontalpodautoscaler_status_condition{
            horizontalpodautoscaler="queue-worker",
            condition="ScalingActive", status="false"}) == 1
    for: 5m

Pilih workload pertama dengan dua tes: satu jam idle memakan biaya nyata, dan cold start tidak merugikan siapa pun. Release blog membingkai kecocokannya sebagai pekerjaan yang bisa menunggu di durable queue, dan itulah filter yang akan saya pakai.

Aturan yang saya bawa dari sini: scale to zero adalah janji tentang metric, bukan tentang workload. HPA hanya bisa mengidlekan queue consumer karena panjang queue hidup lebih lama dari worker, jadi miliki dulu jalur metric itu, kesegarannya, dan alert-nya, lalu periksa apakah ada job yang melewati downscale window. Jika keduanya benar, 1.37 menghapus satu add-on dari cluster Anda. Jika salah, kegagalannya akan terlihat persis seperti queue yang kosong.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apakah HorizontalPodAutoscaler Kubernetes mendukung scale to zero?

Ya. Scale to zero naik ke Beta di Kubernetes v1.37 dan aktif secara default lewat feature gate HPAScaleToZero. Anda mengatur spec.minReplicas ke 0 pada HPA yang memakai minimal satu metric object atau external, dan API server akan menolak HPA yang tidak memenuhinya.

Bisakah saya men-scale Deployment ke nol berdasarkan CPU atau memori?

Tidak. Metric CPU dan memori hanya ada selama Pod berjalan, jadi pada nol replica tidak ada yang bisa diukur dan tidak ada yang bisa memicu scale up. Gunakan metric object atau external yang hidup lebih lama dari worker, misalnya panjang queue dari broker yang diekspos lewat Prometheus Adapter.

Bagaimana HPA membedakan workload scaled-to-zero dari yang di-pause?

HPA mencatat condition ScaledToZero pada statusnya. Nilainya True selama HPA menahan workload di nol, dan False dengan reason NotScaledToZero setelah naik lagi. Workload di nol tanpa ScaledToZero True dianggap di-pause manual, dan HPA tidak akan membangunkannya.

Apakah saya masih butuh KEDA jika HPA bisa scale to zero?

Tidak selalu. Jika Anda sudah menyajikan satu metric external lewat adapter, fitur native mengurangi satu komponen. Dokumentasi KEDA juga menjelaskan activation threshold terpisah dan idleReplicaCount yang tidak ditawarkan dokumentasi HPA, jadi workload yang membutuhkannya masih lebih cocok memakai KEDA.

Apa yang terjadi jika metrics adapter gagal saat workload berada di nol?

HPA melaporkan ScalingActive False dengan reason seperti FailedGetExternalMetric dan tidak bisa scale up karena tidak bisa melihat queue. Workload tetap di nol sampai metric dipulihkan atau Anda menaikkannya manual. Alert untuk panjang queue di atas nol saat ScalingActive false akan menangkapnya lebih awal.