HPA のゼロスケールが v1.37 から既定有効に、CPU やメモリだけの HPA では使えない
v1.37 で HPA の minReplicas に 0 を書けるようになった。使えるメトリクスの条件、手動停止との区別、control plane を上げる途中の落とし穴を、queue consumer を持つチーム向けに整理する。
v1.37 で HPA の minReplicas に 0 を書けるようになった。使えるメトリクスの条件、手動停止との区別、control plane を上げる途中の落とし穴を、queue consumer を持つチーム向けに整理する。
v1.37 の HPA は、minReplicas: 0 を feature gate の操作なしで受け付ける。HPAScaleToZero が Beta に上がり、kube-apiserver と kube-controller-manager の両方で既定有効になった。対象は object メトリクスか external メトリクスを持つ HPA だけで、CPU やメモリの resource メトリクスしか持たない HPA に 0 を書いても API サーバーが拒否する。
アドオンや外部コンポーネントで queue consumer や batch processor を 0 に落としているチームは、その役目が core に入ったことになる。GPU や専有 CPU を確保した Pod を待機させている構成では、最後の 1 Pod を消せる。control plane を段階的に上げている最中なら、先に落とし穴がある。両コンポーネントが揃う前に minReplicas: 0 の HPA を作ると workload が 0 のまま放置されうる。HTTP を受けるサービスは、この機能だけでは 0 にできない。
2026年9月2日付の Kubernetes ブログに載っている例は、queue-worker という Deployment を Prometheus の queue_consumer_lag という external メトリクスで 0 から 10 replica の間で動かす。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: queue-worker
annotations:
kubernetes.io/description: "Scales queue-worker based on the number of queued tasks"
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: queue-worker
minReplicas: 0
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: queue_consumer_lag
selector:
matchLabels:
name: worker_tasks
target:
type: Value
value: "30"
target.type が Value で値が "30" なので、キューに溜まったタスク 30件ごとに 1 replica を要求する。キューが空なら 0 まで下げる。タスクが来れば、external メトリクスは Pod がなくても読めるので HPA が replica 数を計算し直し、maxReplicas の 10 で頭打ちになる。
resource メトリクスが使えない理由は単純で、CPU もメモリも動いている Pod から取る値だからだ。replica が 0 になった時点で測る対象がなくなり、戻すきっかけも消える。キューの長さは worker がいなくても存在するので、HPA は 0 の間も読み続けられる。API サーバーが minReplicas: 0 の HPA に object か external のメトリクスを最低一つ要求するのは、この制約の裏返しだ。
replica が 0 という状態には二つの意味がありうる。HPA が下げたのか、運用者が手で止めたのかだ。v1.36 で追加された ScaledToZero という status condition がこれを区別する。HPA が 1 以上から 0 へ下げたときに ScaledToZero=True を記録し、以降の reconcile はこの 0 を HPA の管理下として扱って object/external メトリクスを評価し続ける。戻したあとは ScaledToZero=False になり、reason は NotScaledToZero だ。
裏返すと、この condition なしで 0 になっている workload は「一時停止」のままだ。Deployment を手で 0 にすれば autoscaling が止まる挙動は以前から変わらず、HPA は自分が下げていないものを起こさない。Deployment は最低 1 replica で起動しておく必要がある。
状態は kubectl describe hpa queue-worker で見られる。adapter がメトリクスを返せなくなると ScalingActive=False になり、reason には FailedGetExternalMetric のような値が入る。この状態では 0 から自動で戻る手段がないので、メトリクスを復旧するか手動でスケールして capacity を取り戻す。
0 まで下げられるようになっても、HPA の通常の挙動はそのまま乗る。scale down の安定化ウィンドウは既定で 5分あり、キューが一瞬空になっただけで全 worker が消えることを防ぐ。短くしたければ spec.behavior.scaleDown で調整する。
上げる側にはコールドスタートがある。HPA がメトリクスの変化を観測し、Pod をスケジュールし、アプリケーションが起動するまでの時間だ。仕事が durable なキューに溜まって待てるなら問題にならない。Kubernetes の Service は Ready な Pod がない間リクエストをバッファしないので、HTTP のようなリクエスト駆動の workload には別のバッファ層が要る。ブログはその層に何を使うかまでは示していない。
節約が大きいのは、Pod ごとに専有 CPU や GPU のような高価なリソースを確保している場合だ。最後の 1 Pod が待機しているだけで GPU を握り続ける構成なら、0 に落とす意味がある。
external メトリクスを HPA に見せるには metrics adapter が要る。ブログは Prometheus Adapter を一例として挙げ、externalRules で系列を公開する設定を載せている。
externalRules:
- seriesQuery: '{__name__="queue_consumer_lag",name!=""}'
metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (name)
resources:
overrides:
namespace:
resource: namespace
adapter の導入手順や discovery の規則は監視基盤ごとに違う。HPA を作る前に、External Metrics API から値が返るかを直接確認する。
kubectl get --raw \
'/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_consumer_lag?labelSelector=name%3Dworker_tasks'
worker_tasks の現在値が返らなければ、メトリクスが取れない HPA は 0 から戻れないので、先にメトリクスの経路を直す。
feature gate HPAScaleToZero は kube-apiserver と kube-controller-manager の両方に関わる。API サーバーが minReplicas: 0 を受け付け、controller manager が condition ベースのスケーリングを実行する。役割が分かれているので、片方だけ新しい状態が危ない。
version skew のあるアップグレード中は、両コンポーネントがこの機能を持ち、かつ有効になってから minReplicas: 0 の HPA を作る。gate が無効な controller manager は replica 0 を手動停止として扱うため、API サーバーが受理した HPA の workload が 0 のまま戻らないことがありうる。v1.37 の control plane では Storage Version Migration も既定有効で GA になっているので、gate の既定値が変わる項目はまとめて確認しておくと手戻りが少ない。
逆方向にも手順がある。gate を無効にする前、または condition ベースの実装がないバージョンへ戻す前に、次の二つを済ませる。
minReplicas を 1 以上に変える無効化後の controller manager は 0 を手動停止として扱うので、飛ばすと起こす主体がいなくなる。
最初の Alpha 実装は v1.16 に入った。v1.36 で ScaledToZero condition と、自動の scale down と手動停止を区別する controller の挙動が加わり、v1.37 で external メトリクスから 0 へ下げて戻す integration test と e2e test を足した上で既定有効になった。GA は Beta での運用フィードバックを集めてから判断するとされている。機能のオーナーは SIG Autoscaling で、設計は KEP-2021 にある。
minReplicas: 0 の HPA を作らないkubectl get --raw で叩き、対象メトリクスの値が返ることを確かめるkubectl describe hpa で ScaledToZero と ScalingActive の遷移を見るminReplicas を 1 以上へ戻し、0 の workload を手で上げる」を入れるHTTP を受けるサービスをこの機能だけで 0 にするのは、Service がリクエストを保持しない以上、別のバッファ層なしには成立しない。バッファ層を持たないなら、対象は queue consumer と batch processor に絞るのが素直だ。