Lambda PythonでLayerを作ってみる

Lambda の Layer 機能を使い汎用的な関数などを共通化してみます。
開発はServerless Frameworkで行います。

Layer作成

以下のようなディレクトリ構成でLayerを作成します。
Layerのコードは「/opt」以下に展開されます。
Python3.7 のライブラリの検索パスは「/opt/python」となっているので、
展開された時にインポートできるよう「layer/python」ディレクトリ以下にスクリプトを作成する必要があります。

layer-s3
├── serverless.yml
└── layer
    └── python
        └── s3util.py

serverless.yml と s3util.py を以下のように作成します。

service: layer-s3

provider:
  name: aws
  runtime: python3.7
  region: ap-northeast-1
  role: arn:aws:iam::************:role/Lambda-Roll
  deploymentBucket:
    name: 'sls-src'
    serverSideEncryption: AES256

layers:
  s3util:
    path: layer
import boto3

def list(bucket, prefix):
    s3 = boto3.client('s3')
    object_list = []
    next_token = ''

    while True:
        if next_token == '':
            response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, Delimiter='/')
        else:
            response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix, ContinuationToken=next_token, Delimiter='/')

        if response.get('Contents') is None: return []
        for o in response.get('Contents'):
            object_list.append(o.get('Key'))

        if 'NextContinuationToken' in response:
            next_token = response['NextContinuationToken']
        else:
            break

    return object_list

Layerデプロイ

$ cd layer-s3
$ sls deploy

AWS LambdaのLayersに作成されていればOK

レイヤー呼び出しサンプル作成

$ sls create --template aws-python3 --path layer-s3-example
$ cd layer-s3-example
$ sls plugin install -n serverless-python-requirements

serverless.yml と handler.py を以下のように作成します。

service: layer-s3-example

provider:
  name: aws
  runtime: python3.7
  region: ap-northeast-1
  role: arn:aws:iam::************:role/Lambda-Roll
  deploymentBucket:
    name: 'sls-src'
    serverSideEncryption: AES256

plugins:
  - serverless-python-requirements

functions:
  S3List:
    handler: handler.execute
    description: "S3レイヤー呼び出しサンプル"
    layers:
      - arn:aws:lambda:ap-northeast-1:************:layer:s3util:1
import s3util

def execute(event, context):
    bucket = event['bucket']
    prefix = event['prefix']
    list = s3util.list(bucket, prefix)
    return list

実行

正常に実行されました!

$ sls invoke -f S3List -d '{"bucket":"my-bucket-******","prefix":"test"}'
[
    "test.txt"
]

さすがにLayerを使うとローカル実行はできないようです。

$ sls invoke local -f S3List -d '{"bucket":"my-bucket-8368652","prefix":"test"}'
~~
ModuleNotFoundError: No module named 's3util'

最後に

これで汎用的な処理をLayerにまとめておけば、開発効率がぐっと上がりそうです!
ローカル実行できなくなるのでデバッグは少し面倒になるかもしれません。
個人的にはLayerどんどん使っていこうと思います!

参考