AWS CDK では、デプロイや合成をするたびに cdk.out というディレクトリが生成されるのをご存知でしょうか?その存在について書きました。
目次
cdk.out とは
cdk.out とは、クラウドアセンブリと呼ばれるファイル群を格納するためのディレクトリです。
cdk synth コマンドや cdk deploy コマンドを実行すると、CDK コードからクラウドアセンブリが生成され、そのクラウドアセンブリが cdk.out ディレクトリに保存されます。
クラウドアセンブリとは
クラウドアセンブリとは、CDK アプリケーション(CDK コード)の合成(synthesize)によって生成されるファイル群です。CDK CLI がその情報を参照して AWS 環境にリソースをデプロイします。
つまりクラウドアセンブリは、CDK コードで記述したインフラ定義を AWS 環境にデプロイするために CDK が使用する中間成果物と言えます。

クラウドアセンブリの構成要素
クラウドアセンブリは、主に以下のファイルやディレクトリで構成されます。
- (スタック名).template.json
- CDK コードによって生成された CloudFormation テンプレート
- (スタック名).assets.json
- CDK コードで使用されるアセットの情報を記述したファイル
- (スタック名).metadata.json
- CDK コードで使用されるメタデータを記述したファイル
- manifest.json
- クラウドアセンブリのメタデータを記述したファイル
- tree.json
- CDK コードで定義されたリソースの Construct ツリー構造を表すファイル
- cdk.out
- Cloud Assembly スキーマのバージョン情報が格納されたファイル
- asset.(ハッシュ値)/
- CDK コードで使用されるアセットファイルを格納したディレクトリ
- アセットごとに計算されたハッシュ値をディレクトリ名として使用
- 各アセットは S3 アセットと Docker イメージアセットの 2 種類に分類され、
cdk deployコマンド実行時にcdk bootstrapコマンドで作成された S3 バケットや ECR リポジトリにアップロードされる - Docker イメージアセットは
cdk deployコマンド実行時にビルドされる(cdk synthコマンド実行時にはビルドされない)
- assembly-(ステージ名)/
- Stage Construct を使用している場合、ステージごとに生成されるクラウドアセンブリを格納するディレクトリ
cdk.out の必要性
cdk.out は上記でご説明した通り、CDK コードから生成された中間成果物であるクラウドアセンブリを格納するためのディレクトリです。
では、なぜこのディレクトリが必要なのでしょうか?デプロイのために必要な情報をファイルとして生成し、さらに保存する必要があるのか?と思うかもしれません。
それを説明する前にまず、CDK コマンドの使い方によって合成が複数回実行されること、そしてそれによるリスクについて説明します。
合成を複数回実行するリスク
cdk deploy コマンドでは、内部で synth コマンドと同等の合成処理も実行されます。
例えば、cdk synth を実行した後に cdk deploy を実行した場合、synth と deploy のどちらでも合成が実行され、CDK アプリケーションを 2 回合成することになります。
この場合、2 回目の合成実行で、1 回目の合成結果と異なる出力が作成されるリスクがあります。これにより、1 回目の合成結果を確認し、承認したにも関わらず、次の合成処理で異なる結果が生成されてしまうと開発者の想定と違う挙動を引き起こす可能性が発生してしまいます。
一方で、cdk synth を実行せずに cdk deploy を実行する場合でも、1 つの CDK アプリケーションで開発環境(dev)と本番環境(prod)などの複数環境を管理する場合、それぞれの環境でデプロイをする際に毎回合成が実行されることになります。開発環境での合成と本番環境での合成が違うタイミングで実行されることで、想定していない環境差異が混入するリスクもあります。
またリスク以外にも、そもそも合成処理を 2 回行うということは冗長でもあり、非効率であることは言うまでもありません。特に大規模なアプリケーションの場合、合成にはかなりの時間がかかることがあるため、デプロイ時間の大幅な増加につながる可能性があります。
--app オプションによる cdk.out の再利用
合成を複数回実行することによるリスクを説明しましたが、ではどうすれば合成を複数回実行することによるリスクを回避できるのでしょうか?
cdk deploy コマンドでは、それを回避するためのオプションとして、--app (-a) オプションがあります。
このオプションには、cdk.out ディレクトリのパスを指定することができます。こちらを指定することで、すでに合成されたクラウドアセンブリを使用してデプロイすることができ、通常のデプロイコマンドの際に実行される合成をスキップすることが可能です。前回の cdk synth 以降にコードを変更していないことがわかっている場合は、このオプションでクラウドアセンブリを再利用することで冗長な合成処理を回避することができます。
npx cdk deploy --app ./cdk.out
また、CDK コードを直接合成するためのコマンドを指定することで、明示的に合成とデプロイの実行を分離することもできます。このコマンドは、通常、cdk.json ファイルの app プロパティに定義されているコマンドと同じものになります。
npx cdk deploy --app "npx ts-node --prefer-ts-exts bin/cdk-sample.ts" # Node.js で TypeScript を実行できる環境の場合 npx cdk deploy --app "node bin/cdk-sample.ts"
このオプションによって、合成結果である cdk.out を指定して、開発環境と本番環境などの複数環境で同じクラウドアセンブリを使用してデプロイすることができます。つまり、--app オプションで、cdk.out を再利用することができるのです。これにより、合成を複数回実行することによるリスクを回避し、さらに冗長な合成処理を回避することができます。
npx cdk synth npx cdk deploy -a ./cdk.out DevStack npx cdk deploy -a ./cdk.out ProdStack
合成とデプロイの分離
--app オプションで cdk.out を再利用することで、合成を複数回実行することによるリスクを回避できると説明しました。これはまさに、cdk.out によって合成とデプロイを分離できるからこそ実現できることなのです。
つまりこのように、合成とデプロイを分離できることこそが、AWS CDK における cdk.out の必要性であると言えるでしょう。
また、クラウドアセンブリに対応する仕組みであれば CDK CLI 以外のアプリケーションからもクラウドアセンブリを使用してデプロイすることができます。例えば、cdk-assets などのツールもクラウドアセンブリを参照してアセットの管理を行っています。このように、合成は CDK、デプロイは CDK CLI 以外のツールで行うといった、合成とデプロイの分離が可能になることで、CDK アプリケーションの開発において柔軟なワークフローを実現することができます。
synthesize once, deploy many
コンテナなどのアプリケーション開発では "build once, deploy many" というアプローチがあります。これは、アプリケーションを一度だけビルドし、そのビルド成果物を複数の環境にデプロイするという考え方です。
CDK における cdk.out を --app オプションで再利用するアプローチはまさにそれと同じような考え方です。合成は一度だけ行い、その後は何度でもデプロイできるという、"synthesize once, deploy many" とも言えるでしょう。
cdk.out のその他のメリットやユースケース
アセットのキャッシュ
cdk.out には、CDK コードで使用されるアセットファイルを格納する asset ディレクトリが含まれています。ここには、Lambda や ECS などで使用されるコードファイルや Dockerfile などのファイルが格納されています。
CDK の仕組みとして、合成の際に合成対象の CDK コードで使用されるアセットと同じハッシュ値を持つアセットがすでに存在する場合、そのアセットのバンドル処理が省略され、既存のアセットが再利用されます。つまり、cdk.out はアセットのキャッシュとしても機能していると言えます。
CI/CD パイプラインでの cdk.out の活用
cdk.out によって複数の環境で同じクラウドアセンブリを使用してデプロイすることができると言いましたが、具体的には以下のように GitHub Actions などの CI/CD パイプラインで cdk.out を活用することができます。
jobs: synthesize: runs-on: ubuntu-latest steps: - run: npm ci - run: npx cdk synth - uses: actions/upload-artifact@v6 with: name: cdk-out path: cdk.out deploy-dev: needs: synthesize steps: - uses: actions/download-artifact@v6 - run: npx cdk deploy --app cdk.out DevStack deploy-prod: needs: deploy-dev steps: - uses: actions/download-artifact@v6 - run: npx cdk deploy --app cdk.out ProdStack
参考資料
今までの説明で、一度の合成をもとに複数回のデプロイが可能になるとお話ししましたが、そもそも CDK スタックの作成方法には、静的スタック作成と動的スタック作成の 2 種類があることをご存知でしょうか?
今回ご紹介した合成を一度だけ行うアプローチは、静的スタック作成のアプローチで可能になります。一方で動的スタック作成においても他のメリットがあるため、それぞれの違いについて気になる方は、以下の『AWS CDK 環境管理ガイド 〜静的 vs 動的スタック作成〜』という記事もぜひご覧ください。こちらは、以前に私がドイツの AWS Hero である Thorsten Höger 氏と共同執筆した記事の和訳記事になります。
また、CDK の元メンテナーの方の記事も参考になるので、ぜひご覧ください。
cdk.out の肥大化とその対処
cdk.out の肥大化
cdk.out は CDK において重要な役割を果たす存在であるということがわかりましたが、合成を繰り返すことで新しいファイルやディレクトリが増えていき、気が付いたら数 GB に達していることもあります。
これは、アプリケーションのファイルが更新されると新たにそのアセットのディレクトリが作成されるためです。特に、Docker を使ったコンテナアプリケーションの場合、サイズが特に大きくなりがちです。
また、cdk deploy コマンドでは Docker イメージのビルドも実行されるため、cdk.out だけでなく、ローカルの Docker に保存されたビルドイメージもどんどん増えていくことになります。
cdk-agc
そんな肥大化した cdk.out を最適化するために、cdk-agc というツールを作りました。
(とても使い勝手が良いので、よかったら GitHub のスターもお願いします!)
具体的には、以下のことをやってくれます。
- cdk.out 内にある、現在のスタックで使用するもの以外の古いアセットファイル・ディレクトリの削除
- 削除されるアセットが Docker アセットの場合、ローカルでビルドされた Docker イメージも自動削除
$TMPDIRに溜まった一時的な CDK ディレクトリの削除- インストール操作不要で、
npxをつけてコマンドを叩けばすぐに実行可能
これにより、cdk.out を最適化し、Docker に溜まったイメージも削除され、ローカルのディスクスペースを節約することができます。さらに、CI/CD で cdk.out をキャッシュしている場合、キャッシュサイズを大幅に削減できるため、CI/CD パイプラインの効率も向上します。
インストール操作不要で、npx をつけてコマンドを叩けばすぐに実行できるので、ぜひお気軽に試してみてください。
-d でドライラン、-k で保持する期間の指定、-t で $TMPDIR 内の一時ディレクトリのクリーンアップもできるので、用途に合わせて使い分けてみてください。
# 最適化したい cdk.out のある CDK プロジェクトに移動 cd my-cdk-project # cdk.out をクリーンアップ(Docker アセットのビルド済みイメージも削除) npx cdk-agc # ドライラン機能で削除されるファイルを確認 npx cdk-agc -d # 24 時間以内に変更されたファイルを保持 npx cdk-agc -k 24 # $TMPDIR 内の一時ディレクトリをクリーンアップ npx cdk-agc -t
最後に
cdk.out はあまり注目されない存在ですが、CDK アプリケーションの開発において重要な役割を果たす存在です。cdk.out を理解し、適切に活用することで、CDK アプリケーションの開発をより効率的に行うことができるでしょう。