- はじめに
- HPA v2の詳細分析
- VPAのアーキテクチャと運用戦略
- KEDAによるイベント駆動スケーリング
- HPA・VPA・KEDAの比較分析
- 複合スケーリングパターン
- 運用時の注意点
- 障害事例と復旧手順
- プロダクションチェックリスト
- 参考資料
- おわりに

はじめに
プロダクションのKubernetesクラスタにおいて、オートスケーリングは選択ではなく必須だ。トラフィック急増時にPodが不足すればサービス障害につながり、過剰プロビジョニングは毎月数百万ウォンの不要なクラウドコストを発生させる。Kubernetesエコシステムは、この問題を解決するために3つの中核的なオートスケーラーを提供している。
- HPA (Horizontal Pod Autoscaler): CPU、メモリ、カスタムメトリクスをもとにPodのレプリカ数を水平方向に拡張
- VPA (Vertical Pod Autoscaler): 過去の使用量の分析をもとに、Podのリソース要求値/制限値を自動調整
- KEDA (Kubernetes Event-Driven Autoscaler): メッセージキュー、HTTPリクエスト数、cronスケジュールなど外部イベントソースをもとに、ワークロードを0からNまでスケーリング
本記事では、各オートスケーラーのアーキテクチャとプロダクション環境における踏み込んだ構成戦略を扱い、実際の障害事例と復旧手順、そしてプロダクション投入前に必ず確認すべきチェックリストまで総合的に整理する。
HPA v2の詳細分析
アーキテクチャとメトリクス収集の流れ
HPA v2(autoscaling/v2)はKubernetesの標準的な水平拡張メカニズムだ。HPAコントローラーはデフォルトで15秒周期(--horizontal-pod-autoscaler-sync-period)でメトリクスを収集し、目標使用率と現在の使用率の比率を計算してレプリカ数を決定する。
メトリクス収集の流れは次のとおりだ。
- Metrics Server: kubeletのcAdvisorからCPU/メモリのメトリクスを収集し、
metrics.k8s.ioAPIとして公開 - Custom Metrics Adapter: Prometheus、Datadogなどからカスタムメトリクスを
custom.metrics.k8s.ioAPIとして公開 - External Metrics Adapter: クラスタ外部システムのメトリクスを
external.metrics.k8s.ioAPIとして公開
スケーリングアルゴリズム
HPAの中核となる計算式は次のとおりだ。
desiredReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue))
例えば、現在3個のPodがCPUを80%使用していて目標が50%であれば、ceil(3 * (80/50)) = ceil(4.8) = 5個に拡張される。複数のメトリクスが指定されている場合、HPAは各メトリクスについて独立に計算したうえで もっとも大きい値 を採用する。
プロダクション向けHPA v2マニフェスト
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: '1000'
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 5
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 120
selectPolicy: Min
重要なポイントは behavior フィールドだ。
- scaleUp: 安定化ウィンドウを60秒に設定し、1分間で最大50%または5個のPodのうち大きい方だけ拡張する。
- scaleDown: 安定化ウィンドウを300秒(5分)に設定し、2分間で最大10%ずつしか縮小しない。ここまで保守的に縮小してこそ、トラフィックが再び増加したときに対応できる。
Metrics Serverのインストール確認
# Metrics Serverの動作確認
kubectl top nodes
kubectl top pods -n production
# HPAの状態確認
kubectl get hpa api-server-hpa -n production -o yaml
# HPAのイベント確認
kubectl describe hpa api-server-hpa -n production | grep -A 20 "Events"
kubectl top nodes が失敗する場合は、Metrics Serverがインストールされていないか正常に動作していない。この場合HPAはメトリクスを収集できず、スケーリングがまったく動作しない。
VPAのアーキテクチャと運用戦略
VPAの構成要素
VPAは3つの主要コンポーネントで構成される。
- Recommender: 過去のリソース使用量とOOMイベントを分析し、最適なCPU/メモリの要求値を計算する。ヒストグラムベースのアルゴリズムで、P95使用量を基準に推奨値を導き出す。
- Updater: 現在のPodのリソース設定が推奨値から大きく外れると、Podを退去(eviction)させて新しい推奨値が適用されるようにする。
- Admission Controller: 新しく作成された、または再起動されたPodに推奨リソース値を自動的に適用するWebhookだ。
運用モード
VPAは4つの updateMode をサポートする。
- Off: 推奨値の提供だけを行い、実際の変更はしない。プロダクション導入の初期に推奨される。
- Initial: Pod作成時にのみ推奨値を適用する。すでに実行中のPodは変更しない。
- Recreate: 推奨値との差が大きい場合にPodを再作成する。PodDisruptionBudgetと併用する必要がある。
- Auto: Kubernetes 1.27+でIn-Place Resource Resizeがサポートされている場合、Podを再起動せずにリソースを調整する。
プロダクション向けVPAマニフェスト
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: 'Off'
resourcePolicy:
containerPolicies:
- containerName: api-server
minAllowed:
cpu: '100m'
memory: '128Mi'
maxAllowed:
cpu: '4'
memory: '8Gi'
controlledResources:
- cpu
- memory
controlledValues: RequestsAndLimits
プロダクション環境では必ず Offモードで開始 し、最低1-2週間にわたって推奨値の安定性を観察したうえで、AutoまたはRecreateモードへ切り替える必要がある。minAllowed と maxAllowed を必ず設定し、異常な推奨値が適用されるのを防ぐ。
VPAの推奨値の確認
# VPAの推奨値を確認
kubectl describe vpa api-server-vpa -n production
# 推奨値と現在のリソース要求値を比較
kubectl get vpa api-server-vpa -n production -o jsonpath='{.status.recommendation.containerRecommendations[0]}'
KEDAによるイベント駆動スケーリング
KEDAのアーキテクチャ
KEDAはKubernetesのHPAを拡張し、外部イベントソースをもとにスケーリングを可能にするCNCF Graduatedプロジェクトだ。KEDAの中核コンポーネントは次のとおりだ。
- KEDA Operator: ScaledObject/ScaledJob CRDを監視し、HPAを自動生成/管理する。イベントがないときはPodを0に縮小し、イベント発生時に1へ活性化したうえでHPAへ制御を渡す。
- Metrics Server (KEDA): 外部イベントソースのメトリクスをKubernetes External Metrics APIとして公開する。
- Scalers: 65種類以上のイベントソース(Kafka、RabbitMQ、AWS SQS、Prometheus、PostgreSQL、Cronなど)に接続するアダプタだ。
スケーリングの流れ
KEDAのスケーリングは2段階で動作する。
- Activation段階: KEDA Operatorがイベントソースを監視し、トリガー条件が満たされるとDeploymentのレプリカを0から1へ活性化する。
- Scaling段階: 活性化された後は、KEDAが作成したHPAがメトリクスをもとに1からNまでの拡張/縮小を担当する。
KEDA ScaledObjectマニフェスト
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor-scaledobject
namespace: production
spec:
scaleTargetRef:
name: order-processor
pollingInterval: 15
cooldownPeriod: 300
idleReplicaCount: 0
minReplicaCount: 1
maxReplicaCount: 100
fallback:
failureThreshold: 3
replicas: 5
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-broker:9092
consumerGroup: order-processor-group
topic: orders
lagThreshold: '50'
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: http_requests_total
query: sum(rate(http_requests_total[2m]))
threshold: '100'
中核となる構成要素を見ていくと、次のとおりだ。
- pollingInterval: イベントソースを確認する周期(秒)。デフォルト値は30秒で、応答性が重要な場合は15秒に短縮する。
- cooldownPeriod: 最後のトリガー活性化のあと0へ縮小するまで待機する時間(秒)。
- idleReplicaCount: イベントがないときに維持するレプリカ数。0に設定するとScale-to-Zeroが有効になる。
- fallback: メトリクス収集に失敗した場合の安全装置。
failureThresholdの回数だけ連続で失敗すると、replicasに指定した数を維持する。
KEDAのインストールと確認
# HelmでKEDAをインストール
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace
# KEDAの状態確認
kubectl get pods -n keda
kubectl get scaledobjects -n production
kubectl get hpa -n production
# ScaledObjectの詳細確認
kubectl describe scaledobject order-processor-scaledobject -n production
HPA・VPA・KEDAの比較分析
| 項目 | HPA | VPA | KEDA |
|---|---|---|---|
| スケーリング方向 | 水平 (Pod数の増減) | 垂直 (リソース要求/制限の調整) | 水平 (イベント駆動でのPod数の増減) |
| 標準メトリクス | CPU、メモリ | CPU、メモリの使用履歴 | 外部イベントソース (65+スケーラー) |
| カスタムメトリクス | サポート (Adapterが必要) | 非サポート | 組み込みでサポート |
| Scale-to-Zero | 非サポート (minReplicasは1以上) | 該当なし | サポート |
| Podの再起動 | 不要 | 必要 (Recreateモード) | 不要 |
| 適したワークロード | ステートレスなWebサービス、API | すべてのワークロード (リソース最適化) | イベント駆動、バッチ処理、キュー処理 |
| Kubernetes組み込み | はい | 別途インストールが必要 | 別途インストールが必要 |
| HPAとの関係 | - | CPU/メモリメトリクスで衝突する可能性 | HPAを内部的に生成/管理 |
| 学習コスト | 低い | 中程度 | 中程度〜高い |
| コスト最適化 | 中程度 (過剰拡張の可能性) | 高い (right-sizing) | 高い (Scale-to-Zero) |
利用シナリオ別の推奨
- 一般的なWebサービス: HPA(CPUベース)+ VPA(Offモードでモニタリング)
- イベント駆動のマイクロサービス: KEDA(Kafka/SQSトリガー)
- バッチ処理: KEDA(ScaledJobで処理完了時に自動終了)
- APIゲートウェイ: HPA(RPSベースのカスタムメトリクス)
- レガシーアプリケーション: VPA(垂直拡張が唯一の選択肢である場合)
複合スケーリングパターン
HPA + VPAの組み合わせ
HPAとVPAを同時に使う際にもっとも重要な原則は、同じメトリクスで衝突させない ことだ。
# HPA: カスタムメトリクス(RPS)のみをもとに水平拡張
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 30
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: '500'
---
# VPA: CPU/メモリのみ垂直調整 (HPAはカスタムメトリクスのみ使うため衝突しない)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: 'Auto'
resourcePolicy:
containerPolicies:
- containerName: api-server
controlledResources:
- cpu
- memory
minAllowed:
cpu: '200m'
memory: '256Mi'
maxAllowed:
cpu: '2'
memory: '4Gi'
このパターンではHPAがRPS(秒間リクエスト数)だけを基準にPod数を調整し、VPAがCPU/メモリのリソースを最適化する。両者が同じメトリクス(CPU/メモリ)を使うと、HPAがPodを増やして使用率を下げ、VPAがリソースを減らして使用率を再び上げるという 無限ループ(thrashing)が発生する。
HPA + KEDAの組み合わせ
KEDAは内部的にHPAを生成するため、別途HPAを作る必要はない。ただし、複数のイベントソースを1つのScaledObjectにまとめることはできる。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: multi-trigger-scaler
namespace: production
spec:
scaleTargetRef:
name: api-server
minReplicaCount: 2
maxReplicaCount: 50
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: nginx_connections_active
query: sum(nginx_ingress_controller_nginx_process_connections)
threshold: '100'
- type: cron
metadata:
timezone: Asia/Seoul
start: 30 8 * * 1-5
end: 30 18 * * 1-5
desiredReplicas: '10'
この例は、Prometheusメトリクスにもとづく動的スケーリングと、Cronにもとづく予約スケーリングを組み合わせている。平日の午前8時30分から午後6時30分までは最低10個のPodを維持しつつ、同時にアクティブな接続数に応じて追加の拡張ができる。
運用時の注意点
1. フラッピング(Flapping)の防止
HPAが頻繁にスケールアップ/ダウンを繰り返す現象をフラッピングと呼ぶ。これを防ぐには次を確認する。
behavior.scaleDown.stabilizationWindowSecondsを300秒以上に設定- メトリクスの変動が大きい場合は
averageUtilizationの目標を10-15%余裕を持たせて設定 toleranceの値(デフォルト0.1、つまり10%)を理解して活用する。現在のメトリクスが目標の90-110%の範囲内であればスケーリングは発生しない
2. リソース制限(Limits)の設定漏れ
PodにCPU/メモリのlimitsが設定されていないと、HPAのパーセントベースのスケーリングが動作しない。必ず resources.requests を設定しなければならない。
resources:
requests:
cpu: '500m'
memory: '512Mi'
limits:
cpu: '1000m'
memory: '1Gi'
3. メトリクスの遅延(Lag)
Metrics Serverには約15秒の遅延があり、Prometheusベースのカスタムメトリクスはscrape intervalとadapterの処理時間まで合わせると30-60秒の遅延が発生しうる。トラフィック急増時にはこの遅延の間、既存のPodに過負荷がかかりうるため、次を検討する。
minReplicasを平均トラフィック基準よりやや高めに設定- Readiness Probeを適切に構成し、新しいPodがトラフィックを受け取れるようになるまでの時間を最小化
- 急増が予測できる場合は、KEDAのCronトリガーで事前に拡張
4. VPAとHPAを同時に使う際の衝突
VPAが Auto または Recreate モードで、かつHPAがCPU/メモリのメトリクスを使うと、両者が互いに相反する判断を下す。必ず次のいずれかを選ぶ。
- HPAはカスタムメトリクスのみを使い、VPAはCPU/メモリを調整
- VPAを
Offモードに設定し、推奨値の参照だけにとどめる
5. Cluster Autoscalerとの連携
HPAがPod数を増やしても、クラスタに利用可能なノードがなければPodはPending状態のままになる。Cluster Autoscaler(またはKarpenter)が連携されていて初めて、ノードレベルの拡張が行われる。
障害事例と復旧手順
事例1: Metrics Serverの障害によるHPAの無力化
症状: すべてのHPAの TARGETS 列に unknown が表示され、Pod数が固定される。
# 診断
kubectl get hpa -A
kubectl top nodes # 失敗する場合はMetrics Serverの問題
# Metrics Serverの状態確認
kubectl get pods -n kube-system | grep metrics-server
kubectl logs -n kube-system deployment/metrics-server --tail=50
復旧手順:
- Metrics Server Podの再起動:
kubectl rollout restart deployment/metrics-server -n kube-system - APIServiceの登録確認:
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml - 問題が継続して発生する場合はMetrics Serverを再インストール
- 復旧するまで手動でレプリカ数を調整:
kubectl scale deployment/api-server --replicas=10 -n production
事例2: OOM Killの連鎖発生
症状: VPAの推奨値が実際のピーク使用量より低く設定され、Podが繰り返しOOMKilledされる。
# OOMイベントの確認
kubectl get events -n production --field-selector reason=OOMKilling --sort-by='.lastTimestamp'
# Podの再起動回数の確認
kubectl get pods -n production -o custom-columns=NAME:.metadata.name,RESTARTS:.status.containerStatuses[0].restartCount
復旧手順:
- VPAの
updateModeを直ちにOffへ変更し、以降のPod変更を防止 - 影響を受けるDeploymentのメモリ要求/制限を手動で引き上げ
- VPAの
maxAllowedの値が実際のピーク使用量を十分にカバーしているか確認 - 最低2週間
Offモードで推奨値の安定性を再観察
事例3: スケーリングストーム(Scaling Storm)
症状: HPAが急速に拡張した直後に縮小を繰り返し、Podが生成/削除され続ける。
# HPAのイベントで頻発するSuccessfulRescaleを確認
kubectl describe hpa api-server-hpa -n production | grep SuccessfulRescale
復旧手順:
behavior.scaleDown.stabilizationWindowSecondsを600秒以上に増加scaleDown.policiesの縮小比率を5%以下に制限- メトリクスソースの変動性を分析し、適切な平滑化(smoothing)を適用
- 必要に応じてHPAを一時的に無効化し、手動管理へ切り替え
事例4: KEDAのメトリクス収集失敗
症状: ScaledObjectの状態が Unknown で、連携しているHPAのメトリクスが収集されない。
# ScaledObjectの状態確認
kubectl get scaledobject -n production
kubectl describe scaledobject order-processor-scaledobject -n production
# KEDA Operatorのログ確認
kubectl logs -n keda deployment/keda-operator --tail=100 | grep -i error
復旧手順:
- イベントソース(Kafka、Prometheusなど)の接続状態を確認
- ScaledObjectに
fallback設定があるか確認。なければ追加し、メトリクス失敗時に安全なレプリカ数を維持 - KEDA Operatorの再起動:
kubectl rollout restart deployment/keda-operator -n keda - 認証情報(TriggerAuthentication)が期限切れになっていないか確認
プロダクションチェックリスト
デプロイ前に次の項目を必ず確認する。
HPA関連:
- Metrics Serverが正常に動作し、
kubectl top podsが成功するか minReplicasが平常時のトラフィックを処理できる最小値になっているかmaxReplicasがクラスタの容量とコストの上限に収まっているかbehavior.scaleDown.stabilizationWindowSecondsが300秒以上か- 対象のDeploymentに
resources.requestsが設定されているか
VPA関連:
- プロダクションへの初適用時に
updateMode: "Off"で開始しているか minAllowedとmaxAllowedが適切に設定されているか- PodDisruptionBudgetが構成されているか
- HPAと同じメトリクス(CPU/メモリ)で衝突していないか
KEDA関連:
fallback設定がメトリクス障害時に安全なレプリカ数を保証するかcooldownPeriodがワークロードの特性に合わせて設定されているか- TriggerAuthenticationの認証情報が有効か
- Scale-to-Zero利用時にCold StartがSLAの範囲内に収まるか
共通:
- Cluster AutoscalerまたはKarpenterがノードレベルの拡張を処理するか
- オートスケーリングのイベントに対する通知(Alerting)が構成されているか
- Grafanaダッシュボードでレプリカ数、メトリクス値、スケーリングイベントをモニタリングできるか
- Runbookが作成されており、チームメンバーが把握しているか
参考資料
- Kubernetes Horizontal Pod Autoscaling 公式ドキュメント
- Kubernetes Vertical Pod Autoscaling 公式ドキュメント
- KEDA 公式サイト
- Kubernetes Autoscaling Patterns: HPA, VPA and KEDA - Spectro Cloud
- Scaling Kubernetes the Right Way: HPA, VPA, CA, Karpenter, and KEDA - CloudPilot AI
- HPA vs VPA vs KEDA Performance and Cost Trade-offs - Kubeify
- Kubernetes HPA Troubleshooting - OneUptime
- VPA GitHub Repository - kubernetes/autoscaler
おわりに
Kubernetesのオートスケーリングは、単にHPAを1つデプロイすれば終わりというものではない。プロダクション環境では、メトリクス収集の安定性、スケーリングアルゴリズムの理解、Cooldown戦略、障害時の復旧手順まで総合的に設計する必要がある。
中核となる原則を整理すると次のとおりだ。
- 単一のオートスケーラーに依存するな: HPA、VPA、KEDAにはそれぞれ強みがある。ワークロードの特性に合わせて組み合わせよ。
- 縮小は保守的に: スケールアップは速く、スケールダウンはゆっくり。トラフィックの再増加に備える必要がある。
- メトリクス障害に備えよ: Metrics Server、Prometheus、外部イベントソースのいずれも障害が起こりうる。Fallback戦略を必ず構成せよ。
- まず観察、自動化は後から: VPAはOffモードで開始し、HPAのbehaviorを保守的に設定したうえで、段階的に自動化のレベルを引き上げよ。
- モニタリングと通知は基本だ: オートスケーリングの判断過程をGrafanaダッシュボードで可視化し、異常なスケーリングパターンに対する通知を設定せよ。
オートスケーリングを正しく構築すれば、トラフィックが急増しても安定してサービスを提供しながら、クラウドコストを30-50%削減できる。本記事で扱ったパターンとチェックリストが、プロダクション環境におけるオートスケーリング戦略の策定に実質的な助けとなれば幸いだ。