blog top

コンテナ Lambda お手軽環境構築

AWSベースイメージを使ったコンテナLambdaはチュートリアルがあります(例: Node.js, Python)。

この手順通りに進めると確かにローカル開発できるのですが、ソースコードの変更ごとにdocker buildが発生するので非常に面倒です。

そこで、チュートリアルのDockerfileは変えずに、docker runのオプションを駆使してローカル開発をしてみました。

※以下ではPythonのベースイメージを使用しています。

準備

まず以下のようなファイル構成にします。

sample_function
├── Dockerfile
├── lambda_function.py
└── requirements.txt

Dockerfileは以下のようになります(公式の通りです)。

FROM public.ecr.aws/lambda/python:3.12

# Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# Install the specified packages
RUN pip install -r requirements.txt

# Copy function code
COPY lambda_function.py ${LAMBDA_TASK_ROOT}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "lambda_function.handler" ]

lambda_function.pyは単純なprintreturnだけにしました(ここは任意です)。

def handler(event, context):
    print("Hello, world!")
    return "ok"

ここではまだライブラリを使用しないのでrequirements.txtは空で良いです。

ビルド

sample_functionディレクトリに入って以下のコマンドを実行します。

docker build --platform linux/amd64 -t sample_function .

手元の環境ではM1 Mac (arm64) で実行しているので--platformが入っています。

実行

以下の通りdocker runで実行します。ここで該当のスクリプトをマウントするようにしています。

docker run --rm --platform linux/amd64 -v $(pwd)/lambda_function.py:/var/task/lambda_function.py -p 9000:8080 sample_function lambda_function.handler

また、Lambdaへのリクエストはこのようになります。

curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

以下は実行結果のスクリーンショットです(左: docker run、右: curl)。

ローカルでコンテナLambdaを実行した結果

ただし、ホットリロードのような機能は無いので、ソースコードを変更するたびにdocker runし直す必要があります。

参考: ホットリロードを実装してみた

※ここからは実験的なものなので、あまり実用的では無いかもしれません。

以下は1秒ごとにソースコードの変更を検知し、コンテナを起動し直すbashスクリプトです。

#!/bin/bash

COMMAND="docker run -d --rm --platform linux/amd64 -v $(pwd)/lambda_function.py:/var/task/lambda_function.py -p 9000:8080 sample_function lambda_function.handler"

# 監視するファイルのパス
FILE_TO_WATCH="./lambda_function.py"

# 初期タイムスタンプを取得
initial_timestamp=$(stat -f "%a" "$FILE_TO_WATCH")

# コンテナを起動し、コンテナIDを取得
container_id=$($COMMAND)

log() {
  docker logs -f $container_id &> ./logs.txt &
}

log

# コンテナを削除する関数
cleaning() {
  docker kill $container_id > /dev/null
}

# 終了時にコンテナを削除
trap cleaning EXIT

# 無限ループでファイルの変更を監視
while true; do
  # 現在のタイムスタンプを取得
  current_timestamp=$(stat -f "%a" "$FILE_TO_WATCH")

  # タイムスタンプが変更された場合
  if [ "$current_timestamp" -ne "$initial_timestamp" ]; then
    echo "File has been modified."

    cleaning

    container_id=$($COMMAND)

    log

    # タイムスタンプを更新
    initial_timestamp=$current_timestamp
  fi

  # 次のチェックまで待機
  sleep 1
done

このときログはlogs.txtに出力されるので、tail -f logs.txtなどで確認することができます。