こちらは、AWS LambdaとServerless Advent Calendar 2020 25日目の記事になります!
クリスマスの公開ですが季節感はないです
悪しからず🦵
はじめに
AWS re:Invent 2020にてLambdaのコンテナイメージのサポートが発表されましたが、
我らが愛するServerless Frameworkでも早々にサポートが行われたので、お試しで使ってみました🎄
serverless.yml
における定義については、こちらのブログにある通り、 <account>.dkr.ecr.<region>.amazonaws.com/<repository>@<digest>
の形式で指定する必要があるそうです
タグでなく、イメージのダイジェストで指定する必要があるとのことなので、 sls deploy
時にいい感じに注入する必要があります🥛
作ったもの
以下で公開しています🐙
https://github.com/yktakaha4/lambda-docker-serverless
なるべく簡単に動作を試せるように、
環境構築からデプロイまでに必要なもろもろをGitHub Actionsにまとめてます
変更すべき箇所はREADMEにまとめていますので併せてご覧ください
ポイント
Dockerファイルは、 public.ecr.aws/lambda/xxxxx
から作成する必要があるそうです
こちらにてベースイメージが公開されています🐋
あと、従来は serverless.yml
にて関数のエントリーポイントを設定していたものと思いますが、Dockerfileにて設定しておく必要があるようです
個人的に、ECSのScheduled Taskで単一のコンテナに複数の関数を含めて、定義ごとに動かすものを変えるということをやっていたのですが、現状だと個別にビルドしておく必要がある感じなのでしょうか...?
FROM public.ecr.aws/lambda/nodejs:12 AS builder
WORKDIR /opt/build
COPY . .
RUN npm ci
RUN npm run build
##### ##### ##### #####
FROM public.ecr.aws/lambda/nodejs:12 AS runner
COPY --from=builder \
/opt/build/package*.json \
./
COPY --from=builder \
/opt/build/dist \
./dist
RUN npm ci --only=production
# !!! ここポイント !!!
CMD ["dist/index.handler"]
イメージのビルドとECRへのプッシュが済んだら、イメージのダイジェストを取得し、 sls
コマンドに渡せるように環境変数に設定します
従来の set-env
は現在無効となっているので、新しい書き方でやってます
あと、今回初めて知ったのですが、GitHub Actionsの環境にはデフォルトで aws
コマンドがインストールされてるんですね...!
jobs:
deploy:
runs-on: ubuntu-18.04
timeout-minutes: 300
steps:
# 略
# ECR
- uses: aws-actions/amazon-ecr-login@v1
id: login-ecr
- run: |
docker build -t $REGISTRY/$REPOSITORY:$TAG .
docker push $REGISTRY/$REPOSITORY:$TAG
# !!! ここポイント !!!
echo "IMAGE_DIGEST=$(aws ecr describe-images --repository-name $REPOSITORY --image-ids imageTag=$TAG --output text --query 'imageDetails[0].imageDigest')" >> $GITHUB_ENV
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: lambda-docker-serverless-repos
TAG: latest
# Serverless Framework
- run: npm ci
- run: npm run deploy
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
IMAGE_DIGEST: ${{ env.IMAGE_DIGEST }}
serverless.yml
では、従来 handler
を使っていたところを、 image
でコンテナを指定します
#{AWS::AccountId}
など、シャープで始まるものは [serverless-pseudo-parameters]
(https://www.serverless.com/plugins/serverless-pseudo-parameters)の働きにより実値が埋め込まれます
service: lambda-docker-serverless
provider:
name: aws
stage: prod
region: ${env:AWS_DEFAULT_REGION}
deploymentBucket: lambda-docker-serverless-deployment
functions:
index:
# !!! ここポイント !!!
image: "#{AWS::AccountId}.dkr.ecr.#{AWS::Region}.amazonaws.com/lambda-docker-serverless-repos@${env:IMAGE_DIGEST}"
events:
- http:
path: index
method: post
cors: true
plugins:
- serverless-pseudo-parameters
今回は、こんなシンプルな関数を作ってみました
リクエストで受け取った名前を大文字にして、挨拶を返す処理になります👋
import { APIGatewayProxyHandler } from "aws-lambda";
import "source-map-support/register";
interface Request {
name?: string;
}
export const handler: APIGatewayProxyHandler = async (event) => {
try {
const { name } = JSON.parse(event.body ?? "{}") as Request;
return {
statusCode: 200,
body: JSON.stringify({
message: `Hello, ${(name ?? "nanashi-san").toUpperCase()} !`,
}),
};
} catch (e) {
return {
statusCode: 500,
body: JSON.stringify({
error: String(e),
}),
};
}
};
https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/index
にアクセスしてみます
Insomniaで実行してみると、ちゃんと動いてそうでいい感じです!
おわりに
当初は、関数内でPuppeteerを動かそうと少し試していたのですが、
従来の一番安定したやり方だったchrome-aws-lambdaを使うよりも楽に構築できると思いきや、ライブラリの不足やディレクトリの権限周りの問題で結構留意することが多かったので諦めました...
(ちなその残骸はこちらにあります⚰️)
従来実行環境の微妙な差異に悩まされることもちょいちょいあったように思いますが、
今後コンテナベースの環境で開発ができるようになると利便性が上がるので、引き続き動向を追っていきたいですね🦌🦌🎅🎁