なぜ今か:HPAと標準VPAが干渉する構造と動的調整が求められた背景

KubernetesにおいてHPA(Horizontal Pod Autoscaler)でResourceメトリクスの type=Utilization を指定する場合、CPU利用率の計算における分母はPodのCPU requestsであり、Pod単位のResource CPU値では全コンテナの使用量が合算されます[1]。対象Podの各コンテナに対応するrequestsが設定されていない場合、HPAは使用率の計算を行えず、そのメトリクスに基づくスケーリング処理を実行しません[2]。HPA単独が直接調整するのはPodのレプリカ数です[3]。

HPAの基礎的な要求レプリカ数(desiredReplicas)の計算は、次の算式によって行われます[4]。

$$\text{desiredReplicas} = \left\lceil \text{currentReplicas} \times \frac{\text{currentMetricValue}}{\text{desiredMetricValue}} \right\rceil$$

このように、desiredReplicasは現在のメトリクス値と目標値の比率によって決定されます[5]。

一方で、標準VPA(Vertical Pod Autoscaler)のCPU requests推奨値は、対象Podの現在および過去の実使用量データの分析に基づいて算出されます[6]。GKEのVPAは、対象Pod群の使用量データを集約して推奨値を計算します[7]。

CPU Utilization型HPAではCPU requestsが利用率の分母となるため、実使用量が同一であってもVPAがrequestsを引き下げるとHPAの計算する利用率は上昇し、requestsを引き上げると利用率は低下します[8]。実使用量が不変のままVPAがCPU requestsを引き下げると、HPAから見た利用率とdesiredReplicasは増加する方向に働きます[9]。

負荷総量がほぼ一定でPod間に均等配分される状況においては、HPAがレプリカ数を増やすとPodあたりの使用量が下がり、次のVPA requestsを引き下げる方向に働き得る一方で、レプリカ数減少時には逆方向に働き得ると考えられます。実際の変動方向や変動幅は負荷の配分状況やPodごとの偏りに依存します[10]。

リソースの静的設定には常に運用のトレードオフが存在します。CPU limitsが実需要を下回る場合はCPUスロットリングのリスクが生じ、CPU requestsが小さい場合はPodの配置が容易になる一方で競合時の配分が小さくなり、requestsが大きすぎる場合はノード配置効率の低下や課金方式に応じた費用の増大につながります[11]。CPU requestsを固定する方式は、Pod配置の判断材料とCPU競合時の配分重み(weight)を一定に保つ静的運用であり、設定の予測性を優先できる一方で、実需要の変化に合わせた調整は運用者が継続して担う必要があります[12]。

標準VPAは過去の利用実績とリアルタイム需要に基づいてコンテナのrequestsおよびlimitsを調整しますが、推奨値の適用に伴うPodの更新や再作成によってワークロードの中断を引き起こす場合があります[13]。

標準VPAとHPAにおいて、CPUまたはメモリの同一リソースメトリクスを併用することはGKEの公式推奨から外れます[14]。HPAをrequests-per-secondなどのカスタムメトリクスで稼働させれば、VPAによるrequests変更がHPAのCPU利用率の分母を直接変える干渉経路は避けられますが、Cloud Monitoring等へのメトリクス送信や取得パイプラインの構築が必要です[14]。

GKEは、HPA rightsizing with VPA(Preview)を利用する場合を除き、CPUまたはメモリの同一メトリクスで標準VPAとHPAを併用しないよう案内しています[15]。従来の公式な代替構成としては、別メトリクスの採用や、CPUを水平方向にスケールさせメモリを垂直方向に調整するMultidimensional Pod Autoscalingの構成が提示されていました[15]。

後輩CPU使用率でスケールさせているPodに対して標準VPAがCPU requestsを書き換えると、計算の分母が動いてHPAのレプリカ数判断が乱れてしまいます。requestsを固定して運用した方が安全に見えますが、動的な調整が求められるのはどうしてですか?

先輩固定すればスケジューリングの予測性は保てるけれど、設定値が小さければCPUスロットリングのリスクを抱え、大きすぎればリソースの無駄や費用の増大につながるよ。需要の変化に応じた調整を運用者が手動で抱え続ける負担を解消するために、HPAと協調する動的な調整機能が求められたんだ。

後輩静的な安全マージンに依存する運用の負担を減らすために、HPAとVPAが互いの設定値を織り込んで動く仕組みが必要になったわけですね。

何が変わるか:HPA rightsizing modeの協調機構とCPU requestsの調整仕様

GKE HPA rightsizingは2026-10-02にPublic Previewとして発表されました[16, 17]。GKEバージョン 1.36.3-gke.1630000 以降で利用可能です[16]。

本機能では、ワークロードの集約された過去の需要実績と、HPAの設定値(最小レプリカ数、最大レプリカ数、目標CPU利用率)を入力として考慮し、VPAがコンテナのCPU requestsを調整します[18, 19, 20, 21, 22]。これにより、低トラフィック時の資源効率と高需要時の耐障害性の両立を目指しています[20]。

確認した公式ガイドで示されているのは入力項目と調整の目的であり、各HPA設定値をCPU requests値へと換算する具体的な計算式や重み付けの記載は見当たりません。そのため、最小・最大レプリカ数からrequests値を直接算出する算式は断定できません[23]。

rightsizingの計算対象はCPUのみに限定されています[19, 24]。VPAでメモリも管理対象に含める場合、メモリのrequestsおよびlimitsは標準VPAのロジックに基づいて調整されます[24, 25]。

公式設定例のHPAメトリクスにはCPUのResource utilizationが用いられています[26]。1つのPod内に複数のコンテナが存在する構成では、HPAの設定で ContainerResource メトリクスタイプを使用し、プライマリコンテナのCPU利用率をスケーリング基準に設定することが案内されています[26, 27]。これにより、サイドカーのリソース使用量をスケーリング判定から除外し、HPAとVPAが同一のコンテナ基準でリソース需要を評価できるようになります[26, 27]。

補足:ContainerResourceメトリクスとは

Pod全体の合計値ではなく、Pod内の特定のコンテナが消費するリソース使用量を対象としてHPAのスケーリングを評価するメトリクス種別です。

設定マニフェストでは、HPAの scaleTargetRef と VPAの targetRef の双方が同一の apps/v1 Deployment を指定します[28]。

rightsizing機能は、VPAリソースに所定のアノテーション gke.io/autoscaling-hpa-rightsizing-mode: "gke-hpa-rightsizing-mode-policy" を付与することで有効化されます[29]。VPAの updateMode を "Off" に設定すると、コンテナのrequestsを変更せずに推奨値の生成のみを行います[29]。自動適用を行う際は updateMode に "InPlaceOrRecreate" または "Recreate" を指定します[29]。

InPlaceOrRecreate モードでは、Podを再起動することなくインプレースでCPU requestsを更新します[30]。ノードのリソース状況等によりインプレース更新が実行できない場合は、Podの退避・再作成へとフォールバックします[30, 31]。また、VPA設定内の minAllowed および maxAllowed フィールドによって、設定したリソース境界の範囲外へコンテナサイズが変更されるのを防ぎます[29]。

誰に影響するか:クラスタ種別・運用体制・ワークロード適合性の判定条件

公式ガイドはGKE AutopilotおよびGKE Standardの両クラスタを対象としています[32]。利用にはクラスタでVPAが有効化されている必要があり、Autopilotでは既定で有効、Standardでは明示的な有効化操作が必要です[32, 33]。

クラスタ種別による課金構造の違いは次の通りです。

一般的なAutopilot環境ではPodのCPU requestが課金基準となるため、CPU requestを引き下げることでCPU課金の削減が可能です[34]。ただし、Autopilotが適用する最小値やCPU・メモリ間の比率制約(1:1から1

.5の範囲)により、削減幅が抑制される場合があります[34]。

Standardクラスタのノードプールでは基盤となるVM単位で課金が行われます。そのため、CPU requestsを引き下げても稼働ノード数が減少しない間は請求額に直接反映されず、ノードの集約や縮退につながった段階で費用削減の効果が生じると考えられます[35]。

rightsizingの対象コンテナ数には上限が設けられています。単一クラスタ内の全ワークロード合計で最大60,000コンテナ、単一のVPAリソースあたり最大1,000コンテナまで設定可能です[36]。

ワークロードの適合性に関しては、VPAは長期稼働し、Pod間の使用パターンが均質なワークロードに適しています[37]。同一のVPA対象においてPod間のCPU使用パターンに偏りがある場合、負荷の低いPodに過剰配分し、負荷の高いPodに過小配分する推奨値を生成するリスクが存在します[38]。

すでにリクエスト率などのカスタムメトリクスを基準にHPAをスケールさせているワークロードでは、CPU requestsの変動がHPAの利用率計算に干渉しないため、干渉回避というrightsizing固有の利点は小さくなり、得られる効果はCPU requestsの適正化にとどまると考えられます[39]。

Argo CDなどの継続的デリバリーツールを運用している場合、VPAによるリソース変更をマニフェストからの差分と判定し、元のマニフェスト値へ強制的にロールバックする可能性があります[40]。このため、公式ガイドではArgo CDの ignoreDifferences 属性などを用い、VPAによるリソース変更差分を同期対象から除外する設定例を案内しています[40]。

本機能の導入に適した組織体制としては、Public Preview機能の利用を許容できる環境において、アプリケーション担当者がサービスの品質やSLOを監視し、Platform/SRE担当者がHPAやVPAの挙動を追跡して緊急時の停止操作まで担える体制が想定されます[41]。

後輩GitOpsでリソースを管理している場合、VPAがCPU requestsを書き換えても、Argo CDなどが検知してマニフェスト通りの値へ巻き戻してしまう懸念がありますね。

先輩その通りだね。だからマニフェスト管理ツール側で、VPAによるリソース変更の差分を同期対象外にするignoreDifferencesなどの除外設定をあらかじめ組み込んでおく運用設計が欠かせないよ。

後輩自動化機能同士が競合して元に戻される事態を防ぐために、インフラ側の動的変更とデリバリー側の同期設定の境界を整えておく必要があるのですね。

実機検証に見るスケーリング挙動と数値データの解釈限界

GKE AutopilotのRapidチャンネル、GKEバージョン 1.36.3-gke.1630000、東京リージョン(asia-northeast1)にて、公式サンプルイメージ registry.k8s.io/hpa-example を用いた実機検証が行われました[42]。

HPAは autoscaling/v2 の Resource CPU Utilizationを使用し、sample-app Deployment を最小2、最大10レプリカ、目標CPU利用率70%でスケールさせました[43]。VPAは同一Deploymentを対象にrightsizingアノテーションと updateMode: "InPlaceOrRecreate" を設定し、controlledResources: ["cpu"] でCPU requestsのみを制御対象とし、許容範囲(minAllowed / maxAllowed)を100m〜1000mとしました[44]。

負荷はBusyBox 1.36の loadgen からService経由でHTTPリクエストを繰り返し送信し、loadgen を0台から1台、2台へと増加させた後、0台へと停止させる手順で投入されました[45]。観察期間は初期状態の23:20から観察終了の翌00

、このうち loadgen の稼働時間は約33.5分間、負荷停止後の観察時間は約26分間でした[46]。

評価にあたっては、レプリカ数、Podあたりおよび合計のCPU requests、合計CPU使用量、HPA CPU利用率、VPAの各推奨値(TARGET、LOWER、UPPER)が追跡されました。インプレース更新の成立確認にはPod UID、再起動回数、イベントログが参照されました[47]。

検証フェーズ時点レプリカ数(Pods)PodあたりCPU request(mCPU)合計CPU requests(mCPU)合計CPU使用量(mCPU)HPA CPU利用率(%)
初期状態23
2200400-0%
負荷1台・安定時23
6640(TARGET)--63%
負荷急増時23
10407.5(平均)4075175455%
適正化後23
56403200189757%
負荷停止後00
2640(維持)---

ベースラインとなる負荷のない初期状態(23

)では、レプリカ数2台、各PodのCPU requests 200m(合計400m)、HPA利用率0%でした。本検証にはHPA単独や固定requestsを適用した対照群が存在しないため、比較試験ではなく単一構成における前後観察として読み解くのが妥当です[48]。

最初の負荷投入(loadgen 1台)に伴い、HPAがレプリカ数を初期の2から上限の10まで引き上げた後、6レプリカへと推移しました[49]。VPA TARGETは100mから305m、約640mへと上昇し、最終的に6レプリカ、HPA利用率63%で安定したと報告されています[49]。

負荷を2台に増加させた直後の23

、10レプリカ、合計CPU requests 4075m、CPU使用量合計 1754m、HPA利用率 55% でした[50]。その後、23
(各Pod 640m)、合計CPU requests 3200m、CPU使用量合計 1897m、HPA利用率 57% へと適正化され、合計CPU requestsが約21%削減されたと報告されています[50]。

負荷停止後、HPAは約6分間で5レプリカから最小値の2レプリカへと縮小しました[51]。VPA TARGETは約5分間で645mから270mへと低下しました[51]。一方で、稼働中PodのCPU requestsは翌00

。これは現在のrequest値が当時のVPA LOWER(250m)からUPPER(1000m)の許容枠内に収まっていたためと説明されています[51]。

既存PodのCPU requests引き上げ時、PodのUIDは維持され、再起動回数は0回のままでした。イベントログにおいても ResizeDeferred から ResizeCompleted への遷移が確認され、インプレース更新が正常に機能したことが確認されています[52]。

一方で、VPAの設定でCPUのみを制御対象にしていたにもかかわらず、イベントログではAutopilotの動作によりメモリrequestが512Miから656Miへと自動増額されていました。これはAutopilotにおけるCPUとメモリの比率制約(1:1から1

.5)を満たすための自動補正が行われたためと説明されています[53]。

23:41と23

、当該時点におけるCPU requestsの割当合計値が低下した点にとどまります。負荷生成側の状態が変化していることに加え、実リクエスト数、レイテンシ、エラー率、CPUスロットリング、実際の請求額の測定が行われていないため、同一負荷の下で処理性能を維持できたことや、インフラ総額のコスト削減が達成されたことの証明には至っていません[54]。

また、約1時間という単一の短時間検証は、短期的なHPAの追従とVPAのインプレース更新の動作確認にはなりますが、長期的なトラフィック周期を捉えたrightsizingの裏付けとは見なせません。Googleの公式ガイドにおいても、典型的なトラフィック周期を捉えるため少なくとも24時間から数日間の事前観察を行うよう案内されています[55]。

実務における段階的な導入・切替手順と運用の監視・停止判断

実務での導入は段階的に進める必要があります。まず対象となるDeploymentに対してHPAを先行して安定稼働させておきます[56]。

次に、同一Deploymentを対象とするrightsizingアノテーション付きのVPAリソースを、updateMode: "Off" に設定した状態で追加します[56]。この際、意図しないリソース変動を防ぐため、minAllowed および maxAllowed の境界値を定義します[56]。

推奨値の観察期間としては、システムの代表的な負荷サイクルを捉えるため、少なくとも24時間、負荷の周期性に応じて数日間にわたって観察を継続します[57]。観察期間中にVPAが提示する推奨メトリクス(Target、Lower Bound、Upper Bound、Uncapped Target)が、対象アプリケーションの性能要件および許容コスト枠に適合していることを確認します[56]。

推奨値の妥当性を確認した後、updateMode を "InPlaceOrRecreate" へと切り替えます。これにより、Podの再起動を伴わずにCPU requestsの変更を動的に適用でき、インプレース更新が困難な場合は再作成へとフォールバックします[30]。

自動適用を開始した後は、多角的なメトリクス監視体制を敷く必要があります。

  • VPA層: KCP_VPA の決定イベントログを追跡し、推奨値の更新(Update recommendation)、インプレース適用(Apply recommendation in place)、Podの退避(Evict Pod)の各動作を確認します[58]。
  • HPA層: 現在のメトリクス値と目標値の乖離、レプリカ数の推移、HPAのConditions(ScalingActive、AbleToScale等)や直近のEventsを確認します[58]。
  • アプリケーション層: リクエストレート、エラー率、レスポンスレイテンシ、実際のCPUおよびメモリ使用量を並行して監視します[58]。

停止判断の基準としては、既存のエラー率やレイテンシに関するSLOアラートが発火した場合、HPAのConditionステータスがFalseとなりスケーリング失敗を示した場合、またはVPAの適用失敗イベントが発生した場合に、自動適用を停止して原因究明に移行することが運用案として推奨されます[59]。

rightsizingを停止する際は、VPAリソース自体を削除するか、またはVPAの updateMode を "Off" に変更します[60]。

自動更新モード(InPlaceOrRecreateなど)を有効にしたまま、rightsizingアノテーションのみを削除する操作は避ける必要があります。アノテーションのみを削除するとVPAが標準の動作へと後退し、CPU Utilization型HPAとの干渉が再発してスケーリングが不安定になる危険があるためです[60]。

コスト・耐障害性・スケーリング遅延におけるトレードオフ

CPU requestsの設定変更は、スケジューリングとリソース競合時の挙動に直接影響を与えます。CPU requestsを小さく設定すると、kube-schedulerによるノードへのPod配置が容易になります[61]。

一方で、KubernetesにおいてCPU requestsはCPU競合時における割り当ての重み付け(weight)を定義します。そのため、requestsを引き下げると、ノード内でCPU資源の競合が発生した際の相対的なCPU時間配分が小さくなる可能性があります[61]。なお、コンテナが使用できるCPU時間の上限(ハードシーリング)を規定するのはrequestsではなくCPU limitsです[61]。

コスト削減を追求して過度なリソース削減を行うと、可用性リスクを招きます。VPAの推奨値におけるLower Boundを下回るCPU requestsでは、アプリケーションの安定稼働が保証されず、性能低下や障害につながるリスクがあります[62]。そのため、安全マージン(余裕容量)を縮小する判断を行う際には、Lower Boundを下限の安全目安として厳格に扱う必要があります[62]。

VPAの役割は中長期的な需要実績に基づくCPU requestsの適正化であり、突発的なトラフィック急増への対応はHPAが担います[63]。

しかし、CPU requestsを極限まで削減してノード内の余剰リソース(ヘッドルーム)を使い切っている場合、HPAが迅速にPod増を要求しても、クラスタオートスケーラーが新たなVMノードをプロビジョニングするまでの待ち時間が発生します。その結果、新規Podが実際に稼働可能になるまでに遅延が生じ、高負荷時の応答遅延リスクが増大する可能性があります[64]。

さらに、GKEのHPAにはスケールダウン時の頻繁な変動(スラッシング)を防ぐため、既定で300秒(5分間)の安定化ウィンドウが設定されています[65]。これにより、突発的な需要が去った後もレプリカ数の縮小には一定のタイムラグが生じます[65]。耐障害性を確保するための安定化遅延と、即座にリソースを解放したいコスト効率との間には構造的なトレードオフが存在します。

後輩コスト削減のためにCPU requestsを極限まで切り詰めると、突発的な負荷急増が起きた際にノードの余剰リソースが不足し、Podの起動待ちによるレイテンシ悪化を招くリスクがありますね。

先輩その通りだね。requestsはPodのスケジューリングだけでなくCPU競合時の重み付けにも影響するから、削りすぎればノード内での処理優先度が下がるよ。急な需要スパイクへの即応はHPAに任せつつ、VPAのLower Boundを安全性の基準として適切な下限を維持する設計が欠かせないんだ。

後輩単純なリソース削減量だけを追うのではなく、耐障害性やスケーリング遅延とのバランスを客観的に評価してガードレールを敷く意識が重要ですね。