APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Azure Container AppsのKEDAでService Busのキューに応じたスケーリングを行う

はじめに

こんにちは、ACS事業部の吉川です。
本記事はQiitaのAzure Advent Calendar 2022の14日目の記事です。

qiita.com

Azure Container Appsには KEDA という仕組みが統合されています

keda.sh

KEDAはKubernetesのPodをイベントドリブンでスケールさせる仕組みです。
これを利用すると、Pod数を普段は0台にして課金を抑え、必要な時だけ起動して処理を実行し、完了したらまた0台に戻す…といった使い方ができます。

今回Azureのメッセージングサービスである Service Bus のキューをトリガーにしたスケーリング方法を解説します。

構成イメージ

以下の構成を例に挙げて説明します。

クライアントはService Busのキューに対してメッセージを追加していきます。
Container AppsのKEDAはそのキューを監視してメッセージの有無・メッセージの量を確認します。
キュー内にメッセージがあればKEDAがPodをスケールアウトさせます。0台の状態から1台起動することもできますし、キュー内のメッセージが多くて処理が滞る場合にはPod数を更に増やしていって処理能力を増大させることも可能です。
起動したPodはキューからメッセージを取り出して処理していきます。すべてのメッセージの処理が終わりキューが空になったらKEDAがPodをスケールインさせます。

このように キューの長さに合わせて処理するPodの数を増減してくれる というのがポイントです。

環境作成

実際にAzure上に環境を作ってみましょう。

Service Busのキューからメッセージを取り出すPythonスクリプトを準備しました。

import os
from azure.servicebus import ServiceBusClient, ServiceBusMessage

CONNECTION_STR = os.environ["CONNECTION_STRING"]
QUEUE_NAME = os.environ["QUEUE_NAME"]

servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR, logging_enable=True)

with servicebus_client:
    receiver = servicebus_client.get_queue_receiver(queue_name=QUEUE_NAME)
    with receiver:
        for msg in receiver:
            print("Received: " + str(msg))
            receiver.complete_message(msg)

取り出したメッセージの内容を Received:という文字列と共に標準出力に表示するだけのシンプルなものです。
これをコンテナイメージとしてビルドして、Azure Container RegistryにPushしておきます。

FROM python
WORKDIR /usr/src/app

RUN pip install azure-servicebus

COPY app.py .

ENTRYPOINT ["python"]
CMD ["app.py"]
ACR_NAME=acrsample

docker build -t ${ACR_NAME}.azurecr.io/servicebus-receiver:latest .
docker push ${ACR_NAME}.azurecr.io/servicebus-receiver:latest

Service BusとContainer Appsも作成していきます。

# リソース名などの定義
RG_NAME=rg-sample
SB_NAME=sb-sample
QUEUE_NAME=sbq-sample
LOCATION=japaneast
CAE_NAME=cae-sample
ACA_NAME=aca-sample

# リソースグループ作成
az group create --location $LOCATION --name $RG_NAME

# Service Bus名前空間作成
az servicebus namespace create -n $SB_NAME -g $RG_NAME -l $LOCATION --sku Basic

# Service Busキュー作成
az servicebus queue create -n $QUEUE_NAME -g $RG_NAME --namespace-name $SB_NAME

# Service Busの接続文字列を取得
CONNECTION_STRING=$(az servicebus namespace authorization-rule keys list \
  --name RootManageSharedAccessKey -g $RG_NAME --namespace-name $SB_NAME \
  --query primaryConnectionString --output tsv)

# Container Apps環境作成
az containerapp env create -n $CAE_NAME -g $RG_NAME -l $LOCATION

# Container Apps作成
az containerapp create -n $ACA_NAME -g $RG_NAME \
  --image ${ACR_NAME}.azurecr.io/servicebus-receiver:latest \
  --system-assigned \
  --registry-server ${ACR_NAME}.azurecr.io \
  --registry-identity system \
  --environment $CAE_NAME \
  --min-replicas 0 --max-replicas 10 \
  --secrets connection-string=$CONNECTION_STRING \
  --env-vars CONNECTION_STRING=secretref:connection-string QUEUE_NAME=$QUEUE_NAME PYTHONUNBUFFERED=1 \
  --scale-rule-name servicebus-queue \
  --scale-rule-type azure-servicebus \
  --scale-rule-metadata connectionFromEnv=CONNECTION_STRING queueName=$QUEUE_NAME

Container Appsの作成オプションが複雑なのでちょっと解説。

  --image ${ACR_NAME}.azurecr.io/servicebus-receiver:latest \
  --system-assigned \
  --registry-server ${ACR_NAME}.azurecr.io \
  --registry-identity system \

コンテナイメージの指定を行うオプションです。
--registry-server でACRのURLを指定して --registry-identity system を付けることで、ACRからマネージドIDで認証してイメージをPullできるようになります。

  --secrets connection-string=$CONNECTION_STRING \

シークレットとしてService Busの接続文字列を登録しています。

  --env-vars CONNECTION_STRING=secretref:connection-string QUEUE_NAME=$QUEUE_NAME PYTHONUNBUFFERED=1 \

Podに渡す環境変数の設定です。CONNECTION_STRINGの値はシークレットの値を参照するように設定しています。

  --min-replicas 0 --max-replicas 10 \
  --scale-rule-name servicebus-queue \
  --scale-rule-type azure-servicebus \
  --scale-rule-metadata connectionFromEnv=CONNECTION_STRING queueName=$QUEUE_NAME

ここがKEDAの設定パラメーターです。
最小・最大のレプリカ数とKEDAの typemetadata を設定しています。
--scale-rule-name はContainer Appsの設定に付ける任意の名前なので、好きな値を入れればOKです。

typemetadata に何を指定すればよいのかはKEDAのドキュメントを確認します。Azure Service Bus用のドキュメントは以下にあります。

keda.sh

ドキュメント上ではYAML形式で記載されていますが、それぞれの値を上記のコマンドのように設定すればよいです。
typeには azure-servicebus を指定し、metadataとしてconnectionFromEnv (Service Busの接続文字列を格納した環境変数) とqueueName(スケールのトリガーとするService Bus内のキュー) を設定しています。

ポータルからKEDAの設定を行う場合は以下のように記述していきます。

ポータルからの設定では、

  • HTTPトリガー(リクエスト数に応じてスケールする)
  • Azureキュー(Service Busでなくストレージアカウントのキューストレージを利用する)

以外のトリガーを使う場合、種類 欄を カスタム に設定するのがポイントです。

スケーリングの動作確認

環境ができあがったら早速動作確認してみましょう。
Service Busのキューにメッセージを登録するPythonスクリプトを準備しました。

import os
import sys
from azure.servicebus import ServiceBusClient, ServiceBusMessage

CONNECTION_STR = os.environ["CONNECTION_STRING"]
QUEUE_NAME = os.environ["QUEUE_NAME"]

args = sys.argv
count = int(args[1])

servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR, logging_enable=True)

with servicebus_client:
    sender = servicebus_client.get_queue_sender(queue_name=QUEUE_NAME)
    batch_message = sender.create_message_batch()
    for _ in range(count):
        batch_message.add_message(ServiceBusMessage("Sample Message!!!"))
    sender.send_messages(batch_message)
    print(f"Sent a batch of {count} messages")

これを手元の環境から実行してメッセージを登録します。
まずは1件登録してみましょう。

python sender.py 1

Container Appsの Replica Count メトリックを見てみると…

キューに登録したタイミングでレプリカ数が0⇒1にスケールアウトされていることが確認できました。

次にもっと大量のメッセージを登録してみましょう。1000件ほど登録してみます。

python sender.py 1000

最大レプリカ数に設定した10までスケールアウトされていることが確認できます。

そして、しばらく放置してキューにメッセージが無い状態が続くと…

10⇒0にスケールインしたことが確認できます。

おわりに

Azure Container AppsのKEDAによるスケーリングの動作について解説しました。
Container Appsは起動時間に応じた課金体系で、利用していないときに0台にスケールインすることでコストを抑えることができます。
「使った分だけお金を払う」というクラウドのメリットを享受するためにも、ぜひ積極的に活用していきましょう!

私達ACS事業部はAzure・AKSを活用した内製化のご支援をしております。ご相談等ありましたらぜひご連絡ください。

www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です!
切磋琢磨しながらスキルを向上できる、エンジニアには良い環境だと思います。ご興味を持っていただけたら嬉しく思います。

www.ap-com.co.jp

本記事の投稿者: 吉川 俊甫
AKS/ACAをメインにインフラ系のご支援を担当しています。 Shunsuke Yoshikawa - Credly