$30 off During Our Annual Pro Sale. View Details »

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
    株式会社ディー・エヌ・エー

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. © 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),
    ...
    }

    View Slide

  11. © 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リポジトリを指定
    });

    View Slide

  12. © 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’)),
    });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. © 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');

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. © 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イメージでも対応されるといいなあ

    View Slide

  22. © DeNA Co., Ltd. 22
    まとめ

    View Slide

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

    View Slide

  24. © DeNA Co., Ltd. 24

    View Slide