以下のような内容を実現しようとしてみたメモ
以下のように Lambda 関数とレイヤを同一のリポジトリ、および SAM ファイルで管理します
. ├── config.dev ├── config.prod ├── config.template ├── deploy ├── src │ └── python36 │ ├── hello_world │ │ ├── __init__.py │ │ ├── app.py │ │ └── requirements.txt │ └── layers │ └── requests │ ├── __init__.py │ ├── python │ └── requirements.txt ├── template.yaml └── test └── ...(略)...
今回は単純に依存ライブラリ requests に依存する簡単な Lambda 関数を考えます
# -*- coding: utf-8 -*- import requests def lambda_handler(event, context): url = 'https://checkip.amazonaws.com' res = requests.get(url) return { 'status_code': res.status_code, 'ip_addr': res.text.strip(), }
当然 requests への依存解決が必要です。SAM を用いると requirements.txt に反応して依存ライブラリを解決してデプロイパッケージを作成してくれます。しかしながらこれをレイヤーに分離したい場合どのようにすればよいでしょうか。
基本的に SAM は LayerVersion に関しても CodeUri で指定したディレクトリ配下のものを単純に zip で固めてデプロイします。したがって以下のようなデプロイ用の簡単なシェルスクリプトがあると便利です。
S3Bucket= # deploy target s3 bucket S3KeyPrefix= # deploy target key prefix layers=("requests") for layer in $layers; do pushd src/python36/layers/$layer/ docker run --rm -v "$PWD":/var/task -w /var/task lambci/lambda:build-python3.6 pip install -r requirements.txt -t python popd done sam build sam package --output-template-file packaged.yaml --s3-bucket $S3Bucket --s3-prefix "$S3KeyPrefix" sam deploy --template-file packaged.yaml --stack-name ${Stage}SamLambdaLayer --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides \ "STAGE=$stage"
もちろん依存ライブラリ自体は SCM の対象から外したいので、以下のような .gitignore を書いておけばよいでしょう。
.aws-sam src/*/layers/*/* !src/*/layers/*/requirements.txt !src/*/layers/*/__init__.py
SAM ファイルは以下のようになります
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-lambda-layers Globals: Function: Timeout: 10 Runtime: python3.6 MemorySize: 128 Resources: # IAM Role for Lambda Function LambdaFunctionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub LambdaFunctionRole AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: '2012-10-17' ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # Function Layers RequestsLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: !Sub Requests Description: Requests(python 3.x) ContentUri: src/python36/layers/requests RetentionPolicy: Retain CompatibleRuntimes: - python3.6 - python3.7 # Lambda Functions and Log Groups HelloWorldFunction: Type: AWS::Serverless::Function Properties: FunctionName: HelloWorld CodeUri: src/python36/hello_world Handler: app.lambda_handler Role: !GetAtt LambdaFunctionRole.Arn Layers: - !Ref RequestsLayer HelloWorldLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/HelloWorld RetentionInDays: 7
本番、開発環境を分離するためには以上の内容について関数名に Prod/Dev などといったプレフィックスをつければ OK。これに伴いデプロイスクリプトを少し変更する必要があります。
いい感じに動くサンプルを 53ningen/sam-lambda-layers にあげておきましたので必要な方はどうぞ。
ウェブ界隈でエンジニアとして労働活動に励んでいる @gomi_ningen 個人のブログです