echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【AWS CDK】LambdaをDockerイメージでデプロイする方法(後編)

今回のお題

前回、LambdaをDockerイメージでデプロイする方法の前編として「AWS CDKの定義」について書きました。

今回はその後編として「Dockerfileの定義&ハマりどころ」について記載したいと思います。

  • 前編:AWS CDKの定義(これは前回)
  • 後編:Dockerfileの定義&ハマりどころ(今回はここ)

Dockerfileの定義

Dockerfileの定義ですが、これは下記AWS公式ドキュメントに沿って記載すればOKです。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-image.html

上記ドキュメントにも記載がある通り、Amazon ECR リポジトリでもLambda用のnode.jsイメージが公開されているので、特に理由がない限りはそれを使えばOKです。

https://gallery.ecr.aws/lambda/nodejs

実際の記載例は下記の通りです。(前編で紹介したコードそのままです)

なおindex.jsが実際のLambdaのソースコードです。(TypeScriptの場合、事前にビルドしておいて下さい)

# Dockerfile
# バージョンは適宜選択
FROM public.ecr.aws/lambda/nodejs:18
# /var/task/は、Lambda環境変数LAMBDA_TASK_ROOTの値
WORKDIR /var/task/
# Copy function code
COPY index.js .
  
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "index.handler" ]

ポイントはDocker側で /var/task/ フォルダにLambdaのソースコードをコピーすることです。(/var/task/AWSでLambdaソースファイルがコピーされるフォルダで、Lambda環境変数 LAMBDA_TASK_ROOT でも確認できます)

また最後に CMD でエントリポイントとしてハンドラー関数を指定します。(これを忘れると実行できません)

なお上記Dockerfileにはないですが、node_moduleを使用する場合、事前にpackage.jsonのコピーや RUN npm install を忘れないでください。(前回説明した通り、Dockerでデプロイした場合はLambda Layerが使用できないので)

それとDockerfileは上位のファイルは参照できないので、ご注意を。(パスに ../ などは設定できない)

あとは該当のECRにDockerイメージをpushすればOKです。特に問題なければ、Lambdaが実行できるはずです。(Lambda関数URLなりAPI Gateway経由なり)

ハマりどころ:複数のLambda関数をデプロイする場合はどうするの?

次はLambdaをDockerイメージでデプロイする際のハマりどころですが、「複数のLambda関数をデプロイする場合はどうするの」という点です。

Dockerfileの COPY コマンドで複数のLambdaファイルをコピーできますが、CMD コマンドでエントリポイントに指定できるLambda関数は1つだけです。
なので下記のようなコードでは正しく動作しません。

かといって、各Lambda関数ごとにDockerイメージを作成するのは非常に手間です。

# 注意:このコードは正しく動きません
FROM public.ecr.aws/lambda/nodejs:18
WORKDIR /var/task/
# Copy function code
COPY index.js .
COPY index2.js .
COPY index3.js .
  
# このコードを実行しても、エントリポイントとして動くのはindex3.handlerだけです。(上書き更新)
CMD [ "index.handler", "index2.handler", "index3.handler" ]

ではどうやるかというと、下記の方法で実行できます

  • Lambda関数単位でフォルダを分ける
  • エントリポイントをAWS CDK側で定義する

Lambda関数単位でフォルダを分ける

これは名前の通り、Docker側のフォルダをLambda関数単位で分けます。

先ほどの正しく動かないDockerfileを例にすると、下記のように記載すればOKです。

FROM public.ecr.aws/lambda/nodejs:18
WORKDIR /var/task/
  
# Lambda関数単位でフォルダを分ける
COPY index.js ./func1/index.js
COPY index2.js ./func2/index2.js
COPY index3.js ./func3/index3.js
  
# Dockerfile内でエントリポイントを定義しないので、CMDは削除

エントリポイントをCDK側で定義する

エントリポイントはDockerfileではなくAWS CDK側で定義するので、上記の通り CMD は削除します。

その代わり、AWS CDK の DockerImageFunction 側でエントリポイントを定義します。(下記コードを参照)

// fromEcr() を使用してますが、fromImageAsset()でも同じです。  
// ecrRepoは事前に作成したECRのインスタンス
const func1 = new lambda.DockerImageFunction(this, 'Function1', {
  code: lambda.DockerImageCode.fromEcr(ecrRepo, {
    // fromEcrのpropsにcmdがあるので、そこでエントリポイントを定義する
    // 指定するパスは「/var/task/」以下を記載する
    cmd: ['./func1/index.handler'],
  }),
  functionName: 'Function1',
});
  
const func2 = new lambda.DockerImageFunction(this, 'Function2', {
  code: lambda.DockerImageCode.fromEcr(ecrRepo, {
    cmd: ['./func2/index2.handler'],
  }),
  functionName: 'Function2',
});
  
const func3 = new lambda.DockerImageFunction(this, 'Function3', {
  code: lambda.DockerImageCode.fromEcr(ecrRepo, {
    cmd: ['./func3/index3.handler'],
  }),
  functionName: 'Function3',
});

あとはECRにDockerイメージをpushすれば、どのLambda関数も正しく動作するはずです。

告知

明日(9/12(火)) に、名古屋で開催の「JAWS-UG 名古屋 AWS認定試験を受けてみようと思った方へ送る勉強会」イベントにて、「AWS ソリューションアーキテクト アソシエイト試験体験記」についてお話しします。(オフライン)

jawsug-nagoya.doorkeeper.jp

またその翌日の9/13(水)に開催される「JAWS-UG CDK支部 #9」イベントにて、AWS CDK座談会のパネラーの一人として参加します。(オンライン)

jawsug-cdk.connpass.com

さらに9/16(土)に愛媛県松山市で開催される「四国クラウドお遍路 2023 - 四国の外のモノサシを知ってみよう-」イベントにて、「AWS CDKでインフラ、アプリを分離した際に困ったこと」という内容でLTをさせていただきます。(オフライン)

jawsohenro.doorkeeper.jp

上記イベントに参加される方、およびこれから参加しようと思っている方、ぜひよろしくお願いいたします。

それでは、少し時間が空いてしまいましたが、今回はこの辺で。