TL;DR
- Lambda がコンテナをサポートしたらしいので試してみる
- 動かすDocker イメージはLambda のAPI に対応させる必要があるため、今まで使用していたイメージがそのまま動くわけではない
- New for AWS Lambda – Container Image Support | AWS News Blog
概要
re:Invent の発表でコンテナの実行ができるようになったので、ざっくり試してみるだけの記事
ためす
失敗例
単純なAlpine イメージだと動かないらしいので失敗してみる
ECR へコンテナを上げる
env コマンドを実行するだけのイメージを作成
$ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
$ cat <<EOL | docker build -t ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container:latest -
FROM alpine
CMD ["env"]
EOL
ECR へリポジトリを作ってpush
$ aws ecr create-repository --repository-name lambda-container
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container:latest
Lambda の作成
Lambda のコンソールに「コンテナイメージ」が追加されているのでそこから設定。
ECR のパスはタグもしくはハッシュの指定までする必要あり。
自動生成してくれるIAM Role だと権限がたりないので AmazonEC2ContainerRegistryReadOnly と AWSLambdaBasicExecutionRole ポリシーを追加でアタッチする

実行結果

失敗した。
成功例
ドキュメントいわくRuntime interface clients (RIE) に対応する必要がある模様(カスタムランタイムの拡張なイメージ?)
Runtime support for Lambda container images - AWS Lambda
RIE に対応させるには公式で配布しているベースイメージを使用する他、主要な言語に対してはライブラリを提供している模様。
Node.js でRIE に対応させる例
$ npm install aws-lambda-ric
lambda 用イメージをみてみる
- DockerHub
- GitHub
OS としてはAmazonLinux2 を使用していている。
$ docker run -it --entrypoint=bash docker.io/amazon/aws-lambda-provided:latest bash-4.2# cat /etc/system-release Amazon Linux release 2 (Karoo)
ENTRYPOINT で /lambda-entrypoint.sh を実行するようになっており、みてみると aws-lambda-rie を叩いている。
$ docker inspect amazon/aws-lambda-provided | jq '.[].Config.Entrypoint' [ "/lambda-entrypoint.sh" ] $ docker run -it --entrypoint=bash docker.io/amazon/aws-lambda-provided:latest bash-4.2# cat /lambda-entrypoint.sh
#!/bin/sh
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
if [ $# -ne 1 ]; then
echo "entrypoint requires the handler name to be the first argument" 1>&2
exit 142
fi
export _HANDLER="$1"
RUNTIME_ENTRYPOINT=/var/runtime/bootstrap
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
exec /usr/local/bin/aws-lambda-rie $RUNTIME_ENTRYPOINT
else
exec $RUNTIME_ENTRYPOINT
fi
AWS_LAMBDA_* 変数は aws-lambda-rie が挿入してくれる。
HOSTNAME=34c12827a892 AWS_LAMBDA_FUNCTION_VERSION=$LATEST AWS_SESSION_TOKEN= AWS_LAMBDA_LOG_GROUP_NAME=/aws/lambda/Functions LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib LAMBDA_TASK_ROOT=/var/task AWS_LAMBDA_RUNTIME_API=127.0.0.1:9001 AWS_LAMBDA_LOG_STREAM_NAME=$LATEST AWS_LAMBDA_FUNCTION_NAME=test_function PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin PWD=/var/task AWS_SECRET_ACCESS_KEY= LAMBDA_RUNTIME_DIR=/var/runtime LANG=en_US.UTF-8 TZ=:/etc/localtime AWS_ACCESS_KEY_ID= HOME=/root SHLVL=1 _HANDLER=function.sh.handler AWS_LAMBDA_FUNCTION_MEMORY_SIZE=3008 _=/usr/bin/env
カスタムランタイム と同じノリでいけそう
シェルスクリプトを実行してみる
カスタムランタイムと同様にランタイムAPI を実行するようなコードを書いて実行
参考
- https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html
- https://aws.amazon.com/jp/blogs/aws/new-for-aws-lambda-container-image-support/
- https://aripalo.com/blog/2020/aws-lambda-container-image-support/
まずは以下の3ファイルを用意する
- bootstrap
- 名前の通りブートストラップ用コード
- function.sh
- 実行したい処理をこのファイルへ記述する
- 今回は公式リファレンスにあるコードをコピペ
- 基本的に(シェルスクを実行する場合)このファイルを変更して任意の処理を行うことになる
- Dockerfile
- AWS が提供している docker.io/amazon/aws-lambda-provided:latest イメージをベースとして使用して、 bootstrap function.sh の2ファイルを流し込むだけのDockerfile
bootstrap
#!/bin/sh
set -euo pipefail
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
HEADERS="$(mktemp)"
# Get an event. The HTTP request will block until one is received
EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
# Extract request ID by scraping response headers received above
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Run the handler function from the script
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
# Send the response
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
function.sh
function handler () {
EVENT_DATA=$1
echo "$EVENT_DATA" 1>&2;
RESPONSE="Echoing request: '$EVENT_DATA'"
echo $RESPONSE
}
Dockerfile
FROM amazon/aws-lambda-provided:latest COPY bootstrap /var/runtime/bootstrap COPY function.sh /var/task/function.sh RUN chmod 755 /var/runtime/bootstrap /var/task/function.sh EXPOSE 8080 CMD [ "function.sh.handler" ]
動作確認
ビルドして起動してhttp リクエストを受け付け、JSON をPOST してそのまま返ってきたら成功
$ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
$ docker build -t ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container:2
docker run -d -p 9000:8080 ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container:2
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"name":"hoge"}'
Echoing request: '{"name":"hoge"}'
作成したイメージをLambda で実行してみる
ビルドしたイメージをpush
$ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container:2
先程作成したLambda が参照しているイメージのタグを :latest から :2 へ変更

実行

動いた
片付け
消すもの - Lambda - CloudWatch Group - ECR
感想
- Lambda でコンテナを動かすには素のDocker イメージだと使えないのでRIE に対応する必要がある
- 公式で配布しているDocker イメージを使うか、ライブラリのインストールをする
- マネージドシェルスク実行サービスがほしかったので良いかなーと思ったけど、CodeBuild なりLambda のカスタムランタイムでよさそう。
- 雑なシェルスクを動かしたいとき、何も考えずにalpine イメージに乗せて動かしたかった
- 使うとしたらbootstrap ファイルを用意済みのLambda をイメージをECR に用意して、それをベースイメージとして使用するようにするかなという気持ち
- 他のLambda オプション同様、一度起動すると一定時間動いてくれる
- コールドスタートは発生するものの、Lambda の実行毎にpull をしなくてよい。よかった。
- 「VPC やタスク定義やECS クラスター動かすほどじゃないけど、AWS でコンテナ動かしたい」ときに使います。