Function Compute でコンテナの Web アプリを作る

## はじめに
[Function Compute](https://www.alibabacloud.com/help/product/50980.htm) の Web アプリ機能でコンテナを起動できるようになったので、試してみます。

> Migrate Web applications to Function Compute
>>https://www.alibabacloud.com/help/doc-detail/161884.htm

Function Compute では HTTP、[OSS](https://www.alibabacloud.com/help/product/31815.htm) トリガーで起動する関数や、時間駆動で起動する関数を作成することができ、Web アプリ機能では、Python、Java 等の Web アプリを作成することができます。


## アーキテクチャ

![t_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/4c7986c0-5911-f6b3-c672-7425e4ce4ff5.png)

GitHub に Dockerfile 及びソースコードをプッシュすると、[Container Registry](https://www.alibabacloud.com/help/product/60716.htm) が Webhook 経由でプッシュを検知し、Dockerfile からコンテナイメージをビルドします。

> Build an image for a Java application by using a Dockerfile with multi-stage builds
>> https://www.alibabacloud.com/help/doc-detail/173175.htm

Function Compute は指定した Container Registry のイメージリポジトリからイメージを取り出し、コンテナをデプロイ及び起動します。デプロイ進捗を割合で指定することもでき、カナリアデプロイを実現できます。

> Manage web applications
>> https://www.alibabacloud.com/help/doc-detail/162396.htm


### Special Thanks
本記事を作成するにあたり、SB Cloud 様のブログを参考にさせて頂きました。

> Alibaba Cloud Container Registry (ACR) でコンテナイメージのビルドパイプラインを実装する
>> https://www.sbcloud.co.jp/entry/2020/04/01/container_registry

では手順を説明していきます。


## 1. GitHub にユーザーを作成する

GitHub のソースコードリポジトリとソースコードリポジトリを読み取る `Admin` 権限を持つユーザーを作成します。

今回はソースコードリポジトリ `alieaters-handson-serverless` とユーザー `xxx-develop` を作成します。

![t_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/1c71d641-1997-4fb0-8dd1-3fb0c1c51ec6.png)


## 2. Container Registry に名前空間を作成する

イメージリポジトリを設置する名前空間をまず作ります。

Alibaba Cloud コンソールにログインし、Container Registry の Namespaces を選択します。

上の写真は名前空間 `tech4all` を作成した状況です。

![t_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/b9d1873d-3008-6b4b-2a2c-16b4aa85d33a.png)

![t_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/21f3a63d-7930-ea57-e45d-693ec231925c.png)


## 3. Container Registry にイメージリポジトリを作成する

イメージリポジトリを作成します。

Container Registry の Repositories を選択します。

写真はイメージリポジトリ `tech4all` を作成した状況です。

![t_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/fcdfcafd-1437-bdc2-ba31-deafd80d2268.png)

Function Compute からイメージリポジトリを使用するため、`Public` にします。

※公式ドキュメントには記載がないですが、Function Compute の画面に `Public` にする旨が記載されています。

![t_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/02a9ddd3-b3b2-5f98-45e4-5aa97918883f.png)

GitHub のソースコードリポジトリとソースコードを読み取る GitHub ユーザーを指定します。

![t_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/e90c3d0b-ff1a-31fc-cb5d-b5113902369a.png)

イメージリポジトリが作成されると、自動で GitHub の Webhooks に Container Registry のエンドポイントが追加されます。

![t_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/71647ef5-c22c-9ce5-e6fb-80af8556d054.png)


## 4. Container Registry のイメージリポジトリにビルドルールを作成する

ソースコードリポジトリを起点とするビルドルールを作成します。

Container Registry の Repositories の `tech4all` を選択し、Build を選択します。

今回はソースコードリポジトリの `master` ブランチのルートディレクトリに `Dockerfile` があり、`latest` タグでコンテナイメージをビルドするルールを作成します。

上の写真はビルドルールを作成済みの状態です。

![t_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/046451cd-dc2d-d1c8-2bec-3da8f0c6d44b.png)

![t_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/0ed1a355-a702-a6b9-495e-ff7e462fc4df.png)


## 5. Web アプリを開発する

今回は Flask を使用した Web アプリを開発します。

### ディレクトリ構成
```
/
├── app.py
├── Dockerfile
├── requirements.txt
└─app
    ├── __init__.py
    └── templates
        └── index.html
```

```app.py
import os

from app import create_app

# 環境変数 APP_ENV, APP_KEY, DEBUG を使用
app = create_app(os.environ['APP_ENV'])
app.secret_key = os.environ['APP_KEY']

# 9000 ポートを開放する
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9000, debug=bool(os.environ['DEBUG']))
```

```Dockerfile.txt
FROM python:3.6
EXPOSE 9000
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
```

```requirements.txt
flask
```

```app/__init__.py
from flask import Flask, render_template


def create_app(env):
    app = Flask(__name__)

    @app.route('/', methods=['GET'])
    def index():
        return render_template('index.html')

    return app
```
```app/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>close-clothes</title>
</head>
<body>
{% block content %}
    <form action="/user" method="post">
        <div>
            <input type="text" name="user_name" size="6">
            <select name="role_name">
                <option value="admin">admin</option>
            </select>
            <button>join us!</button>
        </div>
    </form>
{% endblock %}
</body>
</html>
```


## 6. Function Compute に Web アプリを作成する

Function Compute の Application Center を選択します。

Web アプリのフレームワークに Container を選択します。

![t_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/4079ea39-8e72-c8f3-e413-ab054ef42939.png)

注意点は以下の通りです。

1. Container Image
    - イメージリポジトリ `tech4all` の `latest` タグを選択します
1. Start Command
    - Docker の CMD コマンドにあたりますので、 Dockerfile には CMD を記載しないようにします
1. Listener Port
    - Python は 9000 ポートでしか Function Compute が受け付けていませんので 9000 を指定します
1. Environment Variables
    - Web アプリに渡したい環境変数を記載します
1. Application Domain Name
    - Alibaba Cloud DNS を使用したカスタムドメインを使用する場合は Customize Domain Name を選択します

![t_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/b6f1f567-976c-efcb-ca50-c3b675b30db4.png)

デプロイが始まるとログが流れ始めます。

![t_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/6303ce81-8f7e-be5f-e9cd-68c9498a7759.png)


## 7. 関数のポートを 9000 に指定する

関数が出来上がっているので、関数で受け付けるポートに 9000 を指定します。

Function Compute の Services and Functions の `tech4all` を選択します。

![t_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/8fcb29d6-6d58-52c8-fa36-1967ec160abb.png)

写真は既に 9000 に指定した状態です。

![t_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/6aac335b-61f8-fc8e-4270-13a502ed1f82.png)

ポートは関係ないですが、Memory は 1024MB 以上でないと起動しないようになっています。

![t_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/0ab04778-33b0-ae6f-f227-5dde7f1d54c0.png)


## 8. HTTP メソッドを編集

Web アプリで受け付ける HTTP メソッドを編集します。

Function Compute の Services and Functions の `tech4all` を選択します。

![t_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/edfe724f-8e52-772b-ef1f-061021405d10.png)

受け付ける HTTP メソッドを選択します。

![t_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/d7406b05-dd3b-1605-9443-c1c0c36714be.png)


## 9. Web アプリにアクセス

Web アプリの準備が整ったので、早速アクセスしてみます。

Function Compute の Application Center の `tech4all` を選択します。

![t_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/67e0c6a4-c002-571e-ef2c-939a0756717b.png)

Domain Name が出来上がっているのでアクセスします。

![t_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/0da1df67-b588-467a-0f91-317f6f0983df.png)

寂しい画面ですが、HTML が表示されました！

![t_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/626deb39-768d-973e-243f-3482fb1ebff1.png)

アクセスログ及びアプリログは Application Center で確認できます。

![t_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/cdcb9b80-f4a0-922a-1288-3618f6891ac7.png)


## 【appendix】カナリアデプロイを実現する

アプリのデフォルトバージョンに対するカナリアリリースバージョンの割合を設定し、カナリアデプロイを実現します。

Function Compute の Application Center の `tech4all` を選択します。

カナリアデプロイを実現したいバージョンの Set to Canary Release Version を選択します。

![t_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/cfff95f1-5ca7-124e-69e0-eef5c247f163.png)

Canary Release Weight の割合をスライドします。

![t_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57642/54a93c63-4e16-4f10-dc2d-047fd27a5f23.png)


## おわりに
[Elastic Container Instance](https://www.alibabacloud.com/help/product/87486.htm) や [Kubernetes](https://www.alibabacloud.com/help/product/85222.htm) のコンテナ専用の実行環境を使用しなくても、Web アプリを作成することができました。

これらのサービスと Function Compute との使い分けは、アプリの規模であると考えられます。

互いに連携しあうコンテナが複数ある場合や、同一コンテナを複数起動する場合は、コンテナの管理が煩雑になるため、先に上げたコンテナ専用サービスを使用することが望ましいです。

Function Compute にはアプリの呼び出しを監視する機能やデプロイ機能もあるため、単一コンテナを起動させたい場合にお勧めです。