Pulumiで極めるブルーグリーン・インフラストラクチャ:ゼロダウンタイムのその先へ
インフラストラクチャをコードとして管理する(IaC)時代において、私たちは長年のパラドックスに直面してきた。
「宣言的にインフラを安全に定義できる」というメリットと、「インフラの更新には常にデストラクト(破壊)とクリエイト(作成)のタイムラグ=ダウンタイムが伴う」という物理的制約の衝突である。
特に、ALB(Application Load Balancer)のターゲットグループアタッチメント、永続性を求めないステートレスなコンテナ群、そしてDNSの伝播遅延を伴うルーティング変更を、単一のステートファイル上で直列に実行しようものなら、本番環境のSLAは容易に崩壊する。
世の多くのエンジニアは、この限界を前にして「Blue/Greenデプロイメントはアプリケーションレイヤー(ECSやK8sのRolling Update等)の仕事」と諦める。だが、それは間違いだ。
インフラストラクチャそのものをBlueとGreenの二重系として構築し、Pulumiのエンジンと状態管理の深淵をハックすることで、インフラ層を含めた真のゼロダウンタイム・デプロイメントを完全自動化できる。
本稿では、Pulumiの `alias` 機能とスタックライフサイクルのプリミティブを極限まで活用し、ロードバランサーの切り替えから失敗時の瞬時ロールバックまでをコード化する、極限のエキスパート知見を授ける。
—
1. 内部アーキテクチャの理解:なぜ従来のIaCはBlue/Greenで破綻するのか?
多くのIaCツール(Terraform等)でBlue/Greenをやろうとすると、「リソースの置き換え(Replacement)」の挙動に足元をすくわれる。
リソース名やタグを変更して新しい環境(Green)を作ろうとした際、古い環境(Blue)を参照している依存関係が先に削除(Destroy)され、コネクションが切断される現象(Create-Before-Destroyの限界)に直面する。
Pulumiはこの課題に対して、Resource Aliases(エイリアス)とExplicit Dependencies(明示的依存関係)という強力な武器を提供している。
Pulumiのエンジンは、URN(Uniform Resource Name)によってリソースを追跡する。
新しいGreen環境を構築する際、旧Blue環境と共存させ、段階的にトラフィックをシフトさせ、最終的にBlueを安全に消し去るためには、このURNとステートのライフサイクルを完全に掌握しなければならない。
—
2. 設計:コードで体現するデュアル・インフラストラクチャ
以下に示すのは、TypeScriptを用いたPulumiによるブルーグリーンインフラストラクチャのコア実装である。
ここでは、単一のスタック内で `activeColor`(`blue` または `green`)というコンフィグ変数をトリガーにして、ゼロダウンタイムのルーティング切り替えを実現する。
実装コード:完全自動化されたBlue/Greenスタック
import as pulumi from “@pulumi/pulumi”;
import as aws from “@pulumi/aws”;
// コンフィグレーションのロード
const config = new pulumi.Config();
const activeColor = config.require<"blue" | "green">(“activeColor”);
const inactiveColor = activeColor === “blue” ? “green” : “blue”;
// 1. 共通VPCとロードバランサーの取得
const vpc = aws.ec2.getVpc({ default: true });
const alb = new aws.lb.LoadBalancer(“app-alb”, {
internal: false,
loadBalancerType: “application”,
securityGroups: [/ 適切なセキュリティグループID /],
subnets: [/ サブネットIDの配列 /],
});
// 2. ブルーおよびグリーン用のターゲットグループを常時並行稼働
const blueTg = new aws.lb.TargetGroup(“app-tg-blue”, {
port: 80,
protocol: “HTTP”,
vpcId: vpc.then(v => v.id),
targetType: “ip”,
healthCheck: {
path: “/healthz”,
matcher: “200”,
},
});
const greenTg = new aws.lb.TargetGroup(“app-tg-green”, {
port: 80,
protocol: “HTTP”,
vpcId: vpc.then(v => v.id),
targetType: “ip”,
healthCheck: {
path: “/healthz”,
matcher: “200”,
},
});
// 3. アクティブなカラーに基づくリスナーアクションの動的ルーティング
// ここがゼロダウンタイムのキモ:トラフィックの向き先をアクティブカラーに完全同期
const activeTargetGroupArn = activeColor === “blue” ? blueTg.arn : greenTg.arn;
const listener = new aws.lb.Listener(“app-listener”, {
loadBalancerArn: alb.arn,
port: 80,
protocol: “HTTP”,
defaultActions: [{
type: “forward”,
targetGroupArn: activeTargetGroupArn,
}],
});
// 4. ECSサービスをそれぞれのカラーで定義(エイリアスを活用したステート保持)
const createEcsService = (color: “blue” | “green”, tg: aws.lb.TargetGroup) => {
return new aws.ecs.Service(`app-service-${color}`, {
cluster: “my-ecs-cluster”,
desiredCount: 3,
// アクティブなカラーのサービスのみ最新のイメージを強制、非アクティブは最小限にする等のチューニングが可能
taskDefinition: / ECSタスク定義のARN /,
launchType: “FARGATE”,
networkConfiguration: {
subnets: [/ サブネットID /],
assignPublicIp: true,
},
loadBalancers: [{
targetGroupArn: tg.arn,
containerName: “app”,
containerPort: 80,
}],
}, {
// Pulumiのエイリアス機能:リソース名が変更されてもステートの連続性を保証
aliases: [{ name: `app-service-${color}` }],
// ライフサイクル保護:デプロイ中の切断を防ぐため、作成を先に行う
deleteBeforeReplace: false,
});
};
const blueService = createEcsService(“blue”, blueTg);
const greenService = createEcsService(“green”, greenTg);
// エクスポート:現在のルーティング状況を可視化
export const url = alb.dnsName;
export const currentActiveColor = activeColor;
—
3. 自動化パイプラインの深淵:CI/CDからの安全な切り替えとロールバック
コードを書くだけではプロとは言えない。パイプライン上での「安全弁(Safeguard)」の設計こそがSREの腕の見せ所である。
デプロイメントのフローは以下のステップで完全自動化する。
1. Green環境の事前デプロイ: 現在 `blue` が稼働している状態で、`activeColor = blue` のまま、Green側のコンテナイメージを最新版にビルド・プッシュし、インフラを適用する(この時点ではトラフィックは流れない)。
2. スモークテスト: Green側のターゲットグループに紐づくECSタスクのプライベートIPや、テスト用リスナーを経由してヘルスチェックおよびインテグレーションテストを実行。
3. トラフィックシフト(Pulumiコンフィグの変更): テストが成功したら、Pulumiのコンフィグを書き換える。
pulumi config set activeColor green
pulumi up –non-interactive –yes
4. 即座のロールバック機構: 万が一、切り替え後にメトリクス(5xxエラー率、レイテンシ)が異常値を示した場合の自動ロールバックを保証するカスタムスクリプトをCI/CDに組み込む。
ロールバック・シールドスクリプト(TypeScript / Pulumi API利用)
CLIを直接叩くのではなく、Pulumi Automation API(Pulumiをコードからプログラムとして操作する仕組み)を使用することで、高度なヘルスチェック監視付きデプロイメント・エンジンを自作できる。
import { Stack } from “@pulumi/pulumi/automation”;
async function deployWithAutomaticRollback() {
// 既存のスタックをプログラムからロード
const stack = await Stack.select(“production”, {
workDir: “./infra”,
});
console.log(“1. Green環境への事前デプロイ開始…”);
await stack.setConfig(“activeColor”, { value: “blue” }); // まずはBlueのままインフラを最新化
await stack.up({ onOutput: console.log });
console.log(“2. トラフィックをGreenへ切り替え…”);
await stack.setConfig(“activeColor”, { value: “green” });
const upResult = await stack.up({ onOutput: console.log });
console.log(“3. 統合ヘルスチェックの実行…”);
const isHealthy = await runHealthCheck(upResult.outputs.url.value);
if (!isHealthy) {
console.error(“CRITICAL: グリーン環境のヘルスチェックに失敗しました。即座にロールバックします。”);
// 自動ロールバック
await stack.setConfig(“activeColor”, { value: “blue” });
await stack.up({ onOutput: console.log });
throw new Error(“Deployment rolled back successfully due to health check failure.”);
}
console.log(“Deployment completed with zero downtime.”);
}
async function runHealthCheck(url: string): Promise
// 実際にはここで外部APIやDatadog等のメトリクスをポーリングする
// 例: 5xxエラーレートが閾値未満か確認
return true;
}
deployWithAutomaticRollback().catch(err => {
console.error(err);
process.exit(1);
});
—
4. エキスパート向けハック:ステートロックとメモリ管理の最適化
大規模なインフラストラクチャにおいて、Pulumiを実行する際のパフォーマンスチューニングとメモリ管理は、パイプラインの安定性に直結する。
1. プロバイダのプラグインキャッシュと並行度制御
Blue/Greenを単一スタックで管理すると、AWSプロバイダが管理するリソースの数が倍増する。これにより、Pulumiのグラフ走査とリフレッシュフェーズでメモリ消費量が増大する。
CIランナー(GitHub ActionsやGitLab CI)のメモリリークを防ぐため、以下の環境変数を設定し、並行度を制御せよ。
同時リクエスト数を制限し、メモリ枯渇を防ぐ
export PULUMI_PARALLELISM=10
プラグインのダウンロードオーバーヘッドを排除
export PULUMI_PLUGIN_HOME=/opt/pulumi/plugins
2. ステートファイルの肥大化対策とプレビューの高速化
リソースが増えてくると、`pulumi preview` の段階でAWS APIへの過剰なコールが発生し、スロットリング(Rate Exceeded)を引き起こす。
これを回避するため、AWSプロバイダの設定でリトライ回数とバックオフを明示的にチューニングする。
// Pulumiプログラムの先頭でAWSプロバイダを明示的にインスタンス化し、パフォーマンスを最適化
const awsProvider = new aws.Provider(“aws-provider”, {
region: “ap-northeast-1”,
maxRetries: 10,
});
// 以降のリソース定義に provider: awsProvider を紐付ける
—
5. 結び:インフラストラクチャの「静寂」を手に入れろ
真に優れたインフラストラクチャとは、そこに存在すら感じさせないものである。
ユーザーがリクエストを送り続ける最中、バックグラウンドでは新しいコンテナが立ち上がり、ヘルスチェックを通過し、ロードバランサーの重みがアトミックに切り替わる。そのすべてが、Pulumiの精密なステート管理とプログラムの制御力によって、エラーの余地なく自動化されている。
マニュアルをなぞるだけのインフラ管理は今日で終わりにしよう。
コードを書き、エンジンをハックし、ゼロダウンタイムのその先にある「静寂」を、君の手でデプロイせよ。