🐳
Dockerを活用したLambdaの開発&本番環境のおすすめ構成
はじめに
Lambdaの開発&本番環境の構成について
色々と試してきたが、Dockerを利用するのが良かったのでオススメとして紹介
Lambda開発の一般的な課題
-
開発環境の設定
- 開発環境の構築時に、本番環境との差異をなくす構成を取るのに苦労する
- また、環境の作り方によっては開発がしづらい構成になってしまう
-
デプロイの複雑さ
- 複数の環境(DEV、STG、PRDなど)が存在する場合、デプロイが複雑になることがある
- 自動化すれば良いものの、複雑なものを自動化するのにも苦労する
よくある構成と課題
1. エイリアスを使用した環境分割
Lambdaではエイリアスとしてprdやdevといった具合に環境を分けることができる。またリリース時にはdevで開発して検証し切ったものをバージョニングし、prdエイリアスにアタッチすることでリリースできるため、Lambda上でリリース作業が可能になる。
課題
- 実装効率が悪い
- 検証のたびにLambdaへデプロイし
dev
エイリアスで実行させる必要があるため実装効率が悪い
- 検証のたびにLambdaへデプロイし
- エイリアス・バージョン管理が複雑
- リリース時はコンソール上での操作だけで完結するが、逆に言うと手作業を行う必要があるため、操作ミスが本番環境に影響を与えるリスクがある。
- リリース時に気をつけるLambdaの仕様(環境変数など)もいくつかあるため、それを把握していないことによるミスも生まれる。
- 自動デプロイの設定も組めるが、その設定自体も複雑になる。
2. 関数ごとの分割
関数ごとに分離することで、エイリアス・バージョン管理の複雑さを軽減し、操作ミスも防ぐことができる。
課題
- 実装効率が悪い(1と同じく)
- 検証毎にLambdaへのデプロイが必要であるため実装効率が悪い
3. AWS SAMやServerless Frameworkの利用
それぞれのツールについて詳しく記載はしないが、開発環境の構築やデプロイを自動化するためのツールになる。一度環境さえ構築してしまえば、実装からリリースまでの開発効率を非常に上げられる。
課題
- ツールのキャッチアップが必要
- 自分だけが運用するのであれば何も問題ないが、チーム運用となるとチームメンバーのキャッチアップコストも気にかける必要があるため、チームメンバーの技術範囲も検討材料になる。
- 権限譲渡(IAMなど)
- 特にServerless Frameworkの構築時に思ったが、自動デプロイのためにIAMで相当の権限を渡す必要があり、権限の絞り込み設定の手間やセキュリティ的にも心理的にも懸念を感じた。
Dockerを活用したLambdaの開発&本番環境のおすすめ構成
概要
LambdaではZIPファイルのデプロイに加えて、コンテナイメージを利用することもできる。なので結論としては、Dockerコンテナを利用すれば開発環境の用意はもちろん、本番環境にもそのまま利用できるため、一貫性のある構成が作れる。
ディレクトリ構成
├── Dockerfile
├── Dockerfile.dev
├── entrypoint.py
├── lambda_function.py
├── module
│ ├── module_a.py
│ └── module_b.py
├── requirements.txt
└── .env
説明
Dockerfile
Dockerfile
# AWS LambdaのPythonランタイムベースイメージを使用
FROM public.ecr.aws/lambda/python:3.12
# 必要なパッケージのインストール
COPY requirements.txt .
RUN python3.12 -m pip install -r requirements.txt
# Lambda関数のコードとモジュールをコンテナにコピー
COPY lambda_function.py ./
COPY module ./module
# Lambdaハンドラ関数の指定 (lambda_function.pyのlambda_handler関数を指定)
CMD ["lambda_function.lambda_handler"]
- 本番環境用のDockerファイル
- Lambdaランタイムが指定されたハンドラ関数を自動的に呼び出すため、特別なエントリーポイントスクリプトは不要
Dockerfile.dev
Dockerfile.dev
# AWS LambdaのPythonランタイムベースイメージを使用
FROM public.ecr.aws/lambda/python:3.12
# 必要なパッケージのインストール
COPY requirements.txt .
RUN python3.12 -m pip install -r requirements.txt
# Lambda関数のコードとモジュールをコンテナにコピー
COPY lambda_function.py ./
COPY entrypoint.py ./
COPY module ./module
# entrypoint.pyをエントリーポイントとして指定
COPY entrypoint.py .
ENTRYPOINT ["python", "entrypoint.py"]
- 開発環境用のDockerファイル
- コマンドライン引数を使ってイベントをシミュレーションし、Lambda関数をテスト
entrypoint.py
entrypoint.py
import sys
import json
from lambda_function import lambda_handler
def main():
# コマンドライン引数を解析
if len(sys.argv) > 1:
try:
event = json.loads(sys.argv[1])
except json.JSONDecodeError:
print("Invalid JSON input")
return
# Lambda関数の呼び出し
lambda_handler(event, None)
if __name__ == "__main__":
main()
- 開発環境においてコマンドライン引数を渡す場合に使用
- コマンドライン引数をJSON形式のイベントデータとしてパースし、Lambda関数に渡す
- Lambdaでは、イベントが自動的にLambda関数に渡され、ランタイムが指定されたハンドラ関数を呼び出すため、本番環境では特別なエントリーポイントスクリプトは不要
lambda_function.py
lambda_function.py
def lambda_handler(event, context):
# メインロジック
return {
'StatusCode': 200,
'Body': '正常に実行されました'
}
- Lambda関数のメインロジックを含むスクリプト
- イベントデータを受け取り、指定されたタスクタイプに応じて処理を実行
実行方法(ローカル環境)
■ 環境
- Docker
■ 実行方法
-
環境変数の設定
.env
をプロジェクトのルートディレクトリに作成する。 -
Dockerイメージのビルド
$ docker build -t docker-lambda -f Dockerfile.dev .
-
実行
$ docker run --env-file .env '{"event":"event-object"}'
本番環境
■ 環境
- Lambda
- ECR
- EventBridge
■ デプロイ方法
- ECR
[プッシュコマンドの表示]
から手順通りにECRにイメージをプッシュ - Lambda
[新しいイメージをデプロイ]
でECRにプッシュした最新のイメージを選択する
■ テスト方法
- Lambdaのテストイベントから下記の形式で実行可能
{ "event": "event-object" }
■ 実行方法
- EventBridgeで定期実行
利点
- 実装効率
- 開発時には下記のように3ステップがかかってしまうが、そこまで負荷ではない
- 実装
- イメージのビルド
- 実行
- 開発時には下記のように3ステップがかかってしまうが、そこまで負荷ではない
- ツールのキャッチアップ
- Dockerさえ使えれば何をしているかはコードに書かれているため、キャッチアップが容易
- エンジニアはGUIでの操作より、コードを読んでコマンドを実行する方が手っ取り早い
まとめ
コンテナを用いて開発環境だけでなく本番環境まで運用している事例はあまり聞かないが、Lambda開発においてはLambdaがコンテナイメージを標準でサポートしているため、本番での環境構築も運用もスムーズに行える。
また、ECSであれば自然と同じような構成になるが、Lambdaは開発環境の選択肢が多いため、様々な構成が取られている。さらに、コンテナイメージがZIPファイルアップロードより後に採用されたため、あまりこの構成が取られていなかったのかもしれない。
Discussion