Azure Kubernetes Service(AKS)とKEDAを利用したサーバレス

# はじめに

この記事ではAzure Kubernetes Service(以下、AKS)とKEDAを利用したサーバレスなイベント駆動型アプリケーションの構築方法について解説します。

# 想定読者
- Kubernetesを利用したことがある方
- Azureが好きな方

# Kubernetesにおけるスケーリング

Kubernetesにおけるアプリケーション(ポッド)のスケールの方法の１つとしてHPA(Horizontal Pod Autoscaler)があります。これはPodで利用しているリソース消費量(CPUやメモリ)を監視して、高負荷時にポッドを自動でスケールアウトするものです。

この記事で紹介するKEDAは同じく水平スケールを実現するKubernetesのコンポーネントで、イベント駆動型のアプリケーションに対応しています。KEDAではHPAに比べて以下の特徴があります。

- イベントの急激な増加を素早く検知して、処理前にスケールアウトをする
  
  HPAではイベントがPodに到達した後、リソースの消費量等をもとにスケールアウトの判断することが多いです(External Metricsなど、必ずしもそうではありません)。一方でKEDAではイベントソース(キューやストリームなど)の状態を監視して、イベントがPodに到達する前にあらかじめスケールアウトの判断をすることができます。

- イベントが発生していない時に無駄なリソースを消費しない
  
  HPAではスケール範囲が1～nであり、イベントが発生していない場合にもリソースの消費が発生します。一方でKEDAではスケール範囲が0～nであり、イベントが発生していない場合のリソース消費量を抑えることができます。

# KEDAとは

MicrosoftとRed Hatが中心となって開発されたオープンソースプロジェクトで、Kubernetes Event-driven Autoscalingの頭文字をとってKEDAとされています。Kubernetes上で動作するサーバレスなイベント駆動型のオートスケールを実現します。KEDAを利用することで、0～nのスケーリングを実現できます。サーバレスと呼ばれるのはこの特徴があるからです。
## アーキテクチャ
KEDAは大きく3つのコンポーネントにより構成されています。**Controller**と**Scaler**、**Metrics adapter**です。

**Controller**は、KubernetesのDeploymentに対してアクティブ・非アクティブを制御し、0からのスケールを実現しています。

**Scaler**はトリガとして指定された外部リソースからメトリクス(キューの長さなど)を取得します。

**Metrics adapter**は**Scaler**で取得したメトリクスをHPAに提供します。従来のHPAが対応しているメトリクスに加えて、豊富な情報を利用してオートスケールを実現できます。

詳細な情報は公式サイトの[こちら](https://keda.sh/docs/2.4/concepts/)を参照してください。

![keda-arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155066/66e4a26e-d692-ba86-700f-85a1b1a942a5.png)

## サポートするイベントソース

KEDAで処理できるイベントソースとして、以下Azureリソースを利用したものに対応しています。ここで挙げたトリガ以外にも様々なトリガに対応しているので、KEDAのサイトをご覧ください。

- Azure Storage Queue
- Azure Service Bus Queue
- Azure Event/IoT Hubs
- Azure Log Analytics
- Azure Monitor

## Azure Functionsとの統合

さらにAKSでKEDAを利用するうえで便利なことの１つに、Azure Functionsと統合できることが挙げられます。
Azure Functionsのコードをそのままにコンテナ化することで、AKS上にホストすることができるのです。
ここからは実際にその流れを解説していきます。

# AKSとKEDAを利用してサーバレスアプリケーションを構築する

Azure Storage QueueをトリガとしたAzure FunctionsをAKSにデプロイするシナリオを想定します。

## 事前準備（ローカルPC）
ローカルPCに以下ツールをインストールします。

- [Azure Functions Core Tools](https://github.com/Azure/azure-functions-core-tools)

  Azure Funtionsを開発するためのコマンドラインツールです。AKSにKEDAをインストールしたりAKSにデプロイしたりする機能が含まれています。

- [Azure CLI](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli)

  Azureリソースを操作するのに利用します。

- [kubectl](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/)

  kubernetesを制御するためのコマンドラインツールです。本記事では動作確認などに利用します。

- [Docker](https://docs.docker.com/get-docker/)

  Azure Functionsのコードをコンテナ化し、レジストリにPushするために利用します。
  

## Azureリソースの構築
Azureの以下リソースを利用します。それぞれのリンクを参考に構築します。

- [Azure Queue Storage](https://docs.microsoft.com/ja-jp/azure/storage/queues/storage-quickstart-queues-portal)

- [Azure Container Registry(以下、ACR)](https://qiita.com/kk-ishii/items/720d26467484684fd31d)

- [AKS](https://qiita.com/kk-ishii/items/e0f61e6411d9983a30b3)

  構築後、AKSからACRを参照できるようにするため、以下コマンドを実行してください。

```sh
az aks update -n <AKS名> -g <リソースグループ名> --attach-acr <ACR名>
```

## 手順

### AKSへの接続情報取得

以下コマンドを実行し、AKSへの接続情報を取得します。

```sh
az aks get-credentials --resource-group <リソースグループ名> --name <AKSクラスタ名>
```

`kubectl config get-contexts`を実行したときに作成したAKSクラスターが選択されていればOKです。

### KEDAのインストール

Azure Functions Core Toolsを利用してKEDAをAKSにインストールします。
以下コマンドを実行してKEDAをインストールします。

```sh
kubectl create namespace keda
func kubernetes install --namespace keda
```

コンソールに`KEDA v2 is installed in namespace keda`と表示されればインストール完了です。
試しに`kubectl get pods --namespace keda`を実行すると、kedaのPodが確認できます。

```sh
NAME                                      READY   STATUS             RESTARTS   AGE
keda-metrics-apiserver-8458c8f96d-mcblr   1/1     Running            0          1m
keda-operator-69c986985-sdp46             1/1     Running            0          1m
```

### ACRへのログイン

ACRへコンテナをpushするため、Azure CLIを利用してACRにログインします。

```sh
az acr login -n <ACR名>
```

コンソールに`Login Succeed`と表示されればログイン完了です。


### Azure Functionsのプロジェクトと関数の作成

Azure Functionsのプロジェクトを作成します。AKSではコンテナを利用するため、`--docker-only`オプションをつけてプロジェクトを作成します。また言語はJavaScriptを利用します。

```sh
func init --docker-only --javascript
```

続けて関数を作成します。

```sh
func new --javascript --template "Azure Queue Storage trigger" --name queuefunc
```

作成されたファイルのうち、以下をそれぞれ書き換えます。
- `index.js`：メイン処理

  スケーリングを確認するため待機処理を追加

```js
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

module.exports = async function (context, myQueueItem) {
    sleep(10000); // ←この行を追加
    context.log('JavaScript queue trigger function processed work item', myQueueItem);
};
```

- `local.setting.json`：環境変数の定義

  `AzureWebJobsStorage`に作成したストレージアカウントの接続文字列を追加

```json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=...."
  }
}
```
- `function.json`：関数の定義

  トリガとなるQueueの名前、ストレージアカウントの接続文字列が書かれている環境変数名を追加

```json
{
  "bindings": [
    {
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "Storage Queueの名前",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
```

### AKSへのデプロイ
準備が整ったのでAKSへのデプロイを実施します。
Azure Functionsのプロジェクトのディレクトリで、以下コマンドを実行します。

```sh
func kubernetes deploy --name <関数の名前> --registry <ACR名.azurecr.io> --namespace keda
```

このコマンドを実行すると、内部的に以下の処理が行われています。

1. Dockerイメージのビルド
2. DockerイメージのレジストリへのPush
3. AKSへのマニフェストのデプロイ

実際にACRを確認すると指定した関数名のリポジトリが作成され、イメージがPushされていることが確認できます。（タグはlatest）
   
![acr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155066/9082e5e5-7f40-2b47-dc64-ba9a0e9ddf14.png)


またAKSにデプロイされるオブジェクトは、先ほどのコマンドに`--dry-run`のオプションをつけることでマニフェストを確認できます。

```sh
func kubernetes deploy --name <関数の名前> --registry <ACR名.azurecr.io> --namespace keda --dry-run
```

3つのKubernetesオブジェクトがデプロイされていることを確認できます。

- Deployment
  
  アプリケーション本体

- Secret
  
  `local.setting.json`内で定義した環境変数

- [ScaledObject](https://keda.sh/docs/2.4/concepts/scaling-deployments/)
  
  KEDAのカスタムリソースで、トリガやスケール対象のDeploymentを指定する

## 動作確認

指定したAzure Queue Storageに対して（数100件ほど）メッセージを追加したときのPodの状態になります。作成したqueuefuncがスケールアウトしていることを確認できます。

またメッセージをすべて処理した後の状態でアイドル状態が続けば、対象のPodは１つも存在しない状態になることも確認できます。冒頭で解説した0～nのスケールを確認することができました。

![auto-scaling.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155066/9523740d-5a04-d2ee-3520-4761af8ffa6f.gif)

# おわりに

この記事ではKEDAを利用してAzure FunctionsをAKSで実行する流れについて解説しました。

本文中で解説しきれませんでしたが、プロダクション環境での利用する場合は、環境変数の扱いやコンテナのタグの運用方法など様々な検討事項が残ります。KEDAのサイトやAzure Functionsのリファレンスにヒントがありますのでぜひ参照してみてください。


# 参考
- https://keda.sh/
- https://github.com/kedacore/keda/
- https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-kubernetes-keda
- https://kubernetes.io/ja/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/