コンテンツにスキップ

AWS CDK Deployment Best Practices

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

AWS CDKのデプロイを安全・高速・再現可能にするためのベストプラクティス記事。 Stageを使った環境分離、cdk.out を使ったsynth/deploy分離、asset build/publishとdeployの分離、cdk.context.json のコミットが主題。 CI/CDでCDKを扱うときに、環境ごとの差分、非決定性、デプロイ時間、再現性をどう抑えるかの実装断片が載っている。

本文

この記事は、AWS CDKで複数環境にデプロイする際の実装・運用パターンを整理している。

Stageで環境を表現する

StageAppStack の間に位置する概念で、複数Stackを環境単位にまとめる。Dev、Stg、Prodのような環境を明示的に作り、それぞれにリージョンやAWSアカウントを指定する。

const app = new cdk.App();

new MyStage(app, 'Dev', {
  ...getProps('Dev'),
  env: {
    region: 'us-east-1',
    account: '111111111111',
  },
});

new MyStage(app, 'Stg', {
  ...getProps('Stg'),
  env: {
    region: 'us-east-1',
    account: '222222222222',
  },
});

new MyStage(app, 'Prod', {
  ...getProps('Prod'),
  env: {
    region: 'us-east-1',
    account: '333333333333',
  },
});

個人用Dev環境を作る場合は、cdk deploy のcontextで所有者を渡し、Stack名を動的に変える。

npx cdk deploy -c owner=goto Dev/*
const owner = app.node.tryGetContext('owner') as string;
const devStageName = owner ? `Dev-${owner}` : 'Dev';

new MyStage(app, devStageName, {
  ...getProps('Dev'),
  env: {
    region: 'us-east-1',
    account: '111111111111',
  },
});

個人ごとにAWSアカウントが分かれているなら、環境変数からaccountを渡す設計もできる。

const app = new cdk.App();

new MyStage(app, 'Dev', {
  ...getProps('Dev'),
  env: {
    region: 'us-east-1',
    account: process.env.AWS_ACCOUNT_ID || '111111111111',
  },
});

synthとdeployを分ける

cdk deploy は通常、synth、asset build/publish、CloudFormation deployをまとめて実行する。CIでは、まず cdk synth でCloud Assemblyを作り、その成果物 cdk.out を後続ジョブで使うと、デプロイ対象が固定される。

jobs:
  synth:
    steps:
      - run: npm ci
      - run: npx cdk synth
      - uses: actions/upload-artifact@v4
        with:
          name: cdk.out
          path: cdk.out

  deploy-dev:
    needs: synth
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: cdk.out
          path: cdk.out
      - run: npx cdk deploy --app cdk.out --require-approval never Dev/*

  deploy-stg:
    needs: deploy-dev
    steps:
      - run: npx cdk deploy --app cdk.out --require-approval never Stg/*

  deploy-prod:
    needs: deploy-stg
    steps:
      - run: npx cdk deploy --app cdk.out --require-approval never Prod/*

この分離により、環境ごとにsynth結果が変わるリスクを避けやすくなる。

asset build/publishとdeployを分ける

LambdaやDocker imageなどのassetがある場合、cdk deploy 内でasset build/publishも実行される。これを分離すると、assetのビルドとアップロードを先にまとめて行い、その後のdeployではCloudFormation更新に集中できる。

jobs:
  synth:
    steps:
      - run: npm ci
      - run: npx cdk synth

  publish-assets:
    needs: synth
    steps:
      - run: npx cdk publish-assets --unstable=publish-assets --app cdk.out Dev/*
      - run: npx cdk publish-assets --unstable=publish-assets --app cdk.out Stg/*
      - run: npx cdk publish-assets --unstable=publish-assets --app cdk.out Prod/*

  deploy-dev:
    needs: publish-assets
    steps:
      - run: npx cdk deploy --app cdk.out --require-approval never Dev/*

同一job内で複数環境向けにasset publishすると、Docker layer cacheが効きやすく、2回目以降のbuildを短縮できる。環境ごとにAWSアカウントが分かれている場合は、必要に応じてロールを切り替える。

cdk.context.json をコミットする

cdk.context.json は、synthesis時にAWSアカウントから取得した値をキャッシュするファイル。Vpc.fromLookup()StringParameter.valueFromLookup() のようなcontext methodを使うと、CDK CLIがAWS SDKで情報を取得して保存する。

const vpc = Vpc.fromLookup(this, 'Vpc', {
  vpcId,
});

const parameter = StringParameter.valueFromLookup(this, parameterName);

このファイルをコミットする主な理由は2つ。

  • 非決定的な挙動を避ける
  • デプロイ速度を上げる

例えば最新AMIをlookupしている場合、実行時点によって取得されるAMIが変わり、意図せずEC2が置き換わる可能性がある。cdk.context.json をコミットしておけば、以後のsynthでは同じ値を参照し、構成が安定する。

また、contextが未キャッシュの場合、CDKは一度dummy値でsynthし、その後AWSから値を取得して cdk.context.json を更新し、再度synthする。コミット済みならこの往復を減らせる。

要点

  • Stageで環境単位を明示し、複数Stackをまとめる。
  • 個人Dev環境はcontextやAWS accountで分けられる。
  • CIでは cdk synthcdk deploy --app cdk.out を分けると再現性が上がる。
  • asset build/publishをdeployと分けると、デプロイ時間と失敗範囲を制御しやすい。
  • cdk.context.json は非決定性を抑え、synthの再実行を減らすためにコミットする。

タグ

aws #aws-cdk #deployment #ci-cd #cloudformation #infrastructure-as-code