はじめに
Azure FunctionsがKubernetes上で実行できるのをご存知でしょうか。 Azure Functionsの公式ドキュメントにも記載があります。
ドキュメントで紹介はされているけど、実際はどうなんだろう?というテーマで今回試してみました。その内容と大小さまざまなつまづきがありましたのでそうした情報も合わせて紹介したいと思います。
今回実施した内容はHTTP Triggerアプリケーションを動かす、というものです。
KEDAのインストール
ドキュメントにある通り、Azure FunctionsはKEDAを利用して実現しています。 KEDAはオートスケーリングを実現するミドルウェアで、Podを0からnまでのスケーリングを 実現することができます。ということでまずはKEDAをインストールです。。
このサイトで記載されている手順に沿って実施すれば簡単にインストールできます。
export NAMESPACE="keda" helm repo add kedacore https://kedacore.github.io/charts helm repo update kubectl create namespace ${NAMESPACE} helm install keda kedacore/keda --namespace ${NAMESPACE}
func kubernetes install
でKEDAをインストールすることもできるようですが、
今回はあえてHelmを使ってインストールしています。
Functionの作成
アプリケーションは今回Javaを利用することにしました。
こちらに書いてあるとおり、
mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=11 -Ddocker
と実行すれば作成できます。
Javaを選んだのは特に理由はなかったのですが、今回引っかかった原因の1つが言語選択にありました。
Java以外の言語の場合 func init
コマンドでプロジェクトの作成ができます。また、コンテナ用のDockerfileもfunc init
で作成されます。VSCode上のExtensionでも同様です。しかしJavaだけは例外でした。
アプリケーションコードは他の言語同様作成できるのですが、Dockerfileは作成してくれません。
func init --docker-only
を使っても同様で、Javaの場合は .dockerignore ファイルしか作成してくれません。
Javaを利用する場合は、上記の mvn
コマンドを使って作成しなければなりません。
Javaを選択される方はこの点ご注意ください。
Functionのデプロイ
特に作成されたコードを変更することもせず、デプロイを実行してみます。 デプロイにはローカル環境にDocker CLIがインストールされている必要があります。この点もご注意ください。 事前にACRもご用意ください。
# 事前にACRにログインし、dockerコマンドでPushできるようにする az acr login -n ${ACR_NAME} func kubernetes deploy --name java-fn --registry ${ACR_NAME}.azurecr.io
func kubernetes deployでは 1. docker build 1. docker push 1. kubernetes apply
が実行され、AKS上に - Deployment (name: <func-name>-http) - Service/Loadbalancer (name: <func-name>-http) - Secret (name: <func-name> ) といったリソースが作成されます。
ここで、お気づきの方もいるかもしれません。 KEDAどこで使っているの? 、 そもそもPodのスケールってどうなっている?
そうなのです。ここまできて大きな落とし穴がありました。
HTTP トリガーを公開する Azure Functions は使用することはできますが、KEDA では直接管理されません。
HTTP Triggerのサポート
そうなのです。通常のFunctionsのようなスケーリングまでは func
コマンドで対応していないのです。
ではどうするか。自分自身で
- Horizontal Pod Autoscaler のYAMLを追加する
- KEDAのHTTP Triggerの機能を利用してスケーリング設定を行う
といった方法で対応しなければなりません。 (ちゃんとはじめからドキュメントを読み込んでいれば実行する前に気づいたことではありますが・・・)
この対応であれば、HTTP以外の部分でFunctionsのBindingを行わない限り、わざわざFunctionsを使う必要性はなさそうですね。
KEDAを使ってHTTP Trigger Autoscale
KEDA本体は実はHTTP Triggerをサポートしていません。これまではPrometheus Triggerを使うのが一般的でした。 今回KEDAを調べていくと、KEDA HTTP Add-on というものが存在することに気づきました。
方式は上記Webページにあるように 前段に Ingress Serverを挿入、Backendに keda-http-interceptor を挟んで、Function Serviceに接続します。
せっかくですから、こちらのAdd-onを使ってみようと思います。
インストールは helm で実行できます。ここで注意事項。Add-onとアプリケーションは同じnamespaceにデプロイしてください。(HTTP add-onの制約事項です)
helm install --create-namespace -n ${NAMESPACE} http-add-on kedacore/keda-add-ons-http
前段に Ingressやinterceptorが挟まるので Functions の service-type をCluster IPに変更してデプロイし直します。
# 前にデプロイしたFunctionを削除 kubectl delete deploy <func-name>-http kubectl delete service <funct-name>-http kubectl delete secret <func-name> # あらためて ${NAMESPACE} にデプロイ func kubernetes deploy --name java-fn --registry ${ACR_NAME}.azurecr.io --service-type ClusterIP --namespace ${NAMESPACE}
Service TypeがLoadBalancerからCluster IPに変わっていると思います。
続いてAdd-on用の設定です。設定内容はAdd-onのサンプルを参考にさせていただきました。
最初は公開用のIngressの設定です。こちらからAdd-onのinterceptorにリクエストを転送します。 (name, host等はご自身の環境に合わせて変更してください)
ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: <func-name>-ingress namespace: ${NAMESPACE} annotations: kubernetes.io/ingress.class: addon-http-application-routing # nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: yourhost.example.com http: paths: - path: / pathType: Prefix backend: service: name: keda-add-ons-http-interceptor-proxy port: number: 8080
続いてAdd-onの設定です。HTTPScaleObjectの設定内容の詳細は ドキュメント を参照してください。
httpscaledobject.yaml
kind: HTTPScaledObject apiVersion: http.keda.sh/v1alpha1 metadata: name: <func-name> namespace: ${NAMESPACE} spec: host: yourhost.example.com scaleTargetRef: deployment: <func-name>-http service: <func-name>-http port: 80 replicas: min: 0 max: 5
設定が正しければ、先にデプロイしていたFunctionのPodが0にスケールインすると思います。 うまくいかの場合は以下の点を見直してください。
- HTTPScaleObjectのdeploymentやservice, portの内容
- それぞれをデプロイしたnamespace (同一でなければなりません)
Podが0の状態でリクエストを受け付けた場合、検証した環境ではPodされるまで15秒程度かかりました。 1からのスケールアウトの時とは起動までの時間が異なり長くなりますのでその点はご注意ください。
スケール数を変えてみる(そして不具合に遭遇)
最後におまけです。Scale数をMinimum 1に変更してみましょう。やり方は簡単。 httpscaledobject.yamlの内容を書き換えるだけ。
replicas: min: 1
これをデプロイしたら完了・・・。のはずでした。ところがいくらやってもPodが0にスケールインしてしまう。 この問題を調べてみましょう。
kubectl describe HTTPScaledObject <func-name> -n ${NAMESACE}
で確認してもなかなかわかりません。
実はHTTPScaledObjectは内部でScaledObjectを作成しています。レプリカ数等はこちらで管理しています。
kubectl get ScaledObject -n ${NAMESPACE}
で確認すると <func-name>-app
という名称で作られているのがわかります。こちらを確認すると・・・HTTPScaledObjectの変更内容が反映されていない。
これを手がかりにAdd-onのIssueを探してみると、ありました。
修正は完了しているようですが、まだ公開はされていないようです。 今回はとりあえず手動で一度ScaledObjectを削除してから再度HttpScaledObjectをデプロイします。
kubectl delete ScaledObject <func-name>-app -n ${NAMESPACE} kubectl apply -f httpscaledobject.yaml
これで期待通りの値になりました。
HTTP Add-on は実環境で使うにはまだ少し早いかもしれません。 しかし、簡単にHTTP Triggerを使ったスケーリングが実現できるAdd-onとして今後に期待したいと思います。
いかがだったでしょうか。 Azure FunctionsをPaaS環境ではなく、AKS上で動かすという機会はあまりないと思いますので、これが何かの参考になれば幸いです。