2023/9/30(土) 19時~開催の「JAWS FESTA 2023 in Kyusyu 直前スペシャル!!」における私の発表「AWS CDKでインフラ、アプリを分離した際に困ったこと」の発表資料になります。 #jawsfesta #jawsfesta2023 #jawsug
© DeNA Co., Ltd. 1AWS CDKでインフラ、アプリを分離した際に困ったこと鈴木 正樹エンターテイメント事業本部 オープンプラットフォーム事業部ゲームプラットフォーム部 サーバーG株式会社ディー・エヌ・エー
View Slide
© DeNA Co., Ltd. 2鈴木 正樹・クラウドアーキテクト(ほぼAWS)・IaC(特にAWS CDK)& Lambda が好き・TypeScript / Node.js / VS Codeなど株式会社DeNA エンターテイメント事業本部 所属AWS Community Builder(Serverless) ※2023〜https://github.com/smt7174@makky12https://makky12.hatenablog.com/© DeNA Co., Ltd.自己紹介@makky12.bsky.social
© DeNA Co., Ltd. 3アジェンダアーキテクチャ概要CDK定義とソースコードの分離イメージタグの共有213初回デプロイ時のイメージタグの扱い4まとめ5
© DeNA Co., Ltd. 41● 本発表は9/16(土)開催の「四国クラウドお遍路2023」の内容をベースに、内容を加筆・修正したものです● 時間の関係で、下記事項の説明は省略します○ GitHub の設定・運用に関する話○ CI/CD(特にCD)に関する話● 今回の解決方法が「ベストプラクティス」というわけではないです○ あくまで解決方法の一例です※ 発表に関して
© DeNA Co., Ltd. 5アーキテクチャ概要
© DeNA Co., Ltd. 61 アーキテクチャ概要● アーキテクチャはおおむね下記の通り(関係分のみ記載)● 赤枠で囲った部分が重要1※インフラ側のCodePipeline(CPl)でリソースを作成※アプリ側のCPlでdocker push&イメージタグ付与※CPl自体はどちらもインフラ側で作成
© DeNA Co., Ltd. 7CDK定義とソースコードの分離
© DeNA Co., Ltd. 81● LambdaおよびECSのソースコードはアプリ側に存在● Lambdaの定義自体(=CDKの定義)はインフラ側に存在アーキテクチャ概要(再掲)※インフラ側のCodePipeline(CPl)でリソースを作成※アプリ側のCPlでdocker push&イメージタグ付与※CPl自体はどちらもインフラ側で作成
© DeNA Co., Ltd. 92● ソースコードは以下の2種類(厳密には3種類だけど)○ ECS用(Webフロントエンド&バックエンド)○ Lambda用(Webサイト外から行いたい一部処理用)● ECS用はインフラ側にソースコードは不要(cdk deployでエラーにならない)○ タスク定義でECRを指定する● Lambda用はインフラ側にソースコードが必要では?○ CDKの仕様上、ソースコードのファイルパスが必須(だと思ってた)○ 実際にアプリ側にソースがある○ どうすれば正常にcdk deployできる?CDK定義とソースコードの分離
© DeNA Co., Ltd. 103 補足説明// Lambdaの例const function1 = new aws_lambda_nodejs.NodejsFunction(this, 'LambdaFunction1', {entry: '../lambda/index1.ts' // これ});const function2 = new aws_lambda.Function(this, 'LambdaFunction2', {code: aws_lambda.Code.fromAsset('../lambda'), //これもhandler: index2.handler',runtime: aws_lambda.Runtime.NODEJS_18_X,});// ECSコンテナはタスク定義でECRリポジトリを指定すればOKで、ソースコードのパス指定は不要const ecsWebContainer = taskDefinition.addContainer('WebContainer', {image: ecs.ContainerImage.fromEcrRepository(repo),...}
© DeNA Co., Ltd. 114● Lambda はコンテナイメージでもデプロイ可能● CDKにも DockerImageFunction という Constructs が用意されている● ソースコードをECRから取得する設定にすれば、ファイルパスは不要(下記参照)※ ECRリポジトリにLambdaイメージがないと、cdk deploy時にエラーになります※ マネジメントコンソールでコードを閲覧できない、Lambda Layerが使えない...など制約もあります対応方法:Lambda をコンテナイメージでデプロイするconst repo = new aws_ecr.Repository(this, 'EcrRepository');const function3 = new aws_lambda.DockerImageFunction(this, 'LambdaFunction3',{code: aws_lambda.DockerImageCode.fromEcr(repo), // 引数にECRリポジトリを指定});
© DeNA Co., Ltd. 125● 「初回のみ、仮のLambdaイメージをECRにpushする」という方法もあります● 「cdk-ecr-deployment」を使用すると、比較的簡単に実装可能です● URL:https://github.com/cdklabs/cdk-ecr-deployment● イメージタグの扱いは「初回デプロイ時のイメージタグの扱い」で触れます補足説明2:初回デプロイ時のLambdaイメージについてimport * as ecr_deployment from 'cdk-ecr-deployment';// ECRリポジトリの定義は省略const image = new DockerImageAsset(this, 'LambdaInitialOnlyImageAsset', {directory: path.resolve(__dirname, '../docker/initial_lambda'),});new ecr_deployment.ECRDeployment(this, 'LambdaInitialOnlyImage', {src: new ecr_deployment.DockerImageName(image.imageUri),dest: new ecr_deployment.DockerImageName(repo.repositoryUriForTag(‘initial’)),});
© DeNA Co., Ltd. 13イメージタグの共有
© DeNA Co., Ltd. 141 アーキテクチャ概要(再々掲)● アプリ側でイメージタグを採番&docker pushを実行● LambdaやECSの定義自体(=CDK定義)はインフラ側に存在※インフラ側のCodePipeline(CPl)でリソースを作成※アプリ側のCPlでdocker push&イメージタグ付与※CPl自体はどちらもインフラ側で作成
© DeNA Co., Ltd. 152 アプリ側で採番されたイメージタグのインフラ側への共有● アプリ側の CodePipelineでdocker push を実行○ この時にコンテナイメージのタグを採番(commit hashの先頭7文字)● インフラ側でcdk deployする際に、Lambdaにイメージタグの指定が必須○ 未指定の場合「latest」になってしまう(≒latest アンチパターン)● アプリ側&インフラ側で、イメージタグを共有する仕組みが必要● イメージタグの共有はどのように実現する?※ ECSは imagedefinitions.json を使用することで、イメージタグの即時反映が可能※ Lambda にそのような仕組みは存在しない
© DeNA Co., Ltd. 163 対応方法:パラメータストアを使う● アプリ側で docker push 後に、イメージタグをパラメータストアに保存する● インフラ側でcdk deployする際に、上記パラメータストアのイメージタグを取得する● CodePipeline自体はどちらもインフラ側で実装しているので、キー名の共有は容易○ CodeBuildの環境変数に設定しておく● 現在は、このパラメータストアを利用した対策がメジャーな模様○ https://mazyu36.hatenablog.com/entry/2023/03/05/115631○ https://speakerdeck.com/tomoki10/ideal-and-reality-when-implementing-cicd-for-ecs-on-fargate-with-aws-cdkconst imageTag = aws_ssm.StringParameter.valueForStringParameter(this, 'ImageTagKeyName');
© DeNA Co., Ltd. 17初回デプロイ時のイメージタグの扱い
© DeNA Co., Ltd. 181 初回デプロイ時のイメージタグの扱い● 初回デプロイ時は、パラメータストアにイメージタグが存在しない● この状態でcdk deployを行うと、ParameterNotFoundエラーで強制終了する● CFnでのデプロイ時に、try〜catchでのエラーハンドリングはできない(※)○ 「初回デプロイ時だけどうこう...」といったことはできない○ CDKを使用したパラメータ取得の処理は使えない● どうすれば、初回デプロイ時も問題なくデプロイ可能になる?※ 次スライドで説明
© DeNA Co., Ltd. 192 補足説明:エラーハンドリングができない理由● cdk deploy時に、内部的には以下の処理を行う○ CFnテンプレートの作成(cdk synth)○ CFnデプロイ(cdk deploy) ※上記CFnテンプレートを使用する● CFnテンプレート作成時には、パラメータストアからの値の取得は行われない○ 「Token」という仮の値が設定される● CFnデプロイ時に、パラメータストアからの値の取得を行う● CFnテンプレートは json/yaml ファイルなので、そもそも「エラーハンドリング」という機構が存在せず、エラーハンドリングができない
© DeNA Co., Ltd. 203 対応方法:AWS CLIでイメージタグを取得する● AWS CLIを使用して、CFnテンプレート作成時にPSからイメージタグを取得する○ テンプレート作成時なら、エラーハンドリングを使える● 初回(≒イメージタグがない場合)のみ、仮のタグを設定する(「initial」など)○ すぐにイメージ(=タグ)をアプリ側で更新するので問題なし● ECRの設定で、イメージタグをイミュータブル(上書き禁止)にする○ 何かのエラーでイメージタグが取得できなかった場合タグ重複エラーになるので、仮のLambdaイメージがpushされる事態を防げる
© DeNA Co., Ltd. 214 補足説明3:docker push時にLambdaでイメージタグを即時反映する● AWS CLIを使用すると、Lambdaでもdocker push時にイメージタグの即時反映が可能○ ECSのimagedefinitions.jsonのような仕組みが可能● docker push直後にaws lambda update-function-codeを実施する○ image-urlオプションにイメージタグを指定する● インフラ側とアプリ側で、Lambda関数名を合わせておく必要がある○ Lambda関数の新規追加時などに漏れが発生しないように注意● imagedefinitions.jsonみたいな仕組みがLambdaイメージでも対応されるといいなあ
© DeNA Co., Ltd. 22まとめ
© DeNA Co., Ltd. 231 まとめ● Lambdaをコンテナデプロイすることで、ソースコードの分離が可能○ インフラ側にソースコードを保存しなくても、デプロイ可能● インフラ側とアプリ側で値の共有には、パラメータストアを使用する○ 安全、かつ容易に値の共有が可能● AWS CLIの使用も検討する○ CDKだけではどうしようもないケースもある● 場合によっては「手作業でカバー」と割り切るのも一つの手かも○ 「初回限定」など発生頻度が限られる単純作業、あるいはCDKで困難な作業など1
© DeNA Co., Ltd. 24