Slide 1

Slide 1 text

© DeNA Co., Ltd. 1 AWS CDKでインフラ、アプリ を分離した際に困ったこと 鈴木 正樹 エンターテイメント事業本部 オープンプラットフォーム事業部 ゲームプラットフォーム部 サーバーG 株式会社ディー・エヌ・エー

Slide 2

Slide 2 text

© DeNA Co., Ltd. 2 鈴木 正樹 ・クラウドアーキテクト(ほぼAWS) ・IaC(特にAWS CDK)& Lambda が好き ・TypeScript / Node.js / VS Codeなど 株式会社DeNA エンターテイメント事業本部 所属 AWS Community Builder(Serverless) ※2023〜 https://github.com/smt7174 @makky12 https://makky12.hatenablog.com/ © DeNA Co., Ltd. 自己紹介 @makky12.bsky.social

Slide 3

Slide 3 text

© DeNA Co., Ltd. 3 アジェンダ アーキテクチャ概要 CDK定義とソースコードの分離 イメージタグの共有 2 1 3 初回デプロイ時のイメージタグの扱い 4 まとめ 5

Slide 4

Slide 4 text

© DeNA Co., Ltd. 4 1 ● 本発表は9/16(土)開催の「四国クラウドお遍路2023」の内容をベースに、内容を加筆・ 修正したものです ● 時間の関係で、下記事項の説明は省略します ○ GitHub の設定・運用に関する話 ○ CI/CD(特にCD)に関する話 ● 今回の解決方法が「ベストプラクティス」というわけではないです ○ あくまで解決方法の一例です ※ 発表に関して

Slide 5

Slide 5 text

© DeNA Co., Ltd. 5 アーキテクチャ概要

Slide 6

Slide 6 text

© DeNA Co., Ltd. 6 1 アーキテクチャ概要 ● アーキテクチャはおおむね下記の通り(関係分のみ記載) ● 赤枠で囲った部分が重要 1 ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成

Slide 7

Slide 7 text

© DeNA Co., Ltd. 7 CDK定義とソースコードの分離

Slide 8

Slide 8 text

© DeNA Co., Ltd. 8 1 ● LambdaおよびECSのソースコードはアプリ側に存在 ● Lambdaの定義自体(=CDKの定義)はインフラ側に存在 アーキテクチャ概要(再掲) ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成

Slide 9

Slide 9 text

© DeNA Co., Ltd. 9 2 ● ソースコードは以下の2種類(厳密には3種類だけど) ○ ECS用(Webフロントエンド&バックエンド) ○ Lambda用(Webサイト外から行いたい一部処理用) ● ECS用はインフラ側にソースコードは不要(cdk deployでエラーにならない) ○ タスク定義でECRを指定する ● Lambda用はインフラ側にソースコードが必要では? ○ CDKの仕様上、ソースコードのファイルパスが必須(だと思ってた) ○ 実際にアプリ側にソースがある ○ どうすれば正常にcdk deployできる? CDK定義とソースコードの分離

Slide 10

Slide 10 text

© DeNA Co., Ltd. 10 3 補足説明 // 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), ... }

Slide 11

Slide 11 text

© DeNA Co., Ltd. 11 4 ● 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リポジトリを指定 });

Slide 12

Slide 12 text

© DeNA Co., Ltd. 12 5 ● 「初回のみ、仮の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’)), });

Slide 13

Slide 13 text

© DeNA Co., Ltd. 13 イメージタグの共有

Slide 14

Slide 14 text

© DeNA Co., Ltd. 14 1 アーキテクチャ概要(再々掲) ● アプリ側でイメージタグを採番&docker pushを実行 ● LambdaやECSの定義自体(=CDK定義)はインフラ側に存在 ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成

Slide 15

Slide 15 text

© DeNA Co., Ltd. 15 2 アプリ側で採番されたイメージタグのインフラ側への共有 ● アプリ側の CodePipelineでdocker push を実行 ○ この時にコンテナイメージのタグを採番(commit hashの先頭7文字) ● インフラ側でcdk deployする際に、Lambdaにイメージタグの指定が必須 ○ 未指定の場合「latest」になってしまう(≒latest アンチパターン) ● アプリ側&インフラ側で、イメージタグを共有する仕組みが必要 ● イメージタグの共有はどのように実現する? ※ ECSは imagedefinitions.json を使用することで、イメージタグの即時反映が可能 ※ Lambda にそのような仕組みは存在しない

Slide 16

Slide 16 text

© DeNA Co., Ltd. 16 3 対応方法:パラメータストアを使う ● アプリ側で 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-cdk const imageTag = aws_ssm.StringParameter.valueForStringParameter(this, 'ImageTagKeyName');

Slide 17

Slide 17 text

© DeNA Co., Ltd. 17 初回デプロイ時のイメージタグの扱い

Slide 18

Slide 18 text

© DeNA Co., Ltd. 18 1 初回デプロイ時のイメージタグの扱い ● 初回デプロイ時は、パラメータストアにイメージタグが存在しない ● この状態でcdk deployを行うと、ParameterNotFoundエラーで強制終了する ● CFnでのデプロイ時に、try〜catchでのエラーハンドリングはできない(※) ○ 「初回デプロイ時だけどうこう...」といったことはできない ○ CDKを使用したパラメータ取得の処理は使えない ● どうすれば、初回デプロイ時も問題なくデプロイ可能になる? ※ 次スライドで説明

Slide 19

Slide 19 text

© DeNA Co., Ltd. 19 2 補足説明:エラーハンドリングができない理由 ● cdk deploy時に、内部的には以下の処理を行う ○ CFnテンプレートの作成(cdk synth) ○ CFnデプロイ(cdk deploy) ※上記CFnテンプレートを使用する ● CFnテンプレート作成時には、パラメータストアからの値の取得は行われない ○ 「Token」という仮の値が設定される ● CFnデプロイ時に、パラメータストアからの値の取得を行う ● CFnテンプレートは json/yaml ファイルなので、そもそも「エラーハンドリング」とい う機構が存在せず、エラーハンドリングができない

Slide 20

Slide 20 text

© DeNA Co., Ltd. 20 3 対応方法:AWS CLIでイメージタグを取得する ● AWS CLIを使用して、CFnテンプレート作成時にPSからイメージタグを取得する ○ テンプレート作成時なら、エラーハンドリングを使える ● 初回(≒イメージタグがない場合)のみ、仮のタグを設定する(「initial」など) ○ すぐにイメージ(=タグ)をアプリ側で更新するので問題なし ● ECRの設定で、イメージタグをイミュータブル(上書き禁止)にする ○ 何かのエラーでイメージタグが取得できなかった場合タグ重複エラーになるので、 仮のLambdaイメージがpushされる事態を防げる

Slide 21

Slide 21 text

© DeNA Co., Ltd. 21 4 補足説明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イメージでも対応されるといいなあ

Slide 22

Slide 22 text

© DeNA Co., Ltd. 22 まとめ

Slide 23

Slide 23 text

© DeNA Co., Ltd. 23 1 まとめ ● Lambdaをコンテナデプロイすることで、ソースコードの分離が可能 ○ インフラ側にソースコードを保存しなくても、デプロイ可能 ● インフラ側とアプリ側で値の共有には、パラメータストアを使用する ○ 安全、かつ容易に値の共有が可能 ● AWS CLIの使用も検討する ○ CDKだけではどうしようもないケースもある ● 場合によっては「手作業でカバー」と割り切るのも一つの手かも ○ 「初回限定」など発生頻度が限られる単純作業、あるいはCDKで困難な作業など 1

Slide 24

Slide 24 text

© DeNA Co., Ltd. 24