クイックセットアップ:5分でKEDAをデプロイ
Kubernetes標準のHorizontal Pod Autoscaler(HPA)は、CPUやメモリ使用率に依存するHTTPサービスには効果的に機能します。しかし、非同期キューのワーカー処理では全く事情が異なります。例えば、50,000件の動画エンコードジョブが蓄積しているにもかかわらず、ワーカースレッドのCPU使用率が12%にとどまることがあります。逆に、単一の計算負荷が高いタスクによってCPU使用率が95%に達し、キューが空であるにもかかわらず不要なスケールアウトがトリガーされることもあります。
Kubernetes Event-driven Autoscaling(KEDA)はこの課題を解決します。KEDAはカスタムメトリクスサーバーおよびライフサイクルコントローラーの両方として機能します。外部キューを直接監視し、ジョブが到着するとPodを起動し、キューがアイドル状態になるとデプロイメントをゼロまでスケールダウンして、アイドル状態のワーカーノードにかかるインフラコストを削減します。
1. HelmによるKEDAのインストール
公式のHelmチャートを使用して、任意のKubernetesクラスター(v1.27以上)にKEDAをインストールします。
# KEDAのHelmリポジトリを追加および更新
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
# KEDAを専用の名前空間にデプロイ
helm install keda kedacore/keda \
--namespace keda \
--create-namespace \
--set metricsServer.dnsPolicy=ClusterFirstWithHostNet
2. インストールの確認
オペレーターPodとメトリクスAPIサーバーPodの両方が稼働していることを確認します。
kubectl get pods -n keda
# 出力例:
# NAME READY STATUS RESTARTS AGE
# keda-operator-7b6494cb68-x28qf 1/1 Running 0 45s
# keda-operator-metrics-apiserver-585474775d-n4vwx 1/1 Running 0 45s
コアコンセプトと本番向けスケーラー
KEDAは主に2つのカスタムリソース定義(CRD)を利用します。
- ScaledObject: イベントソース(RabbitMQやSQSなど)をDeploymentまたはStatefulSetにマッピングし、レプリカ数の最小値・最大値や目標しきい値を定義します。
- TriggerAuthentication: 認証情報、接続文字列、クラウドIAMロールをデプロイメントマニフェストから分離して管理します。
0から1へのスケーリングはKEDAが直接処理します。最初のPodが起動すると、KEDAはメトリクスを標準のKubernetes HPAに引き渡し、1からNレプリカへのスケールアウトを管理します。
1. RabbitMQのキュー長に応じたスケーリング
image-tasksというRabbitMQキューからジョブを処理する画像リサイズサービスを例に挙げます。目標は、バックログメッセージ30件ごとに1つのPodを割り当て、最大レプリカ数を20に制限することです。
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-credentials
namespace: default
type: Opaque
data:
# 次の文字列のBase64エンコード値: amqp://user:[email protected]:5672/vhost
host: YW1xcDovL3VzZXI6cGFzc3dvcmRAcmFiYml0bXEuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbDo1NjcyL3Zob3N0
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: rabbitmq-trigger-auth
namespace: default
spec:
secretTargetRef:
- parameter: host
name: rabbitmq-credentials
key: host
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: image-processor-scaler
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: image-processor
minReplicaCount: 0
maxReplicaCount: 20
pollingInterval: 15
cooldownPeriod: 60
triggers:
- type: rabbitmq
metadata:
queueName: image-tasks
mode: QueueLength
value: "30"
authenticationRef:
name: rabbitmq-trigger-auth
2. Apache KafkaのコンシューマーグループLagに応じたスケーリング
Kafkaのスケーリングでは、単純なメッセージ数ではなくコンシューマーグループのLagを対象とします。KEDAはブローカーのパーティションオフセットを照会し、アクティブなコンシューマー全体での未コミットLagの総数を算出します。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: telemetry-consumer-scaler
namespace: default
spec:
scaleTargetRef:
name: telemetry-consumer
minReplicaCount: 1
maxReplicaCount: 16
pollingInterval: 10
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-cluster-kafka-bootstrap.kafka.svc:9092
consumerGroup: telemetry-workers
topic: device-telemetry
lagThreshold: "100"
offsetResetPolicy: latest
3. IAMロール(IRSA)を使用したAWS SQSでのスケーリング
Kubernetes Secretに静的なAWSアクセスキーを保存するのは避けるべきです。Amazon EKS上で実行する場合は、IAM Roles for Service Accounts(IRSA)またはEKS Pod Identityを使用してKEDAを認証します。
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: keda-aws-sqs-auth
namespace: default
spec:
podIdentity:
provider: aws-eks
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor-scaler
namespace: default
spec:
scaleTargetRef:
name: order-processor
minReplicaCount: 0
maxReplicaCount: 50
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/order-processing-queue
queueLength: "10"
awsRegion: us-east-1
authenticationRef:
name: keda-aws-sqs-auth
オートスケーリング動作の微調整
1. Podのフラッピング防止
メッセージの流入がスパイクすると、過剰なオートスケーリングのループが発生しやすくなります。Podが起動し、数秒でキューを処理し尽くして直ちに終了し、次のバッチが到着した際に再び起動するという挙動です。ScaledObject内でHPAの動作を直接調整することで、スケールダウンを安定化させることができます。
spec:
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
2. キュートリガーとCPUフォールバックの組み合わせ
KEDAは単一のワークロードに対して複数のトリガーを設定する複合トリガーをサポートしています。複数のメトリクスを定義した場合、コントローラーは各トリガーの目標値を個別に計算し、最も高いレプリカ数を算出したトリガーを採用します。
spec:
triggers:
- type: rabbitmq
metadata:
queueName: high-priority-jobs
mode: QueueLength
value: "10"
authenticationRef:
name: rabbitmq-trigger-auth
- type: cpu
metadata:
type: Utilization
value: "75"
本番運用における注意点と落とし穴
イベント駆動型ワークロードを大規模に運用すると、いくつかの運用上の落とし穴が見えてきます。本番稼働前に以下の3つのルールを確認してください。
1. Kafkaパーティション数と最大レプリカ数の整合
Kafkaのパーティションは、同一コンシューマーグループ内では1つのコンシューマーからしか読み取ることができません。トピックに12個のパーティションしかない状態でmaxReplicaCount: 30を設定した場合、18個のPodは全く作業を行わないままCPUとメモリを消費することになります。maxReplicaCountはパーティションのキャパシティに合わせて上限を設定してください。
2. スケールダウン時の処理中ジョブの保護
Kubernetesはレプリカを終了する際にSIGTERMシグナルを送信します。ワーカーが60秒かかるバッチ処理の40秒目を実行している最中に強制終了されると、接続の切断や処理の重複が発生します。
PodスペックでterminationGracePeriodSecondsを延長し、ワーカーアプリケーション側でシグナルを適切に捕捉してハンドリングできるように実装してください。
spec:
template:
spec:
terminationGracePeriodSeconds: 120
containers:
- name: worker
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "touch /tmp/draining && sleep 5"]
3. ブローカーへのポーリング負荷の監視
KEDAは定期的にキューのエンドポイントをポーリングします(デフォルト: 30秒)。ポーリング間隔を2秒に設定したScaledObjectリソースを200個デプロイすると、ブローカーに対して毎秒100回のHTTPまたはAMQP管理クエリが発生します。1秒未満の極めて厳格なキュー応答が求められる場合を除き、ポーリング間隔は15〜30秒に維持することをおすすめします。

