概要
Lambda + API GatewayをSAMを使って構成するときに、カスタムイメージを使ったlambda関数に関する情報があまり出て来なかったので、試行錯誤した内容を残す。具体的には以下の内容。
- lambda関数をカスタムイメージで作るときに、作った関数をunit testするときのtips
カスタムイメージを使ってlambda関数をunit testすることの難しさ
カスタムイメージを前提としたLambda + APIGatewayを構成しようとすると、大体以下のようなディレクトリ構成になる。
.
├── README.md
├── __init__.py
├── lambda_function
│ ├── Dockerfile
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── events
│ └── event.json
├── samconfig.toml
├── template.yaml
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_handler.py
一方、lambda関数を動かすカスタムイメージとして、公式では以下のようなDockerfile
を例示している。
# Define function directory
ARG FUNCTION_DIR="/function"
FROM python:buster as build-image
# Install aws-lambda-cpp build dependencies
RUN apt-get update && \
apt-get install -y \
g++ \
make \
cmake \
unzip \
libcurl4-openssl-dev
# Include global arg in this stage of the build
ARG FUNCTION_DIR
# Create function directory
RUN mkdir -p ${FUNCTION_DIR}
# Copy function code
COPY app/* ${FUNCTION_DIR}
# Install the runtime interface client
RUN pip install \
--target ${FUNCTION_DIR} \
awslambdaric
# Multi-stage build: grab a fresh copy of the base image
FROM python:buster
# Include global arg in this stage of the build
ARG FUNCTION_DIR
# Set working directory to function root directory
WORKDIR ${FUNCTION_DIR}
# Copy in the build image dependencies
COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}
ENTRYPOINT [ "/usr/local/bin/python", "-m", "awslambdaric" ]
CMD [ "app.handler" ]
要は以下の感じ。
- 特定のディレクトリ(
FUNCTION_DIR
)にpythonのresourceを一通りpip install
- multi-stage buildでまっさらな環境にコピーして、lambda関数の実行環境・コマンド等を整備
ただ、この通り作ると、以下の観点でlambda関数のunit testがやりづらくなる。
-
ローカル(imageの外)でのテストがやりづらい
- 処理がpythonで完結しない(python以外のモジュールを呼び出してる、など)場合は、それらをローカルにインストールする必要があるが、unit testのためだけにそれする?
-
buildしたimageを使っても、微妙にテストしづらい
- テストを実行するにあたって、
ENTRYPOINT
が微妙に邪魔
- テストを実行するにあたって、
multi-stage buildで生成される中間imageを利用する
ところで先ほどのDockerfileの例を見ると、multi-stage buildが利用されている。
multi-stage buildを使うと、最終的に生成されるimage以外に中間stageも生成される(以下の<none>
ってやつ)。これを使えば良いのでは?というのが、今回のコンセプト。
REPOSITORY TAG IMAGE ID CREATED SIZE
lambda-function latest 77fa66e7adc8 12 minutes ago 3.37GB
<none> <none> 70dd38f37db9 12 minutes ago 4.29GB
具体的には、FUNCTION_DIR
にテストに必要な資材をmountし、<none>
のイメージを使ってテストを実施する、という感じ。
docker run --rm
--mount type=bind,src="$PWD/lambda_function",dst="$FUNCTION_DIR/lambda_function"
--mount type=bind,src="$PWD/tests",dst="$FUNCTION_DIR/tests"
-w $FUNCTION_DIR
$(docker images -f "dangling=true" -q)
/bin/bash -c "pip install pytest pytest-mock mocker && python -m pytest tests"
終わりに
なんかもっといい方法ありそうなんだよなぁ、、、