今回のお題
前々回、及び前回にわたり「LambdaをDockerイメージでデプロイする方法」について記載しました。
今回はその最終編ということで、前回紹介した「ハマりどころ」の続きを記載しようと思います。
なお今回触れる事項は、前々回に少し触れた「インフラ側とアプリ側でソース管理を分ける」ケースで発生するものです。
アジェンダ
- アプリ側のimageタグの設定をCDKにも反映するには?
- 初回のみイメージがなくてエラーになる現象を回避するには?
アプリ側のimageタグの設定をCDKにも反映するには?
インフラ側とアプリ側でソース管理を分ける場合、LambdaのDocker Imageに付与するイメージタグは、基本アプリ側に依存します。(よくあるのが、GitHubコミットハッシュの先頭数文字」)
そして、CDKを更新する場合に、CDKのLambda関数の定義にこのイメージタグを設定する必要があります。
前々回に紹介したEcrImageCodeProps
のpropsに「tagOrDigest」というプロパティがあり、これのデフォルト値は「latest」です。
なのでイメージタグを指定しないと該当Lambda関数が参照するイメージタグが強制的に「latest」になってしまい、当然そんなタグが付いたイメージはECRにないのでエラーになってしまいます。
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.EcrImageCodeProps.html
そのため、デプロイ時にアプリ側Lambdaのimageタグを取得する必要があります。
対策
これの対策としては「CDKとアプリ側でイメージタグを共有する仕組みを作る」方法があります。
やり方は色々ありますが「AWS SSM パラメータストアで値を共有する」方法がセキュアでよいのではないかと思います。
具体的には手順は下記の通り感じです。
- アプリ側でLambda関数を更新(=イメージタグを採番)する際、そのイメージタグをパラメータストアに登録する
- CDK側でデプロイを実施する際、1のパラメータストアの値を取得する
- 2の値を
tagOrDigest
に設定する
ポイントとしては下記の4点です。
- パラメータストアへの登録は、AWS CLIを使うと良いです。
- AWS CDKではパラメータストアへの登録はできません。(取得は可能)
- パラメータストアの値は、
aws-cdk-lib.aws_ssm
のStringParameter.valueForStringParameter()
で取得可能。- ただし通常の値のみ。「安全な文字列」はこれでは取得できません。
- 代わりに
SecretValue.ssmSecure().toString()
を使います。 - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.SecretValue.html#static-ssmwbrsecureparametername-version
- パラメーターストアのキー値は、アプリ側とCDK側で共有しておく
- 可能ならば環境変数などで設定しておく(CodePipelineなど)
- 初回のCDKデプロイ時(=アプリ側がまだ存在していない)のみ、該当キーの値が存在せずエラーになるので、何か対策しておく(例えば下記)
// 関連部分以外は省略 // パラメータストアからイメージタグタグの取得 const tag = StringParameter.valueForStringParameter(this, 'lambdaImageTag'); // イメージタグを適用(tagプロパティはdeprecatedなので使わないこと) const lambdaDockerEcrFunction = new lambda.DockerImageFunction(this, 'SampleLambdaDockerEcrFunction', { code: lambda.DockerImageCode.fromEcr(ecrRepo, { tagOrDigest: tag, }), });
初回のみイメージがなくてエラーになる現象を回避するには?
前々回でも触れましたが、DockerImageFunction
でLambda関数を作成する場合、指定したECRに該当Lambdaのイメージがないとデプロイ時にエラーになってしまいます。
通常はそれで問題ないですが、初回デプロイ時だけはアプリ側がまだ存在していないため、そのままだと100%エラーになってしまいます。
なので、初回デプロイ時のみこの問題に対策する必要があります。
対策:
これについては「初回限定の仮イメージを登録する」ことで対処可能です。
※初回限定なので、Lambda定義さえあれば中身は何でもよいです。
で、この「初回限定イメージのECR登録」については「cdk-ecr-deployment」を使用すると非常にシンプルに実装できます。
この「cdk-ecr-deployment」を用いて、下記の処理を行うことで対応できます。
- パラメータストアにイメージタグの値を取得する
- 1の値が取得不可だったら(=初回デプロイ時のみ発生)、イメージタグに仮の値を設定する(下記ソースでは「initail」)
- イメージタグが仮の値だったら、cdk-ecr-deploymentを使用してECRに仮のイメージを設定する*2
import * as ecr_deployment from 'cdk-ecr-deployment'; import { aws_ecr } as ecr from 'aws-cdk-lib'; import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets'; import path from 'path'; const INITIAL_IMAGE_TAG = 'initial'; // 1および2 // getParameterStoreValueは、AWS CLIでパラメータストアからイメージタグの値を取得する関数。(なければ空文字を返す) const imageTag = getParameterStoreValue(LATEST_IMAGE_TAG_KEY_NAME, profile) || INITIAL_IMAGE_TAG; // ECRの作成 const repo = new ecr.Repository(this, 'Repository', { ...(省略) }); // 3(初回のみ仮イメージをECRにpush) if (imageTag === INITIAL_IMAGE_TAG) { // 仮イメージの設定。 // directoryにはDockerfileファイルがあるフォルダのパスを指定 const image = new DockerImageAsset(this, 'DockerImageAssetInitialOnly', { directory: path.resolve(__dirname, '../docker'); }); new ecr_deployment.ECRDeployment(this, 'InitialOnlyImage', { // ここでは仮イメージをECRに設定 src: new ecr_deployment.DockerImageName(image.imageUrl), dest: new ecr_deployment.DockerImageName(repo.repositoryUriForTag(INITIAL_IMAGE_TAG)), // なおsrcにはローカルのイメージ以外にも、ECRパブリックリポジトリやDocker Hubなどのイメージも指定可能。 // src: new ecr_deployment.DockerImageName(`public.ecr.aws/docker/library/nginx:mainline-alpine3.18-slim`), }); }
ちなみにこのあたりの「アプリとインフラを分離した際に発生する問題」に関しては、9/30(土)に開催されたJAWS FESTA 2023 in Kyusyu 直前スペシャル!! にて「AWS CDKでインフラ、アプリを分離した際に困ったこと」という内容で発表を行いましたので、よろしければそちらの資料をご参照ください。
宣伝
11/19(日) 開催のJSConf JPにて、発表をさせて頂くことになりました。
内容としては「最近話題の(?)Bun について、実際にプロダクトワークロード(とりあえずLambda)で動かしたらどうなの?」という内容になりますので、よろしくお願いいたします。
それでは、今回はこの辺で。