
ゲーム
イベント
マガジン
技術ブログ
G-gen の西です。当記事では、Google Kubernetes Engine(GKE)において、Horizontal Pod Autoscaler(HPA)と Vertical Pod Autoscaler(VPA)の競合を解消し、コスト効率と耐障害性の両立を可能にする機能 HPA rightsizing の実機検証結果を紹介します。 概要 HPA rightsizing とは 前提条件 制約事項 検証の概要 検証環境 検証の流れ 設定手順 VPA 決定ログの有効化 検証用マニフェストの作成 マニフェストの適用と初期状態の確認 動作確認 検証結果の概要 負荷開始とスケールアウトの確認 負荷増加とインプレースリサイズの確認 負荷停止とスケールインの確認 決定ログの確認 概要 HPA rightsizing とは HPA rightsizing は、ワークロードの低トラフィック時におけるコスト効率の向上と、高需要時における耐障害性を両立させるための Google Kubernetes Engine(以下、GKE)の機能です。 これまで Kubernetes において、CPU 使用率に応じて Pod のレプリカ数を増減させる Horizontal Pod Autoscaler (以下、HPA)と、過去のリソース使用量から CPU requests を調整する Vertical Pod Autoscaler (以下、VPA)を同じワークロードで併用すると、スケーリングの競合が発生し、動作が不安定になる問題がありました。HPA の CPU 使用率は CPU requests を基準に計算されるため、VPA が requests を変更すると HPA の判断基準が変わり、互いの制御が打ち消し合うためです。 HPA rightsizing はこの競合を解消し、同一のワークロード上で HPA と VPA を連携して動作させることができます。以下の構成図のように、急激なアクセス増加には HPA が即座にスケールアウトして対応し、VPA は中長期的なリソース使用実績と HPA の設定値をもとに、1台あたりの最適な CPU requests を調整します。 HPA rightsizing 検証環境 通常の HPA・VPA 併用時と、HPA rightsizing を有効にした場合の違いは、以下のとおりです。 比較項目 通常の HPA・VPA 併用時 HPA rightsizing 有効時 CPU ベースの HPA と VPA の併用 互いの制御が競合するため非推奨 同じワークロード上で連携して動作可能 VPA による推奨値の計算方法 コンテナのリソース使用履歴のみを参照 使用履歴に加え、HPA の目標 CPU 使用率やレプリカ数の傾向も考慮 オートスケールの役割分担 どちらか一方のみを CPU メトリクスに適用 HPA が短期的な需要変動をレプリカ数でスケーリングし、VPA が中長期の適正 requests を算出 参考 : 水平 Pod 自動スケーリングについて 参考 : 垂直 Pod 自動スケーリングについて 参考 : VPA を使用して HPA ワークロードのサイズを適正化する 参考 : GKE release notes(October 02, 2026) なお、2026年10月現在、HPA rightsizing は Preview 提供です。Preview の機能を使用する上での注意点については、以下の記事も参照してください。 blog.g-gen.co.jp 前提条件 HPA rightsizing を使用するには、以下の要件があります。詳細は以下の公式ドキュメントを参照してください。 GKE のバージョンは 1.36.3-gke.1630000 以降が必要 クラスタで VPA が有効であることが必要。GKE Standard モードは --enable-vertical-pod-autoscaling で有効化作業が必要 参考 : VPA を使用して HPA ワークロードのサイズを適正化する - ベスト プラクティスと注意点 制約事項 HPA rightsizing には、以下の制約事項があります。 rightsizing の計算対象は CPU のみ。VPA でメモリも制御する場合、メモリは通常の VPA ロジックで調整される 1クラスタ内で設定できるコンテナは、全ワークロード合計で最大 60,000個(1つの VPA リソースあたり最大 1,000個) VPA が動的に変更した requests を、Argo CD などの CD ツールが差分と誤認してマニフェストの値に上書きしてしまう場合があるため、requests を同期対象から除外する必要あり その他の制約事項については、以下の公式ドキュメントを参照してください。 参考 : VPA を使用して HPA ワークロードのサイズを適正化する - ベスト プラクティスと注意点 検証の概要 検証環境 検証環境は以下のとおりです。 項目 設定値 クラスタタイプ GKE Autopilot(Rapid チャンネル) GKE バージョン 1.36.3-gke.1630000 リージョン 東京(asia-northeast1) アプリケーション registry.k8s.io/hpa-example 負荷テストツール loadgen 検証用の負荷テストには、Kubernetes 公式の HPA チュートリアルで紹介されている手法を参考に、HTTP リクエストを送信し続ける loadgen というツールを使用しました。 参考 : Horizontal Pod Autoscaler Walkthrough - Increase load 検証の流れ 以下の流れで検証を実施し、レプリカ数と CPU requests の推移を観察します。 フェーズ 負荷状況 確認内容 0 負荷なし デプロイ直後の初期状態(2台、200m) 1 負荷の開始 HPA による即座のスケールアウトと、VPA による推奨値算出 2 負荷の増加 新規 Pod への推奨値適用、既存 Pod のインプレース更新、Pod 数の適正集約 3 負荷の停止 HPA によるスケールインと、低負荷時における VPA requests の引き下げ挙動 設定手順 VPA 決定ログの有効化 Autopilot クラスタでは、VPA の決定ログ( KCP_VPA )がデフォルトでは有効化されていません。VPA が推奨値を算出した根拠を Cloud Logging で確認するために、クラスタのロギング設定に KCP_VPA を追加します。 # 現在のロギング設定を確認 $ gcloud container clusters describe hpa-vpa-lab \ --region = asia-northeast1 \ --format =" yaml(loggingConfig) " ----- 出力例 ----- loggingConfig: componentConfig: enableComponents: - SYSTEM_COMPONENTS - WORKLOADS # 既存の設定に KCP_VPA を追加 $ gcloud container clusters update hpa-vpa-lab \ --region = asia-northeast1 \ --logging = SYSTEM_COMPONENTS,WORKLOADS,KCP_VPA # 反映を確認 $ gcloud container clusters describe hpa-vpa-lab \ --region = asia-northeast1 \ --format =" yaml(loggingConfig) " ----- 出力例 ----- loggingConfig: componentConfig: enableComponents: - SYSTEM_COMPONENTS - WORKLOADS - KCP_VPA 参考 : 垂直 Pod オートスケーラーのイベントログを収集する 検証用マニフェストの作成 検証用のマニフェストは、以下のとおり 1つのファイルにまとめて定義します。 HPA rightsizing の有効化は、VPA リソースに gke.io/autoscaling-hpa-rightsizing-mode: "gke-hpa-rightsizing-mode-policy" のアノテーションを付与します。HPA 側の設定を変更する必要はありません。 # hpa-vpa-lab.yaml apiVersion : v1 kind : Namespace metadata : name : hpa-vpa-lab --- # 1. サンプルアプリケーション(Deployment) apiVersion : apps/v1 kind : Deployment metadata : name : sample-app namespace : hpa-vpa-lab spec : replicas : 2 selector : matchLabels : app : sample-app template : metadata : labels : app : sample-app spec : containers : - name : sample-app image : registry.k8s.io/hpa-example ports : - containerPort : 80 resources : requests : cpu : 200m memory : 512Mi ephemeral-storage : 1Gi limits : memory : 512Mi ephemeral-storage : 1Gi --- # 2. Service(ClusterIP) apiVersion : v1 kind : Service metadata : name : sample-app namespace : hpa-vpa-lab spec : ports : - port : 80 targetPort : 80 selector : app : sample-app --- # 3. HPA(CPU 使用率 70% を目標に 2〜10台でスケール) apiVersion : autoscaling/v2 kind : HorizontalPodAutoscaler metadata : name : sample-app-hpa namespace : hpa-vpa-lab spec : scaleTargetRef : apiVersion : apps/v1 kind : Deployment name : sample-app minReplicas : 2 maxReplicas : 10 metrics : - type : Resource resource : name : cpu target : type : Utilization averageUtilization : 70 --- # 4. VPA(HPA rightsizing を有効化し、InPlaceOrRecreate で適用) apiVersion : autoscaling.k8s.io/v1 kind : VerticalPodAutoscaler metadata : name : sample-app-vpa namespace : hpa-vpa-lab annotations : gke.io/autoscaling-hpa-rightsizing-mode : "gke-hpa-rightsizing-mode-policy" spec : targetRef : apiVersion : apps/v1 kind : Deployment name : sample-app updatePolicy : updateMode : "InPlaceOrRecreate" resourcePolicy : containerPolicies : - containerName : "sample-app" controlledResources : [ "cpu" ] minAllowed : cpu : 100m maxAllowed : cpu : 1000m --- # 5. 負荷テストツール(loadgen) apiVersion : apps/v1 kind : Deployment metadata : name : loadgen namespace : hpa-vpa-lab spec : replicas : 0 selector : matchLabels : app : loadgen template : metadata : labels : app : loadgen spec : containers : - name : loadgen image : busybox:1.36 command : [ "sh" , "-c" ] args : - | while true ; do wget -q -O- http://sample-app.hpa-vpa-lab.svc.cluster.local > /dev/ null done resources : requests : cpu : 250m memory : 256Mi limits : cpu : 250m memory : 256Mi なお、マニフェストで指定している updateMode: "InPlaceOrRecreate" は、ノードに空きリソースがあれば Pod を再起動・再作成せずにインプレースで CPU requests を動的に変更し、空き容量が不足する場合にのみ安全に Pod 再作成へフォールバックする動作モードです。 参考 : InPlaceOrRecreate モード マニフェストの適用と初期状態の確認 マニフェストをクラスタに適用し、初期状態を確認します。 # マニフェストの適用 $ kubectl apply -f hpa-vpa-lab.yaml # 初期状態の確認 $ kubectl -n hpa-vpa-lab get pods -l app =sample-app \ -o custom-columns =' NAME:.metadata.name,UID:.metadata.uid,SPEC_CPU:.spec.containers[0].resources.requests.cpu,ACTUAL_CPU:.status.containerStatuses[0].resources.requests.cpu,RESTARTS:.status.containerStatuses[0].restartCount ' ----- 出力例 ----- NAME UID SPEC_CPU ACTUAL_CPU RESTARTS sample-app-586b7c6b9f-7zjqd e969447d-1897-4e1a-ad5e-76d85158a270 200m 200m 0 sample-app-586b7c6b9f-84rsn d9286783-fbe1-43eb-b740-f2fe561330f9 200m 200m 0 マニフェストの指定どおり、2台の Pod がそれぞれ CPU requests 200m で起動しました。インプレース更新を検証するため、この UID を記録しておきます。 動作確認 検証結果の概要 検証全体を通じた、HPA のレプリカ数、CPU 使用率、および VPA 推奨値の推移グラフは以下のとおりです。 検証結果の推移グラフ 各フェーズにおける代表的なタイミングのリソース推移を以下の表にまとめました。 各用語は以下のとおりです。 TARGET VPA が算出した Pod 1台あたりの推奨 CPU requests LOWER / UPPER VPA がリサイズを行わない許容範囲(下限値 / 上限値。現在の requests がこの範囲内に収まっている間はリサイズを抑止) これらの指標を踏まえた各フェーズの推移は以下のとおりです。 タイミング レプリカ数 Pod 1台あたりの CPU requests requests の合計 CPU 使用量の合計 HPA 使用率 VPA TARGET フェーズ0 初期状態(23:20) 2 200m × 2 400m - 0% - フェーズ1 安定時(23:40) 6 200m × 3、305m × 3 1515m 952m 63% 635m フェーズ2 負荷急増時(23:41) 10 200m × 3、305m × 3、 640m × 4 4075m 1754m 55% 640m フェーズ2 適正化後(23:54) 5 640m × 5 3200m 1897m 57% 645m フェーズ3 負荷停止後(00:01) 2 640m × 2 1280m - 0% 270m グラフおよび表から読み取れるように、負荷の増減に対して HPA が即座にレプリカ数を追従させつつ、VPA が緩やかに推奨値を押し上げ、適正な Pod 数と requests に収束していく様子が確認できます。 特にフェーズ2では、負荷が増加したにもかかわらず、適正化後(23:54)の requests 合計が 4075m から 3200m へと約 21% 削減されました。1台あたりの requests を引き上げて Pod 数を集約することで、リソース効率の向上が見て取れます。 以降では、各フェーズにおける詳細な動作とリソースの推移を確認します。 負荷開始とスケールアウトの確認 23:21:31 に loadgen を 1台にスケールさせ、負荷を開始しました。 # 負荷を開始する $ kubectl -n hpa-vpa-lab scale deploy/loadgen --replicas = 1 時刻 経過 出来事 23:22 約1分 HPA がレプリカ数を 2 → 4 → 7 → 10台(上限)にスケールアウト 23:22 約1分 VPA の初期推奨値が算出される( TARGET 100m) 23:23 約2分 TARGET が 305m に上昇 23:24 約2.5分 10台中 5台の CPU requests がインプレースで 200m → 305m に引き上げられる 23:27〜23:31 約6〜10分 負荷分散で使用率が下がったため、HPA がレプリカ数を 10 → 8 → 7 → 6台にスケールイン 23:32〜23:40 約11〜19分 TARGET が 640m に上昇。6台・使用率 63% で安定 負荷開始直後に HPA が 10台までスケールアウトしたのは、一時的な CPU 使用率の高騰によるものです。その後は負荷分散に伴い 6台に収束しました。 また、開始約 2.5分で 10台中 5台の requests がインプレース更新されました。Cloud Logging を参照すると、稼働中の Pod 数を維持するために一度の更新数が抑えられていることが確認できます。 in-place update pod deferred: the number of running pods that are not being resized is at or below the minimum required なお、終盤で TARGET (635m)と既存 requests に差があっても更新されなかったのは、許容範囲( LOWER 120m〜 UPPER 1000m)内に収まっていたためです。 負荷増加とインプレースリサイズの確認 23:40:13 に loadgen を 2台に増やしました。 # 負荷をさらに増加させる $ kubectl -n hpa-vpa-lab scale deploy/loadgen --replicas = 2 時刻 経過 出来事 23:40〜23:41 約1分 HPA がレプリカ数を 6 → 8 → 10台にスケールアウト。新しい4台は起動時から 640m で稼働する 23:43 約3分 LOWER (許容下限)が 120m → 545m に上昇 23:44〜23:45 約4〜5分 既存の 6台の CPU requests がインプレースで 640m に引き上げられる (10台すべて 640m) 23:46〜23:49 約6〜9分 1台あたりの CPU requests が引き上げられたことで使用率が低下し、HPA がレプリカ数を 10 → 5台にスケールイン 23:54 約14分 5台・640m・使用率 57% で安定(CPU 使用量合計 1897m に対し requests 合計 3200m) 負荷増加の直後にスケールアウトした Pod の状態を確認します。 # 負荷増加直後の Pod 一覧 $ kubectl -n hpa-vpa-lab get pods -l app =sample-app \ -o custom-columns =' NAME:.metadata.name,SPEC_CPU:.spec.containers[0].resources.requests.cpu,START:.status.startTime ' ----- 出力例 ----- NAME SPEC_CPU START sample-app-586b7c6b9f-257qb 640m 2026-10-03T14:40:38Z sample-app-586b7c6b9f-7zjqd 200m 2026-10-03T14:20:20Z sample-app-586b7c6b9f-84rsn 305m 2026-10-03T14:20:20Z sample-app-586b7c6b9f-cknb7 305m 2026-10-03T14:22:05Z sample-app-586b7c6b9f-gqnlq 305m 2026-10-03T14:22:01Z sample-app-586b7c6b9f-j4gn9 640m 2026-10-03T14:40:47Z sample-app-586b7c6b9f-n2n5f 640m 2026-10-03T14:40:38Z sample-app-586b7c6b9f-pxf6p 640m 2026-10-03T14:40:51Z sample-app-586b7c6b9f-qmfsh 200m 2026-10-03T14:22:01Z sample-app-586b7c6b9f-t4n8l 200m 2026-10-03T14:22:17Z 新しくスケールアウトした 4台の Pod は、起動直後から VPA 推奨値の 640m で稼働していることが確認できます。 フェーズ2の直前と引き上げ後で継続稼働している Pod を比較すると、名前と UID は変わらずに CPU requests のみが更新されています。 RESTARTS も 0 のままであり、Pod の再作成やコンテナの再起動は発生していません。 # フェーズ2直前と引き上げ後の比較(抜粋) ----- フェーズ 2 直前 ----- NAME UID RESTARTS SPEC_CPU ACTUAL_CPU sample-app-586b7c6b9f-7zjqd e969447d-1897-4e1a-ad5e-76d85158a270 0 200m 200m sample-app-586b7c6b9f-84rsn d9286783-fbe1-43eb-b740-f2fe561330f9 0 305m 305m ----- 引き上げ後 ----- NAME UID RESTARTS SPEC_CPU ACTUAL_CPU sample-app-586b7c6b9f-7zjqd e969447d-1897-4e1a-ad5e-76d85158a270 0 640m 640m sample-app-586b7c6b9f-84rsn d9286783-fbe1-43eb-b740-f2fe561330f9 0 640m 640m Pod のイベントログにも、インプレースリサイズの記録が残っています。 # リサイズ関連のイベント(7zjqd と qmfsh の分を抜粋) $ kubectl -n hpa-vpa-lab get events --field-selector involvedObject. kind =Pod \ --sort-by = .lastTimestamp | grep -E ' Resize|InPlaceResized ' ----- 出力例 ----- Normal InPlaceResizedByVPA pod/sample-app-586b7c6b9f-7zjqd Pod was resized in place by VPA Updater. Normal ResizeStarted pod/sample-app-586b7c6b9f-7zjqd Pod resize started: { " containers " : [ { " name " : " sample-app " , " resources " : { " limits " : { " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " } , " requests " : { " cpu " : " 640m " , " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " }}} ] , " generation " :2 } Warning ResizeDeferred pod/sample-app-586b7c6b9f-qmfsh Pod resize OutOfmemory: { " containers " : [ { " name " : " sample-app " , " resources " : { " limits " : { " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " } , " requests " : { " cpu " : " 640m " , " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " }}} ] , " generation " :2, " error " : " Node didn't have enough resource: memory, ...(省略) " } Normal ResizeCompleted pod/sample-app-586b7c6b9f-7zjqd Pod resize completed: { " containers " : [ { " name " : " sample-app " , " resources " : { " limits " : { " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " } , " requests " : { " cpu " : " 640m " , " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " }}} ] , " generation " :2 } Normal InPlaceResizedByVPA pod/sample-app-586b7c6b9f-qmfsh Pod was resized in place by VPA Updater. Normal ResizeStarted pod/sample-app-586b7c6b9f-qmfsh Pod resize started: { " containers " : [ { " name " : " sample-app " , " resources " : { " limits " : { " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " } , " requests " : { " cpu " : " 640m " , " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " }}} ] , " generation " :2 } Normal ResizeCompleted pod/sample-app-586b7c6b9f-qmfsh Pod resize completed: { " containers " : [ { " name " : " sample-app " , " resources " : { " limits " : { " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " } , " requests " : { " cpu " : " 640m " , " ephemeral-storage " : " 1Gi " , " memory " : " 656Mi " }}} ] , " generation " :2 } 一部の Pod で一時的に ResizeDeferred (ノードの空き容量不足)が出力されたものの、数秒後に ResizeCompleted となり、Pod の再作成へフォールバックすることなくリサイズが完了しています。 またイベントログでは、VPA の対象を CPU のみに指定しているにもかかわらず、メモリが 512Mi から 656Mi に自動調整されました。これは Autopilot の「CPU とメモリの比率(1 : 1〜1 : 6.5)」の制約を満たすため、Autopilot 側が自動でメモリを補正したためです。Autopilot はリソース requests に応じて課金されるため、VPA で CPU のみを制御する場合でも、比率制約によるメモリ増加とコストへの影響を考慮する必要があります。 参考 : Autopilot でのリソース リクエスト 負荷停止とスケールインの確認 23:55:01 に loadgen のレプリカ数を 0 にスケールインし、負荷の生成を停止しました。 # 負荷を停止する $ kubectl -n hpa-vpa-lab scale deploy/loadgen --replicas = 0 時刻 経過 出来事 23:57〜23:58 約2〜3分 LOWER が 585m → 250m に減少する 23:58〜00:00 約3〜5分 TARGET が 645m → 270m に減少する 00:00〜00:01 約6分 HPA がレプリカ数を 5 → 4 → 2台( minReplicas )にスケールイン 00:01〜00:21 約6〜26分 スケールイン後に稼働している2台の CPU requests は 640m のまま維持される 負荷停止後、HPA は約 6分で最小値の 2台までスケールインしました。VPA の推奨値( TARGET )も約 5分で 270m まで減少しました。 一方、Pod の CPU requests は、負荷停止から 26分間引き下げられませんでした。このときの推奨値の許容範囲( LOWER 250m〜 UPPER 1000m)内に収まっていたため、引き下げは即座には行われませんでした。 なお、観察中に 1台の Pod がプリエンプトにより再作成された際は、その時点の推奨値( TARGET : 270m)で起動しました。 決定ログの確認 Cloud Logging に記録された VPA の決定ログ( KCP_VPA )を確認することで、VPA の判断根拠を追跡できます。 推奨値の更新ログ( UPDATE_RECOMMENDATION )を参照すると、 recommendationMode が HPA_RIGHTSIZING となっており、HPA rightsizing の専用アルゴリズムで推奨値が計算されていることが確認できます。 { " recommendationMode ": " HPA_RIGHTSIZING ", " confidence ": " HIGH ", " recommendedResources ": [ { " containerName ": " sample-app ", " lowerBound ": { " cpu ": " 575m " } , " target ": { " cpu ": " 645m " } , " uncappedTarget ": { " cpu ": " 645m " } , " upperBound ": { " cpu ": " 1 " } } ] } 西 拓磨 (記事一覧) クラウドソリューション部 クラウドエンジニアリング2課 東京都在住 2026年9月に G-gen にジョイン。前職ではSIerとしてオンプレミス、AWSなどのインフラ設計構築業務に従事。 インフラの経験をベースに現在はGoogle Cloudの学習中。 好きなことはランニング、ゲーム、自宅サーバーで色々検証をすること。
皆さま、こんにちは。 Weekly AWSでは、毎週 AWS プロダクトのアップデート情報をお届けしています。 それでは、先週 (10月12日~18日) の主な AWS アップデート情報を紹介します。 今週の注目アップデート AWS Lambda は「Lambda Web Functions」を発表 AWS Lambda は、コードが直接 HTTP リクエストを処理する新しいサーバーレスコンピューティングプリミティブ「Lambda Web Functions」を発表しました。ポートをリッスンし、通常の HTTP サーバーと同様にリクエストを処理できます。 主な特長は以下の通りで

























