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

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

chaliceのメリットとして、以下が挙げられます。
- シンプルにLambdaをコード管理できる
- デプロイがSAMに比べて、簡易でデプロイが早い
- 今回は、Lambdaのみですが、API Gateway、S3, SNS, SQSといったAWSサービスも利用できる
- IAM ポリシーを自動生成してくれる

今回は、APIGatewayは、使用せず、複数のLambdaのコード管理をするために、Chaliceを使用します。

# IAMユーザーの設定
ローカルのmacのターミナルで`chalice`をインストールします。

```terminal
$ python -V
Python 2.7.18

$ pip install chalice


$ chalice --version
chalice 1.26.0, python 3.9.2, darwin 21.3.0
```

ChaliceでLambdaをアップするためには、アップするAWSアカウント内でIAMユーザーを作成し、アクセスキーとシークレットキー等の認証情報をローカルの`「~/.aws/credentials」`と`「~/.aws/config」`に保存する必要があります。

IAMポリシーを以下の権限で作成します。
可能であれば、送信元IP制限すると、セキュリティ面で良いかと思います。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:GetPolicyVersion",
                "iam:GetPolicy",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "iam:ListRoles",
                "iam:CreateRole",
                "iam:PutRolePolicy",
                "iam:PassRole",
                "iam:ListAttachedRolePolicies",
                "iam:DeleteRolePolicy",
                "lambda:*",
                "logs:GetLogEvents",
                "logs:FilterLogEvents",
                "iam:ListRolePolicies",
                "iam:GetRolePolicy"
            ],
            "Resource": "*"
        }
    ]
}
```
このポリシーをアタッチしたIAMユーザーを作成します。
![スクリーンショット 2022-03-20 15.14.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/0b9ea076-f393-6c0c-d33e-38d672329e5e.png)

# 名前付きプロファイル設定
ローカルで名前を指定したプロファイルを作成します。
defaultとは別に、名前を`chalice`としたプロファイルを作成します。
`Config プロファイル`には、プレフィックスに`profile`をつけることを忘れないようにしましょう。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html

アクセスキー と シークレットキー 、regionを記入します。

```
$ sudo vim ~/.aws/config
[default]
region = ap-northeast-1
output = json

[profile chalice]
region = ap-northeast-1
output = json
source_profile = kimotsuki_taxi_chalice


$ sudo vim ~/.aws/credentials
[default]
aws_access_key_id=xxxxxxxxxxx
aws_secret_access_key=xxxxxxxxxxx

[chalice]
aws_access_key_id=xxxxxxxxxxx
aws_secret_access_key=xxxxxxxxxxx
```
# Chaliceのプロジェクト作成

プロジェクト名を`chalice_test`とし、作成します。
```terminal
$ chalice new-project chalice_test
Your project has been generated in ./chalice_test
$ cd chalice_test 

chalice_test $ ls -la

.chalice
.gitignore
app.py
requirements.txt
```

今回は、API Gatewayは使用しないため、`app.py`ファイルを以下のように修正します。

```terminal
$ vim app.py
```

```diff_python:app.py
from chalice import Chalice
- app = Chalice(app_name='helloworld')
+ app = Chalice(app_name='chalice_test')

- @app.route('/')
- def index():
-     return {'hello': 'world'}

+ @app.lambda_function(name='reservation')
+ def reservation(event, context):
+     return dispatch(event)
```

# chalice デプロイ
デプロイは、ローカル端末で `chalice deploy` コマンドを実行するだけです。

「~/.aws/credentials」と「~/.aws/config」で設定されている AWS の認証情報を元に、 Chalice がデプロイを行います。

なお、 Chalice を使う場合には「Lambda 用の IAM ロールにどういったポリシーを付与すべきか」を考慮する必要はありません。
 Chalice では API の内容を自動的に判断して、最小限のポリシーを付与してくれるようです。


デプロイのコマンドには、プロファイル名の`chalice`を指定して、デプロイしましょう。
`--profile chalice`がない場合、プロファイル名`default`で設定されたAWSアカウントに対してデプロイされてしまいます。

```terminal
chalice_test　　$ chalice deploy --profile chalice

Creating deployment package.
Updating policy for IAM role: chalice_test-dev
Creating lambda function: chalice_test-dev-reservation
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chalice_test-dev-reservation

$ tree -la
.
├── .chalice
│   ├── config.json
│   ├── deployed
│   │   └── dev.json
│   └── deployments
│       └── f7623481a504b5abd265324b4bc6f003-python3.9.zip
├── .gitignore
├── __pycache__
│   └── app.cpython-39.pyc
├── app.py
└── requirements.txt
```
`chalice_test-dev-reservation`というLambdaが作成されました。

![スクリーンショット 2022-03-20 16.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/03b43e58-9404-50f9-ceab-cab1360189df.png)

# Lambdaの開発
Lambdaの開発についてですが、基本app.pyではコードは最小限にし、ルートディレクトリ配下に`chalicelib`を作成し、その配下に`reservation.py`ファイルを作成し、このファイルにコードを書いて開発します。

```terminal
$ ls
__pycache__		app.py			requirements.txt
$ mkdir chalicelib
$ cd chalicelib 
$ touch reservation.py
$ ls
reservation.py

$ vim reservation.py
```

```py:reservation.py
def dispatch(event):
  return { "test" : "reservation"}
```
```terminal
$ vim app.py
```

```diff_python:app.py
from chalice import Chalice
+ from chalicelib.reservation import dispatch
app = Chalice(app_name='chalice_test')

@app.lambda_function(name='reservation')
def reservation(event, context):
    return dispatch(event)
```
`from chalicelib.reservation import dispatch`の意味は、「`chalicelib`ディレクトリ配下の`reservation.py`ファイルの`dispatch`関数を使用する」を表しております。

それでは、ルートディレクトリ内で、デプロイ後、テストしましょう。

```terminal
$ cd ..               
chalice_test $ chalice deploy --profile chalice
```
テストを実行すると、問題なく実行されました。
```json:Response
{
  "test": "reservation"
}
```
![スクリーンショット 2022-03-20 16.38.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/a25ad204-2577-e702-7f90-947de0d0b5e8.png)

## 別のLambdaを作成
もう一つLambdaを作成しましょう。
先程と同様に、`chalicelib`配下にファイルを作成します。

```terminal
$ cd chalicelib 
$ touch cancel.py
$ ls
cancel.py

$ vim cancel.py
```

```py:cancel
def cancel_for_user(event):
  return { "test" : "cancel"}
```
```terminal
$ vim app.py
```
```diff_python:app.py
from chalice import Chalice
from chalicelib.reservation import dispatch
+ from chalicelib.cancel import cancel_for_user
app = Chalice(app_name='chalice_test')

@app.lambda_function(name='reservation')
def reservation(event, context):
    return dispatch(event)

+ @app.lambda_function(name='cancel')
+ def cancel(event, context):
+     return cancel_for_user(event)
```
ルートディレクトリ内で、デプロイ後、テストしましょう。
```terminal
$ cd ..               
chalice_test $ chalice deploy --profile chalice
```
新しく作成された`chalice_test-dev-cancel`Lambdaのテストをすると、問題なく実行されました。
```json:Response
{
  "test": "cancel"
}
```

![スクリーンショット 2022-03-20 16.48.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/30a93e89-857a-bbce-0815-91c3eeb9af78.png)


# Lambda layer
Lambdaでは、`Lambda Layer`という、ライブラリや自作モジュールを複数のLambdaに適用できる機能がありますが、chaliceでも使用できます。
Lambda_layerを使用するパターンとして、`自作モジュール`と`外部ライブラリ`の2パターンがありますので、説明していきます。

## 自作モジュールの場合
自作モジュールでは、2パターンあります。
### AWSコンソールのLambda_layerにアップ
1つ目は、作成した自作モジュールを AWSコンソール のLambda_layerにアップする方法です。

Lambda_layerをアップする方法は、以前記事を書きましたので、参考にするとLambda_layerにアップできます。

https://qiita.com/holdout0521/items/58a968cc940051bd2fbd

アップすると、コンソールのレイヤーから、ARNが発行されますので、コピーしておきます。

![スクリーンショット 2022-03-21 10.25.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/6b1d49dc-fbed-54fc-c8de-cafe296e406b.png)


`.chalice/config.json`ファイルに、`layers`を加え、先程コピーしたARNを貼り付けます、

```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "dev": {
-      "api_gateway_stage": "api"

+      "layers": [
+        "arn:aws:lambda:ap-northeast-1:************:layer:python_layer:1",
+        "arn:aws:lambda:ap-northeast-1:************:layer:test:1"
+      ]
    }
  }
}
```

これで、作成した自作モジュールを使用できます。
ただし、この方法ですと、自作モジュールを修正するたびに、アップし直す必要があり、手間がかかる場合があります。
その場合は、2つ目の方法が簡単です。

### 簡易Lambda_layerをchalice内で作成
自作モジュールは、chalice内の複数のLambdaにしか適用しない場合、こちらの方法がおすすめです。

個人的には、自作モジュールの場合、`chalicelib`ディレクトリ配下に`lambda_layer.py`ファイルを作成し、そこにまとめると良いかと思います。

適当に、lambda_layerの自作モジュールを作成します。
```
$ vim chalicelib/lambda_layer.py
```
```py:lambda_layer.py
from datetime import datetime, timedelta

def week_day():
    dateJST = datetime.today() + timedelta(hours=9)
    weekday = dateJST.strftime('%a')
    return weekday
```

```terminal
$ vim chalicelib/reservation.py
```

```diff_python:reservation.py
+ from chalicelib.lambda_layer import week_day
def dispatch(event):
-  return { "test" : "reservation"}
+  return {"test" : week_day()}
```
デプロイし、テストしましょう。
```terminal             
chalice_test $ chalice deploy --profile chalice
```
問題なく実行されました。

```json:Response
{
  "test": "Sun"
}
```
chalice内の複数のLambdaにしか使えませんが、修正が楽ですね。

## 外部ライブラリの場合

例として、外部ライブラリの`requests`を使用する方法について説明します。
まず`pip list`で、requestsライブラリがインストールされていないか確認します。

### 外部ライブラリインストール

```terminal
$ pip list
```

インストールされていない場合、インストールしましょう

```
$ pip install requests

Successfully installed requests-2.27.1

WARNING: You are using pip version 21.2; however, version 22.0.4 is available.
You should consider upgrading via the '/usr/local/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip' command.
```

---

### インストール時の警告

:::note warn
WARNINGとして、`pip`のバージョンをインストールするように出ておりますが、アップグレードしないようにしましょう。
Chalice1.26.0 は、pip < 21.3 もしくは、pip >= 9 に依存しています。
:::

pip を 最新バージョン (22.0.4) にした場合、以下のエラーが発生します。

```
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.
chalice 1.26.0 requires pip < 21.3, >= 9, 
but you have pip 22.0.4 which is incompatible.
```

このエラーの対処として、pip を 21.2までダウングレードしましょう。

```terminal
$ pip install --upgrade pip==21.2 
```

---
では、本題に戻ります。

`requirements.txt`に`requests`を加えます。イコールは2つですので、注意してください。`==`

```diff_txt:requirements.txt
requirements.txt==2.27.1
```

下記のサイトでも`requests`の最新バージョンが確認できます。

https://pypi.org/project/requests/

reservation.py で requestsを使用します。

```diff_python:reservation.py
from chalicelib.lambda_layer import week_day
+ import requests

+ request_url = "https://qiita.com/"
+ requests_get = requests.get(request_url)
+ status_code = requests_get.status_code

def dispatch(event):
  return {
    "test" : week_day(),
+     "module": status_code
  }
```

Lambda layer にまとめるには `.chalice/config.json` に以下の通り automatic_layer: true を記載します。


```diff_json:config.json
{
  "version": "2.0",
  "app_name": "chalice_test",
  "stages": {
    "dev": {
+     "automatic_layer": true,
      "layers": [
        "arn:aws:lambda:ap-northeast-1:************:layer:python_layer:1",
        "arn:aws:lambda:ap-northeast-1:************:layer:test:1"
      ]
    }
  }
}
```

デプロイして、テストを実行しましょう。

```terminal
$ chalice deploy --profile chalice
Creating shared layer deployment package.
Creating app deployment package.
Creating lambda layer: chalice_test-dev-managed-layer
Updating policy for IAM role: chalice_test-dev
```
Lambda layerが作成されていることが分かります。

```json:Response
{
  "test": "Mon",
  "module": 200
}
```
また、Lambda layer に `chalice_test-dev-reservation`という名前で作成されていることが確認できました。
![スクリーンショット 2022-03-21 11.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/f177599f-944b-37d6-4723-7e79e4d2741a.png)


あとは、環境ごとにセットアップなどもやっていきますが、長引きそうなので、次回に続きます。

https://qiita.com/holdout0521/items/3f7c5d53f03b9b0d66ea

# 参照
基本的に、ドキュメントがわかりやすいです。

https://aws.github.io/chalice/quickstart.html