はじめに:テナント追加が「深夜の手作業」になっていませんか?
SaaSビジネスが順調に成長し、新しい顧客との契約が決まった瞬間。それは喜ばしいことであるはずが、開発チームにとっては悪夢の始まりかもしれません。
「また深夜に、新しいテナント用のインフラを手作業で構築しないと…」
テナント追加のたびに発生する手作業での環境構築は、時間とコストを浪費するだけでなく、ヒューマンエラーによる設定ミスやセキュリティリスクの温床となります。この「手動プロビジョニングの壁」は、SaaSビジネスのスケーラビリティを阻害する深刻なボトルネックです。
しかし、もしCI/CDパイプラインが、単にコードをデプロイするだけでなく、テナントの追加に応じてインフラそのものを動的に、かつ自動で構築してくれるとしたらどうでしょうか?
この記事では、AWS CDK (Cloud Development Kit) の中でも特に強力なCDK Pipelinesを使い、テナントのオンボーディングを完全に自動化する、先進的なCI/CDパイプラインの設計と実装方法を徹底解説します。
全体アーキテクチャ:コントロールプレーンとCDK Pipelinesの連携
このアーキテクチャの核心は、テナント情報を管理する「コントロールプレーン」と、CI/CDパイプラインを連携させることにあります。
graph TD
A[1. 管理者がテナント情報をDynamoDBに登録] --> B{DynamoDB Table<br>(Tenant Store)}
B -- DynamoDB Streams --> C{Lambda Trigger}
C --> D[2. LambdaがCodePipelineの実行を開始]
D --> E{CDK Pipeline}
E -- 3. Synthステージで... --> F[DynamoDBから<br>テナントリストを取得]
F --> G[テナント毎にStageを<br>動的にパイプラインへ追加]
E -- 4. Deployステージで... --> H[テナント用スタックを<br>CloudFormationにデプロイ]
- テナント情報の登録: 管理者が、テナントIDや契約プラン(例:
premium
)といった情報をDynamoDBテーブルに登録します。 - パイプラインのトリガー: DynamoDB Streamsがテーブルの変更を検知し、Lambda関数をトリガー。このLambdaがCDK Pipelineの実行を開始します。
- 動的なステージ生成: パイプラインの
Synth
(合成)ステージが、DynamoDBから現在有効なテナントのリストを取得し、そのリストに基づいてデプロイすべきインフラ(Stage
)をプログラムで動的にパイプライン自身に追加します。 - デプロイ: パイプラインは、生成されたステージに従い、CloudFormationを通じて各テナントに必要なリソース(専用S3バケット、IAMロールなど)をデプロイします。
【実装】CDK Pipelinesで「動的ステージング」を実現する
この魔法のような仕組みを実現する、CDKコードの核心部分を見ていきましょう。
Step 1: テナント設定ストアの構築 (DynamoDB)
まずは、テナント情報を格納するDynamoDBテーブルをCDKで定義します。tenantId
をパーティションキーとし、tier
(契約プラン)やstatus
(状態)といった属性を持たせます。
// lib/tenant-store-stack.ts
new dynamodb.Table(this, 'TenantStore', {
partitionKey: { name: 'tenantId', type: dynamodb.AttributeType.STRING },
stream: dynamodb.StreamViewType.NEW_IMAGE,
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
Step 2: テナントごとのリソーススタックを定義
次に、各テナントにプロビジョニングするリソースを、再利用可能なStack
として定義します。このスタックは、tenantId
やtier
をプロパティとして受け取り、リソースの構成を動的に変更できるように設計します。
// lib/tenant-stack.ts
interface TenantStackProps extends cdk.StackProps {
tenantId: string;
tier: 'standard' | 'premium';
}
export class TenantStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: TenantStackProps) {
super(scope, id, props);
new s3.Bucket(this, 'TenantBucket', {
bucketName: `my-saas-${props.tenantId}-bucket`,
});
if (props.tier === 'premium') {
// プレミアムテナント向けの追加リソースをここに定義
}
}
}
Step 3: パイプラインのSynth
ステージで魔法をかける
最後に、CDK Pipelinesを構築します。核心は、Synth
ステージ(CodeBuildStep
)の中で、DynamoDBからテナントリストを取得し、その情報を使ってTenantStack
をループでインスタンス化する点です。
// lib/pipeline-stack.ts
const pipeline = new CodePipeline(this, 'SaaSPipeline', {
synth: new CodeBuildStep('Synth', {
commands: [
'npm ci',
'npm run build',
'npx cdk synth'
],
}),
});
// これは概念的なコードです。実際の実装では、Synthの前に
// LambdaやCodeBuildアクションを追加してDynamoDBから情報を取得し、
// cdk synthコマンドのコンテキストとして渡す必要があります。
// --- Synthステージで実行されるアプリケーションコード内での処理 ---
// const tenants = await getTenantsFromDynamoDB();
// for (const tenant of tenants) {
// pipeline.addStage(new TenantStage(this, tenant.id, {
// stackName: `tenant-${tenant.id}-stack`,
// tenantId: tenant.id,
// tier: tenant.tier
// }));
// }
この「パイプライン実行中のインフラ定義」こそが、CDK Pipelinesのプログラマビリティを最大限に活かした、動的プロビジョニングの鍵となります。
テナントのライフサイクル管理
このアーキテクチャは、テナントのライフサイクル全体を自動化します。
- オンボーディング: DynamoDBに新しい項目を追加するだけで、数分後にはそのテナント専用のインフラが自動で構築されます。
- プラン変更: DynamoDBの
tier
属性を更新してパイプラインを再実行すれば、差分のリソース(例:プレミアム機能)が自動でデプロイされます。 - オフボーディング:
status
をinactive
に変更し、対応するCloudFormationスタックをcdk destroy
で安全に削除します。
まとめ:CI/CDは、もはやコードを届けるだけのものではない
AWS CDK Pipelinesを使った動的プロビジョニングは、単なるデプロイの自動化ではありません。それは、SaaSビジネスの成長に合わせて、インフラ自身が自己増殖し、最適化していく「生きた工場」を構築するようなものです。
手作業によるプロビジョニングから脱却し、CI/CDパイプラインにテナントのライフサイクル管理を委ねること。それこそが、スケーラブルで競争力のあるSaaSを構築するための、現代的なアプローチなのです。
コメント