はじめに
開発部の山下です。
Lambdaはそれ自身にデプロイしたコードだけでなく、Lambda layerのコードも利用できます。 1つのLambda layerは複数のLambdaにアタッチできるため、utilモジュールなどを入れておくと便利だと思うかもしれません。
が、それはやってはいけません。なぜならば「Lambda layerがLambdaのコンテナに展開される」というLambdaの仕組みに依存してしまうため、あなたのコードはローカルで動かなくなってしまいます。
ローカルで動かないコードはローカルでテストできず、修正のたびにAWSにデプロイして動作を確認する必要があります。 エディタによるプログラミング支援も受けられなくなります。具体的には関数の引数が分からなくなり、定義ジャンプ等々の機能が利用できなくなります。
簡単な方針があります。Lambda layerは外部依存コードのみ配置し、自作のコードは一切置かないことです。
この記事ではLambda + Lambda layerという構成のプロジェクトの作り方を提案します。
フォルダ構成
こちらのフォルダ構成を提案します。記事ではステップバイステップでこのプロジェクト構成になるように紹介します。
Lambdaのデプロイ
以下の内容で sample-repo/app/function/b1/src/main.py に保存します。
def handler(event, context) -> dict[str, Any]:
return {
"status": "ok",
}
そして以下の内容を template.yaml に保存します。
AWAWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Parameters:
Prefix: { Type: String }
Globals:
Function:
Runtime: python3.11
Resources:
FunctionB1:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${Prefix}-b1
CodeUri: ./app/function/b1
Handler: src.main.handler
このLambdaは以下のコマンドでデプロイできます
sam deploy --stack-name dev-lambda-layer-sample --capabilities CAPABILITY_NAMED_IAM --resolve-s3 --parameter-overrides Prefix=dev-lambda-layer-sample
デプロイ後はこの様にコンソールから実行できます。
外部パッケージの利用
pyyaml をLambdaで利用したくなったとします。 外部パッケージ依存のためLambda layerの出番です。
ローカルの依存解決にはpoetryを利用します。まずレポジトリルートで poetry init します。これで作る .venv がローカルで使う依存コードです。
$ poetry init
This command will guide you through creating your pyproject.toml config.
Package name [aws-lambda-layer-sample]:
Version [0.1.0]:
Description []:
Author [yamashita-optarc <yamashita@optarc.net>, n to skip]:
License []:
Compatible Python versions [^3.11]: ~3.11.0
基本的にデフォルトのままで問題ありませんが、Compatible Python versionsのところだけLambdaのPythonバージョンと合わせる必要があります。 ~3.11.0 で 3.11.x を指定したことになります。3.11系で固定したいのでこのように指定します。
そして pyyaml を普段と同じ様にインストールします。
poetry add pyyaml
main.py を次のように変更します
from typing import Any
import yaml
def yaml_loads(target: str) -> dict[str, Any]:
return yaml.safe_load(target)
def handler(event: dict[str, Any], context: Any) -> dict[str, Any]:
target_yaml = '''\
a: 1
b: 2
c: 3
'''
print(yaml_loads(target_yaml))
return {
"status": "ok",
}
通常のPythonプロジェクトと同じため、エディタによる支援が受けられています。
ローカルテスト
pytest によるローカルテストを行ってみます。 フォルダを作り、Pythonにモジュールと認識してもらうため、各フォルダに __init__.py を作成します。
mkdir -p app/function/test_b1
touch app/function/test_b1/__init__.py
touch app/function/b1/__init__.py
touch app/function/b1/src/__init__.py
touch app/function/__init__.py
touch app/__init__.py
そして以下の内容で app/function/test_b1/test_main.py に保存します。
from app.function.b1.src import main
def test_main1():
assert main.yaml_loads('a: 1\nb: 2\nc: 3\n') == {'a': 1, 'b': 2, 'c': 3}
def test_main2():
assert main.yaml_loads('a: "1"\nb: "2"\nc: "3"\n') == {'a': '1', 'b': '2', 'c': '3'}
pytest をインストールし、 pytest を実行するとテストが実行されます。
$ poetry add pytest -D
$ poetry run pytest
================================================== test session starts ==================================================
platform darwin -- Python 3.11.5, pytest-7.4.2, pluggy-1.3.0
rootdir: /Users/conao/dev/optarc/ojt-yamashita-code/prj/aws-lambda-layer-sample
collected 2 items
app/function/test_b1/test_main.py .. [100%]=================================================== 2 passed in 0.02s ===================================================
Lambda layerの作成
pyyamlが入ったLambda layerを作成します。
app/function-layerをLayer置き場ということにして、新しくLambda Layer用のpoetryプロジェクトを作成します。
$ mkdir -p app/function-layer/b1
$ cd app/function-layer/b1
$ poetry init
This command will guide you through creating your pyproject.toml config.
Package name [b1]:
Version [0.1.0]:
Description []:
Author [yamashita-optarc <yamashita@optarc.net>, n to skip]:
License []:
Compatible Python versions [^3.11]: ~3.11.0
そしてレポジトリルートと同じパッケージをインストールします。 Lambda layerとしてAWSにデプロイされるため、いわゆるdevパッケージは必要ありません。( pytest など)
$ poetry add pyyaml
実際のLambda layerを作る方法は少し複雑です(ドキュメント)。そのため以下の内容を Makefile として保存し、 make を使って簡単にパッケージングできるようにします。
all:
.PHONY: init
init: dist/python ## create lambda layer
dist/python: requirements.txt
rm -rf dist
mkdir -p $@
poetry run pip install -r requirements.txt --target $@
rm -rf dist/python/boto*
.PHONY: init.docker
init.docker: requirements.txt ## create lambda layer using x86 docker
docker run --platform linux/amd64 --rm -v $(CURDIR):$(CURDIR) -w $(CURDIR) python:3.11 pip install -r requirements.txt --target dist/python
rm -rf dist/python/boto*
requirements.txt: pyproject.toml
poetry export -f requirements.txt --without-hashes > $@
.PHONY: clean
clean: ## clean all
rm -rf python requirements.txt .venv dist
保存できたら make init で dist フォルダができます。
もしM1 Macを使っている場合などでLambdaとCPUアーキテクチャが異なる場合はdockerを利用して作成することもできるようになっています。 その場合は make init.docker を実行します。
Lambda layerのデプロイ
template.yaml を以下に変更します。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Parameters:
Prefix: { Type: String }
Globals:
Function:
Runtime: python3.11
Resources:
FunctionLayerB1:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: !Sub ${Prefix}-b1
ContentUri: ./app/function-layer/b1/dist
FunctionB1:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${Prefix}-b1
CodeUri: ./app/function/b1
Handler: src.main.handler
Layers:
- !Ref FunctionLayerB1
デプロイは先程と同じく以下のコマンドです。
sam deploy --stack-name dev-lambda-layer-sample --capabilities CAPABILITY_NAMED_IAM --resolve-s3 --parameter-overrides Prefix=dev-lambda-layer-sample
コンソールから実行し、 pyyaml が動いていることが確認できました。
この方法を使うことでローカルでの開発体験を損なうことなく、自由にLambdaで外部依存パッケージを使えるようになります。poetryでlockファイルを利用しているためバージョン固定もできています。
ぜひ試してみてください。
Comentários