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 でコンテナ動かしたい」ときに使います。