Traffine I/O

日本語

2022-10-02

AWS Chalice

AWS Chalice とは

AWS Chaliceとは、Lambdaを中心としたサーバーレスアプリケーションを開発するためのフレームワークです。API Gatewayとの連携も自動化することができ、簡単にREST APIを開発することができます。他には、S3やSQSなどのイベントからのトリガーなど、AWS Lambdaを使う際の設定を自動化することができます。

AWS Chalice の使い方

インストール

以下のコマンドでChaliceをインストールします。

$ pip install chalice

必要に応じてAWSのクレデンシャルの設定をします。

$ aws configure

helloworldという名前のChaliceプロジェクトを作成します。

$ chalice new-project helloworld

プロジェクトの作成により以下のようなファイルが生成されます。

.
├── .chalice
│   └── config.json
├── .gitignore
├── app.py
└── requirements.txt

ここで重要なのがapp.pyconfig.jsonです。app.pyはアプリケーションのロジックを記述するファイルで、config.jsonはアプリケーションの設定項目を記述するファイルです。

app.pyは以下のようになっています。API Gatewayのエンドポイント/にGETメソッドでアクセスすると{'hello': 'world'}というレスポンスを返すようになっています。

app.py
from chalice import Chalice

app = Chalice(app_name = 'helloworld')

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

プロジェクト作成時点でconfig.jsonは以下のようになっています。

config.json
{
  "version": "2.0",
  "app_name": "helloworld",
  "stages": {
    "dev": {
      "api_gateway_stage": "api",
      "autogen_policy": true
    }
  }
}

stagesでは任意の環境や環境変数を設定することができます。autogen_policyではChaliceが自動でIAMロールを作成するかどうかを設定することができます。autogen_policyfalseにする場合はiam_policy_fileに自身で作成したポリシーファイルのパスを記述します。例として以下のようにconfig.jsonを編集することができます。

config.json
{
  "version": "2.0",
  "app_name": "helloworld",
  "autogen_policy": false,
  "iam_policy_file": "custom-policy.json",
  "stages": {
    "dev": {
      "api_gateway_stage": "dev",
      "environment_variables": {
        "ENV": "dev"
      }
    },
    "prod": {
      "api_gateway_stage": "prod",
      "environment_variables": {
        "ENV": "prod"
      }
    }
  }
}

ローカル実行

以下のコマンドでChaliceプロジェクトをローカル実行することができます。

$ chalice local

デプロイ

以下のコマンドで簡単にプロジェクトをデプロイすることができます。必要に応じて、AWSのプロフィールやconfig.jsonで設定したstageを選択することができます。

$ chalice deploy
$ chalice deploy --profile chalice
$ chalice deploy --state dev

デプロイが成功するとAWS上で以下のリソースが自動で生成されます。

  • IAMロール
  • Lambda関数
  • API Gateway

リソースを削除

デプロイしたChaliceプロジェクトを削除する場合は以下のコマンドを実行します。

$ chalice delete

CORS を有効化

app.pycors=Trueを追記することで簡単にCORS対応することができます。

app.py
@app.route('/', methods = ['GET'], cors = True)

Cognito の認証を有効化

APIへのアクセスをAWSのCognitoの認証により制限したい場合は以下のようなコードを記述します。

app.py
from chalice import CognitoUserPoolAuthorizer

authorizer = CognitoUserPoolAuthorizer("MyPool", provider_arns=[POOL_ARN])

@app.route('/', methods = ['GET'], authorizer=authorizer)

そしてAPIにリクエストを投げる際にヘッダのAuthorizationに有効なCognitoのIDトークンを付与するとアクセスすることができます。

デコレータ

Chaliceではデコレータを使うことでLambdaと他のAWSサービスとの連携が可能です。パターンとして以下が挙げられます。

  • AWS Lambda:@app.lambda_function
  • AWS Lambda + API Gateway :@app.route
  • AWS Lambda + Amazon CloudWatch Events:@app.schedule
  • AWS Lambda + Amazon S3:@app.on_s3_event
  • AWS Lambda + Amazon SQS:@app.on_sqs_message
  • AWS Lambda + Amazon SNS:@app.on_sns_message

app.pyの分割

ChaliceでAPIの開発をしていると、プログラムの規模が大きくなるにつれてapp.pyが肥大化していき、生産性や保守性が悪くなってしまいます。解決方法の一つとして、ChaliceのBlueprintsという機能を使い、app.pyを任意の単位で分割することができます。

例えば以下のapp.pyがあるとします。

app.py
from chalice import Chalice
app = Chalice(app_name='helloworld')

@app.route('/a')
def a():
    return {'hello': 'a'}

@app.route('/b')
def b():
    return {'hello': 'b'}

これからapp.pyを分割していきます。現在のディレクトリ構成は以下のようになっています。

helloworld/
├── app.py
└── requirements.txt

以下のようにディレクトリを切ります。Chaliceでは自作モジュールはchalicelibというディレクトリに格納する必要があります。

helloworld/
├── app.py
├── chalicelib
│   ├── __init__.py
│   └── blueprints
│     ├── __init__.py
│     ├── a.py
│     └── b.py
└── requirements.txt

app.pyのエンドポイントをa.pyb.pyにそれぞれ移行します。

chalicelib/blueprints/a.py
from chalice import Blueprint
extra_routes = Blueprint(__name__)

@extra_routes.route('/a')
def a():
    return {'hello': 'a'}
chalicelib/blueprints/b.py
from chalice import Blueprint
extra_routes = Blueprint(__name__)

@extra_routes.route('/b')
def b():
    return {'hello': 'b'}

app.pyを以下のように修正します。

app.py
from chalice import Chalice
from chalicelib.blueprints.a import extra_routes as a    # 追加
from chalicelib.blueprints.b import extra_routes as b    # 追加

app = Chalice(app_name='helloworld')
app.register_blueprint(a)
app.register_blueprint(b)

これでapp.pyを分割することができました!

Chalice のテスト

Chaliceはchalice.testというテストクライアントを提供しており、Chaliceアプリケーションのテストに使用することができます。このクライアントでは、Lambda関数やイベントハンドラを直接呼び出したり、REST APIをテストしたりすることができます。

まずは以下のコマンドでpytestをインストールします。

$ pip install pytest

そして以下のようにディレクトリを切ります。

helloworld/
├── app.py
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   └── test_app.py
└── requirements.txt

conftest.pyに以下のコードを記述します。

tests/conftest.py
from typing import Generator

import pytest
from app import app
from chalice.test import Client

@pytest.fixture(scope="module")
def client() -> Generator:
    with Client(app) as c:
        yield c

test_app.pyにはpytestのコードを書いていきます。例えば以下のようなコードになります。

tests/test_app.py
from chalice.test import Client
from app import app

def test_helloworld(client: Client):
    response = client.http.get("/")
    assert response.status_code == 200

以下のコマンドでテストを実行することができます。

$ py.test tests/test_app.py

========================= test session starts ==========================
platform darwin -- Python 3.7.3, pytest-5.3.1, py-1.5.3, pluggy-0.12.0
rootdir: /tmp/testclient
plugins: hypothesis-4.43.1, cov-2.8.1
collected 2 items

test_app.py ..                                                    [100%]

========================= 2 passed in 0.32s ============================

参考

https://aws.github.io/chalice/
https://aws.github.io/chalice/topics/blueprints.html
https://aws.github.io/chalice/topics/testing.html

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!