LoginSignup
45
42

More than 5 years have passed since last update.

Lambda(Python) や API Gateway の管理を Chalice でやってみた

Last updated at Posted at 2018-09-26

Chalice を使う機会があったので、
せっかくなので備忘録も兼ねてまとめました。
ちなみに Python 歴は1ヶ月未満です。

Chalice とは?

https://github.com/aws/chalice
Amazon 産の AWS 向け Python フレームワークです。
AWS Lambda を使用する際に簡単に管理が出来るようになります。

また API Gateway, S3, SNS, SQS, CloudWatch Event などといった連携サービスもそのまま一緒に管理できます。

Chalice では、
Python の Chalice モジュールを利用しつつ、
コマンドラインツールで chalice コマンドを叩いてデプロイ等をする形になります。

また chalice delete コマンドを用いて、簡単に削除もできます。

開発環境

前提

  • AWS Credentials が設定されていること
  • Python 3.6 系が使えること

開発環境のバージョン確認

$ python --version
Python 3.6.6

$ pip --version
pip 10.0.1

$ chalice --version
chalice 1.6.0

インストール方法

$ pip install chalice

Hello World!

https://github.com/aws/chalice#quickstart
後述する @app.route を使って、
API Gateway + Lambda のデプロイが実施されます。

$ chalice new-project chibi-chalice

$ tree -a chibi-chalice/
chibi-chalice/
├── .chalice
│   └── config.json
├── .gitignore
├── app.py
└── requirements.txt

$ cd chibi-chalice

$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev
Creating Rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev
  - Rest API URL: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/

# このように `@app.route` を実装して chalice deploy を叩くと
# IAM Role が自動的に作成され、
# Lambda Function が自動的に作成され、
# API Gateway が自動的に作成されます。
動作確認
# もう API が叩ける状態に!
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/
{"hello": "world"}

Local でも実行できる!

$ chalice local
Serving on 127.0.0.1:8000
$ curl localhost:8000
{"hello": "world"}

各種使用方法

Hello World を試したところで、
自動的に API Gateway + AWS Lambda が簡単にデプロイできることがわかりました。

Chalice は Decorator を使って各種サービス向けの実装ができるため、
掘り下げて色々デプロイして遊んでみます。

@app.route

@app.route は、
Hello World でも使用した「API Gateway + AWS Lambda」の定義になります。

デコレーション定義

def route(self, path, **kwargs):

サンプル

from chalice import Chalice

app = Chalice(app_name='chibi-chalice')


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

このような形で実装してデプロイを行えば、
Hello World のように GET API が叩けるわけです。

POST や PUT を実装する場合

@app.route('/', methods=['POST'])
def index():
    request = app.current_request
    return {
        'method': request.method,
        'json-body': request.json_body
    }
動作確認
$ curl -X POST -H "Content-Type:application/json" -d '{"Hello":"World"}' https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/
{"method": "POST", "json-body": {"Hello": "World"}}

URL にパラメータを使う場合

@app.route('/users/{user}')
def index(user):
    return {'user': user}
動作確認
$ curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/users/chibi
{"user": "chibi"}

URL クエリを使う場合

@app.route('/users/{user}')
def index(user):
    request = app.current_request
    return {
        'user': user,
        'query': request.query_params
    }
動作確認
$ curl https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/users/chibi?hoge=foo
{"user": "chibi", "query": {"hoge": "foo"}}

レスポンスをカスタマイズする

from chalice import Chalice, Response

app = Chalice(app_name='chibi-chalice')


@app.route('/')
def index():
    return Response(body='Hello World!',
                    status_code=200,
                    headers={'Content-Type': 'text/plain', 'hoge': 'foo'})
動作確認
$ curl -v https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Content-Length: 12
< hoge: foo
Hello World!

CORS サポート

# このように書けば良いらしい
@app.route('/', methods=['PUT'], cors=True)

@app.lambda_function

@app.lambda_function は、
純粋な「AWS Lambda」の定義になります。

デコレーション定義

def lambda_function(self, name=None):

サンプル

from chalice import Chalice

app = Chalice(app_name='chibi-chalice')


@app.lambda_function()
def handler(event, context):
    return {'hello': 'lambda'}
デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev-handler
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxx:function:chibi-chalice-dev-handler

# このように `@app.lambda_function` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function が自動的に作成されます。
# ※ API-Gateway は作成されません。
動作確認
$ aws lambda invoke --function-name chibi-chalice-dev-handler outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

$ cat outputfile.txt 
{"hello": "lambda"}

Lambda 関数名を任意に指定する場合

@app.lambda_function(name='MyFunction')
def handler(event, context):
    return {'hello': 'MyFunction'}
動作確認
$ aws lambda invoke --function-name chibi-chalice-dev-MyFunction outputfile.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

$ cat outputfile.txt 
{"hello": "MyFunction"}

@app.schedule

@app.schedule は、
「CloudWatch Event + AWS Lambda」の定義になります。

デコレーション定義

def schedule(self, expression, name=None):

サンプル

from chalice import Chalice, Rate

app = Chalice(app_name="chibi-chalice")


@app.schedule(Rate(1, unit=Rate.MINUTES))
def handle_schedule(event):
    print(event)
    return {"hello": "world"}
デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev-handle_schedule
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_schedule
# このように `@app.schedule` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function が自動的に作成されます。
# ※スケジュールイベントはきっと設定されているはず。。。
動作確認
$ aws events list-rules --name-prefix chibi-chalice-dev-handle_schedule
{
    "Rules": [
        {
            "Name": "chibi-chalice-dev-handle_schedule-event",
            "Arn": "arn:aws:events:ap-northeast-1:xxxxxxxxxx:rule/chibi-chalice-dev-handle_schedule-event",
            "State": "ENABLED",
            "ScheduleExpression": "rate(1 minute)"
        }
    ]
}

別の記述方法

@app.schedule('rate(1 minutes)')
def handle_schedule():
    print(event)
    return {'hello': 'schedule'}

@app.on_s3_event

@app.on_s3_event は、
「S3 + AWS Lambda」の定義になります。

デコレーション定義

def on_s3_event(self, bucket, events=None, prefix=None, suffix=None, name=None):

サンプル

from chalice import Chalice

app = Chalice(app_name='chibi-chalice')


@app.on_s3_event(bucket='chibi-existing-bucket')
def handle_s3_event(event):
    print(f'Object uploaded for bucket: {event.bucket}, key: {event.key}')

※ Bucket は存在している必要があります。

デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev-handle_s3_event
Configuring S3 events in bucket chibi-existing-bucket to function chibi-chalice-dev-handle_s3_event
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_s3_event
# このように `@app.on_s3_event` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function が自動的に作成され、
# S3 events を設定してくれます。
動作確認
$ aws s3api get-bucket-notification-configuration --bucket chibi-existing-bucket
{
    "LambdaFunctionConfigurations": [
        {
            "Id": "xxxxxxxxxx",
            "LambdaFunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_s3_event",
            "Events": [
                "s3:ObjectCreated:*"
            ]
        }
    ]
}

@app.on_sqs_message

@app.on_sqs_message は、
「SQS + AWS Lambda」の定義になります。

デコレーション定義

def on_sqs_message(self, queue, batch_size=1, name=None):

サンプル

from chalice import Chalice

app = Chalice(app_name='chibi-chalice')


@app.on_sqs_message(queue='chibi-existing-queue')
def handle_sqs_message(event):
    for record in event:
        print(f'Message body: {record.body}')

※Queue は存在している必要があります。
※Queue の「デフォルトの可視性タイムアウト」は Lambda のタイムアウト以上に設定する必要があります。

デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev-handle_sqs_message
Subscribing chibi-chalice-dev-handle_sqs_message to SQS queue chibi-existing-queue
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_sqs_message
# このように `@app.on_sqs_message` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function が自動的に作成され、
# SQS の Lambda トリガーを設定してくれます。
動作確認
$ aws lambda list-event-source-mappings --function-name chibi-chalice-dev-handle_sqs_message
{
    "EventSourceMappings": [
        {
            "UUID": "xxxxxxxxxx",
            "BatchSize": 1,
            "EventSourceArn": "arn:aws:sqs:ap-northeast-1:xxxxxxxxxx:chibi-existing-queue",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_sqs_message",
            "LastModified": 1537413283.145,
            "State": "Enabled",
            "StateTransitionReason": "USER_INITIATED"
        }
    ]
}

@app.on_sns_message

@app.on_sns_message は、
「SNS + AWS Lambda」の定義になります。

デコレーション定義

def on_sns_message(self, topic, name=None):

サンプル

from chalice import Chalice

app = Chalice(app_name='chibi-chalice')


@app.on_sns_message(topic='chibi-existing-topic')
def handle_sns_message(event):
    print(f'Received message with subject: {event.subject}, message: {event.message}')

※Topic は存在している必要があります。

デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-chalice-dev
Creating lambda function: chibi-chalice-dev-handle_sns_message
Subscribing chibi-chalice-dev-handle_sns_message to SNS topic chibi-existing-topic
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_sns_message
# このように `@app.on_sns_message` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function が自動的に作成され、
# SNS のサブスクリプションに Lambda を設定してくれます。
動作確認
$ aws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:ap-northeast-1:xxxxxxxxxx:chibi-existing-topic
{
    "Subscriptions": [
        {
            "SubscriptionArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxx:chibi-existing-topic:xxxxxxxxxx",
            "Owner": "xxxxxxxxxx",
            "Protocol": "lambda",
            "Endpoint": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-chalice-dev-handle_sns_message",
            "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxx:chibi-existing-topic"
        }
    ]
}

@app.authorizer

@app.authorizer は、
「API Gateway の Lambda オーソライザー + Lambda」の定義になります。
Lambda のデプロイとオーソライザーの設定が一括でできます。

デコレーション定義

def authorizer(self, name=None, **kwargs):

サンプル

from chalice import Chalice, AuthResponse

app = Chalice(app_name='chibi-auth')


@app.authorizer()
def demo_auth(auth_request):
    token = auth_request.token
    # This is just for demo purposes as shown in the API Gateway docs.
    # Normally you'd call an oauth provider, validate the
    # jwt token, etc.
    # In this exampe, the token is treated as the status for demo
    # purposes.
    if token == 'allow':
        return AuthResponse(routes=['/', '/one', '/two'], principal_id='user')
    else:
        # By specifying an empty list of routes,
        # we're saying this user is not authorized
        # for any URLs, which will result in an
        # Unauthorized response.
        return AuthResponse(routes=[], principal_id='user')


@app.route('/', authorizer=demo_auth)
def index():
    return { 'message': 'root' }

@app.route('/one', authorizer=demo_auth)
def one():
    return { 'message': 'one' }

@app.route('/two', authorizer=demo_auth)
def two():
    return { 'message': 'two' }

※サンプル通りのコードなのだが何故か動かないので /one , /two も追加
※ルート( / )は上手く動かせないのか。。。?
※また、コメントに記載されている通り本来であれば jwt token 等を validate します。

デプロイ
$ chalice deploy
Creating deployment package.
Creating IAM role: chibi-auth-dev
Creating lambda function: chibi-auth-dev
Creating lambda function: chibi-auth-dev-demo_auth
Creating Rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-auth-dev
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:chibi-auth-dev-demo_auth
  - Rest API URL: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/
# このように `@app.authorizer` を実装して chalice deploy を叩くと
# IAM Role、Lambda Function, API Gateway が自動的に作成され、
# API Gateway に Lambda Authorizer が設定されます。
動作確認
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api
{"message":"Unauthorized"}
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api -H "Authorization: allow"
{"Message":"User is not authorized to access this resource"}

$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/one
{"message":"Unauthorized"}
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/one -H "Authorization: allow"
{"message": "one"}

$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/two
{"message":"Unauthorized"}
$ curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/two -H "Authorization: allow"
{"message": "two"}

@app.route (オーソライザ)

@app.authorizer を使わなくても簡単に設定が可能です。

IAM オーソライザ

from chalice import Chalice, IAMAuthorizer

app = Chalice(app_name='chibi-auth')

authorizer = IAMAuthorizer()


@app.route('/iam-auth', methods=['GET'], authorizer=authorizer)
def authenticated():
    return {"success": True}

Cognito User Pool オーソライザ

from chalice import Chalice, CognitoUserPoolAuthorizer

app = Chalice(app_name='chibi-auth')

authorizer = CognitoUserPoolAuthorizer(
    'MyPool', provider_arns=['arn:aws:cognito:...:userpool/name'])


@app.route('/user-pools', methods=['GET'], authorizer=authorizer)
def authenticated():
    return {"success": True}

Lambda オーソライザ

from chalice import Chalice, CustomAuthorizer

app = Chalice(app_name='chibi-auth')

authorizer = CustomAuthorizer(
    'MyCustomAuth', header='Authorization',
    authorizer_uri=('arn:aws:apigateway:region:lambda:path/2015-03-01'
                    '/functions/arn:aws:lambda:region:account-id:'
                    'function:FunctionName/invocations'))


@app.route('/custom-auth', methods=['GET'], authorizer=authorizer)
def authenticated():
    return {"success": True}

まとめ

初めてレベルで Python を触ったけど Chalice 便利!
また何か新しいものを触ったら記事にしたい。

45
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
42