Azure App Service、FlaskとOpenCVの環境構築

# はじめに
- Azure App Service での OpenCV の環境構築は、比較的簡単でした。公式のドキュメントも丁寧だし、Azure CLI ツールの `--help` が充実している様に感じました。
- App Service は、Azureが用意している `組み込みイメージ` と `Dockerfile` を作成する `カスタムイメージ` を利用できます。OpenCV は、後者のカスタムイメージを作成する必要があります。
- 今回も、以前作成した顔認識アプリを利用します。ソースコードは、同一です。Dockerfileが必要になる程度です。
- ソース一式は [ここ](https://github.com/maedamikio/public/tree/master/flask_opencv_app_service) です。
- 参考
    - https://docs.microsoft.com/ja-jp/azure/app-service/containers/tutorial-custom-docker-image


# 概要
- Mac
    - 顔認識アプリ
        - Flask
        - OpenCV
        - Python
    - デプロイツール等
        - Azure CLI
        - Docker
        - Homebrew
- Azure
    - App Service on Linux
    - Container Registry


# 顔認識アプリ
- 以下で解説したものと同一です。
    - [Heroku、FlaskとOpenCVの環境構築](https://qiita.com/maeda_mikio/items/1a6899e104aac7b138fc)
    - [Google App Engine、FlaskとOpenCVの環境構築](https://qiita.com/maeda_mikio/items/10fe47ba3aaeeacb94ed)
    - [AWS Lambda、FlaskとOpenCVの環境構築](https://qiita.com/maeda_mikio/items/a65c9d2e410e7686cb5a)
- 以下のファイルは、同一です。解説は省略します。
    - haarcascade_eye.xml
    - haarcascade_frontalface_default.xml
    - main.py
    - requirements.txt
    - templates/index.html

## Dockerfile
- 元イメージは、`ubuntu:18.04` にしました。今回は、Python ですが、Node.js や PHP 等にも流用しやすいと考えたためです。
- `DEBIAN_FRONTEND=noninteractive` で、apt-get を非対話形式に出来ますね。
- `PYTHONUNBUFFERED=0` は、App Service で、ログを閲覧する時に有用なものです。例えば、`print('Hello, Azure!')` など、標準出力が閲覧出来ます。設定しないと、標準エラーしか閲覧出来ない様です。
- `apt-get` で `python3` に最低限必要なパッケージをインストールします。
- 次に、OpenCVに必要な `libopencv-dev` をインストールします。また、pip で `opencv-python` をインストールします。pip でインストールする理由は、pip パッケージの場合、マルチコアで動作してくれる、最新のバージョンである、などが理由です。
- そして、Azure のコンパネから ssh 接続するための `openssh-server` をインストールします。
- コンパネからの接続は、ユーザー名 `root` パスワード `Docker!` ポート `2222` Ciphers `aes128-cbc,3des-cbc,aes256-cbc` MACs `hmac-sha1,hmac-sha1-96` を設定する必要があります。
- 最後に、`CMD` で sshサーバーと `gunicorn` 経由で `main.py` の `app` を起動します。

```
FROM ubuntu:18.04
ENV DEBIAN_FRONTEND=noninteractive \
    PYTHONUNBUFFERED=0
RUN apt-get update && apt-get install -y --no-install-recommends \
    libopencv-dev \
    openssh-server \
    python3 \
    python3-pip \
    python3-setuptools \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
RUN echo "root:Docker!" | chpasswd && \
    mkdir /var/run/sshd && \
    sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \
    sed -i '$ a Ciphers aes128-cbc,3des-cbc,aes256-cbc' /etc/ssh/sshd_config && \
    sed -i '$ a MACs hmac-sha1,hmac-sha1-96' /etc/ssh/sshd_config
RUN mkdir /code
WORKDIR /code
ADD . /code/
RUN pip3 install -r requirements.txt
EXPOSE 2222 8000
CMD ["/bin/sh", "-c", "/usr/sbin/sshd && gunicorn --bind=0.0.0.0 --timeout 600 main:app"]
```

# デプロイ
- Azure でアカウントを作成してください。

## Azure CLI
- Mac用の手順です。その他の環境は、[公式ページ](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli?view=azure-cli-latest)を参照してください。

```
$ brew install azure-cli
```
## ログイン
- @outlook.com など Microsoftのアカウントでログイン出来ます。

```
$ az login
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/390c628d-8206-661c-9db2-997ea136ee09.png)

## リソースグループの作成
- App Service、Container Registry 等のリソースを共通のグループとして管理するためのものです。
- リソースグループ名は `flask-opencv` にしました。ロケーションは、東日本(japaneast)にしました。

```
$ az group create --name flask-opencv --location japaneast
```

## Container Registry の作成、ユーザー名、パスワード
- リソース名は `flaskopencv` にしました。Container Registry は `-` が使えないのでした。
- リソースグループ名は、上記で作成した `flask-opencv` ですね。
- --sku は `Basic` です。無料の `F1` は無いようです。残念。。。
- --admin-enabled は、既定で無効になっている管理者ユーザーを有効にしています。
- 最後に `username` と `password` を確認します。今回は `qBXsw4NcyMGmYe+AxbN9XTXtl8DmpzDI` ですね。

```
$ az acr create --name flaskopencv --resource-group flask-opencv --sku Basic --admin-enabled true

$ az acr credential show --name flaskopencv
{
  "passwords": [
    {
      "name": "password",
      "value": "qBXsw4NcyMGmYe+AxbN9XTXtl8DmpzDI"
    },
    {
      "name": "password2",
      "value": "qTSksf3ssm=330OxC0+VAaM1nYwOGzOv"
    }
  ],
  "username": "flaskopencv"
}
```

## docker login、build、tag、push
- Container Registry へログインします。上記で取得したユーザーとパスワードを使います。
- Dockerfile を元に、build、tag を付与して、レジストリへ push します。

```
$ docker login flaskopencv.azurecr.io --username flaskopencv

$ docker build -t flask-opencv .

$ docker tag flask-opencv flaskopencv.azurecr.io/flask-opencv:latest

$ docker push flaskopencv.azurecr.io/flask-opencv:latest
```

## App Service プランの作成
- App Service を Linux で作成します。リソース名やリソースグループ名は、上記と命名を一緒にしています。
- --sku は 無料の F1 が利用できます。

```
$ az appservice plan create --name flask-opencv --resource-group flask-opencv --sku F1 --is-linux
```

## App Service の作成
- 上記で作成したプランに HTTPサービスである `webapp` を作成します。
- 上記で `docker push` したコンテナイメージを利用します。
- リソース名、リソースグループ名、リソースプラン名などは、共通としてます。

```
$ az webapp create --name flask-opencv --resource-group flask-opencv --plan flask-opencv --deployment-container-image-name flaskopencv.azurecr.io/flask-opencv:latest
```

## App Service から Container Registry へのアクセスするための設定
- レジストリの名前、アドレス、ユーザー名、パスワードなどを設定しています。

```
$ az webapp config container set --name flask-opencv --resource-group flask-opencv --docker-custom-image-name flaskopencv.azurecr.io/flask-opencv:latest --docker-registry-server-url https://flaskopencv.azurecr.io --docker-registry-server-user flaskopencv --docker-registry-server-password qBXsw4NcyMGmYe+AxbN9XTXtl8DmpzDI
```

## ポート設定
- Dockerfile で gunicorn を ポート 8000 で動かしている事を設定しています。

```
$ az webapp config appsettings set --name flask-opencv --resource-group flask-opencv --settings WEBSITES_PORT=8000
```

## ログ設定
- コンテナ内で発生した、標準出力と標準エラーのログを閲覧出来る様にします。
- Dockerfile で `PYTHONUNBUFFERED=0` を設定しました。この環境変数が無いと、`print('Hello Azure!')` など標準出力されないので注意して下さい。これで、4日程度悩みまくりました。たった1行の違いでした。。。

```
$ az webapp log config --name flask-opencv --resource-group flask-opencv --docker-container-logging filesystem
```


# 動作確認
- URL は App Service に指定したサービス名が含まれる https://flask-opencv.azurewebsites.net/ となります。
- 画像(jpeg)と処理回数(1〜100)を送信し、顔認識の画像を受信します。
- 処理回数は、性能評価用に準備したものです。今回は、10回にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/aa4c1e19-c8c7-1327-14ff-663f017b5525.png)

## 送信画像と結果画像
- 顔認識に使う画像は、一部界隈ではおなじみの顔画像を使いました。
- Heroku用アプリ、Google App Engine用アプリ、Lambda用アプリで利用した物と同じです。

![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/925ca82a-c503-8415-4655-cb3017be5bb6.png)

## ログの確認
- 今回は、約16秒でした。1回あたり、1.6秒ですね。まぁまぁかな？

```
$ az webapp log tail --name flask-opencv --resource-group flask-opencv
2019-12-06T14:53:31.507178845Z opencv version: 4.1.2, elapsed second: 16.534130334854126
```

## ssh接続
- Azure コンパネの開発ツールに ssh があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/aae63754-242e-cbf3-ee0d-33d00055ee02.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/f09309a1-8b6f-06d3-9ab0-6caf45a4b9c0.png)

## cpu確認
- Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz が 1個でした。
- `flags` に `avx` があるから、`TensorFlow` も動きそうだ。よし。

```
# cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 45
model name      : Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz
stepping        : 7
microcode       : 0xffffffff
cpu MHz         : 2199.978
cache size      : 20480 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology eagerfpu pn
i pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 popcnt aes xsave avx hypervisor lahf_lm ssbd ibrs ibpb stibp kaiser xsaveopt
bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass
bogomips        : 4399.95
clflush size    : 64
cache_alignment : 64
address sizes   : 44 bits physical, 48 bits virtual
power management:
```

## メモリ
- 2GBあるな。サンプルアプリなら十分ですね。

```
# free
              total        used        free      shared  buff/cache   available
Mem:        1963824      818372      129492       19744     1015960      865928
Swap:       1910780       98144     1812636
```

## load average
- 負荷も少なそう。App Service でデプロイした環境って、共有の仮想環境ではないのか。。。

```
# w
 15:51:18 up 3 days,  9:25,  3 users,  load average: 0.44, 0.46, 0.49
```

# おわりに
- Azure App Service で Flask と OpenCV を使う方法と動作確認をしました。
- Azure CLI でデプロイが完結出来るのは良いと思いました。
- 今回で、`Heroku` `GCP App Engine` `AWS Lambda` と `Azure App Service` での、Flask と OpenCV の環境構築シリーズは完了です。
- それぞれ、デプロイ用の設定ファイルを準備するだけで、各環境を利用出来ました。アプリ側のコードの修正は、不要でした。まぁ、当たり前かもしれませんが、確認出来たのは良かったかも。
- 本当は、`Azure Functions` を使いたかったのですが、難易度高そうだったので諦めました。
- 次は、各環境で `Keras` と `ResNet` を使ったサンプルアプリを作ろうかな？と思っております。