CfnとCDKにおけるスタック分割の方針
AWSにおいてインフラをコードとして管理する際、CloudFormation(以下Cfn) や AWS CDK を利用すると、スタック単位でリソースを管理できます。
しかし、スタックを大きくまとめすぎると運用上の問題が発生しやすく、逆に分割しすぎると複雑になってしまうというトレードオフがあります。本記事では、CfnとCDKそれぞれにおけるスタック分割のメリット・デメリットと、プロジェクトに応じた最適な方針を考えるうえでのポイントを解説します。
1. スタック分割が重要な理由
1.1 1つのスタックが大きくなりすぎる問題
Cfnのテンプレートでは、1つのファイルに1つのスタックを定義します。そのため、1スタックに大量のリソースを定義すると以下のような問題が発生します。
-
目的のリソースが探しづらい
テンプレートファイルが肥大化し、どこにどのリソースがあるか分かりづらく、変更時に手間がかかる。 -
リソース数の制限に引っかかる
Cfnでは1スタックあたり最大500リソース、200パラメータまでなどのクォータ制限があり、大規模になると引っかかる可能性がある。 -
チーム間でのコンフリクトが増える
同一テンプレートを複数メンバーで編集すると、ソース管理での衝突(コンフリクト)が発生しやすい。 -
デプロイや更新のスコープが広がり時間がかかる
頻繁に更新するリソースと更新しないリソースを同一スタックにすると、Cfnが差分チェックの範囲が拡大し、ビルドやデプロイ時間が長引き、開発のボトルネックとなる可能性がある。
1.2 1つのスタックにまとめる利点
一方で、すべてを1スタックにまとめておくと以下のようなメリットがあります。
-
依存関係の管理がシンプル
スタック間の参照定義が不要なので、依存関係が1スタックに収まり、構成管理が単純になる。 -
変更の単位がわかりやすい
すべてを1回のdeploy
操作でまとめて更新できるので、一連のリソース変更を一度に適用できる。
2. スタック分割のトレードオフ
以上を考慮し、スタックを分割するかどうかを決めるうえで考慮すべき要素を大きく4つにまとめました。
要素 | 分割した場合のメリット | 分割した場合のデメリット |
---|---|---|
ソース管理のしやすさ | テンプレート・ファイルがコンパクトになり、 編集しやすい |
ファイルが増えすぎ管理が煩雑になる |
開発のしやすさ | チームごとで担当スタックを分割すると、 コンフリクトを避けられる |
チーム間でのスタック間での依存関係が複雑化し、把握するのが大変になるケースがある。 |
リソース更新時の手間 | 個別スタックの更新範囲が限定されるため、 影響範囲が局所化する |
スタック間の依存がある場合、 変更するリソースに応じて、更新の順序や手順を考慮する必要がある |
デプロイにかかる時間 | 更新対象のみデプロイすればよく、 1回あたりのデプロイが短時間で済む |
全体としてはデプロイ回数が増え、 まとめてデプロイできないデメリットもある |
スタックを増やすと「1回の更新スコープは狭くなるが、管理対象(スタック数)が増える」というトレードオフがあります。
逆にスタックをまとめると「依存関係はシンプルになるが、更新時に無関係なリソースまで含まれる」というトレードオフを抱えます。
3. スタック分割の方針・事例
3.1 小規模の場合:シンプルに1スタック
- リソース数が多くない(数十程度まで)
- 1チーム、もしくはメンバー数が少ない
- 将来的に大きくならない見込みがある
上記に該当するプロジェクトは、まずは1スタックでシンプルに運用、もしくはリソースの種類ごとで分けたい場合はネストされたスタックで対応する方がよいと思います。
あえて分割すると逆に複雑さが増す場合も多いため、将来的な変更や拡張が想定されない段階ではまずまとめて管理する方が効果的です。
3.2 環境ごとにスタックを分割
- 同一アカウントでdev / stg / prod など複数の環境がある
- それぞれの環境で構成やリソースが多少異なる
環境単位でスタックをまるごと複製し、ステージング環境や本番環境に適用するパターンです。
CfnやCDKでは、環境変数やパラメータを切り替えることで同じテンプレートを複数のスタックとして展開可能です。
3.3 チームごとにスタックを分割
- チームやサービス単位で担当領域が異なる
- 複数の機能開発を並行して行う
- 開発中チーム間でテンプレートのコンフリクトの可能性がある
この場合、チームやサービスごとにスタックを分割することで、コンフリクトを減らし運用がしやすくなります。
たとえば「ネットワーク/セキュリティチーム」「アプリケーションチーム」「DBチーム」が別々にAWSリソースをメンテナンスする構成です。スタック間でのリソースの参照が必要になった際は、CfnのExport/ImportやCDKのStack
でクロススタック参照を使ってやり取りを行います。また、
パラメーターストアに値を格納し、その値を他のスタックから参照させることで、直接的な依存を回避する方法もあります。
参考:CDKでスタック間のパラメーターを受け渡す5つの方法とケース別の最適解について考えてみた
3.4 ライフサイクルの違いでスタックを分割
-
ネットワーク層:VPC, Subnet, RouteTable, SecurityGroup, VPCEndpoint など
-
データベース層:RDS, ElastiCache, DynamoDB など
-
アプリケーション層:ECS, Lambda, EC2 など
-
ネットワーク層とデータベース層は頻繁に変更が発生しにくいリソース群である一方、アプリケーション層の多くのリソースから参照されます。そのため、ネットワークや永続データベースは独立したスタックとしておくことが推奨されます。
-
チーム間でどちらで担当するか曖昧なリソースについては、ライフサイクルを基準に担当を判断するでも良いでしょう。また、チーム内において担当するリソースのスタックを分割する際もライフサイクルを基準としたスタック分割は有効な手段になります。
4. CDKの場合のスタック分割アプローチ
Cfnの場合は「1ファイル = 1スタック」ですが、AWS CDK ではスタックとソースファイルが1:1ではありません。
4.1 Constructによるファイル分割
CDKではConstruct
と呼ばれる抽象化オブジェクトを定義することで、単一スタックを複数ファイルに分割できます。
これにより「大規模1スタック」の欠点だった“ソースコードの肥大化”や“編集の衝突”をある程度緩和でき、チーム開発がしやすくなります。
// 例: MyApplicationStack.ts
import * as cdk from "aws-cdk-lib";
import { MyNetworkConstruct } from "./my-network-construct";
import { MyAppConstruct } from "./my-app-construct";
export class MyApplicationStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ネットワーク関連をConstructとして切り出し
const network = new MyNetworkConstruct(this, "MyNetwork", {});
// アプリケーション関連を別Constructとして切り出し
const app = new MyAppConstruct(this, "MyApp", {
vpc: network.vpc,
});
}
}
これらのConstructは1つのCDKスタックにまとめられた状態ですが、ソースファイルは複数に分かれるため、同一スタック内でもリソースのグループごとでファイルを分けることによって、同一スタック内でも担当者ごとで扱うファイルを分けることができコード管理がしやすくなります。
4.2 スタック分割とConstructの使い分け
CDKではConstructで「ファイル分割・論理分割」ができるものの、実際のデプロイ単位はスタック単位です。
つまり、「デプロイの単位」「変更のスコープ」を分けたい場合は、従来どおりスタックを分割する必要があります。
- まずはConstructでファイル分割し、ソース管理をシンプルにする
- 必要に応じてスタックを分割し、更新スコープを最適化する
というステップで段階的に検討するのがおすすめです。
5. スタック分割の選択フローチャート
実際に「どのようにスタックを分割するか?」はプロジェクト規模やチーム体制、将来的な拡張予定などによって変わります。
以下はCfnのスタック分割におけるフローチャートです。あくまで一例ですが、判断材料としてご活用ください。
6. スタック依存関係の例
下記はスタックを分割した場合の依存関係を示す例で、 各スタックがどのスタックを参照しているかを可視化しています。(矢印は参照元から参照先)
このとき、スタック間で相互参照を作らないようスタックを分割することが重要です。
※ リソースの種類や数に応じて、更にスタックを細かい分割の検討も必要です
7. まとめ
本記事では、CloudFormationおよびAWS CDKにおけるスタック分割の重要性と、そのメリット・デメリットについて解説しました。以下がポイントです。
- 1スタックでまとめすぎると管理が大変になる一方で、依存関係がシンプルになるメリットもある
- スタック分割は「チーム体制」「リソース数」「ライフサイクル」「依存関係の複雑さ」などを踏まえ行う
- CDKではConstructを使ってコード分割できるが、デプロイ単位を分けたい場合はスタック分割が必要
- ネットワークやDBなど、変更頻度が低いリソースは独立スタックとすると運用が安定しやすい
スタック分割の判断は、現在のプロジェクト状況だけでなく、将来的にリソースが増える可能性や運用体制の変化も考慮することが重要です。まずは小さく始めて必要に応じて分割し、最終的に無理のない運用を目指してください。
Discussion