🐳

Dockerを活用したLambdaの開発&本番環境のおすすめ構成

に公開

はじめに

  • Lambdaの開発&本番環境の構成について色々と試してきた結果、Dockerを活用する構成に落ち着いてきたので、オススメとして&自身のメモとして残しておきます!
  • konan0802/lambda-docker-templateにソースコードを公開しています

Lambda開発の一般的な課題

  • 開発環境の構築
    • 本番環境と同じように動作できる構成をどのように取るか
  • 複数環境におけるデプロイ
    • DEV、STG、PRDなど環境が増えると、デプロイが複雑になる
    • 自動化すれば良いものの、複雑なものは自動化するのには苦労する

よくある構成例と課題

1. Lambda関数内でのエイリアス分離 or Lambda関数ごと分離

  • Lambda関数内でエイリアス(prd、stgなど)で環境を分けることができ、stgで検証したバージョンをprdにアタッチすることでリリースが行える。
  • また当然ながらLambda関数ごとに分離する構成も取ることも手である。
  • しかし、あくまでもSTG環境としての利用でありDEV環境として利用するには都度デプロイして検証するのは手間となる。

2. AWS SAMServerless Framework の利用

  • Lambdaの開発環境やデプロイを自動化するためのツール。
  • 一度セットアップすれば、実装からリリースまでの効率を非常に高められる。
  • しかしツール自体のキャッチアップが必要であり、チームメンバーのキャッチアップコストなども検討材料になる。
  • またこの手のツールはIAMで相当の権限を渡す必要があり、権限設定やセキュリティに懸念を感じることがある。

Dockerを活用したLambdaの開発&本番環境のおすすめ構成

概要

  • LambdaではZIPファイルのデプロイに加えて、コンテナイメージを利用することもできる。
  • Dockerを使えば開発環境の用意はもちろん、本番環境にもそのまま利用できるため、一貫性のある構成を作ることができる。

ディレクトリ構成

├── .env
├── Dockerfile
├── Dockerfile.dev
├── README.md
├── app
│   ├── __init__.py
│   ├── entrypoint.py
│   ├── lambda_function.py
│   ├── modules
│   │   ├── __init__.py
│   │   └── module_a.py
│   └── repositories
│       ├── __init__.py
│       └── sample_db.py
└── requirements.txt

コード例

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 app ./

# Lambdaハンドラ関数の指定 (lambda_function.pyのlambda_handler関数を指定)
CMD ["lambda_function.lambda_handler"]

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 app ./

# entrypoint.pyをエントリーポイントとして指定
ENTRYPOINT ["python", "entrypoint.py"]

entrypoint.py

ローカル開発のエントリーポイント。引数をJSONとしてLambda関数に渡す。

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_handler(event, None)
    else:
        lambda_handler(None, None)

if __name__ == "__main__":
    main()

lambda_function.py

Lambdaハンドラ本体。モジュールの呼び出しやDBモックとの連携を行います。

lambda_function.py
from modules import module_a
from repositories import sample_db


def lambda_handler(event, context):
    """
    サンプルLambdaハンドラ。
    module_aとsample_dbの両方を呼び出し、処理結果をまとめて返すだけ。
    """

    greeting = module_a.greet_user(event.get("name", "NoName"))
    db_response = sample_db.mock_db_access()

    print(
        {
            'StatusCode': 200,
            'Greeting': greeting,
            'DBResponse': db_response,
            'ReceivedEvent': event
        }
    )

    return {
        'StatusCode': 200,
        'Body': '正常に実行されました'
    }

実行方法(ローカル環境)

■ 環境

  • Docker

■ 実行方法

  1. 環境変数の設定
    .envをプロジェクトのルートディレクトリに作成する。
  2. Dockerイメージのビルド
    $ docker build -t docker-lambda-dev -f Dockerfile.dev .
    
  3. 実行
    $ docker run --rm docker-lambda-dev '{"name":"Alice"}'
    

本番環境

■ 環境

  • Lambda
  • ECR
  • 必要に応じてEventBridgeなど

■ デプロイ方法

  1. ECR
    [プッシュコマンドの表示]から手順通りにECRにイメージをプッシュ
  2. Lambda
    [新しいイメージをデプロイ]でECRにプッシュした最新のイメージを選択する

■ テスト方法

  • Lambdaのテストイベントから下記の形式で実行可能
    {
      "name": "Alice"
    }
    

利点

  • 実装効率
    • 開発時には下記のように3ステップがかかってしまうが、まぁそこまで負荷ではない
      1. イメージのビルド
      2. 実行
  • ツールのキャッチアップ
    • Dockerさえ使えれば何をしているかはコードに書かれているため、キャッチアップが容易
    • エンジニアはGUIでの操作より、コードを読んでコマンドを実行する方が手っ取り早い

まとめ

コンテナを用いて開発環境だけでなく本番環境まで運用している事例はあまり聞かないが、Lambda開発においてはLambdaがコンテナイメージを標準でサポートしているため、本番での環境構築も運用もスムーズに行えます。
より詳しいソースコードはkonan0802/lambda-docker-templateを参照して、ぜひ試してみてください。

GitHubで編集を提案

Discussion