Node.jsなLambdaを、お手軽にコンテナ化して運用したい。
分かりやすいメリットとしては、以下のようなものがある。
-
Lambda Layers等で頑張って導入していた外部ライブラリを全てイメージ内に同梱できるため、取り回し易い
- Lambda Runtime同梱のAWS-SDKと異なるバージョンのAWS-SDKを使いたい場合も、これに該当する
- コードパッケージサイズ上限が10GiBであるため、コードサイズに関して普段ほぼ気にしなくてよくなる
サンプルアプリ
環境変数 NAME
を出力するLambdaである「my-app
」を例として話を進めていく(あくまで例なので、処理自体に深い意味は無い)。
index.js
環境変数を読み込むために、外部ライブラリとして env-var
を使用している。
const env = require('env-var');
const NAME = env.get('NAME').required().asString();
exports.handler = async (event) => {
console.debug('Say hello to CloudWatch Logs');
return `It's me, ${NAME}!`;
};
package.json
おそらくこんな感じ。
env-var
をインストールしたからには package-lock.json
も生成されているのであろう。
{
"name": "my-app",
"version": "1.0.0",
"private": true,
"main": "index.js",
"dependencies": {
"env-var": "^7.4.1"
}
}
コンテナ化
公式の導入手順と、以下の情報を、合わせて眺めていくと分かり易いはず。
Dockerfile
Dockerfileを作成する。
AWSベースイメージを土台として、手元の構成を再現すればよい。
FROM public.ecr.aws/lambda/nodejs:18
COPY index.js ./
COPY package.json ./
COPY package-lock.json ./
RUN npm clean-install
CMD ["index.handler"]
Amazon ECR
イメージの保存先であるECRリポジトリを、Lambdaと同リージョンにて作成する。
Lambdaサービスがイメージを読み込むことになるため、その動作を許可しておくこと。
以下はCloudFormationでの設定例。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions
#
# ...略...
#
Resources:
EcrRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: my-app
#
# ...略...
#
RepositoryPolicyText:
Fn::ToJsonString:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
イメージの作成とプッシュ
イメージを作成してECRリポジトリにプッシュする。
以下はコマンド例。
docker build . --tag my-app:local
aws ecr get-login-password | docker login --username AWS --password-stdin 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag my-app:local 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:v1.0.0
docker push 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:v1.0.0
Lambdaでのイメージ使用
Lambdaの設定において、プッシュしたイメージを指定する。
以下はCloudFormationでの設定例。
AWSTemplateFormatVersion: '2010-09-09'
#
# ...略...
#
Resources:
LambdaFunc:
Type: AWS::Lambda::Function
Properties:
PackageType: Image
Code:
ImageUri: 111122223333.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:v1.0.0
#
# ...略...
#
Environment:
Variables:
NAME: Red
以上
このように、AWSベースイメージを元に簡単にコンテナ運用することができる。
TypeScript組み込み例
TypeScriptにした場合でも基本は変わらず、トランスパイルしたものをイメージに組み込む、という処理をそれっぽく行えばよい。
以下は組み込み例。
package.json (TypeScript)
esbuild
した成果物を dist
ディレクトリに出力するような build
を設定する。
{
"name": "my-app",
"version": "1.0.0",
"private": true,
"main": "index.js",
"dependencies": {
"env-var": "^7.4.1"
},
"devDependencies": {
"esbuild": "^0.19.8"
},
"scripts": {
"build": "rm -rf dist && esbuild index.ts --bundle --minify --sourcemap --platform=node --target=node18 --outdir=dist"
}
}
Dockerfile (TypeScript)
ベースイメージにて用意されている環境変数 LAMBDA_TASK_ROOT
(ベースイメージの WORKDIR
でもある)を利用して、ビルド出力物である dist
ディレクトリの中身だけをイメージに組み込む。
FROM public.ecr.aws/lambda/nodejs:18 AS builder
COPY index.ts ./
COPY package.json ./
COPY package-lock.json ./
COPY tsconfig.json ./
RUN npm clean-install
RUN npm run build
FROM public.ecr.aws/lambda/nodejs:18
COPY --from=builder ${LAMBDA_TASK_ROOT}/dist/* ./
CMD ["index.handler"]