chaliceはAWSの公式ツール。api gatewayとlambdaを pythonのflaskライクな書き方で定義できる

設定ファイルよりもpythonを書くのが楽な人には使いやすそう

色々機能を試してみたのでトピックごとにメモしておく

環境

  • python: 3.7
  • chalice: 1.12.0

インストールコマンド

python3 -m pip install chalice

プロジェクト作成コマンド .chaliceが作成される

chalice new-project helloworld

lambda関数の作成

redshiftのクエリ結果をs3にuploadする処理を作る

リポジトリにも全体のスクリプトを上げている

デコレータを付ける。lambda関数名はnameで指定できる

@app.lambda_function(name='redshift_to_s3')
def main(event, context) -> None:
    query = load_query()
    res = exec_query(query)
    dataframe_to_s3(res, filename=OutputFileName, output_s3_path=S3Path)

各種イベントをトリガーにすることもできる

cron実行(5分ごと)

@events.schedule('cron(*/5 * * * ? *)')

s3 uploadイベント

@app.on_s3_event(bucket='bucket_name')

sqsイベント

@app.on_sqs_message(queue='queue-name')

環境変数の設定

.chalice/config.jsonに記述する

↓のようにenvironment_variablesに記述する

{
  "version": "2.0",
  "app_name": "redshift_to_s3",
  "stages": {
    "dev": {
      "lambda_functions": {
        "main": {
          "lambda_timeout": 600,
          "subnet_ids": ["subnet-xxxx"],
          "security_group_ids": ["sg-xxxx", "sg-xxxx"]
        }
      },
      "manage_iam_role": false,
      "iam_role_arn": "arn:aws:iam::xxxx:role/lambda-role",
      "environment_variables": {
        "CLUSTER_ENDPOINT": "xxxx.xxxx.ap-northeast-1.redshift.amazonaws.com",
        "CLUSTER_IDENTIFIER": "xxxx",
        "DB_PORT": "5439",
        "DB_NAME": "db",
        "DB_USER": "redshift_user",
        "QUERY_PATH": "sql/test.sql",
        "S3_BUCKET": "bucket",
        "S3_PATH": "unload/path",
        "OUTPUT_FILENAME": "test"
      }
    }
  }
}

stagesごとに記述でき、デプロイする時に変えることができる

chalice deploy --stage dev

外部ファイルの追加

./chalicelib以下にファイルを置いておけばデプロイ時に追加される

モジュールならば

from chalicelib.module import func などとしてimportできる

ファイルならば

import os
filepath = os.path.join(
        os.path.dirname(__file__), 'chalicelib', filename)

などとすれば取得できる

デプロイ&削除

各種リソースの作成と削除をしてくれる

chalice deploy
Creating deployment package.
Creating lambda function: dev-hello-name
chalice delete
Deleting function: arn:aws:lambda:ap-northeast-1:xxxx:function:dev-hello-name

おまけ

policyの自動生成

chaliceは、boto3で定義、実行したメソッドを読んで、iam policyを自動生成してくれる

生成するiam policyを↓cliで確認することもできる

 chalice gen-policy --filename ./chalicelib/sagemaker.py
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "sagemaker:CreateEndpoint",
        "sagemaker:CreateEndpointConfig",
        "sagemaker:DeleteEndpoint",
        "sagemaker:UpdateEndpoint"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "xxxxxxx"
    }
  ]
}

指定したスクリプト。sagemakerにモデルをデプロイしたりするスクリプト

import os
import boto3
import json
import time
from chalice import Blueprint, BadRequestError

endpoint = Blueprint(__name__)

client = boto3.client('sagemaker')


@endpoint.route('/config/{name}', methods=['POST'])
def create_endpoint_config(name):
    body = endpoint.current_request.json_body
    if 'ModelName' not in body:
        raise BadRequestError('Missing model name in request body')
    model_name = body['ModelName']
    instance_type = 'ml.t2.medium'
    instance_count = 1

    if 'InstanceType' in body:
        instance_type = body['InstanceType']
    if 'InstanceCount' in body:
        instance_count = body['InstanceCount']

    response = client.create_endpoint_config(
        EndpointConfigName=name,
        ProductionVariants=[{
            'InstanceType': instance_type,
            'InitialInstanceCount': instance_count,
            'InitialVariantWeight': 1,
            'ModelName': model_name,
            'VariantName': 'AllTraffic'}]
    )

    print("create endpoint Config Arn: " + response['EndpointConfigArn'])
    return {'response': {'arn': response['EndpointConfigArn']}}


# deploy & hosting
@endpoint.route('/{name}', methods=['POST'])
def create_endpoint(name):
    body = endpoint.current_request.json_body
    if 'EndpointConfigName' not in body:
        raise BadRequestError('Missing endpoint config name in request body')

    response = client.create_endpoint(
        EndpointName=name,
        EndpointConfigName=body['EndpointConfigName']
    )
    print('create endpoint arn:', response['EndpointArn'])
    return {'response': {'arn': response['EndpointArn']}}

@endpoint.route('/{name}', methods=['PUT'])
def update_endpoint(name):
    body = endpoint.current_request.json_body
    if 'EndpointConfigName' not in body:
        raise BadRequestError('Missing endpoint config name in request body')

    response = client.update_endpoint(
        EndpointName=name,
        EndpointConfigName=body['EndpointConfigName']
    )

    print(response)
    return {'response': response}


@endpoint.route('/{name}', methods=['DELETE'])
def delete_endpoint(name):
    response = client.delete_endpoint(
        EndpointName=name
    )

    return {'response': response}

SAM用package生成

AWS SAMというサーバレス用のフレームワーク用のファイル群が生成させる AWS サーバーレスアプリケーションモデル (AWS SAM) の使用 - AWS Lambda

chalice package output
ls ./output/
deployment.zip	sam.json

ソースファイル群(deployment.zip)と作成されるリソースの設定ファイル(sam.json)が生成される

参考