目次
13 章

オートスケーリング

運用クラスタの負荷変動を人の介入なしで吸収する3次元の自動調整を扱います。HPA (Pod 数) · VPA (Pod リソース) · Cluster Autoscaler (ノード数) の役割分離、metrics-server の前提、HPA の autoscaling/v2 マニフェストと比例式アルゴリズム、scale up · down の非対称な behavior、custom metric と KEDA、VPA の updateMode と HPA · VPA の衝突、Karpenter までを一連の流れで整理します。

第12章 Health check までの流れは、1つの Pod がどう立っているかの話でした。第11章 resources.requests / limits で1つの Pod がどれだけのリソースを要求しどこまで使えるかを決め、第12章 でその Pod が生きているか · トラフィックを受ける準備ができたか · ゆっくり立ち上がっているかを3種類の probe で表現しました。単一 Pod のモデルまではそこで仕上がります。しかし運用の負荷は単一 Pod モデルの上で揺れます — 昼休みのトラフィックが2倍に跳ね、未明には1/10に減り、マーケティングキャンペーンが回る1日は丸1日普段の5倍です。この変動を人が毎回 kubectl scale deployment ... --replicas=... で追いかける運用は長く続きません。本章では、その空白を埋める 3次元の自動調整HPA / VPA / Cluster Autoscaler を一連の流れで整理します。

本章の終わりには 運用クラスタの負荷が揺れるとき、人が毎回手で追いかけなくてもよい一段 が手に入ります。同時にその自動化が回るのに必要な前提 — requests の存在、metrics-server、behavior の合理的な値、CA タグ — が1まとまりで見えます。

オートスケーリングが解いてくれるもの #

運用クラスタで負荷が変動するパターンは普通次の3つのうち一つです — 時間帯別の変動 (昼 · 夜)、イベント性の急増 (キャンペーン · セール · ニュース)、そしてワークロード追加による累積増加。人が手で合わせる運用は普通次の段階を経て限界に到達します。

  1. 最初は replicas をたっぷり取っておけば十分です。普段の2倍くらいを常に立ち上げておきます。
  2. 時間が経つと、その「たっぷりの値」がある時間帯には足りず、ある時間帯には無駄だと分かります。費用もリソースも両方から漏れています。
  3. 誰かが平日昼 · 夜 · 週末の3つのスロットに別々の replicas を適用するスケジュールを作ります。しばらくは回ります。
  4. キャンペーンが入ったり外部トラフィックが跳ねる事故が一度起きると、人が未明に起きて kubectl scale を打ちます。まもなくその作業が繰り返されます。

K8s がこの問題を表現する方式が 3次元のオートスケーラ です。それぞれ別の軸を自動で調整します。

オートスケーラ何を調整信号対象
HPA (Horizontal Pod Autoscaler)Pod 数 (replicas)CPU · メモリ使用率、custom metricDeployment / StatefulSet
VPA (Vertical Pod Autoscaler)Pod のリソース要求 · 上限 (requests / limits)過去の CPU · メモリ使用の推移Deployment / StatefulSet
Cluster Autoscaler (CA)ノード数Pending 状態の Pod、空のノードクラウドのノードグループ (ASG / MIG / VMSS)

3つの軸が互いに補完関係だという点が重要です。HPA が Pod を増やしてもノードに余裕がなければ新しい Pod は Pending に止まります。そのとき CA がノードをさらに立ち上げてくれます。VPA は別のサイクルで「このワークロードは実はメモリを 1Gi 程度必要とする」という事実を推奨値として教えてくれます。3つが一緒に回るとき初めて負荷変動が人の介入なしで吸収されます。

metrics-server という前提 #

オートスケーリングが回るには、クラスタの中に 現在のリソース使用量を教えてくれるコンポーネント がなければなりません。K8s 本体はそのメトリクスを直接持っていません。代わりに標準化されたインターフェース (metrics.k8s.io) を提供し、そのインターフェースを満たすコンポーネントをクラスタに別途インストールしておく形です。最もよくある実装が metrics-server です。

metrics-server はクラスタの各ノードで kubelet の /metrics/resource エンドポイントを周期的にスクレイプして、ノードと Pod の CPU · メモリ使用量をメモリに持っています。その数値を kubectl top や HPA コントローラが API で照会します。

metrics-server がインストールされているか確認
kubectl top nodes
kubectl top pods -A
出力例
NAME        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
node-1      450m         22%    1.8Gi           45%
node-2      320m         16%    1.5Gi           37%

数値が見えれば metrics-server が生きていて、error: Metrics API not available のようなメッセージが出ればインストールされていないか死んでいるのです。環境別のインストール方法は次のとおりです。

環境metrics-server の状態
minikubeminikube addons enable metrics-server 一度で有効化
kind直接インストールが必要 (kubectl apply -f または Helm)
EKS直接インストールが必要。Helm または公式マニフェスト
GKE基本有効化
AKS基本有効化

EKS は運用クラスタを作った直後には metrics-server が抜けています。HPA · VPA を使うには最初にインストールしなければならないコンポーネントです。CPU · メモリ以外にキューの長さ · リクエスト数のような custom metric で HPA を回したいなら、metrics-server の代わりに (または一緒に) Prometheus と Prometheus Adapter、KEDA のようなコンポーネントがその役割を担います — 後でもう一度見ます。Prometheus 自体のセットアップとクラスタの可観測性モデルは 第19章 可観測性 で本格的に扱います。

HPA — Pod 数を自動で調整 #

最もよく使われ最初に導入するオートスケーラが HPA です。replicas フィールドを人が書く代わりに、メトリクスの平均値を見て K8s が自動で埋めてくれる モデルです。

HPA マニフェスト — CPU 基準 #

最も単純な形は CPU 使用率基準です。

hpa-cpu.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

核心フィールドを一行ずつ押さえておきます。

  • apiVersion: autoscaling/v2 — HPA の現在の stable バージョンです。v1 は CPU 一つだけを扱えて単一メトリクスに縛られています。v2 から multi-metric、custom metric、scale up · down の非対称動作 (behavior) がすべて可能です。新しいマニフェストはほぼ常に v2 を使います。
  • scaleTargetRef — どのワークロードの replicas を調整するかの対象です。Deployment / StatefulSet / ReplicaSet に付けられます。DaemonSet はノード数に縛られているので HPA の対象ではありません。
  • minReplicas / maxReplicas — 自動調整の下限と上限です。運用で意図せず0や数百個になる事故を防ぐ安全網です。下限を1より大きくすると可用性、上限を合理的にすると費用 · リソース保護になります。
  • metrics — どの信号を見て決めるかの配列です。上の例は type: Resource (CPU · メモリのような標準リソース) に target.type: Utilization (使用率)、averageUtilization: 70 (平均70%) です。すべての Pod の CPU 使用率の平均が70%になるように replicas を合わせます。

minReplicas を1ではなく2以上にする理由を一つ押さえておくと — 1つの Pod が死んだり更新で終了する瞬間にもトラフィックを受ける別の Pod がなければならないという可用性の要求です。第12章 で readiness probe でトラフィック進入を統制しましたが、その統制は同じ Pod の中の話です。Pod 自体の不在は別の Pod が埋めなければなりません。

HPA アルゴリズム — 比例式一行 #

HPA が新しい replicas の値を決める式は単純です。

HPA の desired replicas
desiredReplicas = ceil( currentReplicas * (currentMetricValue / targetMetricValue) )

言葉でほどくと — 現在の平均が目標の何倍か を見て、その比率で Pod 数を増やします。例で見ると明快です。

currentReplicascurrentMetric (CPU 平均)targetMetric計算新しい replicas
570%70%5 × 1.0 = 55 (変化なし)
5140%70%5 × 2.0 = 1010
535%70%5 × 0.5 = 2.5 → ceil3
10105%70%10 × 1.5 = 1515

分子に入る使用率 (Utilization) の定義が重要です。CPU 使用率は Pod の requests に対する比率 です。ある Pod が requests.cpu: 500m を持っていて実際に 700m を使っているなら、使用率は140%です。

この定義のため 第11章 と直結する一つの落とし穴が生まれます — ワークロードに resources.requests がないと HPA の Utilization メトリクスは動作しません。 分母が定義されないからです。HPA を導入する前に対象 Deployment に CPU · メモリの requests が入っているかから確認しなければなりません。このチェックを抜かすと HPA が unknown<unknown> 状態で止まります。診断木の完成版は 第27章 kubectl デバッグパターン で整理します。

requests なしでも回したいなら target.typeUtilization の代わりに AverageValue にして絶対値 (例: 200m) を書く道があります。使用率ではなく絶対値基準で比較します。ただしこの形はよくなく、運用の標準は requests + Utilization です。

multi-metric — 複数の信号を一緒に見る #

metrics 配列に複数の項目を入れると HPA は各メトリクスごとに desired replicas を別々に計算し、その中で最も大きい値 を採用します。CPU とメモリを同時に見ると次のとおりです。

hpa-cpu-memory.yaml — metrics 部分
metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

CPU 基準では5個で十分なのにメモリ基準では8個が必要な状況なら、HPA は8個を採用します。2つのうちより負担の大きい方に合わせる保守的な選択です。

この形がよく意味を持つワークロードはメモリキャッシュを持っているサービスです。CPU は暇だがメモリが満ちていくパターンが見えるとき、HPA がその信号を逃さないようにするにはメモリも一緒に見なければなりません。

scale up と scale down の非対称 — behavior #

HPA が一つの比率で毎回滑らかに調整してくれるわけではありません。そのままにしておくと運用で2つの事故が起きます — 負荷が一時的に落ちたとき Pod を速く減らしすぎて、再び負荷が上がるとき cold start で応答が跳ねることです。そして 負荷が一時的に跳ねたとき Pod を過度に増やして リソースと費用が漏れることです。この2つを扱うフィールドが behavior です。

hpa-behavior.yaml — behavior 部分
behavior:
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
      - type: Percent
        value: 100
        periodSeconds: 15
      - type: Pods
        value: 4
        periodSeconds: 15
    selectPolicy: Max
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    selectPolicy: Max

3つを押さえておきます。

  • stabilizationWindowSeconds — 決定の安定化ウィンドウです。scale down の基本値が300秒 (5分) であることが運用の核心の安全装置です。CPU が5分間ずっと低い値のときだけ本当に減らします。一時的に落ちてからまた上がる信号では減らしません。scale up は普通0秒にして即座に反応させます。
  • policies — 1回でどれだけ変化させるかのポリシーです。Percent (現在の数の比率) と Pods (絶対数) の2種類で、periodSeconds はそのポリシーの周期です。上の例は scale up の場合15秒ごとに「現在の100% (掛ける2倍) または +4個」の2つのうちより大きい変化を許容します。
  • selectPolicy: Max / Min — 複数の policy のうちどれを採用するか決めます。Max は最も攻撃的な変化、Min は最も保守的な変化です。

非対称の運用の意味を一行に縮めると — 上げるときは速く、下げるときはゆっくり です。負荷が跳ねて応答時間が伸びる事故はユーザに即座に見えますが、Pod が1、2個多く立ち上がっている費用は短い時間の間は微々たるものです。一方速く減らしすぎると、再び増やす間に cold start が発生してユーザにそのまま見えます。この非対称を behavior で明示的に固めておくパターンが運用の標準です。

behavior 自体を書かないと K8s の合理的な基本値 (scale up 即座、scale down 5分安定化) が適用されます。最初に導入するときは基本値で始めてもよく、ワークロードの特性に合わせて調整していくのが普通の流れです。

HPA 適用と動作の確認 #

HPA 適用と状態確認
kubectl apply -f hpa-cpu.yaml
kubectl get hpa
kubectl describe hpa web
get hpa 出力例
NAME   REFERENCE        TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   55%/70%         2         20        4          5m

TARGETS カラムの 55%/70% が現在の平均 / 目標値です。この値が <unknown>/70% で見えると metrics-server が生きていないか、対象ワークロードに requests がないのです。kubectl describe hpa のイベントセクションに FailedGetResourceMetric のようなメッセージが一緒に出ます。

負荷テストで動作を一度確認してみるコマンドは次のとおりです。

負荷を与えて scale up を観察
kubectl run load-gen --rm -it --image=busybox -- /bin/sh
# コンテナの中で
while true; do wget -q -O- http://web.default.svc.cluster.local; done

別のターミナルで kubectl get hpa -w で見ると、TARGETS が70%を超える時点から REPLICAS が比例式どおり増えていく形が見えます。負荷を止めると5分くらい後からゆっくり減っていきます。

Custom metric と KEDA — CPU · メモリの先 #

CPU · メモリだけでは十分に表現されないワークロードがあります。

  • キューコンシューマ — SQS · Kafka · RabbitMQ からメッセージを受けて処理するワーカーです。キューの長さが本当の信号であって CPU ではありません。キューが溜まっていてもワーカーの CPU は暇なことがあります。
  • API ゲートウェイ — 秒あたりのリクエスト数 (RPS) や同時接続数がリソース使用より直接的な信号です。
  • イベント駆動ワークロード — 仕事があるときだけ回る関数型ワークロードです。

これらのワークロードに HPA の CPU 基準だけを適用すると、負荷の本当の変化時点より一拍遅れたり、そもそも信号を逃します。

Prometheus Adapter #

HPA が CPU · メモリ以外のメトリクスを見るようにする最初の道が Prometheus Adapter です。クラスタに Prometheus がインストールされていてワークロードたちがメトリクスを公開しているなら、Prometheus Adapter はその Prometheus の PromQL の結果を K8s の custom.metrics.k8s.io API として公開してくれます。HPA がそのメトリクスを標準メトリクスのように使えるようになります。

マニフェストの metrics 配列に type: Pods または type: External を書いてどの PromQL の結果を見るかを表現します。深く入る部分は後続の K8s 深掘り本へ後回しにしますが、形だけ見せておくと次のとおりです。

custom metric 例 — 抜粋
metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "100"

各 Pod の平均 RPS が100になるように replicas を合わせます。メトリクスの定義 (http_requests_per_second) は Prometheus Adapter の設定ファイルで PromQL で書きます。

KEDA — イベント駆動の 0 → N #

KEDA (Kubernetes Event-Driven Autoscaling) は一段さらに進んだコンポーネントです。HPA ができない2つを解いてくれます。

  • 0 → N スケーリング — 標準 HPA の minReplicas は1以上でなければなりません。仕事がないとき Pod を完全に0に減らせません。KEDA はキューが空いている時間の間ワークロードを0に減らし、新しいメッセージが到着すると1に立ち上げます。費用の面で大きな差になります。
  • 多様なイベントソースの直接接続 — SQS、Kafka、RabbitMQ、Redis Streams、PostgreSQL、Prometheus など50種を超えるソースをビルトインで支援します。Prometheus Adapter のように PromQL を組んでおかなくても KEDA の ScaledObject マニフェスト1枚でキューの長さベースのスケーリングができます。
KEDA ScaledObject — SQS 例 抜粋
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: sqs-worker
spec:
  scaleTargetRef:
    name: sqs-worker
  minReplicaCount: 0
  maxReplicaCount: 30
  triggers:
    - type: aws-sqs-queue
      metadata:
        queueURL: https://sqs.ap-northeast-2.amazonaws.com/.../my-queue
        queueLength: "10"
        awsRegion: ap-northeast-2

KEDA は内部的に標準 HPA を作って動作します — ScaledObject が適用されるとそれに対応する HPA が自動で生成され、KEDA が外部メトリクスを K8s メトリクス API として公開します。標準 HPA の上に一層の利便を載せた形だと見ればよいです。キューコンシューマやイベントワーカーが多いクラスタではほぼ標準ツールになりつつあります。

VPA — Pod のリソース要求を自動で調整 #

HPA が「Pod 何個」の次元なら VPA は「Pod 1つの大きさ」の次元です。第11章 で人が使用量データを見て requests を決めるサイクルを扱いました。VPA はその作業を自動化しようとする試みです — ワークロードの過去の CPU · メモリ使用の推移を見て推奨値を算出し、ポリシーに従ってその値を適用して Pod を再生成します。

3つのコンポーネント — recommender / updater / admission-controller #

VPA は単一のコントローラではなく3つのコンポーネントのまとまりです。

コンポーネント役割
recommenderメトリクスを集めて推奨 requests の値を算出。VPA オブジェクトの status.recommendation に記録
updaterrecommender の推奨値と現在の Pod の値が大きくずれると Pod を evict (再生成させる)
admission-controller新しい Pod が作られるとき mutating admission webhook で推奨値を注入

3つのコンポーネントがサイクルを作ります — recommender が推奨値を計算しておき、updater が大きな差を発見して Pod を殺し、新しい Pod が作られるとき admission-controller が推奨値を適用したマニフェストで立ち上げます。人の介入なしで requests がワークロードの実際の使用量に合わせて更新されます。

VPA マニフェストと updatePolicy #

vpa-web.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed:
          cpu: 50m
          memory: 64Mi
        maxAllowed:
          cpu: 2
          memory: 4Gi

updateMode の3つの値が運用の意思決定の核心です。

updateMode動作
"Off"推奨値を算出するだけで適用はしません。人が見てマニフェストに反映
"Initial"Pod が新しく作られる時点にだけ推奨値を適用。すでに立ち上がっている Pod はそのまま
"Auto" (= Recreate)大きな差が見えると Pod を evict して新しい推奨値で再生成

Auto が自動化の極みに見えますが運用では慎重にならなければなりません。VPA が Pod を evict するということは、その Pod が一度死んでまた立ち上がらなければならない という意味です。StatefulSet や単一 replica で立ち上がっているワークロード、または 第12章 の startup probe の時間が長いワークロードでは evict が直ちに可用性に影響します。

最初に VPA を導入するときはほぼ常に "Off" にするのが標準パターンです。数日 · 数週間の間推奨値を集め、その値が合理的だと確認したあとに初めて InitialAuto へ移すか、あるいはその推奨値を人がマニフェストに反映して commit します。

VPA 推奨値の確認
kubectl describe vpa web
status.recommendation 部分 — 抜粋
Status:
  Recommendation:
    Container Recommendations:
      Container Name:  web
      Lower Bound:
        Cpu:     150m
        Memory:  256Mi
      Target:
        Cpu:     350m
        Memory:  512Mi
      Upper Bound:
        Cpu:     800m
        Memory:  1Gi

Target が核心の推奨値です。Lower BoundUpper Bound は統計的な信頼区間だと見ればよいです。この推奨値が現在のマニフェストの requests と大きく異なるなら、人がその差を検討してマニフェストに反映するのが運用の保守的な方式です。

resourcePolicy の minAllowed / maxAllowed #

上のマニフェストの resourcePolicyminAllowedmaxAllowed は推奨値の上 · 下限です。この安全網がないと VPA が暇な時間帯の値を見て requests を小さく推奨しすぎたり、一時的なメモリリークのパターンを見て大きく推奨しすぎる事故が起きます。運用ではこの2つの値をほぼ常に書いておく方がよいです。

VPA がインストールされていないクラスタ #

HPA の metrics-server と違い VPA は K8s 本体に含まれていません。EKS · GKE · AKS すべて別途インストールが必要です — 普通 公式 GitHub のマニフェスト または Helm chart でインストールします。GKE だけが管理型オプションを提供します。

HPA と VPA の衝突 — 同じメトリクスに両方かけないこと #

運用でよく見る落とし穴を一つ押さえておきます。同じワークロードに CPU 基準の HPA と CPU 基準の VPA を同時にかけると振動が発生します。 理由は単純です。

  1. CPU 負荷が上がります。HPA が比例式どおり Pod 数を増やします。
  2. Pod が増えたので Pod あたりの平均 CPU 使用量が下がります。
  3. VPA (Auto) がその下がった使用量を見て「requests を減らさなければ」と判断します。推奨値を下げて Pod を再生成します。
  4. requests が下がったので同じ使用量を分母で割っても使用率 (Utilization) は再び上がります。HPA が再び Pod を増やします。

振動が止まらないサイクルです。回避パターンは2つです。

  • HPA と VPA のメトリクスを分離 — 例えば HPA は CPU 基準、VPA はメモリ基準。2つのサイクルが互いの分母 · 分子を揺らしません。
  • VPA は updateMode: "Off" — 推奨値だけ算出して自動適用はしません。人が検討してマニフェストで反映します。HPA はそのまま動作します。

ほとんどの運用クラスタは2つ目のパターンを行きます。HPA が動的な負荷調整を担い、VPA は推奨値ツールにして人が四半期ごとに一度ずつマニフェストに反映します。この分離が最も安全な出発線です。

Cluster Autoscaler — ノード次元の調整 #

HPA が Pod を増やしてもその Pod が入るノードリソースがなければ Pod は Pending 状態で止まります。第11章 で見たスケジューリング可能性の式 — ノードの allocatable からすでに予約された requests の合計を引いた余裕分が新しい Pod の requests 以上でなければならないという — が満たされない状態です。この空白を埋めるのが Cluster Autoscaler です。

動作モデル #

CA の動作は2方向で単純です。

  • scale upPending 状態の Pod が見えると、その Pod の requests を受け入れられそうなノードがノードグループに追加されるようにクラウド API を呼びます。AWS なら ASG の desired capacity を増やし、GCP なら MIG、Azure なら VMSS を増やします。
  • scale down — 一定時間の間使用率が低いノードがあると、そのノード上の Pod を別のノードへ移してノードを終了します。移せない Pod (例: PV がそのノードにだけ付いている場合、cluster-autoscaler.kubernetes.io/safe-to-evict: "false" のアノテーションが付いた Pod) があると、そのノードはそのままにします。

CA の決定はメトリクスではなく requests とスケジューリング可能性 に基づきます。実際の使用量が暇でも requests の合計がノードを満たしていると新しい Pod は Pending になり、CA はノードを追加します。このモデルが 第11章 の「requests はスケジューラの本当の通貨」という表現と正確に同じ層です。

クラウド環境別のノードグループ #

CA はクラウド provider と対で回ります。環境別のマッピングは次のとおりです。

クラウドノードグループの抽象化備考
AWS EKSAuto Scaling Group (ASG) または EKS managed node groupアベイラビリティゾーンごとに別の ASG を推奨
GCP GKEManaged Instance Group (MIG)基本有効化。GKE Autopilot はノード自体が抽象化される
Azure AKSVirtual Machine Scale Set (VMSS)AKS クラスタオプションで有効化
オンプレミスCluster API + provider環境によって異なる

EKS の場合 CA は普通 Helm chart でインストールします。ASG に適切なタグを付けて CA がその ASG を発見 · 管理するようにするセットアップが一度必要です。GKE はクラスタ生成時にオプション一行でオンになります。

Karpenter — EKS のより速い代替 #

CA の設計は「ASG に desired capacity を1つさらに要求 → その ASG の launch template に従ってノードが作られる → kubelet がクラスタに登録」のサイクルを経ます。ノードの spec が ASG にあらかじめ定められているという点が制約です — Pending Pod が大きなメモリを要求するのに ASG のインスタンスタイプが小さなノードしか作れないと、そのノードを立ち上げても Pod が依然として Pending に残る事故が起きます。

Karpenter は EKS 環境で CA のより速い代替として定着しつつあります。Karpenter の違いは2つです。

  • Pending Pod を見てノードの spec を動的に決定 — あらかじめ定義された ASG ではなく、Pending Pod の requests と toleration に最もよく合うインスタンスタイプをその都度選んで EC2 API で直接立ち上げます。
  • 速いプロビジョニング — ASG という一段階を経ないので、ノードが立ち上がってクラスタに合流するまでの時間が普通より短いです。

EKS の新規クラスタでは CA の代わりに Karpenter を導入する流れが増えています。GKE · AKS の同級のツールはまだ EKS の Karpenter ほど定着していません。EKS 環境の Karpenter のセットアップとコスト最適化パターンは 第21章 EKS クラスタセットアップ第28章 コスト最適化 で本格的に扱います。

CA が動作しないよくある理由 #

CA が意図どおり回らないパターンをいくつか押さえておきます。

  • ノードに cluster-autoscaler-related タグがない — AWS の場合 ASG に k8s.io/cluster-autoscaler/enabled のようなタグがあって初めて CA がその ASG を管理対象とみなします。
  • PodDisruptionBudget が厳しすぎる — scale down 時に Pod を移そうとしても PDB が止めるとノードを殺せず減りません。PDB の運用マニュアルは 第30章 アップグレード戦略 で本格的に扱います。
  • cluster-autoscaler.kubernetes.io/safe-to-evict: "false" のアノテーション — このアノテーションが付いた Pod があるノードは CA が終了しません。システム Pod やローカルディスクに依存する Pod に付く場合が多いです。
  • Pending の理由がリソース不足ではないnodeSelectoraffinity のミスマッチ、または PV の AZ ミスマッチ (第9章 PV / PVC / StorageClassWaitForFirstConsumer が解いてくれる部分) のために Pending なら、ノードをさらに立ち上げてもその Pod は依然として Pending です。CA の責任の外です。

kubectl describe pod のイベントセクションと CA Pod のログ (kubectl logs -n kube-system -l app=cluster-autoscaler) を一緒に見ると、どちらの理由かが見分けられます。

3次元の協業 — 負荷急増の一連の流れ #

3つのオートスケーラが一緒に回る形を一つのシナリオで追いかけてみます — 普段の5倍のトラフィックが入ってくるマーケティングキャンペーン開始直後です。

負荷急増の一連の流れ
t=0s    キャンペーン開始。トラフィック 5x 進入。
        Deployment 'web': replicas=4, requests.cpu=500m
        すべての Pod の CPU 平均 130% (目標 70% に対し)

t=15s   HPA がメトリクス収集·計算。
        desired = ceil(4 * (130/70)) = 8
        replicas: 4 -> 8 へ変更要求。

t=20s   K8s が新しい Pod 4個を作ろうとする。
        しかしノードの利用可能 CPU が不足。
        新しい Pod 2個は Running、2個は Pending。

t=30s   CA が Pending Pod を発見。
        ASG に desired capacity +1 要求。
        新しいノードがクラウドでブート開始。

t=120s  新しいノードがクラスタに Ready で合流。
        Pending 状態だった Pod 2個がそのノードにスケジュール。
        Running。

t=135s  HPA が再び測定。平均 80%。
        desired = ceil(8 * (80/70)) = 10。replicas: 8 -> 10。
        2個さらに必要 — 今回は新しいノードの余裕リソースに入る。
        ...

キャンペーンが終わってトラフィックが普段に戻ると逆順で減ります — HPA の scale down 安定化ウィンドウ (5分) のあと Pod がゆっくり減り、CA が空いていくノードを発見して終了します。ノード終了は普通使用率が低い状態が一定時間 (基本10分くらい) 維持されたあとに始まるのでより保守的です。

このサイクルが人の介入なしで回るという点が核心です。しかし回るのに必要な前提 — ワークロードに requests が書かれていて、metrics-server が生きていて、HPA の behavior が合理的で、ASG に CA タグが付いていて、ノードのインスタンスタイプがワークロードに合うこと — がすべて 第11章 からのモデルの上に立っています。オートスケーリングはそのモデルを動的に回す最後の一層だと見ればよいです。

運用導入パターン — どこから始めるか #

3つのオートスケーラをすべて同時にオンにするのがよさそうに見えますが、運用の推奨される流れは保守的です。

  1. HPA から導入 — 最も馴染みがあり、事故の危険が最も少ないです。対象ワークロードに requests が書かれているか確認し、minReplicas ≥ 2 にして、CPU 70% のような標準値から始めます。数日 · 数週間の間動作を観察しながら behavior を調整します。
  2. VPA は updateMode: "Off" で推奨値だけ — Pod を再生成するポリシーは最初からオンにしません。recommender の推奨値を数日集めてみたあと、合理的だという判断が立てば人がマニフェストに反映します。Auto へ移すのは、そのワークロードの evict 影響が少ないという確信が立つときだけにします。
  3. CA はクラウド環境ならほぼ必須 — minikube · kind のような学習環境では意味がありませんが、クラウドクラスタで CA なしで運用するとノードの desired capacity を人が毎回追いかけなければなりません。EKS は最初から CA (または Karpenter) を一緒にインストールしておくパターンが標準です。
  4. Custom metric / KEDA はワークロードの特性に合わせて — CPU · メモリの信号で十分に表現されるワークロードには無理して Prometheus Adapter を導入する理由がありません。キューコンシューマやイベントワーカーのように信号の種類が異なるワークロードに限って導入します。

この流れを一行に縮めると — HPA はほぼすべてのワークロードに基本、VPA は推奨値ツールとして始め、CA はクラウドなら必須、KEDA は必要な所に です。

練習問題 #

  1. 上の本文の hpa-cpu.yaml を適用したあと、kubectl run load-gen --rm -it --image=busybox -- /bin/sh で負荷テストを回します。kubectl get hpa -wkubectl get pods -w を別のターミナルで一緒に見ながら、TARGETS カラムの使用率の変化と REPLICAS が比例式どおりどう増えていくかを時間順に記録します。負荷を止めたとき scale down までかかる時間が §「scale up と scale down の非対称」の5分安定化ウィンドウとどう噛み合うかを一段落で整理します。
  2. requests が抜けた Deployment に HPA をかけてみてください。kubectl get hpaTARGETS がどの値で見えるか、kubectl describe hpa の Events にどのメッセージが出るかを記録します。§「HPA アルゴリズム」で押さえた分母 (requests) が定義されないときの動作を自分の表現で一段落に整理し、第11章 のリソースモデルとどうつながるかをメモします。
  3. 同じワークロードに CPU 基準の HPA と CPU 基準の VPA (updateMode: Auto) を一緒にかけたとき、§「HPA と VPA の衝突」で押さえた振動がどう作られるかを段階別のシミュレーションで書いてみます (時間 t=0、t=30s、t=60s、t=90s のように)。その次に2つの回避パターン (メトリクス分離 / VPA Off) がそれぞれどの段階で振動を断ち切るかを表に整理します。

一行まとめ: 運用負荷の変動を人が追いかけずに吸収する3次元の自動調整は、HPA (Pod 数)、VPA (Pod requests / limits)、Cluster Autoscaler (ノード数) が互いに補完関係で回る。HPA の比例式は requests が分母なので 第11章 のリソースモデルが前提で、scale up 即座 / scale down 5分安定化の非対称が cold start 事故を防ぐ標準の形だ。HPA · VPA を同じメトリクスに一緒にかけると振動が起きるので、VPA は updateMode: "Off" で推奨値ツールとして始めるのが安全だ。CA と Karpenter は Pending Pod を見てノードを自動で追加する。

次の章 #

本章までのシリーズは ワークロードをどう回すか のモデルを一連の流れとして追ってきた流れでした。第8章 のコントローラ、第9章 の永続データ、第10章 の外部進入点、第11章 のリソース要求、第12章 の健康信号、本章の自動調整。これだけが1つのワークロードを運用クラスタに立ち上げて回すモデルの1まとまりです。

次の章のテーマは視点を一段移します — 複数のユーザ · 複数のチーム · 複数のワークロードが1つのクラスタを一緒に使う環境のポリシー です。誰がどのオブジェクトにどの動作をできるかの権限モデル RBAC、Pod 同士のネットワーク通信をホワイトリストで統制する NetworkPolicy、1つのネームスペースがクラスタリソースをどこまで使えるかの上限 ResourceQuotaLimitRange。この3つがマルチテナント · 運用クラスタの標準の安全網です。

第14章 RBAC / NetworkPolicy / ResourceQuota では、この3つのオブジェクトのマニフェストと動作、そして運用の推奨パターンを一連の流れで追いかけながら2部を締めくくります。

X