BentoMLのAWSへのデプロイ

# はじめに
最近Model ServingのOSSとして[BentoML](https://github.com/bentoml/BentoML)に注目しています。BenteMLではクラウド環境へのデプロイをサポートする[bentoctl](https://github.com/bentoml/bentoctl)というOSSも合わせて公開しています。

https://github.com/bentoml/bentoctl

現在サポートされているデプロイ先のクラウドは以下になります。
- AWS Lambda
- AWS SageMaker
- AWS EC2
- Google Cloud Run
- Google Compute Engine
- Azure Container Instances
- Heroku

今回はAWSのLambdaとSageMakerへのデプロイを試してみました。

# 前提
## 環境
クラウドはAWSを利用します。操作するローカルPCは以下のような環境です。
- OS: Ubuntu 20.04(Windows WSL2）
- Docker 20.10.21, Python 3.8.10
- AWS CLI 2.9.10 -- aws configureコマンドでdefaultのプロファイルが設定されていること
- Terraform 1.3.7

bentoctlは内部で[Terraform](https://developer.hashicorp.com/terraform)を利用しています。環境にTerraformがインストールされていない場合は[こちら](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)を参照してインストールを行ってください。

## 事前準備
仮想環境を作って、BentoMLとbentoctlをインストールします。
```sh
# python仮想環境の作成と有効化
python -m venv venv
source venv/bin/activate

# BentoML、bentoctlのインストール
pip install bentoml bentoctl
```

## 推論サービス
BentoMLで作成した推論サービスのbentoが出来上がっている前提とします。推論の内容自体はどのようなものでもOKかと思います。今回は、以前に投稿した[こちら](../sub010_MLflowとBentoMLの連携/README.md)で作成したbentoを利用しています。bentoは`bentoml list`コマンドで確認できます。

```sh:bentoの確認
bentoml list
```
bentoが存在すれば下記のように表示されます。
```log:実行結果
 Tag                                         Size       Creation Time        Path
 winequality-red-inference:rpulvbfdl6ub2aav  16.12 KiB  2023-02-03 10:10:28  ~/bentoml/bentos/winequality-red-inference/rpulvbfdl6ub2aav
```


# パターン1. AWS Lambdaへのデプロイ
推論サービスをLambdaにデプロイしてみます。Lambdaはサーバレスのコンピューティングサービスです。リクエストの数とコードの実行時間の課金となりますので、そこまで高頻度にリクエストが発生しない無い場合などかなり割安に運用できるかと思います。仕様上、15分以内という実行時間の制限がありますが、推論の場合は殆ど問題にならないかと思います。ただし、しばらくリクエストが無い状態からコールされた場合にレスポンスが遅くなるコールドスタートという現象が起こることがあります。これが許容できない場合は後半で説明するSageMakerのリアルタイム推論を利用するのがよいです。

## 1.1 aws-lambda operatorのインストール
Operatorはクラウドサービスとデプロイするためのリソースを作成するためのもので、デプロイ先の環境毎に用意されています。

```sh:aws-lambda operatorのインストール
bentoctl operator install aws-lambda
```

## 1.2 デプロイメントファイルの作成
`deployment_config.yaml`というファイルを作成します。このファイルは`bentoctl init`コマンドを対話形式で実行して作成します。

```sh:デプロイメントファイルの作成
bentoctl init
```
対話形式で入力を求められるので、以下のように入力します。
- name: winequality-red-lambda
- region [us-west-1]: ap-northeast-1

以下の質問はデフォルト値を受け入れるのでそのままEnterします。
- timeout [10]
- memory_size [512]
- filename for deployment_config [deployment_config.yaml]

実行が完了すると、`deployment_config.yaml`ファイルが生成されます。また、terraformの定義ファイル`main.tf`と`bentoctl.tfvars`が生成されます。

## 1.3 イメージの生成
```sh:イメージの生成
bentoctl build -b winequality-red-inference:latest -f deployment_config.yaml
```
このコマンドにより、bentoからDockerイメージが生成され、Amazon Elastic Container Registry (ECR)にプッシュされます。

## 1.4 デプロイ
AWS上に推論環境をデプロイします。
```sh:推論環境のデプロイ
bentoctl apply -f deployment_config.yaml
```

bentoctlコマンドを実行していますが、内部的にはTerraformを使ってデプロイが行われています。これによって下記のような環境が生成されます。

![lambda.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2724847/373a332c-8092-de46-4179-8a3d06ea71c0.png)

- ECRにプッシュされたDockerイメージを元にLambda関数が生成されます。
- 外部からのリクエストを受け付けるためのAPI Gatewayが生成され、Lambdaとの連携が設定されます。API GatewayのプロトコルタイプはHTTP APIになります。
- CloudWatch LogsにはLambdaでの実行ログが記録されます。

## 1.5 推論呼び出し
デプロイができたら推論呼び出しが可能となります。

```sh:推論呼び出し
# API Gatewayエンドポイントの設定
#   bentoctl applyの実行結果からも取得できる
URL=$(terraform output -json | jq -r .endpoint.value)predict

# API呼び出し
curl -X POST -H 'Content-Type:application/json' $URL \
--data '{"columns":["alcohol", "chlorides", "citric acid", "density", "fixed acidity", "free sulfur dioxide", "pH", "residual sugar", "sulphates", "total sulfur dioxide", "volatile acidity"], "data":[[ 10.6, 0.091, 0.09, 0.9976, 8.7, 20, 3.34, 2.5, 0.86, 49, 0.52 ]]}'
```

## クリーンナップ
下記コマンドで環境をきれいに削除できます。
```sh:クリーンナップ
bentoctl destroy -f deployment_config.yaml
```

デプロイやクリーンナップのコマンドはterraformに似ていますね。これによりLambdaの推論環境とECRに登録されたリポジトリが削除されます。`bentoctl build`コマンドで生成されたTerraformのファイルはローカルPCに残りますので、こちらは手動で削除してください。


# パターン2. Amazon SageMakerへのデプロイ
次に推論サービスをSageMakerへデプロイしてみます。具体的にはSageMakerのリアルタイム推論としてデプロイされます。常時起動のインスタンス上で稼働するので、コールドスタートは発生せずすぐに推論処理が行われます。インスタンスタイプも指定できるので、より高速な処理が可能かと思います。ただし、インスタンスが常時稼働のためそれなりの課金も発生するので注意が必要です。

手順ですが、Lambdaの場合とあまり変わりません。

## 2.1 aws-sagemaker operatorのインストール
まずはOperatorのインストールです。既にaws-lambda operatorがインストールされていてもOKです。
```sh:aws-sagemaker operatorのインストール
bentoctl operator install aws-sagemaker
```

## 2.2 デプロイメントファイルの作成
`deployment_config.yaml`ファイルの作成です。対話形式で入力する内容がLambdaの時と少し異なります。

```sh:デプロイメントファイルの作成
bentoctl init
```
対話形式で入力を求められるので、以下のように入力します。（nameやregionは変更してもらっても構いません。）
- name: winequality-red-sagemaker
- Choose an operator: aws-sagemakerを選択 ← 複数のOperatorが入っていると選択式になります
- region: ap-northeast-1

以下の質問はデフォルト値を受け入れるのでそのままEnterします。
- instance_type [ml.t2.medium]
- initial_instance_count [1]
- timeout [60]
- enable_data_capture [False]
- destination_s3_uri []
- initial_sampling_percentage [1]
- filename for deployment_config [deployment_config.yaml]

実行が完了すると、`deployment_config.yaml`ファイルが生成されます。また、terraformの定義ファイル`main.tf`と`bentoctl.tfvars`が生成されます。

## 2.3 イメージの生成
これ以降のプロセスはLambdaの時と同じになります。
```sh:イメージの生成
bentoctl build -b winequality-red-inference:latest -f deployment_config.yaml
```

## 2.4 デプロイ
```sh
bentoctl apply -f deployment_config.yaml
```

下記のような環境が生成されます。

![sagemaker.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2724847/2b8be563-08b4-1f91-c30c-cb696bd8a305.png)

## 2.5 推論呼び出し
```sh:推論呼び出し
# API Gatewayエンドポイントの設定
#   bentoctl applyの実行結果からも取得できる
URL=$(terraform output -json | jq -r .endpoint.value)predict

# API呼び出し
curl -X POST -H 'Content-Type:application/json' $URL \
--request POST \
--data '{"columns":["alcohol", "chlorides", "citric acid", "density", "fixed acidity", "free sulfur dioxide", "pH", "residual sugar", "sulphates", "total sulfur dioxide", "volatile acidity"], "data":[[ 10.6, 0.091, 0.09, 0.9976, 8.7, 20, 3.34, 2.5, 0.86, 49, 0.52 ]]}'
```

## クリーンナップ
```sh:クリーンナップ
bentoctl destroy -f deployment_config.yaml
```

SageMakerのエンドポイントは稼働し続けると課金も続くので、忘れずにクリーンナップしましょう。


# 実運用における検討ポイント
- 生成されたAPI Gatewayは認証の設定がされておらず、エンドポイントが分かれば誰でもどこからでもコールできる状態になっています。通常は何らかの呼び出し制限を掛ける必要があるかと思いますので、別途検討は必要です。
- terraformの現在のリソース状態を表すterraform.tfstateファイルはローカルに生成されます。実際の運用はS3などでの管理が一般的だと思いますので、その点も手を加える必要があるかと思います。

# おわりに
実運用における課題はまだあると思います。でも、思った以上に簡単にデプロイできたのでちょっと驚きました。