Vue+Flask on DockerのWebアプリケーションをAzureにデプロイする

#はじめに
とあるシステムのプロトタイプを作成した時のメモとなります。Vue+Flaskで構成されたDocker環境で動くSPAのアプリを作成し、WebアプリケーションをAzure App Serviceとしてデプロイしました。特にAzure関係の参考記事が少なく苦労したので紹介したいと思います。また、自分はM1のMacを使用していてDockerがpreview版だったのでそこでも躓きました笑(※今は正式版がでました）

#環境
- Mac OS : 11.1
- docker desktop : 3.3.1
- yarn : 1.10.1
- node : v10.13.0
- python : 3.6.13

#ローカルにデプロイ
まずVue+Flaskのアプリケーションをローカルで実装します。
[参考記事](#参考記事)の記事を参考にさせていただきました。

サンプルプログラムは[ここ](https://github.com/toriitorippy/Vue_Flask_App_Template)にあげました。

主なファイルの構成は以下の通りです(一部省略）。

```
Vue_Flask_App_Template/
  ┝ backend/
     ┝ app.py
     ┝ Dockerfile
     └ requirements.txt
  ┝ frontend/
     ┝ src/
        └ main.js
     ┝ util
     ┝ Dockerfile
     └ vue.config.js
  ┝ docker-compose.yml
```
フロントエンドのvueコンテナとバックエンドのFlaskをそれぞれ作成しています。
ローカルではdocker-composeを使ってデプロイしました。

```docker-compose.yml
version: '3'
services:
  vue:
    build: ./frontend
    ports:
      - "8080:8080"
    volumes:
      - .:/shared
    working_dir: /shared/frontend
    stdin_open: true
    tty: true
    depends_on:
      - flask
    command: yarn run serve

  flask:
    build: ./backend
    ports:
      - "5000:5000"
    volumes:
      - .:/shared
    working_dir: /shared/backend
    environment:
      FLASK_DEBUG: 1
      FLASK_APP: ./app.py
    tty: true
    command: python app.py
```
以下のコマンドでデプロイします。

```
docker-compose run vue yarn
docker-compose build
docker-compose up vue
```
デプロイが成功すると簡単ですが以下のようなWebアプリケーションができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/7fcb4342-1c06-a2ec-a823-e4b595107ad3.png" width=70%>
簡単なアプリケーションですが、backend側で作成したランダムな数をフロントエンド側で描写するというものが完成しました。

# Azureにデプロイ
[課題](#課題)にも書きますが、複数コンテナーが現在プレビュー段階のため試行錯誤しましたがうまくいきませんでした。そのため、今回はbackend側とfrontend側で二つのWebアプリケーションを作成し通信するという方法を取りました。

### Dockerfile作成
frontend側とbackend側のDockerfileはそれぞれ以下の通りです。

```
# backend/Dockerfile

FROM python:3.6

WORKDIR /app

ADD . /app

RUN pip install --trusted-host pypi.python.org -r requirements.txt

EXPOSE 80

CMD ["python", "app.py"]
```

```
# frontend/Dockerfile

FROM node:10.13.0

WORKDIR /frontend

COPY . /frontend

RUN yarn install

EXPOSE 80

CMD ["yarn", "run", "serve"]
```

### Portの変更
Azure App Serviceではカスタムコンテナーがポート80で立ち上がるのを前提としているため、backendとfrontendのポートをどちらも80に設定し直します。具体的には、app.py内のport、vue.config.js内のportを現状のものから変更します。

```backend/app.py
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80, debug=True)
```
```frontend/vue.config.js
module.exports = {
    assetsDir: 'static',
    devServer: {
      port: 80,
      host: '0.0.0.0',
      disableHostCheck: true,
    },
  };
```
### イメージのビルドと格納を行う
[公式のドキュメント](https://docs.microsoft.com/ja-jp/learn/modules/deploy-run-container-app-service/3-exercise-build-images?pivots=csharp)を参考に、Azure Container Registryを使用してイメージのビルドと格納を行います。

まずAzureのポータルでAzure Container Registryをよしなに作成します。
その後、backendのディレクトリに入り、次のコマンドを実行します。container_registry_nameはレジストリの[ログインサーバー]から取得できます。
このコマンドにより、フォルダーの内容がAzure Container Registryに送信され、Dockerファイルの手順に従ってイメージがビルドされ格納されるらしいです。

```
az acr build --registry <container_registry_name> --image <image_name> .
```
frontend側も同様ですが、APIのURLにbackendで出来上がったURLを指定しなくてはいけないため、後ほど行います。

### Webアプリを作成してデプロイ
同じく[公式のドキュメント](https://docs.microsoft.com/ja-jp/learn/modules/deploy-run-container-app-service/5-exercise-deploy-web-app?pivots=csharp)を参考にします。

Azureのポータルから [リソースの作成]＞[Webアプリ]＞[作成] でWebアプリを作成します。
設定の一部を記載します。

- 公開:Dockerコンテナー
- オプション:単一コンテナー
- イメージソース:Azure Container Registry
- レジストリ:container_registry_name（先ほど作成したレジストリ）
- イメージ:image_name（先ほど作成したイメージ）

最後に[確認と作成]を選択し、[作成]します。Webアプリがデプロイするまでしばらくかかります。
できたら[参照]からデプロイされているのを確認します。

### frontend側のデプロイ
まず、先ほどはAPI通信にlocalhostのURLをしていしていたので変更します。
~~~の部分には先ほど作成したbackendのURLを指定します。

```frontend/const/util.py
export const API_BASE_URI= "<~~~.azurewebsites.net>"
```
後は同様の手順でfrontend側のWebアプリケーションも作成します。
frontendのディレクトリに移動し、以下のコマンドを叩きレジストリに格納後、Azure App Serviceでデプロイします。

```
az acr build --registry <container_registry_name> --image <image_name> .
```

### 完成！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/5e46ea44-d317-e513-f3ef-c65a2cefbe00.png" width=60%>

#躓いたポイント
兎に角ポートの設定がめんどうでした。ポート番号を80以外で設定してしまうと簡単にはいかないみたいです。
また、以前使っていた```docker push```を使用する方法([参考](https://docs.microsoft.com/ja-jp/azure/app-service/tutorial-custom-container?pivots=container-linux))がプレビュー版を使っているせいか使えなくなっていたため、今回別の手段を使用しました。

#課題
複数コンテナー(docker-compose)でデプロイできなかったのが今回の課題です。二つのWebアプリケーション間で通信を行なっているため、規模が大きくなるとどうしても重くなってしまいます。
NginxコンテナーやWeb App for Containersを使えばうまくできそうなので次は試してみようと思います。ご存知の方は教えていただければ幸いです。

#最後に
今回は、VueとFlaskで作ったDockerのアプリケーションをAzureでデプロイをしました。
公式のドキュメンテーションも充実していますが、抽象的だったり、カスタムに対応していなかったりして苦労しました。
少しでも参考になれば幸いです。
手探りで行っていたため、足りないところがあると思います。間違っている点などがあれば教えていただければと思います。

#参考記事
[ローカルを汚さずdockerを使ってvue.jsの開発環境を作る[vuecli4]](https://qiita.com/rh_taro/items/ca08b930f704275286a4)
[Vue.jsとFlaskのDocker環境を構築してみた](https://qiita.com/kouchanne/items/417bad58633cc4262012)
[Vue.js+Flaskで画像のアップロード機能](https://qiita.com/rockguitar67/items/f8edc33dd221d8f4e965)