Azure Machine Learningで構築されたDockerイメージの中身 (AzureML v1)
本記事はPaige Liu氏のブログ記事「Inside the Docker image built by Azure Machine Learning service | Paige Liu’s Posts」を著者の許諾の下で翻訳した記事です。
元記事の投稿時期の関係で、Python SDKのサンプルがv1になっています。推論Dockerイメージの中身は大きく変わらないものの、一部の仕様が現在と異なっている点があることご留意ください。
また、記事中に搭載する"Webサービス"はv1の概念で、v2の概念としては"エンドポイント"があります。詳細は下記記事をご参照ください。
- 分かりやすい比較記事:MLOps のための Azure Machine Learning CLI v2 / SDK v2
- v1とv2の全体比較:v1 から v2 への移行 (Docs)
- デプロイ面のv1とv2の比較:デプロイ エンドポイントを SDK v2 にアップグレードする (Docs)
(以下記事本文)
Azure Machine Learning(ML)サービスは、機械学習モデルの開発、トレーニング、テスト、デプロイ、および管理を容易にするクラウドベースの環境です。モデルは、Azure Container Instances、Azure Kubernetes Service、FPGA、またはAzure IoT Edgeデバイス上で実行されるIoTモジュールとして、Webサービスの形でデプロイすることが可能です。これらのすべてのケースで、モデル、その依存関係、および関連ファイルは、スコアリング要求を受信し、推論結果を返すWebサービスのエンドポイントを公開するDockerイメージにカプセル化されています。
Azure Machine Learning Python SDK (v1)を使用すると、モデルを呼び出すWebサービスの作成方法や、DockerfileからDockerイメージを構築する方法について心配する必要がありません。代わりに、以下のように Docker イメージを作成することができます。
(※訳注:下記はPython SDK v1の例。またContainerImage
クラス自体もDeprecated (非推奨)となっているため、あくまで必要なリソースのイメージを掴むためにご参照ください)
image_config = ContainerImage.image_configuration(
execution_script = "score.py", #this file contains init and run functions that you implement
runtime = "python",
conda_file = "myenv.yml" #this file contains conda environment that the model depends on
)
image = ContainerImage.create(
name = "myimage",
models = [model], #this is the trained model object
image_config = image_config,
workspace = ws
)
モデル、実行スクリプト、conda環境ファイルの作成方法については、Azure MLドキュメント (v1)に記載されています。一方で、スコアリング (推論)Webサービスを動作させるために、関連する要素がどのように組み合わされるかはドキュメントに書かれていません。この仕組みを理解することで、トラブルシューティングや、コードやデプロイのカスタマイズに役立てることができます。
Dockerイメージの中身:
- Azure Machine Learning サービスは、ユーザーの実行スクリプト (ここでは
score.py
)に含まれる "init" と"run" 関数を Flask アプリケーションでラップしています。上記画像内では、このアプリに関連するすべてのコードが/var/azureml-app
フォルダに配置されています。create_app.py
がこのアプリのエントリポイントになります。 -
create_app.py
の内部にはapp.run(host='0.0.0', port=9090)
という記述があります (※ポート番号はv1の場合)。つまり、python create_app.py
を実行するだけで、3つのルートでhttp://localhost:9090 に接続できるようになるわけです。-
/
: アプリが正常に実行されていれば "Healthyroot" を返します -
/swagger.json
: Webサービス用のswagger (OpenAPI)を返します -
/score
: スコアリング結果を返します
-
- Flaskで直接Webサービスを実行するのは、あまり良い方法ではないとされています。Flaskは大規模向けのWebサーバではないので、リクエストを逐次処理するためです。gunicornはPythonのWSGI HTTPサーバーで、pre-forkワーカーモデルを利用して効率的に複数のリクエストを並列処理します。gunicornの設定は
/var/azureml-app/gunicorn_conf.py
にあり、127.0.0.1:9090
にバインドされていることが確認できます。Flaskアプリはスタンドアロンではなく、gunicornで起動しています。そのため両者が9090番ポートをリッスンしています。 - gunicornは、起動時にサービスを実行するUNIXのinitスキームであるrunitによって起動されます。Azure MLで作成したイメージを"docker inspect"すると、この起動コマンド
runsvdir /var/runit
が存在しています。これは/var/runit
のサブフォルダーを監視し、サブフォルダー内にrun
ファイルがあれば、そのファイルを実行することを意味しています。この画像では、/var/runit/gunicorn/run
があり、/var/azureml-app
内でgunicorn -c gunicorn_conf.py wsgi:app
が実行されるようになっています。 - gunicornは複数のリクエストを並行して処理することができますが、大規模なプロダクションワークロード向けに構築されたWebサーバーのすべての機能を備えているわけではありません。そこでNginxの出番です。Nginx は静的ファイルを処理し、バックエンドの Python 処理が終わると同時に gunicorn のスレッドを解放して追加のリクエストに対応できるよう、結果をキャッシュして遅いクライアントにも対応できるなど、様々な機能を持っています。この画像では、nginx は runit によって
/var/runit/nginx
の下で起動されています。etc/nginx/sites-available/app
にある設定ファイルを見ると、ポート5001をリッスンし、gunicornがリッスンしている127.0.0.1:9090
に proxy_pass するよう設定されていることがわかります。このイメージを実行しているDockerコンテナの5001番ポートをエクスポートすれば、スコアリングWebサービスのエンドポイントにアクセスできるはずです。
Discussion