こんにちは、年始に研究開発部ArchitectグループからSPEU(Strategic Products Engineering Unit) SREグループに異動したジャン(a.k.a jc)です。 本記事では、異動前に研究開発部Architectグループが開発・運用している、Circuitと呼ばれるKubernetes基盤の管理にイベント駆動のスケーリングを実現するために、KEDAを導入した際の概要を紹介します。
KEDA導入の背景
利用者から、契約書の自動データ化を行うR&Dサービスをプラットフォーム「Circuit」上に構築したいという要望がありました。 開発者の運用負荷・インフラ構築の負担を軽減したい背景です。R&Dサービスの要件は既存APIと異なり、常時待ち受けが不要のため、Amazon SQSキューのメッセージ数に応じてスケールする仕組みを構築することが求められました。具体的な要件は以下の通りです。
- ゼロスケーリングが可能である
- external metricを考慮したスケール可能であること
Knativeでの課題
一方、CircuitではKnativeを使ったHTTPリクエストベースのスケーリングとステージング環境のゼロスケーリングを実現していました。しかし、Knativeはactivator, autoscaler, controller, webhook, servingなど複数のコンポーネントを組み合わせて動作します。この複雑な構成により、いくつかの課題が浮かび上がりました。
- ネットワーク構造の複雑化: debugとmetricsの収集が難しくなっており、Datadogのカスタムメトリクスを利用せざるを得ない状況だった
- ルーティング設定の調整困難: KnativeがIstioのGatewayとVirtualServiceが自動作成するため、細かなネットワークの調整が難しくする
- ゼロスケーリングの活用不足: 0→1だとスケールが間に合わず本番環境では少なくともmin scale: 1が設定されていたため、ゼロスケーリングのメリットを生かせていない
これらの課題を解決するため、Amazon SQSキューのメッセージ数に応じたスケーリングだけでなく、Knativeの代替となるシンプルなイベント駆動スケーリングの仕組みが必要とされました。その候補として選ばれたのがKEDAです。
KEDAとは
KEDA(Kubernetes-based Event Driven Autoscaler)は、Kubernetesのcustom metricを使って、イベント駆動のスケーリングを実現するためのOSSプロジェクトです。 基本的なCPUやメモリの利用率に加え、時間帯(cron)、メッセージキュー、prometheusのmetricなど、さまざまな外部のイベントソースからのmetricに基づいて、スケーリングを行うことが可能です。 検証の結果、KEDAは我々が実現したいこととマッチしていると判断し、導入を決定しました。
続いて、具体的なKEDAの導入方法について詳しく説明します。
導入方法
以下の手順で実施しました。
- KEDA本体のインストール
- Amazon SQSキューのメッセージ数に応じてスケールするaws-sqs-queuescalerの導入
- Knativeを置き換えるため、HTTPリクエストベースのスケーリングを実現するprometheusscalerの導入
KEDA本体のインストール
CircuitではArgoCDでGitOpsを行っており、KEADのインストールもArgoCDを通じて行いました。以下はArgoCDのアプリケーションリソースに以下のマニフェストを追加しました。
後ほどkeda-operatorにAWSのリソースにアクセスする権限を付与するため、serviceAccountにeks.amazonaws.com/role-arnのアノテーションを追加しています。
また、client side applyではエラーが発生するため、ServerSideApply=trueを指定しています(参考: scaledjobs.keda.sh CRD is too long)。
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: keda spec: project: default source: chart: keda repoURL: https://kedacore.github.io/charts targetRevision: 2.16.0 helm: valuesObject: serviceAccount: operator: create: true name: keda-operator automountServiceAccountToken: true annotations: eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/keda-operator-role destination: server: "https://kubernetes.default.svc" namespace: keda syncPolicy: automated: selfHeal: true prune: true syncOptions: - CreateNamespace=true - ServerSideApply=true
KEDAが正常に動作していることを確認するため、以下の簡易マニフェストを作成しました。この例では、メモリ使用率に応じてスケールするデモ用deploymentを設定しています。
補足:
KEDAのmemory scalerはHPAと似ていますが、特定のcontainerNameを指定できる点が異なります。sidecarパターンを使用している場合、全podではなくアプリケーションコンテナのメモリ使用率に基づいてスケーリングが可能です。
apiVersion: apps/v1 kind: Deployment metadata: name: memory-scaler spec: replicas: 1 selector: matchLabels: app: memory-scaler template: metadata: labels: app: memory-scaler spec: containers: - name: stress image: ghcr.io/colinianking/stress-ng args: - "--vm" - "1" - "--vm-bytes" - "500M" - "--timeout" - "600s" resources: limits: memory: "700Mi" requests: memory: "500Mi" --- apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: memory-scaler spec: scaleTargetRef: name: memory-scaler maxReplicaCount: 3 minReplicaCount: 1 triggers: - type: memory metadata: type: Utilization value: "80" # Scale when memory utilization exceeds 80%
aws-sqs-queue scalerの導入
KEDAの基本動作を確認した後、AWS SQS Queue scalerの導入をします。
KEDAがSQSキューへアクセスするためには、適切な権限設定が必要です。KEDAでのアクセスモデルは以下の2種類があります。
- Operator Identity Model
- keda-operatorと- workloadにそれぞれSQSキューへのアクセス権限を与える
- IAMロールの2重管理になってしまうが、最小権限の設定が可能
 
- Pod Identity Model
- workloadのみSQSキューへのアクセス権限を付与し、- keda-operatorには- workloadのアクセス権限を委譲する
- workload用のIAMロールだけを管理すればよいが、- keda-operatorにSQSキューへのアクセス以外の権限も与えることになる
- 追加のTriggerAuthenticationの設定が必要
 


今回はOperator Identity Modelを採用しました。理由としては、過剰な権限付与を避けるためと、シンプルな構成を保つためです。設定例は以下の通りです。
1. SQSキューを作成
resource "aws_sqs_queue" "keda_sqs_queue" { name = "keda-sqs-queue" max_message_size = 2048 message_retention_seconds = 86400 }
2. pod(workload) 用のロールを作成
Podが SQSキューにアクセスできるよう、以下のロールを作成します。その後、KubernetesのServiceAccountにこのロールをアノテーションで指定します。
resource "aws_iam_role" "pod_role" { name = "pod-role" assume_role_policy = "{K8S Service Account assume role policy}" } resource "aws_iam_role_policy" "pod" { name = "pod-role-policy" role = "pod-role" policy = data.aws_iam_policy_document.pod.json } data "aws_iam_policy_document" "pod" { statement { actions = [ "sqs:GetQueueAttributes" ] resources = [ aws_sqs_queue.keda_sqs_queue.arn ] } }
apiVersion: v1 kind: ServiceAccount metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/pod-role name: test-aws-sqs-queue-scaler
3. keda-operator 用のロールを作成
KEDAインストール時すでにannotationで指定しているため、ここではロールだけ作成して権限を付与します。
resource "aws_iam_role" "keda_operator_role" { name = "keda-operator-role" assume_role_policy = "{K8S Service Account assume role policy}" } resource "aws_iam_role_policy" "keda_operator" { name = "keda-operator-policy" role = "keda-operator-role" policy = data.aws_iam_policy_document.keda_operator.json } data "aws_iam_policy_document" "keda_operator" { statement { actions = [ "sqs:GetQueueAttributes" ] resources = [ aws_sqs_queue.keda_sqs_queue.arn ] } }
4. DeploymentとScaledObjectの作成
以下のマニフェストを使って、SQSキューのメッセージ数に応じてスケールするdeploymentとscaledobjectを作成します。Operator Identity Modelを採用したため、identityOwnerはoperatorを指定しなければなりません。
apiVersion: apps/v1 kind: Deployment metadata: name: test-aws-sqs-queue-scaler spec: selector: matchLabels: app: test-aws-sqs-queue-scaler template: metadata: labels: app: test-aws-sqs-queue-scaler spec: serviceAccountName: test-aws-sqs-queue-scaler containers: - name: test-aws-sqs-queue-scaler image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "SQS" resources: requests: memory: 16Mi cpu: "10m" limits: memory: 16Mi apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: scaledobject spec: scaleTargetRef: name: test-aws-sqs-queue-scaler pollingInterval: 10 minReplicaCount: 0 maxReplicaCount: 5 cooldownPeriod: 30 triggers: - type: aws-sqs-queue metadata: queueURL: https://sqs.ap-northeast-1.amazonaws.com/000000000000/keda-sqs-queue queueLength: "2" awsRegion: "ap-northeast-1" identityOwner: operator
prometheus scalerの導入
HTTPリクエストベースのスケーリングを実現するために、KEDA公式がhttp-add-onを提供していますが、注意書きの通りまだまだbeta版です。
The HTTP Add-on currently is in beta. We can't yet recommend it for production usage because we are still developing and testing it. It may have "rough edges" including missing documentation, bugs and other issues. It is currently provided as-is without support.
そのため、本番環境では使わない方が無難だと考え、prometheus scalerを使う方法を選択しました。
以下の設定はIstio Ingress Gatewayを使用する前提の話ですが、リクエスト数のmetricsを取得できれば他のIngress Gatewayの場合でも同様の設定が可能です。
全体的なフローは以下の通りです。

- リクエストの収集: Istio Ingress Gatewayが受け取ったHTTPリクエストをprometheusでmetricとして収集する
- ScaledObjectの作成: ScaledObjectを作成し、prometheus scalerを指定してクエリや閾値を設定する
- スケーリングの実行: KEDAがそのmetric(今回使用しているのはistio_requests_total)をexternal metricsとしてMetric Serverに登録し、HPAがmetricsに基づいてPodのスケーリングを実行する
設定例
1. prometheusにIstioのリクエスト数を収集するための設定
scrape_configs: - job_name: "istio-ingressgateway-monitor" scrape_interval: 30s scrape_timeout: 10s kubernetes_sd_configs: - role: pod namespaces: names: - istio-system
2. ScaledObjectの設定
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: scaledobject spec: scaleTargetRef: name: test-prometheus-scaler pollingInterval: 10 minReplicaCount: 1 maxReplicaCount: 5 cooldownPeriod: 30 triggers: - type: prometheus metadata: serverAddress: http://prometheus-server.prometheus metricName: istio_requests_total_hello_istio threshold: "12" query: sum(increase(istio_requests_total{destination_app="hello-istio"}[2m]))
ポイント
- serverAddress: Prometheusのアドレス
- query: 2分間のリクエスト数を取得
3. External Metricsの確認
上記マニフェストを適用した後、external metricsが正しく登録しているか確認するには、以下のコマンドを実行します。
$kubectl get apiservices | grep metrics.k8s.io v1beta1.external.metrics.k8s.io keda/keda-operator-metrics-apiserver True 8d v1beta1.metrics.k8s.io kube-system/metrics-server True 218d
v1beta1.external.metrics.k8s.ioが登録されていることを確認できます。
まとめ
本記事では、イベント駆動のスケーリングを実現するために、KEDAを導入する際の背景と手順について解説しました。
- aws-sqs-queuescaler を用いて、AWS SQSキューに応じたスケーリングを実現した
- prometheusscalerとistioのmetricを利用して、HTTPリクエストベースのスケーリングを実現した
現時点ではまだ運用時間が短いため、今後新しい知見や課題が出てくるかもしれません。その際には詳細を共有したいと思います。
なお、研究開発部ではMLOps/DevOpsエンジニア・プラットフォームエンジニアを募集中です。興味のある方はぜひご連絡ください!
Sansan技術本部ではカジュアル面談を実施しています
Sansan技術本部では中途・新卒採用向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話します。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。