Challiceを使用し、Lambdaのコードを管理 ②

# はじめに
LambdaをGithubでコード管理するにあたり、chaliceを採用したため、chaliceについてまとめます。
前回の続きになります。

https://qiita.com/holdout0521/items/e6c8b12dab5728d36684

# config.jsonの設定
config.jsonは、以下の2つがわかりやすいです。

https://aws.github.io/chalice/topics/configfile.html

https://www.nooozui.com/entry/2019/10/31/223506

## VPC内でLambdaをデプロイ
VPC内でLambdaを使用する方法を説明します。

`subnet_ids`と`security_group_ids`の2つを設定するだけです。

`subnet_ids`：VPCに配置してあるサブネットのうちどのサブネットを使用するか指定する。
`security_group_ids`：lambdaに設定するセキュリティーグループを指定する

```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "dev": {
+     "subnet_ids": ["subnet-xxxxxxxxxxxxxx", "subnet-xxxxxxxxxxxxxx"],
+     "security_group_ids": ["sg-xxxxxxxxxxxxxx"],
      "automatic_layer": true,
      "layers": [
        "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxx:layer:python_layer:2"
      ]
    }
  }
}
```

## 本番と開発環境のLambdaをそれぞれデプロイ
環境別にLambdaを分けることができます。
環境ごとにLambdaが作成され、環境の名前がLambdaに以下のように反映されます。
dev：`chalice_test-dev-reservation`
prod：`chalice_test-prod-reservation`
```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "automatic_layer": true,
    "dev": {
      "layers": [
        "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:layer:python_layer:1"
      ]
    },
+   "prod": {
+     "layers": [
+       "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:layer:python_layer:2"
+     ]
+   }
  }
}
```
デプロイする際は、ステージごとにデプロイできます。

```terminal
// devをデプロイ
$ chalice deploy --stage dev --profile chalice

Creating deployment package.
Reusing existing deployment package.
Updating policy for IAM role: chalice_test-dev
Updating lambda function: chalice_test-dev-reservation
Updating lambda function: chalice_test-dev-cancel

// prodをデプロイ
$ chalice deploy --stage prod --profile chalice

Creating deployment package.
Reusing existing deployment package.
Creating IAM role: chalice_test-prod
Creating lambda function: chalice_test-prod-reservation
Creating lambda function: chalice_test-prod-cancel
```

新しく`chalice_test-prod-reservation`と`chalice_test-prod-cancel`が作成されました。

## Lambdaごとにconfig.jsonを設定する
環境ごとではなく、Lambdaごとに設定することもできます。

`lambda_functions`内に、関数名を入れることで、設定できます。

```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "automatic_layer": true,
    "dev": {
      "layers": [
        "arn:aws:lambda:ap-northeast-1:586569454024:layer:python_layer:1"
      ]
    },
    "prod": {
      "layers": [
        "arn:aws:lambda:ap-northeast-1:586569454024:layer:python_layer:2"
      ],
+      "lambda_functions": {
+        "reservation": {
+          "lambda_timeout": 120,
+          "lambda_memory_size": 256
+        }
+      }
    }
  }
}
```
`chalice_test-prod-reservation`のみタイムアウトが120 秒、メモリが256 MB、に設定されました。


# LambdaをDynamoDB等のAWSサービスにアクセスできるようにする
DynamoDBなどのAWSサービスにアクセスするため、IAMロールを自身で作成しましょう。

:::note warn
Chaliceは、IAMポリシーの自動生成機能がありますが、実装方法によっては、 本来ソースコードを解析して必要なポリシーを持つ IAM ポリシーを作成してくれるはずが、権限が足りない場合が起こりえます。
そのため、基本的には、自分でIAMポリシーを作成したほうが安全です。
:::


## boto3をインストール（アップグレード）
まず、DynamoDBで使用するライブラリとして`boto3`をインストールもしくは、アップグレードします。

```terminal
$ pip install boto3 --upgrade --user

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
awscli 1.21.2 requires botocore==1.22.2, but you have botocore 1.21.65 which is incompatible.
```
boto3をアップグレードするためには、`awscli`と`botocore`をアップグレードする必要があるようです。以下のコマンドで2つのライブラリをアップグレードできます。

```terminal
$ pip install awscli --upgrade --user

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
boto3 1.18.53 requires botocore<1.22.0,>=1.21.53, but you have botocore 1.24.22 which is incompatible.

Successfully installed awscli-1.22.77 botocore-1.24.22
```
インストールできましたので、 boto3をアップグレードしましょう。
```terminal
$ pip install boto3 --upgrade --user

$ pip list                           
Package            Version
------------------ ---------
awscli             1.22.77
boto3              1.21.22
botocore           1.24.22
```

また、`requirement.txt`にもboto3を加えましょう。

```deff_txt:requirement.txt
requests==2.27.1
boto3==1.21.22
```

## config.jsonを修正
次に`config.json`で`autogen_policy`を`false`にし、`iam_policy_file`を設定します。

**`autogen_policy`**：IAMポリシーを自動的に生成しようとするかどうかを示すブール値。falseの場合、自動生成しません。

**`iam_policy_file`**：autogen_policyがfalseの場合、policyとして、デフォルトで`.chalice/policy-<stage>.json`ファイルが適用される。
`iam_policy_file`を指定すると、読み込むpolicyファイルを指定することができる。

```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "automatic_layer": true,
    "dev": {
+     "autogen_policy": false,
+      "iam_policy_file": "policy-dev-1.json"
    },
    "prod": {
+      "autogen_policy": false
    }
  }
}
```

:::note alert
以下のように修正するとうまくいきません。
`autogen_policy`は各ステージのごとに、記載しましょう
:::

```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "automatic_layer": true,
+   "autogen_policy": false,
    "dev": {
      "iam_policy_file": "policy-dev-1.json"
    },
    "prod": {
    }
  }
}
```
`policy-dev-1.json`と`policy-prod.json`ファイルは、`.chalice`ディレクトリ配下で作成します。
Lambdaで許可するAWSサービスは、EC2, DynamoDB, SES ,CloudWatch Logsとします。

```json:.chalice/policy-dev-1.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:DeleteItem",
        "dynamodb:GetItem",
        "dynamodb:Scan",
        "dynamodb:Query",
        "dynamodb:UpdateItem"
      ],
      "Resource": "arn:aws:dynamodb:ap-northeast-1:xxxxxxxxxxxxxxxxxxxx:table/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DetachNetworkInterface",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["ses:SendEmail", "ses:SendRawEmail"],
      "Resource": "*"
    }
  ]
}
```
```json:.chalice/policy-prod.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "dynamodb:PutItem",
        "dynamodb:DeleteItem",
        "dynamodb:GetItem",
        "dynamodb:Scan",
        "dynamodb:Query",
        "dynamodb:UpdateItem"
      ],
      "Resource": "arn:aws:dynamodb:ap-northeast-1:xxxxxxxxxxxxxxxxxxxx:table/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DetachNetworkInterface",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["ses:SendEmail", "ses:SendRawEmail"],
      "Resource": "*"
    }
  ]
}
```

:::note warn
AWSサービスを使用する上で、どの権限が必要かどうか知る方法の一つを挙げます。
`"autogen_policy": false`を設定する前に、とりあえずコードを書いてデプロイした後、LambdaのIAMポリシーが自動生成されますので、そのポリシーを確認することで、どの権限が必要かどうか確認できます。
:::

デプロイする際は、ステージごとにデプロイします。

```terminal
$ chalice deploy --stage dev --profile chalice

$ chalice deploy --stage prod --profile chalice
```

# GitHubにアップ
ローカルの`chalice_test`ディレクトリ名を`_chalice_test`に変更します。

```terminal
$ mv chalice_test _chalice_test
```
そして、GitHubに`chalice_test`というリポジトリを作成し、クローンします。
`chalice_test`ディレクトリに、`_chalice_test`内のファイルを全て移動させます。

後は、.gitignoreにGitHubにアップしないファイルやディレクトリを記載します。

```.gitignore:.gitignore
.chalice/deployments/
.chalice/venv/
chalicelib/__pycache__
__pycache__
```
最後に、コミットし、プッシュするとGitHubにアップできます。

chaliceを使うにあたり、色々な知見をまとめました。
Lambdaのみをコード管理するために、chaliceを採用する機会が増えそうです。

# 参考

https://qiita.com/SwuBHj8aKGqBKHet/items/08777ad2da3e5d01bfc3