LoginSignup
0
1

KEDAでEvent-driven Autoscalingを試す

Posted at

これはなに

KubernetesにはPodの起動数をリソース使用量などのメトリクスに応じて自動的に増減させる、HorizontalPodAutoscaling(以下、HPA)と呼ばれる仕組みがあります。HPAは広く使われるユースケースでは優れた仕組みですが、Podを完全にゼロにすることはできない仕組みになっています。KEDAはHPAの仕組みを拡張して、Podを完全にゼロにすることができます。

本記事ではKEDA自体のインストール方法などを確かめるためにデモ的な構成でKEDAによるPodのオートスケーリングを試してみます。

やってみよう

クラスタ構築

クラスタはおよそどんな環境でも動くと思います。

筆者はkindクラスタ上で動作確認をしています。

Prometheusのインストール

メトリクスを収集してKEDAからの問い合わせに答えてもらうためにprometheusをインストールします。

ちょっと大げさですが構築が簡単なのでkube-prometheus-stackというhelmチャートを利用します。

helm upgrade -i \
    -n prometheus \
    --create-namespace \
    prom-op \
    kube-prometheus-stack \
    --repo https://prometheus-community.github.io/helm-charts \
    --version 48.4.0

上記で設定しているリリース名prom-opはこの後のingress-nginxの設定や、ScaledObject(KEDAのCustomResource)の設定などに現れるので、変更する場合は注意してください。

ingress-nginxのインストール

Ingress Controllerとしてingress-nginxを導入します。

今回の構成ではこのIngress Controllerが公開するメトリクスをautoscalingの入力として使います。

Helmチャートを使うので、次のvaluesファイルを作っておきます:

ingress-nginx-values.yaml
controller:
  service:
    type: ClusterIP
  extraArgs:
    metrics-per-host: 'false'
  metrics:
    enabled: true
    serviceMonitor:
      enabled: true
      namespace: prometheus
      additionalLabels:
        release: prom-op

controller.extraArgs.metrics-per-hostは、この後作成するIngressリソースにhost名がないため設定しています。利用したいメトリクスはnginx_ingress_controller_requestsという、Ingressリソースごとに公開されるメトリクスなのですが、元となるIngressリソースにhost名が設定されていないとき、デフォルトではそのIngressリソースに関連付けられるメトリクスはingress-nginx controllerから公開されません1公式ドキュメントに記載の通り、この引数を付与することで回避できます。

次のコマンドでインストールします:

helm upgrade -i \
  -n ingress-nginx \
  --create-namespace \
  ingress-nginx \
  ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --version 4.7.2 \
  --values ingress-nginx-values.yaml

KEDAのインストール

KEDAをデフォルトでインストールします。

helm upgrade -i \
  -n keda \
  --create-namespace \
  keda \
  keda \
  --repo https://kedacore.github.io/charts \
  --version 2.11.2

サンプルアプリケーションのデプロイ

再現が簡単になるように、nginxイメージを使います。

操作を簡単にするためにapp/ディレクトリを作って以下のマニフェストをそれぞれファイルとして作成します2

マニフェスト一式(クリックして開く)
app/nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:mainline-alpine
        name: nginx
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        resources: {}
status: {}
app/nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: http
  selector:
    app: nginx
  type: ClusterIP
status:
  loadBalancer: {}
app/nginx-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  creationTimestamp: null
  name: nginx
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - backend:
          service:
            name: nginx
            port:
              name: http
        path: /
        pathType: Prefix
status:
  loadBalancer: {}

最後に、目的のScaledObjectマニフェストを同じくapp/ディレクトリに作成しましょう。

app/nginx-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: nginx
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  idleReplicaCount: 0
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prom-op-kube-prometheus-st-prometheus.prometheus.svc:9090
      query: 'sum by (exported_namespace,exported_service) (irate(nginx_ingress_controller_requests[1m]))'
      activationThreshold: '0'
      threshold: '2'
    metricType: AverageValue

上記のScaledObjectでは、Prometheusから以下のqueryでメトリクスを取得しています。

sum by (exported_namespace,exported_service) (irate(nginx_ingress_controller_requests[1m]))

このメトリクスはingress-nginxが公開して、ServiceMonitorリソース(Prometheus operatorのCustomResource)によって自動的にPrometheusに収集設定がされたメトリクスに含まれています。したがって、動作検証に当たっては、ingress-nginxを通るようにリクエストを送信する必要があります。

以下のコマンドで作成したマニフェストをすべて適用します:

kubectl apply -f app/

applyできたかは以下のコマンドで確認3します:

kubectl get so,hpa,deploy,po,svc,ep

動作確認

ingress/nginxにリクエストを送信します。

リクエストをIngressに届ける方法は環境によっていろいろありますが、どの環境でも使える方法としてport-fowardによる方法を上げると、別のターミナルを立ち上げて以下のコマンドでport-forwardできます:

kubectl -n ingress-nginx port-forward service/ingress-nginx-controller 3080:80

ScaledObjectがDeactivateな状態にあると、nginx Podが一つもないため503になりますが、503エラーもScaledObjectのリクエスト数にカウントされるので、あきらめずにリクエストを送信し続けます。簡易的な以下のコマンドでもよいですし、スクリプト言語を利用しても構いません。

watch -p -n 1 curl -s localhost:3080/

上記コマンドなどで1秒に1回リクエストを送ると、Podが一つ立ち上がってくるはずです。

まずは普通のautoscalingと同じく、scale-outしてみます。

次のコマンド4でリクエストの頻度を約1秒に3回に増やしてみます。

watch -p -n 0.3 curl -s localhost:3080/

HPAオブジェクトを監視していると、メトリクスが3.3程度になることが確認でき、Podの数が2に増えるはずです。

次に、scale-in、特にゼロへのscale-inを試してみましょう。

リクエストを行っていたwatchコマンドをCtrl + Cで中断するだけです。scale-inにはcooldown期間として5分がデフォルトで設定されるので5分ほど待ちます。

するとnginx Podがすべて削除されるはずです。

もう一度リクエストを開始することでゼロからのscale-outも確認できます。

まとめ

KEDAでゼロへのscale-in/scale-outを試してみました。

KEDAではAmazon SQSやApache Kafkaなどのメッセージブローカーのメトリクス、例えばqueueの長さやメッセージの遅延時間などを利用して、ブローカーにメッセージがたまっていることを検知し、Podをautoscaleさせる使い方がより一般的です5が、今回は動作確認のしやすいWebサーバーをでもアプリとして構成してみました。

  1. 複数のIngressのメトリクスが合算されて爆発(オーバーフローのことなのか、急増することなのかはよくわかりません)することを避けるためだそうです。

  2. 1ファイルにまとめても構いませんし、kustomizeを使っても構いませんが、この後のコマンドに影響するので注意してください。

  3. ScaledObjectリソースの省略形がsoです。

  4. watchコマンドが小数を取り扱えない場合はwatch -p -n 1 'for i in $(seq 3); do curl -s localhost:3080 & done'でもよいでしょう。

  5. 今回の構成の場合、Podがゼロのときのリクエストはエラーになってしまい、利用側でリトライを行う必要があります。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1