4. FlaskアプリをAWSにデプロイ

[zappa](https://github.com/zappa/Zappa) を利用して
Flaskアプリのデプロイを行います…が
localhost上のMySQLに接続する実装だったためDBへのアクセスはできません。
環境変数やParameterStore, SecretsManagerを利用してDBのURLを切り替える必要があります。

VPCの勉強終わったら更新します…

---

### AWS Cliの準備

```bash
aws configure
```

APIキーを取得し、aws cliが使用できる状態にします。

# zappaインストール

```bash
(venv) $ pip install zappa
```

Flaskプロジェクトにzappaをインストールします。

# zappa初期化

全部デフォルトでOKです。

```bash
(venv) $ zappa init

███████╗ █████╗ ██████╗ ██████╗  █████╗
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗
  ███╔╝ ███████║██████╔╝██████╔╝███████║
 ███╔╝  ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║
███████╗██║  ██║██║     ██║     ██║  ██║
╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝     ╚═╝  ╚═╝

Welcome to Zappa!

Zappa is a system for running server-less Python web applications on AWS Lambda and AWS API Gateway.
This `init` command will help you create and configure your new Zappa deployment.
Let's get started!

Your Zappa configuration can support multiple production stages, like 'dev', 'staging', and 'production'.
What do you want to call this environment (default 'dev'): 

AWS Lambda and API Gateway are only available in certain regions. Let's check to make sure you have a profile set up in one that will work.
Okay, using profile default!

Your Zappa deployments will need to be uploaded to a private S3 bucket.
If you don't have a bucket yet, we'll create one for you too.
What do you want to call your bucket? (default 'zappa-k7rlsxrxg'): 

It looks like this is a Flask application.
What's the modular path to your app's function?
This will likely be something like 'your_module.app'.
We discovered: app.app
Where is your app's function? (default 'app.app'): 

You can optionally deploy to all available regions in order to provide fast global service.
If you are using Zappa for the first time, you probably don't want to do this!
Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]: 

Okay, here's your zappa_settings.json:

{
    "dev": {
        "app_function": "app.app",
        "aws_region": "ap-northeast-1",
        "profile_name": "default",
        "project_name": "flask-sample",
        "runtime": "python3.9",
        "s3_bucket": "zappa-k7rlsxrxg"
    }
}

Does this look okay? (default 'y') [y/n]: 

Done! Now you can deploy your Zappa application by executing:

        $ zappa deploy dev

After that, you can update your application code with:

        $ zappa update dev

To learn more, check out our project page on GitHub here: https://github.com/Zappa/Zappa
and stop by our Slack channel here: https://zappateam.slack.com

Enjoy!,
 ~ Team Zappa!

```

`zappa_settings.json`が作成され、
デフォルトでは`dev`という名前で環境が作成されます。
'dev', 'staging', 'production'など、複数の環境を切り替えてデプロイできます。

# zappa用ロールを設定。APIGatewayにCognitoを追加

Cognitoの設定は任意です。

```jsonc
{
  "dev": {
    "app_function": "app.app",
    "aws_region": "ap-northeast-1",
    "profile_name": "default",
    "project_name": "flask-example",
    "runtime": "python3.9",
    "s3_bucket": "zappa-7fq7rplg7",

    // ロールの設定
    // https://github.com/zappa/Zappa#using-custom-aws-iam-roles-and-policies
    "manage_roles": false,
    "role_name": "flask-example-dev-ZappaLambdaExecutionRole",

    // Cognitoの設定
    // https://github.com/zappa/Zappa#cognito-user-pool-authorizer
    "authorizer": {
      "type": "COGNITO_USER_POOLS",
      "provider_arns": [
        "arn:xxx"
      ],
      "token_header": "Authorization"
    }
  }
}
```

`"manage_roles": true`にするとzappa用ロールを自動で作成します。
私のaws cliのユーザーにはロール作成権限がなかったせいかエラーになりました。

# zappa用IAMユーザーのポリシー

ネット上の記事を参考にしました。
必要以上の権限が付与されているかもしれませんが、私はこれでデプロイまでできました。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AttachNetworkInterface",
                "ec2:CreateNetworkInterface",
                "ec2:DeleteNetworkInterface",
                "ec2:DescribeInstances",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DetachNetworkInterface",
                "ec2:ModifyNetworkInterfaceAttribute",
                "ec2:ResetNetworkInterfaceAttribute"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": "arn:aws:s3:::*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "kinesis:*"
            ],
            "Resource": "arn:aws:kinesis:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:*"
            ],
            "Resource": "arn:aws:sns:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sqs:*"
            ],
            "Resource": "arn:aws:sqs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:*"
            ],
            "Resource": "arn:aws:dynamodb:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "route53:*"
            ],
            "Resource": "*"
        }
    ]
}
```

# デプロイ実行

```bash
(venv) $ zappa deploy dev  
Calling deploy for stage dev..
Downloading and installing dependencies..
 - pyrsistent==0.18.1: Downloading
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 116k/116k [00:00<00:00, 1.13MB/s]
 - markupsafe==2.0.1: Downloading
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30.6k/30.6k [00:00<00:00, 2.31MB/s]
 - greenlet==1.1.2: Downloading
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 169k/169k [00:00<00:00, 1.42MB/s]
Packaging project as zip.
Uploading flask-example-dev-1643965354.zip (18.4MiB)..
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 19.3M/19.3M [00:48<00:00, 398kB/s]
Waiting for lambda function [flask-example-dev] to become active...
Scheduling..
Scheduled flask-example-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading flask-example-dev-template-1643965474.json (1.6KiB)..
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.65k/1.65k [00:00<00:00, 11.7kB/s]
Waiting for stack flask-example-dev to create (this can take a bit)..
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:16<00:00,  4.02s/res]
Deploying API Gateway..
Waiting for lambda function [flask-example-dev] to be updated...
Deployment complete!: https://xxxxx.ap-northeast-1.amazonaws.com/dev
```

これでデプロイ完了です。

`Deployment complete!: https://xxxxx.ap-northeast-1.amazonaws.com/dev`

にあるURLをブラウザで開けばSwaggerUI画面が開きます。

# 何が行われたのか？

`zappa deploy dev`の裏でなにが起こっているのか？

その辺の情報は[zappa](https://github.com/zappa/Zappa)を見てもあまり書いていないようなので
デプロイするまでに奮闘した経験から書かせてもらいます。

1. `zappa deploy dev`を実行
1. `zappa_settings.json`からCloudFormationテンプレートを作成
1. テンプレートとFlaskアプリをzipファイルにまとめる
1. zipファイルをzappa用ロールを利用してs3にアップロード
1. CloudFormationテンプレートからAPI Gateway, Lambdaなどを作成

という感じのようです。
Flaskアプリは基本的にport: 5000でリクエストを待ち受けるのにどうやってポート変換しているんだろう…。
CloudFormationの知見が不足しているのでそれは後々追っていくことにします。

FlaskだけではなくDjangoやDockerコンテナもzappaを使用してデプロイできます。

`zappa invoke dev "app.db.upgrade"`のようなinvokeコマンドを利用するとデプロイしたアプリの
関数を実行でき、DBのマイグレーションもこれで実行できました。

# その他zappaコマンド

```bash
zappa undeploy    # デプロイしたアプリをAWSから削除
zappa update dev  # デプロイ済みのアプリを更新
zappa tail        # ログの表示。デプロイエラー時の調査などに。CloudWatchのログを表示しているだけ？
```

- [1. Flask 環境構築、簡単なバックエンドサーバー実装](https://qiita.com/YamaguchiRyuta/items/69ab10959767e27b8787)
- [2. FlaskでOpenAPI定義の自動出力、Swagger UIページの作成](https://qiita.com/YamaguchiRyuta/items/617c0299ea7277af4d9b)
- [3. FlaskでORM + MigrationでSQLを一切書かずにDBのCRUDや移行を行う](https://qiita.com/YamaguchiRyuta/items/dd88089b2fd41ea91745)
- [4. FlaskアプリをAWSにデプロイ](https://qiita.com/YamaguchiRyuta/items/ffc64586a40dd9a76742) <-