HPA が思いどおりに動かないとき — よくある設定ミス 6 つ

読了 5分

HPA を掛けておいたのに、トラフィックが押し寄せても増えません。増えるには増えますが、すでに障害が起きた後。あるいは一日中増えたり減ったりを繰り返します。HPA の概念(HPA/VPA/Cluster Autoscaler の区別)は K8s 中級 #6 で扱ったので、この記事は実務で実際に引っかかる設定ミスを症状別に整理します。

まず状態確認のコマンドから見ます。HPA の問題の診断は、大半がこの一行から始まります。

kubectl describe hpa
$ kubectl describe hpa api-server
...
Metrics:              resource cpu on pods (as a percentage of request): <unknown> / 70%
Conditions:
  AbleToScale      True
  ScalingActive    False   FailedGetResourceMetric
Events:
  Warning  FailedGetResourceMetric  unable to get metrics for resource cpu

1. 指標が読めない状態 — <unknown> #

上の出力のように現在値が <unknown> なら、HPA は何の判断もできずに止まっている状態です。大半は二つです。metrics-server がないか死んでいるケース(kubectl top pod が失敗するならこちら)と、次の項目の requests 未設定です。

2. requests がなくてパーセントを計算できない #

CPU の averageUtilization: 70 は「requests に対して 70%」という意味です。分母が requests なので、コンテナに CPU requests がなければ HPA はパーセントを計算できません。limits だけ書いて requests を書き忘れたマニフェストが典型的な原因です。

  • 確認: 対象 Deployment のすべてのコンテナ(サイドカー含む)に CPU requests があるかを見ます。サイドカー一つ抜けただけでも計算が壊れます。
  • 対処: requests を設定します。値の決め方は次の記事で扱います。

同じ理由で、ターゲットパーセントの意味も requests 基準です。requests 500m にターゲット 70% なら、350m でスケールが始まります。limits(例: 2 コア)基準だと勘違いすると「なぜもう増えるのか?」という逆方向の誤解が生まれます。

3. min/max の境界に張り付いている #

症状が「増えない」なら、いちばん拍子抜けする原因から除外します。すでに maxReplicas に到達しているケースです。kubectl get hpa で REPLICAS が MAXPODS と同じなら、設定ではなく容量計画の問題です。逆に minReplicas が大きくてスケールダウンしないのを「ダウンが壊れた」と誤解するケースもあります。

4. 反応が遅い — 急増トラフィックとスケールアップの遅れ #

HPA は即座には反応しません。指標の収集周期、HPA の評価周期(デフォルト 15 秒)、新しい Pod の起動時間(イメージのプル + アプリの起動 + readiness の通過)が足し合わさり、トラフィックの急増から実際の容量確保までは分単位かかります。秒単位のスパイクは HPA では防げません。

  • 対処: 起動が遅いアプリはスケールアップを積極的に(behavior の scaleUp ポリシーで一度に複数個)、平時の余裕を minReplicas で確保します。予測可能なピーク(定時イベント)は先回りで拡張し、根本的には Pod の起動時間そのもの(イメージサイズ、readiness の設計 — K8s 中級 #5)を縮めることがスケーリングの応答性の本質です。

5. フラッピング — 増えたり減ったりの繰り返し #

レプリカが増えると Pod あたりの負荷が下がって指標がターゲットを割り、それで減らすとまたターゲットを超える振動です。HPA にはこれを防ぐ安定化ウィンドウ(デフォルト: スケールダウン 5 分)がありますが、ターゲットをきつく取りすぎた場合(例: 90%)や、指標がもともと揺れるワークロードではデフォルトでは足りません。

  • 対処: behavior.scaleDown.stabilizationWindowSeconds を増やし、ダウンの速度をポリシーで制限します(例: 1 分に 10% ずつ)。ターゲットも指標の自然な変動幅より余裕を持って取ります。

6. 他のコントローラーとの衝突 #

HPA が増やしたのに数分後に元に戻っている症状は、大半は別の何かが replicas を戻しています。マニフェストに replicas を書いたまま GitOps(ArgoCD など)が sync する構成が常連です。HPA が 5 に上げると GitOps が「宣言と違う」と 2 に戻します。VPA を同じ指標(CPU)に auto モードで一緒に掛けているケースも互いに干渉します。

  • 対処: HPA を使うワークロードはマニフェストから replicas フィールドを外すか、GitOps 側でそのフィールドを ignore 処理します。VPA は推奨モードに留めるか、対象の指標を分けます。

補足 — CPU だけを見るスケーリングの限界 #

設定が全部合っていても、ボトルネックが CPU でなければ HPA は空回りします。メモリがボトルネックのワークロード、コネクション数やキューの長さが実際の負荷指標のワークロード(Webhook のコンシューマー、ジョブキュー)は、CPU 70% に達する前にすでに遅くなっています。この場合はカスタム指標(毎秒リクエスト数、キューの深さ)ベースのスケーリングや、イベント駆動のオートスケーラー(KEDA)が適した道具です。何がボトルネックかは、サーバーが遅い理由シリーズの診断手順がそのまま使えます。

まとめ #

  • 診断は kubectl describe hpa から始めます。<unknown> なら metrics-server と requests をまず見ます。
  • ターゲットパーセントは requests 基準です。requests がなければ HPA は動かず、サイドカー一つ抜けても壊れます。
  • HPA の反応は分単位です。秒単位のスパイクは minReplicas の余裕と起動時間の短縮で備えます。
  • フラッピングは安定化ウィンドウとダウンのポリシーで、GitOps の replicas の巻き戻しはフィールドの削除・ignore で解きます。
  • CPU がボトルネックでないワークロードは、カスタム指標や KEDA でスケールの基準そのものを変えます。
X