Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AWS CDKでインフラ、アプリを分離した際に困ったこと

Makky12
September 30, 2023

AWS CDKでインフラ、アプリを分離した際に困ったこと

2023/9/30(土) 19時~開催の「JAWS FESTA 2023 in Kyusyu 直前スペシャル!!」における私の発表「AWS CDKでインフラ、アプリを分離した際に困ったこと」の発表資料になります。 #jawsfesta #jawsfesta2023 #jawsug

Makky12

September 30, 2023
Tweet

More Decks by Makky12

Other Decks in Technology

Transcript

  1. © DeNA Co., Ltd. 1 AWS CDKでインフラ、アプリ を分離した際に困ったこと 鈴木 正樹

    エンターテイメント事業本部 オープンプラットフォーム事業部 ゲームプラットフォーム部 サーバーG 株式会社ディー・エヌ・エー
  2. © 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
  3. © DeNA Co., Ltd. 4 1 • 本発表は9/16(土)開催の「四国クラウドお遍路2023」の内容をベースに、内容を加筆・ 修正したものです •

    時間の関係で、下記事項の説明は省略します ◦ GitHub の設定・運用に関する話 ◦ CI/CD(特にCD)に関する話 • 今回の解決方法が「ベストプラクティス」というわけではないです ◦ あくまで解決方法の一例です ※ 発表に関して
  4. © DeNA Co., Ltd. 6 1 アーキテクチャ概要 • アーキテクチャはおおむね下記の通り(関係分のみ記載) •

    赤枠で囲った部分が重要 1 ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成
  5. © DeNA Co., Ltd. 8 1 • LambdaおよびECSのソースコードはアプリ側に存在 • Lambdaの定義自体(=CDKの定義)はインフラ側に存在

    アーキテクチャ概要(再掲) ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成
  6. © DeNA Co., Ltd. 9 2 • ソースコードは以下の2種類(厳密には3種類だけど) ◦ ECS用(Webフロントエンド&バックエンド)

    ◦ Lambda用(Webサイト外から行いたい一部処理用) • ECS用はインフラ側にソースコードは不要(cdk deployでエラーにならない) ◦ タスク定義でECRを指定する • Lambda用はインフラ側にソースコードが必要では? ◦ CDKの仕様上、ソースコードのファイルパスが必須(だと思ってた) ◦ 実際にアプリ側にソースがある ◦ どうすれば正常にcdk deployできる? CDK定義とソースコードの分離
  7. © 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), ... }
  8. © 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リポジトリを指定 });
  9. © 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’)), });
  10. © DeNA Co., Ltd. 14 1 アーキテクチャ概要(再々掲) • アプリ側でイメージタグを採番&docker pushを実行

    • LambdaやECSの定義自体(=CDK定義)はインフラ側に存在 ※インフラ側のCodePipeline(CPl)でリソースを作成 ※アプリ側のCPlでdocker push&イメージタグ付与 ※CPl自体はどちらもインフラ側で作成
  11. © DeNA Co., Ltd. 15 2 アプリ側で採番されたイメージタグのインフラ側への共有 • アプリ側の CodePipelineでdocker

    push を実行 ◦ この時にコンテナイメージのタグを採番(commit hashの先頭7文字) • インフラ側でcdk deployする際に、Lambdaにイメージタグの指定が必須 ◦ 未指定の場合「latest」になってしまう(≒latest アンチパターン) • アプリ側&インフラ側で、イメージタグを共有する仕組みが必要 • イメージタグの共有はどのように実現する? ※ ECSは imagedefinitions.json を使用することで、イメージタグの即時反映が可能 ※ Lambda にそのような仕組みは存在しない
  12. © 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');
  13. © DeNA Co., Ltd. 18 1 初回デプロイ時のイメージタグの扱い • 初回デプロイ時は、パラメータストアにイメージタグが存在しない •

    この状態でcdk deployを行うと、ParameterNotFoundエラーで強制終了する • CFnでのデプロイ時に、try〜catchでのエラーハンドリングはできない(※) ◦ 「初回デプロイ時だけどうこう...」といったことはできない ◦ CDKを使用したパラメータ取得の処理は使えない • どうすれば、初回デプロイ時も問題なくデプロイ可能になる? ※ 次スライドで説明
  14. © DeNA Co., Ltd. 19 2 補足説明:エラーハンドリングができない理由 • cdk deploy時に、内部的には以下の処理を行う

    ◦ CFnテンプレートの作成(cdk synth) ◦ CFnデプロイ(cdk deploy) ※上記CFnテンプレートを使用する • CFnテンプレート作成時には、パラメータストアからの値の取得は行われない ◦ 「Token」という仮の値が設定される • CFnデプロイ時に、パラメータストアからの値の取得を行う • CFnテンプレートは json/yaml ファイルなので、そもそも「エラーハンドリング」とい う機構が存在せず、エラーハンドリングができない
  15. © DeNA Co., Ltd. 20 3 対応方法:AWS CLIでイメージタグを取得する • AWS

    CLIを使用して、CFnテンプレート作成時にPSからイメージタグを取得する ◦ テンプレート作成時なら、エラーハンドリングを使える • 初回(≒イメージタグがない場合)のみ、仮のタグを設定する(「initial」など) ◦ すぐにイメージ(=タグ)をアプリ側で更新するので問題なし • ECRの設定で、イメージタグをイミュータブル(上書き禁止)にする ◦ 何かのエラーでイメージタグが取得できなかった場合タグ重複エラーになるので、 仮のLambdaイメージがpushされる事態を防げる
  16. © 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イメージでも対応されるといいなあ
  17. © DeNA Co., Ltd. 23 1 まとめ • Lambdaをコンテナデプロイすることで、ソースコードの分離が可能 ◦

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