@gomi_ningen's Website

SAM(Serverless Application Model) と Lambda Layers

以下のような内容を実現しようとしてみたメモ

  • Lambda の Layer も含めて同一の SAM file で管理したい
  • ステージごとに Layer も分離したい
    • 本番環境と開発環境の Layer は真面目に運用するなら分離したいところ
    • バージョンで分けるというのもあるけど、個人的にはそもそもリソースを分離したい

Directory 構造

以下のように 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(), }

Layer とデプロイ

当然 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 ファイル

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 にあげておきましたので必要な方はどうぞ。

Copyright © 53ningen.com