KEDAが切り拓くイベント駆動スケーリングの深淵:Kafka・SQS連動で実現する真のオートスケーリング
テックリードの私たちが日々のインフラ運用で直面する最大のジレンマ。それは、「CPUやメモリ使用率をトリガーにした標準的なHPA(Horizontal Pod Autoscaler)では、現代の非同期メッセージング基盤の負荷を正確に捉えられない」という現実だ。
Apache Kafkaのパーティションに数百万件のメッセージが溜まっていようとも、AWS SQSのキューに爆発的なリクエストが積まれようとも、Pod側のCPU使用率が平穏であれば、HPAはピクリとも動かない。結果として、スループットの低下、レイテンシの悪化、そして夜間におけるオンコールエンジニアの安眠の崩壊がもたらされる。
この悪夢を断ち切る唯一の解が KEDA (Kubernetes Event-driven Autoscaling) だ。
今回は、KEDAのアーキテクチャの本質を解き明かし、KafkaおよびSQSと完全に連動した実践的なスケーリング設定を、実務で即座に使えるベストプラクティスとともに解説する。
—
1. 標準HPAの限界と、KEDAが求められる構造的背景
なぜ標準のHPAではダメなのか。その理由はシンプルに「メトリクスの乖離」にある。
- CPU/Memory HPAの限界: メッセージキューワーカーは、I/O待ち(ネットワークやストレージ)の時間が多い。そのため、キューに数万件のメッセージが滞留していても、CPU使用率は低迷し、Podはスケールアウトしない。結果、処理遅延(Lag)が雪だるま式に増大する。
- KEDAの優位性: KEDAは、外部のイベントソース(Kafka, SQS, Redis等)の「メトリクス(キューの長さやLag)」を直接監視し、KubernetesのカスタムメトリクスAPI経由でHPAを駆動する。さらに、「0 Pod(スケール・フロム・ゼロ)」をネイティブでサポートしており、メッセージがない時は完全にリソースを解放(コストゼロ)し、メッセージ到達と同時に即座にPodを起動できる。
[External Event Source (Kafka/SQS)]
│
▼ (Monitor Lag / Queue Length)
┌─────────┐
│ KEDA │ ──(Scale 0 ⇄ N)──> [Kubernetes Deployment]
└─────────┘
—
2. 実践:AWS SQS および Apache Kafka 連動スケーリング構築
ここでは、実務で最も遭遇頻度の高い「AWS SQS」および「Apache Kafka」をターゲットにしたKEDAの `ScaledObject` 定義のベストプラクティスを提示する。
前提条件
- Kubernetes クラスターに KEDA がデプロイされていること
- AWS環境では IAM Roles for Service Accounts (IRSA) または Secret による認証設定が完了していること
- Kafka環境では SASL/SCRAM または mTLS による認証設定が完了していること
—
パターンA: AWS SQS との連動設定
SQSのキューに溜まったメッセージ数(ApproximateNumberOfMessagesVisible)を監視し、1Podあたり一定数(例: 5メッセージ)を超えるようにスケーリングをコントロールする。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: sqs-worker-scaler
namespace: processing
labels:
app.kubernetes.io/name: sqs-worker
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sqs-worker-deployment
# 最小Pod数を0にしておき、メッセージが来た時だけ起動する(コスト最適化の極み)
minReplicaCount: 0
maxReplicaCount: 30
pollingInterval: 15 # キューをポーリングする間隔(秒)
cooldownPeriod: 300 # メッセージが0になってからスケールインするまでのクールダウン(秒)
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
triggers:
- type: aws-sqs-queue
metadata:
# 監視対象のSQSキューURL
queueURL: https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-production-queue
# 1Podあたりに割り当てるメッセージ数の閾値
queueLength: “5”
# リージョン指定
awsRegion: “ap-northeast-1”
# IAMロールを使用せずシークレットで認証する場合(IRSA推奨だが参考として記載)
# 認証情報の参照先設定
authenticationRef:
name: aws-credentials
—
パターンB: Apache Kafka との連動設定
Kafkaの場合は、単なるメッセージ数ではなく「Consumer Group Lag(コンシューマー・グループの遅延数)」をトリガーにするのが鉄則だ。これにより、コンシューマーが実際にどれだけ遅れているかを正確に捉えられる。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-worker-scaler
namespace: streaming
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: kafka-consumer-deployment
minReplicaCount: 1 # ストリーミング処理ではコネクション維持のため最小1を推奨する場合もある
maxReplicaCount: 50
pollingInterval: 10
cooldownPeriod: 60
triggers:
- type: kafka
metadata:
# Kafkaブローカーのエンドポイント
bootstrapServers: kafka-cluster-kafka-bootstrap.streaming.svc:9092
# 監視対象のトピック名
topic: transaction-events
# 監視するコンシューマーグループID
consumerGroup: transaction-processor-group
# 1Podあたりの許容Lag閾値
lagThreshold: “100”
#オフセットの取得方法(latest / earliest)
offsetResetPolicy: latest
authenticationRef:
name: kafka-auth-secret
—
認証情報(SASL/SCRAMの例)
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: kafka-auth-secret
namespace: streaming
spec:
secretTargetRef:
- parameter: sasl
name: kafka-user-secret
key: sasl
- parameter: username
name: kafka-user-secret
key: username
- parameter: password
name: kafka-user-secret
key: password
—
3. 実務で役立つスケール設定のチューニングポイントと注意点
現場でKEDAを導入し、本番トラフィックを受け止めるときに必ず直面する「罠」と、それを回避するための知見を共有する。
① `pollingInterval` と `cooldownPeriod` のトレードオフ
- 問題点: ポーリング間隔を短くしすぎると(例: 2秒)、AWS SQS APIへのリクエスト制限(Throttling)に抵触したり、Kafkaブローカーへの負荷が増大する。
- 知見: 基本は `10秒〜15秒` を推奨。リアルタイム性がそこまでシビアでないバッチ寄りの処理であれば `30秒` でも十分機能する。逆に `cooldownPeriod` は短すぎるとチャタリング(スケールアウトとインの頻発)を起こすため、最低でも `300秒(5分)` は確保せよ。
② スケール・フロム・ゼロ(Scale from 0)の落とし穴
- 問題点: `minReplicaCount: 0` は極めて魅力的だが、0から1にスケールアップする際、KEDAがPodを起動し、アプリケーションがコンテナイメージをプルし、起動してメッセージキューに接続し始めるまでに数秒〜数十秒のタイムラグ(Cold Start)が発生する。
- 知見: このラグの間、メッセージがさらに蓄積される。クリティカルなリアルタイム処理システムでは、完全に0にせず `minReplicaCount: 1` または `2` を維持し、ベースライン負荷を常時処理できるようにしておくのが、プロダクション環境におけるエンジニアの良心である。
③ HPA Behavior(振る舞い)の明示的定義
KEDAの背後では標準のHPAが動いている。Kubernetesのバージョンアップに伴いデフォルトのスケールダウン挙動が保守的になったとはいえ、意図したタイミングで確実にスケールイン・アウトさせるためには、前述のYAMLのように `advanced.horizontalPodAutoscalerConfig.behavior` を明示的に記述し、急激なトラフィック増に対する立ち上がりの素早さ(`scaleUp`)と、負荷消失時の急激な縮小による処理脱落を防ぐ安定化ウインドウをチューニングすることが不可欠だ。
—
プロフェッショナルへの道:チーム開発と生産性を高めるアプローチ
最後に、組織としてKEDAをスケールさせるための実践知を授ける。
- 設定の共有化(GitOps): `ScaledObject` の定義は、アプリケーションのHelmチャートやArgoCDのアプリケーションリポジトリにマニフェストとして同梱し、開発者がビジネスロジックとスケーリング要件を同一ライフサイクルでPR(Pull Request)レビューできるようにする。インフラチームが手動でHPAをいじる時代は終わった。
- IDE(VS Code / IntelliJ)の神プラグイン: Kubernetes拡張機能に加え、YAML/JSONスキーマバリデーションを有効にしておくこと。KEDAのカスタムリソース(`crd`)のスキーマをローカルに読み込ませておけば、キーボードショートカット(`Ctrl + Space` / `Cmd + Space`)による補完が効き、デプロイ前のYAML構文エラーを完全になくすことができる。
KEDAを使いこなすことは、単にリソースを効率化することではない。システムの負荷耐性をデザインし、予測不可能なトラフィックのうねりに対してシステムを自律神経のように適応させることだ。
さあ、古いCPUベースのHPAを捨て、イベント駆動の深淵へ踏み出そう。