PR

【AWS CI/CD】CDK PipelinesによるSaaS動的プロビジョニング:テナント追加を自動化する実践的パイプライン設計

はじめに:テナント追加が「深夜の手作業」になっていませんか?

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にデプロイ]
  1. テナント情報の登録: 管理者が、テナントIDや契約プラン(例: premium)といった情報をDynamoDBテーブルに登録します。
  2. パイプラインのトリガー: DynamoDB Streamsがテーブルの変更を検知し、Lambda関数をトリガー。このLambdaがCDK Pipelineの実行を開始します。
  3. 動的なステージ生成: パイプラインのSynth(合成)ステージが、DynamoDBから現在有効なテナントのリストを取得し、そのリストに基づいてデプロイすべきインフラ(Stage)をプログラムで動的にパイプライン自身に追加します。
  4. デプロイ: パイプラインは、生成されたステージに従い、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として定義します。このスタックは、tenantIdtierをプロパティとして受け取り、リソースの構成を動的に変更できるように設計します。

// 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属性を更新してパイプラインを再実行すれば、差分のリソース(例:プレミアム機能)が自動でデプロイされます。
  • オフボーディング: statusinactiveに変更し、対応するCloudFormationスタックをcdk destroyで安全に削除します。

まとめ:CI/CDは、もはやコードを届けるだけのものではない

AWS CDK Pipelinesを使った動的プロビジョニングは、単なるデプロイの自動化ではありません。それは、SaaSビジネスの成長に合わせて、インフラ自身が自己増殖し、最適化していく「生きた工場」を構築するようなものです。

手作業によるプロビジョニングから脱却し、CI/CDパイプラインにテナントのライフサイクル管理を委ねること。それこそが、スケーラブルで競争力のあるSaaSを構築するための、現代的なアプローチなのです。

コメント

タイトルとURLをコピーしました