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

aws-cdkで複数アプリの構成標 準化に取り組んだ話 JAWS-UG CDK支部 #12

TSaku0816
February 23, 2024
300

aws-cdkで複数アプリの構成標 準化に取り組んだ話 JAWS-UG CDK支部 #12

TSaku0816

February 23, 2024
Tweet

Transcript

  1. 自己紹介 星野リゾートのインフラエンジニア。内製開発でDevOps目指して仕事をしている。 2023年4月に中途入社する前は小売業の内製開発をしていた(Webアプリ開発とSREが 半々ほど)。AWSとの付き合いは2016年に資格をとって以降ずっと仕事で関わっている。現 在有効なAWS認定はAWS Certified Security - Specialty。 略歴:

    インフラSE(AWSパートナーのSIer) -> Web開発 & AWSインフラ(小売事業の内製開発) -> (現在) 「旅に魔法をかけるITチーム」をコンセプトに、観光 x ITに関してや 内製化を頑張るメン バーの日常などを発信している情報システムグループ公式noteもチェックしてみてください。 https://note.com/hoshino_technote/
  2. AWSインフラ領域を標準化するためにaws-cdkを採用 今までも可能な部分は優先して aws-cdkに移行(CFn -> terraform -> aws-cdk)していて知見もあ り、3rdパーティツールとの比較・議論もそこまで無く決定した。 • 言語はTypeScript

    • 複数アプリのAWSサービス構成をaws-cdkのモノレポで管理する • サーバサイド・インフラ標準化チームとして 4名で活動する ◦ 自分自身はインフラやCI/CD周りの担当
  3. 複数アプリのインフラ構成をCDK管理する 出来上がった CDKディレクトリ構成抜粋。 . ├── cdk.json ├── main-common │ ├──

    bin │ │ └── main-common.ts │ ├── lib │ │ ├── cloudfront-functions │ │ ├── cloudfront-functions-stack.ts │ │ ├── cognito-stack.ts │ │ ├── ecr-repositories-stack.ts │ │ ├── ecs-cluster-stack.ts │ │ ├── ip-restriction-stack.ts │ │ ├── lambda │ │ ├── log-buckets-stack.ts │ │ ├── mq-stack.ts │ │ ├── rds-stack.ts │ │ ├── route53-stack.ts │ │ ├── slack-notify-stack.ts │ │ ├── vpc-lattice-network-stack.ts │ │ └── waf-stack.ts │ ├── test.json │ ├── prod.json │ └── types │ └── main-common-context.ts ├── lib │ ├── constants.ts │ ├── context-reader.ts │ ├── ecs-firelens-function.ts │ ├── ecs-service-construct.ts │ └── types │ └── common-context.ts ├── main │ ├── firelens_container.md │ ├── ABC │ │ ├── bin │ │ ├── fluentbit │ │ ├── lib │ │ ├── test.json │ │ └── types └── secure ├── README.md ├── bin │ └── secure.ts ├── cdk.json ├── common │ ├── lib │ └── secure-common-stack.ts ├── firelens_container.md ├── DEF │ ├── fluentbit │ ├── DEF-stack.ts │ ├── lib │ └── test.json ├── test ├── test.json ├── prod.json └── types └── secure-context.ts
  4. 複数アプリのインフラ構成をCDK管理する アプリ横断的に必要とするリソース群。 . ├── cdk.json ├── main-common │ ├── bin

    │ │ └── main-common.ts │ ├── lib │ │ ├── cloudfront-functions │ │ ├── cloudfront-functions-stack.ts │ │ ├── cognito-stack.ts │ │ ├── ecr-repositories-stack.ts │ │ ├── ecs-cluster-stack.ts │ │ ├── ip-restriction-stack.ts │ │ ├── lambda │ │ ├── log-buckets-stack.ts │ │ ├── mq-stack.ts │ │ ├── rds-stack.ts │ │ ├── route53-stack.ts │ │ ├── slack-notify-stack.ts │ │ ├── vpc-lattice-network-stack.ts │ │ └── waf-stack.ts │ ├── test.json │ ├── prod.json │ └── types │ └── main-common-context.ts ├── lib │ ├── constants.ts │ ├── context-reader.ts │ ├── ecs-firelens-function.ts │ ├── ecs-service-construct.ts │ └── types │ └── common-context.ts • リソースの依存関係上、一番最初に構築する必要 がある • 名前の通り、AWSサービス単位のcdk.Stackで分け ている • クロスリージョンの場合、crossRegionReferences を有効化してSSMパラメータに値を保存することで スタック間参照する • 定数や共通化した関数、L3コンストラクタを `cdk/lib/*`に保存して各アプリのcdk.Stackから呼 び出している ◦ lookupやリソース命名規則の定義 ◦ 複数のコンテキストファイルをマージするた めのcontext-reader • WebアプリはECS Fargateを使う方針を決めていた のでECRやECS Clusterも先に構築している
  5. 複数アプリのインフラ構成をCDK管理する アプリ横断的に必要とするリソース群。 . ├── cdk.json ├── main-common │ ├── bin

    │ │ └── main-common.ts │ ├── lib │ │ ├── cloudfront-functions │ │ ├── cloudfront-functions-stack.ts │ │ ├── cognito-stack.ts │ │ ├── ecr-repositories-stack.ts │ │ ├── ecs-cluster-stack.ts │ │ ├── ip-restriction-stack.ts │ │ ├── lambda │ │ ├── log-buckets-stack.ts │ │ ├── mq-stack.ts │ │ ├── rds-stack.ts │ │ ├── route53-stack.ts │ │ ├── slack-notify-stack.ts │ │ ├── vpc-lattice-network-stack.ts │ │ └── waf-stack.ts ECSサービスの作成(≒デプロイ)は ECSクラスターやECRが存在している必要があり、アプリの cdk プロジェクトではECSサービスの設定やタスク定義だけを意識したい ログ保存のS3バケットはアプリごとに集約する FQDNの命名規則を最低限守るために共用するホストゾーンを作成してアプリごとにゾーン委任して もらう 凡例:{ProjectName}.{EnvName}.{ApexDomain} WAF ACLは共通・顧客向け・社内向けの 3つを作成して、一部のルールは共用している CI/CDを標準化する一環として、デプロイ通知の仕組み (EventBridgeやAWS Chatbot)も同じ仕組 みで構築する ECSサービス作成時にDB接続できないとアプリも起動できない
  6. import { HopTabiWafStack } from '../lib/waf-stack' ; const regionalWafStack =

    new HopTabiWafStack ( app, 'HopCommonRegionalWafStack' , appContext , 'REGIONAL' , wafParameterNames .regionalAclArn , stackProps ); const globalWafStack = new HopTabiWafStack ( app, 'HopCommonGlobalWafStack' , appContext , 'CLOUDFRONT' , undefined , globalRegionStackProps ); 複数アプリのインフラ構成をCDK管理する コード例:AWS WAFをクロスリージョンで構築してスタック間参照させる const wafSsmStack = new cdk.Stack(app, 'HopCommonWafSsmStack' , { ...stackProps, crossRegionReferences: true, }); new ssm.StringParameter (wafSsmStack, 'GlobalAclArn' , { parameterName: wafParameterNames .globalAclArn, stringValue: globalWafStack .webAcl.attrArn, }); new ssm.StringParameter (wafSsmStack, 'HopGlobalCustomerAclArn' , { parameterName: wafParameterNames .globalCustomerAclArn , stringValue: globalWafStack .hopCustomerWebAcl .attrArn, }); wafSsmStack.addDependency(globalWafStack );
  7. 複数アプリのインフラ構成をCDK管理する アプリケーションごと (例)。 ├── main │ ├── web-app │ │

    ├── bin │ │ │ └── web-app.ts │ │ ├── fluentbit │ │ │ ├── backend │ │ │ └── frontend │ │ ├── lib │ │ │ ├── acm_ap-northeast-1.ts │ │ │ ├── backend.ts │ │ │ ├── cloudfront.ts │ │ │ ├── ecs-cluster.ts │ │ │ ├── ecs-deploy-listener-automation.ts │ │ │ ├── frontend.ts │ │ │ ├── lambda_functions │ │ │ └── route53_acm.ts │ │ │ │ │ ├── test.json │ │ ├── prod.json │ │ └── types │ │ └── tabi-raku-contexts.ts • アプリ毎のスタックでもcontext-readerを介して3つの コンテキストを使える ◦ cdk.json ◦ main-common/{profile}.json ◦ {profile}.json • cdkのルートディレクトリはcdk/だけだが、cdk.App() はアプリケーションごとのディレクトリでも作成するた めにcdk実行方法をカスタマイズしている ◦ 参考:app コマンドの指定 • main-commonのリソースを使う(参照)する場合は SSMパラメータストアの値から実際のAWSリソースを lookupする • lib/配下はアプリ構成によって変わり、その中のstack 構成はアプリごとに自由に作成できる Appスタック Globalスタック SSMスタック
  8. 複数アプリのインフラ構成をCDK管理する コード例:cdk実行 # アプリケーションスタックのコンテキスト合成 import readContext from '@/lib/context-reader' ; import

    { WebAppContext } from '../types/web-app-contexts' ; const appContext = readContext <WebAppContext >(app, profileName , appRootDir ); $ npm run cdk:web-app -- ls -c profile=test # cdk/package.json抜粋 { "scripts": { "cdk:web-app": "npx cdk -a \"npx ts-node --prefer-ts-exts web-app/bin/web-app.ts \"", # cdk/lib/context-reader.ts 抜粋 const readContext = <T>( app: cdk.App, profileName : string, appRootDir : string ): T => { const rootProps = app.node.tryGetContext (profileName ); const appProps = JSON.parse( readFileSync (path.join(appRootDir , `${profileName }.json`), 'utf-8') ); return { ...rootProps , ...appProps }; }; コード例:コンテキスト参照の仕組み
  9. 複数アプリのインフラ構成をCDK管理する コード例:アプリケーションスタックの ECSクラスタ export class EcsCluster extends Construct { public

    readonly ecsCluster: ecs.ICluster; constructor(scope: Construct, id: string, appContext: HopUswContext) { super(scope, id); const { profileName, vpcName } = appContext; this.ecsCluster = lookupEcsCluster( scope, 'EcsCluster', profileName, 'tabiuilder', vpcName ); } } # lookupEcsCluster() 抜粋 return ecs.Cluster.fromClusterAttributes (scope, id, { clusterName, vpc, defaultCloudMapNamespace: servicediscovery .PrivateDnsNamespace .fromPrivateDnsN scope, 'Namespace', { namespaceArn, namespaceId, namespaceName } ), }); };
  10. 複数アプリのインフラ構成をCDK管理する 機微データを扱うアプリケーション例。 └── secure ├── README.md ├── bin │ └──

    secure.ts ├── cdk.json ├── common │ ├── lib │ └── secure-common-stack.ts ├── web-app │ ├── fluentbit │ ├── web-app.ts │ ├── lib │ └── test.json ├── test ├── test.json ├── prod.json └── types └── secure-context.ts • 基本的なAWSサービス構成はアプリケーション全般と同様だが、VPC Lattice Serviceとして構築する ◦ Lattice Networkはmain-common(通信元)で作成している • VPC LatticeのL2コンストラクタはないためL1(Cfn*)コンストラクタを使うこ とになったが、標準化したアプリ構成(ALB + ECS Fargate)をそのままセ キュアな構成にすることができる ◦ VPC Lattice構築では改めてクラメソさんに感謝 ◦ 参考:DEVELOPERS iO 2023: VPC間通信ができる新サービスVPC Lattice
  11. Tips: 複数アプリケーションを CDK管理する中で知ったこと • リソース命名規則を強制する何らかの仕組みはあった方が良い ◦ 今回はcdk/lib/constants.tsで管理している • アプリ構成(の作り方)を標準化したい場合、L3コンストラクタや共通関数は必要になった ◦

    cdk/lib/ecs-service-construct.ts:ECS Fargateを作成する(ALBあり or ALBなし) ◦ cdk/lib/ecs-firelens-function.ts:fluent-bitコンテナを追加してFireLens設定する • CI/CD含めて標準化したい場合にcdk管理していることは強力 ◦ ChatbotやEventBridgeでAWSからデプロイ通知 ◦ GitHub Actions(ex. aws-actions/amazon-ecs-deploy-task-definition)からECSをBlue/Greenデプロイ • リージョンが異なるAWSサービスを組み合わせるためにcrossRegionReferencesがある ◦ Route 53のホストゾーンをuse1のスタックとして作成して、apne1のスタックで作成したアプリのカスタムドメイン名を そこにレコード登録する場合など • for文を使ってAWSリソースを作成する場合、将来的にもStack内で論理IDの重複を避けられるように実装する ◦ 後から論理IDを修正する場合、cdkは対象リソースをdestroy & createする... • 標準化チームの総合力が試された 複数アプリのインフラ構成をCDK管理する