Skip to main content

スケールアウト指示から実際にPodが動くまでの5分間。Cluster Autoscalerのノード追加が間に合わない理由

1. HPAは正しく動いているのに、Podが数分間Pendingのままな理由

section01

HPAのメトリクスを確認すると、確かにレプリカ数は増えている。だが kubectl get pods を叩くと、増えたはずのPodが軒並み Pending のまま数分間動かない。デプロイのログにもエラーは出ていない。これはHPAが壊れているのではなく、Cluster Autoscalerによるノード追加という別レイヤーの処理が追いついていないことがほとんどだ。

HPAとCluster Autoscalerは、同じ「オートスケーリング」という言葉でくくられがちだが、担当するレイヤーがまったく違う。HPAはPodの数を調整し、Cluster Autoscalerはノードの数を調整する。Kubernetes公式ドキュメントによれば、HPAのスケールアップ時の安定化ウィンドウ(stabilizationWindowSeconds)はデフォルトで0秒、つまりメトリクスの変化にほぼ即座に反応する設計になっている。

一方でCluster Autoscalerは、既存ノードに空きがなく新しいPodがスケジュールできない状態(unschedulable)を検知してから、クラウドプロバイダーに新しいノードを要求し、それが起動してクラスタに参加するまで待つ必要がある。この**「即応するHPA」と「数分かかるノード追加」のギャップ**こそが、Podが宙に浮いたように見える正体だ。

2. 5分間の内訳を数値で分解する

section02

「数分」という言葉だけでは対策が立てられない。内訳を分解すると、遅延は3つの区間の直列合計であることが分かる。

HPAの反応時間(約1〜3分)

メトリクスサーバーがCPU/メモリ使用率を収集する間隔と、HPAコントローラーの同期周期が重なるため、実際に負荷が上がってからレプリカ数が更新されるまでに1〜3分ほどかかる。Kubernetes公式ドキュメントのbehaviorフィールドの説明にある通り、スケールアップのデフォルトポリシーは15秒周期で「現レプリカ数の100%増加」または「4 Pod追加」のうち大きい方を採用する、比較的積極的な設定だ。

Cluster Autoscalerの反応時間(30秒以内)

cluster-autoscalerのFAQによれば、Cluster Autoscalerは--scan-interval(デフォルト10秒)ごとにunschedulable Podをスキャンしている。Komodorの解説でも、スケールアップの判定自体は「スケジュール不可能なポッドが増加した時点」で反応的にトリガーされるとされており、この検知自体は通常30秒以内に完了する。

クラウド側のノードプロビジョニング(3〜4分、あるいはそれ以上)

最も時間がかかるのがこの区間だ。VMインスタンスの起動、OSブート、kubeletの起動、クラスタへの登録という一連の処理に、Google Cloudの公式トラブルシューティングガイドが示すようなクラウド固有の要因も加わり、通常3〜4分を要する。合計すると、HPAの検知からPodが実際に動き出すまで通常約5分というのが目安になる。

なお、cluster-autoscalerのFAQには--max-node-provision-time(デフォルト15分)というフラグもあり、この時間内にノードが登録されないとスケールアップシミュレーションから除外され、別のノードグループへの割り当てが試みられる。逆に--scale-down-delay-after-add(デフォルト約10分)は、追加した直後のノードをすぐ削除しないための安全マージンだ。

3. 「ノードは足りているはずなのに増えない」を疑うべきチェックリスト

section03

5分待っても状況が変わらない場合は、単純な遅延ではなく設定上の問題を疑うべきだ。Google Cloudの公式トラブルシューティングガイドは、GKEでスケールアップが失敗する典型的な原因をエラーコードとして整理している。

エラーコード原因対応
scale.up.error.out.of.resourcesGPU/CPUリソース不足別ゾーン・別マシンタイプを検討
scale.up.error.quota.exceededCompute Engineのクォータ超過クォータ増加を申請
scale.up.error.ip.space.exhaustedVPCのIP割り当て不足サブネット設計を見直す
no.scale.up.mig.failing.predicateノードアフィニティ/taint/toleration不一致Pod仕様を見直す

同ガイドでは、不健全なノードが45%を超えるとCluster Autoscalerが安全装置としてスケールアップを停止するとも説明されている。kubectl describe configmap cluster-autoscaler-status -n kube-system で現在の判定理由を確認できるのは、クラウド問わず有効な診断手順だ。

K3sのようなセルフホスト・エッジ環境では、この「クラウドAPIのクォータ」という要因は存在しない代わりに、既存ノードのリソース余力そのものが焦点になる。K3s公式のリソースプロファイリングによれば、新しいエージェントノードがクラスタに参加する際、サーバー(コントロールプレーン)側のCPU使用率が基準値から約20%スパイクすることが観測されており、スケーリングを制限する要因は「RAMではなくCPU」であるとされている。オンプレやエッジでノード追加が遅いと感じたら、追加先のクラウド事情だけでなく、コントロールプレーン自体のCPU余力も確認する価値がある。こうした診断をダッシュボード上で可視化できるかどうかは運用負荷に直結する部分で、KuboのCaptain UIはこの手のノード状態・スケジューリング理由を可視化する画面を標準搭載している。

4. 待たずに済ませる方法1 — PriorityClassによる予防的オーバープロビジョニング

section04

ノードプロビジョニングそのものを速くすることはできなくても、「待たなくて済む」状態を事前に作ることはできる。AWS公式コンテナブログが紹介する手法がこれだ。

低優先度のPriorityClassを持つ「pause」コンテナだけのダミーPodを常時デプロイし、クラスタに余剰キャパシティを予約しておく。実際の高優先度ワークロードがPendingになった瞬間、スケジューラーはこのダミーPodを即座にpreemption(立ち退き)させ、空いた枠に本物のPodを配置する。ノードプロビジョニングを待つ必要がないため、体感の遅延はほぼゼロになる。

yaml
# 実ワークロード
resources:
  requests:
    cpu: "1"
    memory: "500Mi"
priorityClassName: high-priority
---
# ダミーの予約枠
resources:
  requests:
    cpu: "1"
    memory: "500Mi"
priorityClassName: low-priority

同ブログではcluster-proportional-autoscalerを使い、クラスタサイズに応じてダミーPodの数を自動調整する構成(例: coresPerReplica: 8, nodesPerReplica: 4)も紹介されている。ただしこの方法は「常に一定の余剰リソースを遊ばせておく」ことが前提であり、コストとのトレードオフになる点は無視できない。どこまで余剰を持つかの判断基準は、バーストの頻度と許容できる遅延次第だ。oneuptimeの分析記事が指摘するように、需要発生からトラフィック受信までの各段階(メトリクス反映・HPA更新・Pod作成・ノード要求・ノード起動・アプリ準備)を実測してボトルネックを特定した上で、オーバープロビジョニング量を設計するのが望ましい。

こうした余剰リソースの管理・コスト最適化は、手動で運用するとかえって属人化しやすい領域でもある。KuboのAI-Driven Deploymentは、こうしたリソース設計の初期構築を自然言語の指示から代行できるため、YAMLでPriorityClassやダミーPodを一つずつ組み上げる作業から解放される。

5. 待たずに済ませる方法2 — Cluster AutoscalerからKarpenterへの乗り換えという選択肢

section05

オーバープロビジョニングでコストと引き換えに遅延を隠すのではなく、ノードプロビジョニングそのものを速くするという選択肢もある。AWS環境であれば、Cluster AutoscalerからKarpenterへの乗り換えがこれにあたる。

CAST AIの2026年比較記事によれば、Cluster Autoscalerはノードグループ(ASG等)という抽象化レイヤーを経由するため通常3〜4分かかるのに対し、Karpenterはクラウド側のAPI(AWSならEC2のRunInstances)を直接呼び出し、Podの要件に合う最適なインスタンスタイプを広い候補群から選ぶことで45〜60秒でノードをReadyにできる。Komodorの解説にもある通り、Cluster Autoscalerは10秒間隔のポーリングというアーキテクチャ上の制約を持つが、Karpenterはイベント駆動でこの制約自体を回避している。

どちらを選ぶべきかは単純な優劣ではなく、運用体制次第だ。すでにAWS EKSでバーストトラフィックが頻繁に発生し、遅延がビジネス影響(SLA違反やユーザー体験の劣化)に直結している場合はKarpenterへの移行を検討する価値が高い。逆に、バーストの頻度が低く運用チームがCluster Autoscalerの挙動を熟知している場合は、方法1のオーバープロビジョニングで十分なことも多い。 マルチクラウドやオンプレを含むK3s環境では、Karpenterはそもそも選択肢に入らないため、方法1の設計、あるいはノードプロビジョニングの主体(VMware/Proxmox/ベアメタル)そのものの起動速度改善が現実的な打ち手になる。

このレイヤーの違いを意識せずに運用するのは、インフラエンジニアにとって地味だが確実に消耗する作業だ。KuboはK3sベースのマネージドKubernetesとして、こうしたノードプロビジョニングの判断・実行そのものをAI-Driven Deploymentに任せられる設計を志向しており、EKSの約58%のコストでフルKubernetesの機能を維持しながら、この種の「待ち時間」を運用者が意識しなくて済む方向を目指している。

6. まとめ

「HPAが遅い」と感じたときの多くは、実際にはHPAではなくCluster Autoscalerによるノードプロビジョニングという別レイヤーの遅延だ。HPAの反応時間(1〜3分)、Cluster Autoscalerのスキャン間隔(10秒)、クラウド側のノード起動(3〜4分)という3つの直列区間が、合計で約5分という体感の遅延を生んでいる。

対策は3段階で考えるとよい。まず--max-node-provision-timeやscale.up.error.*系のエラーコードを確認する診断、次にPriorityClassによる予防的オーバープロビジョニング、そして根本的にアーキテクチャを変えるKarpenterへの移行(AWS環境の場合)だ。どの段階まで踏み込むかは、遅延がビジネスにどれだけの影響を与えているか次第で判断すればいい。

こうしたノード管理の複雑さそのものを運用の関心事から外したいのであれば、KuboのようなマネージドK3sサービスを検討する価値がある。ノードプロビジョニングの設計・監視・コスト最適化までを含めて相談したい場合は、お問い合わせから気軽に話を聞いてみてほしい。

Related articles

← Back to all posts