requirements.txt を Lambdaレイヤーに分離してみた
はじめに
こんにちは、アノテーションのなかたです。
今回は、SAMでrequirements.txt
を Lambdaレイヤーに分離してみました。
Lambdaレイヤーとは
Lambda レイヤーは、補足コードまたはデータを含む .zip ファイル アーカイブです。レイヤーには通常、ライブラリ依存関係、カスタム ランタイム、または構成ファイルが含まれます。
アプリケーションコードのうち、ライブラリやパッケージなどの依存関係を別の層として切り出してレイヤーとして利用するようです。
これにより、アプリケーションコードに関心が集まったり、独立して依存関係のみを更新することができます。
また、レイヤーとして切り出すことにより、別の関数で使い回すこともできます。
やってみる
1. SAMプロジェクトの構築
とりあえずSAMプロジェクトを構築し、SAMプロジェクトがデプロイすることを確認します。
sam init # Python3.13、`hello-world`テンプレート
cd プロジェクトフォルダ
sam build
sam deploy
マネジメントコンソールにてデプロイされているか確認します。
関数の作成ができており、この時点ではLambdaレイヤーは0
と表示されています。
また、Pythonのプロジェクトを作成すると、requirements.txt で requests
ライブラリが依存先としてデフォルで定義されています。
そのため requests
ライブラリに関連したフォルダが展開されています。
2. Lambdaレイヤー用のフォルダを作成する
SAMのLambdaレイヤーリソースにあたるAWS::Serverless::LayerVersion
では、ContentUrl
プロパティ属性があります。
この属性でフォルダを参照し、そのフォルダ下のソースコードやパッケージをLambdaレイヤーとして登録します。
今回は、requirements.txt を別のフォルダに移動し、ライブラリを独立させてみます。
% tree
.
├── README.md
├── __init__.py
├── hello_world
│ ├── __init__.py
│ └── app.py
├── my_layer # Lambdaレイヤーで参照するフォルダ
│ └── requirements.txt # requestsライブラリが定義されている
├── samconfig.toml
├── template.yaml
3. Lambdaレイヤーを定義する
SAMテンプレートに Lambdaレイヤーのサンプルを定義してみます。
コードは公式ドキュメントからお借りしましたが、Pythonのバージョンのみ異なっていたため、3.13
に合わせています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
demo-lambda-layer
Sample SAM Template for demo-lambda-layer
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.13
+ Layers:
+ - !Ref MyLayer
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
+ MyLayer:
+ Type: AWS::Serverless::LayerVersion
+ Properties:
+ ContentUri: my_layer/
+ CompatibleRuntimes:
+ - python3.13
+ Metadata:
+ BuildMethod: python3.13
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
念のため構文があっているかバリデーションしつつ、ビルド、デプロイします。
sam validate
sam build
sam deploy
4. デプロイの確認
マネジメントコンソールから Lambda関数を確認します。
Lambda関数にレイヤーが 1つ登録されていることがわかります。
ソースコードフォルダからもライブラリのフォルダが消えていることがわかります。
5. Lambdaレイヤーにあるライブラリを用いて、プログラムを実行してみる
本当にレイヤーに格納されているライブラリを用いることができるのか確認してみます。
サンプルプログラムにあったrequestsライブラリでIPアドレスを確認するプログラムを実行します。
import json
import requests
def lambda_handler(event, context):
try:
ip = requests.get("http://checkip.amazonaws.com/")
except requests.RequestException as e:
raise e
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world",
"location": ip.text.replace("\n", "")
}),
}
テストを実行したところ、ライブラリが正常に動作していることが確認できました。
Status: Succeeded
Test Event Name: (unsaved) test event
Response:
{
"statusCode": 200,
"body": "{\"message\": \"hello world\", \"location\": \"<IPアドレス>\"}"
}
Function Logs:
START RequestId: dummy-request-id Version: $LATEST
END RequestId: dummy-request-id
REPORT RequestId: dummy-request-id Duration: 147.63 ms Billed Duration: 148 ms Memory Size: 128 MB Max Memory Used: 58 MB
Request ID: dummy-request-id
おわりに
ライブラリが多く存在するSAMプロジェクトにおいて、自作したフォルダとライブラリのフォルダが混在してわからないという問題に直面したことがありました。
今回のようにLambdaレイヤーを用いることで解決できそうです。
参考
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。