Kubernetes を基盤としたプロジェクトで実際に利用している、KEDA というコンポーネントのメジャーバージョンが上がったので追加機能についてまとめる.
KEDAとは
KEDA (Kubernetes Event-driven Autoscaling)は, kubernetes clusterに追加できる単一目的の軽量コンポーネントで,CNCFのSandboxプロジェクトにホストされているkubernetes ベースのイベント駆動オートスケーラーである. 宣言的に定義されたイベントに応じて, Kubernetes の任意のコンテナーのスケーリングを促進できる. Horizontal Pod Autoscaler などの kubernetesコンポーネントと連携して動作し,上書きや重複なしに機能拡張できる.
KEDA を使うと嬉しいこと
Kubernetes Horizontal Pod Autoscaler では CPU , Memory , Metrics API をトリガーに自動スケーリングが可能である. しかし, 多くの点で, それは症状の治療に似ており, 実際の原因ではないことがある. 例えばメッセージングの仕組みを利用していた場合に, 大量のメッセージによって最終的にCPU使用率が上昇するが, この場合本当にスケーリングしたい条件は, キューに滞留しているメッセージの数であったりする. これらの場合, KEDA を用いればシンプルに スケーリング環境を構築できる.
v2の追加機能
追加されたスケール対象
- StatefulSet
- Custom Resource
- ScaledJobs
DeploymentやJobが主な対象だったが、サブリソースとしての Custom Resource
や Jobスケール専用の ScaledJobs
が追加されたり
対象毎の特性に応じたプロパティ設定が可能となった.
追加されたスケーラー
Kubernetes Horizontal Pod Autoscaler でも取得が可能だった CPU , Memory も追加されているのが印象的である. Kubernetesの複雑さの一部を取り除くために, Kubernetes API を使用しなくても対処できるようにするためだと推測している.
事前準備
localに kubernetes 環境を構築する。 今回は minikube を使って cluster の構築を行う. kubernetes.io
インストール
- KEDAをインストールする. (今回はマニフェストを用いる)
$ kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.0.0/keda-2.0.0.yaml namespace/keda created customresourcedefinition.apiextensions.k8s.io/scaledjobs.keda.sh created customresourcedefinition.apiextensions.k8s.io/scaledobjects.keda.sh created customresourcedefinition.apiextensions.k8s.io/triggerauthentications.keda.sh created serviceaccount/keda-operator created clusterrole.rbac.authorization.k8s.io/keda-external-metrics-reader created clusterrole.rbac.authorization.k8s.io/keda-operator created rolebinding.rbac.authorization.k8s.io/keda-auth-reader created clusterrolebinding.rbac.authorization.k8s.io/keda-hpa-controller-external-metrics created clusterrolebinding.rbac.authorization.k8s.io/keda-operator created clusterrolebinding.rbac.authorization.k8s.io/keda:system:auth-delegator created service/keda-metrics-apiserver created deployment.apps/keda-metrics-apiserver created deployment.apps/keda-operator created apiservice.apiregistration.k8s.io/v1beta1.external.metrics.k8s.io created
Namespace
やDeployment
など KEDA のリソースが作成されている.
$ kubectl get all -n keda NAME READY STATUS RESTARTS AGE pod/keda-metrics-apiserver-5bffbfbd68-ftg6q 1/1 Running 0 3m12s pod/keda-operator-7b98595dc7-b4rnb 1/1 Running 0 3m12s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/keda-metrics-apiserver ClusterIP 10.97.130.94 <none> 443/TCP,80/TCP 3m12s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/keda-metrics-apiserver 1/1 1 1 3m12s deployment.apps/keda-operator 1/1 1 1 3m12s NAME DESIRED CURRENT READY AGE replicaset.apps/keda-metrics-apiserver-5bffbfbd68 1 1 1 3m12s replicaset.apps/keda-operator-7b98595dc7 1 1 1 3m12s
イベント駆動スケールを試す
せっかくなので KEDA v2から追加された Metrics API
を試してみる
今回は自分で定義したAPIから Metrics を取得する.
サンプルはこちら
- KEDA の トリガー対象とmetrics用のアプリケーションをデプロイする.
$ make deploy-app
or
$ kubectl apply -f ./deploy/demo/app/
namespace/demo created
service/components-mock created
deployment.apps/components-mock created
deployment.apps/nginx created
demo Namespace
にmetrics用のアプリケーションcomponents-mockとスケール対象のnginxがデプロイされる.
$ kubectl get all -n demo NAME READY STATUS RESTARTS AGE pod/components-mock-6b58c499dd-x6bf7 1/1 Running 0 95s pod/nginx-6799fc88d8-p9jp5 1/1 Running 0 95s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/components-mock ClusterIP 10.99.64.141 <none> 3003/TCP 95s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/components-mock 1/1 1 1 95s deployment.apps/nginx 1/1 1 1 95s NAME DESIRED CURRENT READY AGE replicaset.apps/components-mock-6b58c499dd 1 1 1 95s replicaset.apps/nginx-6799fc88d8 1 1 1 95s
- KEDA の
ScaledObject
をデプロイする
今回デプロイする ScaledObject
の閾値は 2
に設定している.
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: http-scaledobject namespace: demo labels: deploymentName: nginx spec: maxReplicaCount: 5 scaleTargetRef: name: nginx triggers: - type: metrics-api metadata: targetValue: '2' # 閾値 url: "http://components-mock.demo.svc.cluster.local:3003/components" # 対象のREST API valueLocation: "components.worker.tasks" # responseのjson構造
なお, サンプルではレスポンスのデフォルトが 0
のため ScaledObject
がトリガーされnginxが0
に Scale Inする.
$ make deploy-scaledobject
or
$ kubectl apply -f ./deploy/demo/scaledobject/metrics-api.yaml
scaledobject.keda.sh/http-scaledobject created
demo Namespace を確認すると 0
に Scale In したことが分かる.
$ kubectl get po -n demo NAME READY STATUS RESTARTS AGE components-mock-6b58c499dd-x6bf7 1/1 Running 0 7m16s
- 値を変更して, Scale Outさせる
/deploy/demo/app/component.yaml
を変更する
apiVersion: apps/v1 kind: Deployment metadata: labels: app: components-mock name: components-mock namespace: demo spec: replicas: 1 selector: matchLabels: app: components-mock strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: components-mock spec: containers: - name: components-mock image: cabi99/components-mock:0.0.1 env: - name: WORKER_TASKS value: "0" # ここの値を「2」に変更する imagePullPolicy: Always ports: - containerPort: 3003
変更後に再デプロイする.
$ make deploy-app
or
$ kubectl apply -f ./deploy/demo/app/
namespace/demo unchanged
service/components-mock unchanged
deployment.apps/components-mock configured
deployment.apps/nginx unchanged
nginx が Scale Outしていることが確認できる.
$ kubectl get po -n demo -w NAME READY STATUS RESTARTS AGE components-mock-6c494cd68b-4tkmv 1/1 Running 0 32s nginx-6799fc88d8-22q9l 0/1 ContainerCreating 0 4s nginx-6799fc88d8-22q9l 1/1 Running 0 5s
なお、今回は閾値を2にしているので、WORKER_TASKS を 10
に変更してデプロイすれば, maxReplicaCount
で定義した値までScale Outする.
$ kubectl get po -n demo -w NAME READY STATUS RESTARTS AGE components-mock-6d66575d55-r8vxh 1/1 Running 0 2m17s nginx-6799fc88d8-22q9l 1/1 Running 0 4m11s nginx-6799fc88d8-95r6v 1/1 Running 0 2m4s nginx-6799fc88d8-jkdqw 1/1 Running 0 2m4s nginx-6799fc88d8-wkmq4 1/1 Running 0 2m4s nginx-6799fc88d8-xvctr 1/1 Running 0 109s
まとめ
KEDA v2を触ってみたが, v1との使い勝手に大きな差はなかった. しかし, 今回のバージョンアップで CPU と メモリ が追加により, Kubernetes Horizontal Pod Autoscaler と共存する形でなく, 自動スケーリング のリソースは全て KEDA に統一することで シンプル性を担保できる. また, Metrics API を利用すれば, APIが提供されているサービスをトリガーにすることが容易であったり, SQLクエリに基づいてワークフローを自動スケーリングも可能になるので拡張性の高い環境構築に活かせると感じた.