Pulumi CloudとDatadogの極限融合:インフラ変更履歴とオブザーバビリティの完全同期によるMTTR最小化アーキテクチャ
インフラストラクチャ・アズ・コード(IaC)のパラダイムにおいて、我々はコードによる再現性と冪等性を手に入れた。しかし、真のSREの戦場において「コードが正しいこと」と「稼働中のシステムが意図通りであること」の間には、常に深い溝が存在する。
夜間、突如としてレイテンシーが跳ね上がり、エラーレートが閾値を超えたとする。Datadogのダッシュボードには赤いアラートが点滅している。この時、脳裏をよぎる最初の問いは何か?
「直近で誰が、どのスタックをデプロイした?」
この答えを探すために、Pulumi Consoleを開き、Gitのコミットログを漁り、Slackの通知チャンネルをさかのぼる――そんな無駄な数分間を過ごしているようでは、プロフェッショナルなSREとは言えない。
本稿では、Pulumi Cloud(旧Pulumi Service)のWebhookおよび監査ログをDatadogへ完全に統合し、インフラストラクチャの変更イベントとアプリケーショントレースを同一タイムライン上に完璧に同期させるための極限のアーキテクチャを解説する。単なる「連携手順の紹介」ではない。APIのペイロード構造、サーバーレスによるイベント中継の最適化、そして障害時の迅速なロールバック判断を可能にするダッシュボード構築まで、骨の髄まで実戦で使える知見を叩き込む。
—
1. アーキテクチャ全体像:イベント駆動型オブザーバビリティパイプライン
Pulumi CloudのWebhookは、スタックの更新(`update`)の開始、成功、失敗などのライフサイクルイベントをリアルタイムでHTTP POSTとして発火する。これをセキュアに受け止め、DatadogのLogs APIおよびEvents APIにマッピングして流し込む必要がある。
[ Pulumi Cloud ]
│ (Webhook / Signed Payload)
▼
[ AWS Lambda (TypeScript) ] ──(パース & エンリッチメント)
│
├─► [ Datadog Logs API ] (変更詳細・スタック差分)
└─► [ Datadog Events API ] (デプロイ開始/完了のマーカー)
このパイプラインを構築するにあたり、以下の要件を妥協なく満たす必要がある。
1. セキュリティ: HMAC署名(`X-Pulumi-Signature`)の検証による偽装リクエストの完全排除。
2. パフォーマンス: サーバーレス(AWS Lambda)によるスケーラビリティとコールドスタートの最小化。
3. コンテキストの付与: 単なる「デプロイ成功」ではなく、誰が、どのGitコミットで、どのリソース群を変更したかの差分(Diff)要約をDatadogイベントに埋め込むこと。
—
2. Pulumi Webhookレシーバーの実装(TypeScript / AWS Lambda)
まずは、Pulumi CloudからのWebhookを受け取り、署名を検証した上でDatadogへ転送するLambda関数のコードを示す。ここでは、環境変数からシークレットを安全に取得し、メモリ消費と実行時間を極限まで削ぎ落とした実装を行っている。
import { Context, APIGatewayProxyEvent, APIGatewayProxyResult } from ‘aws-lambda’;
import as crypto from ‘crypto’;
import https from ‘https’;
// Datadog APIのエンドポイント(USサイトの例。必要に応じて変更)
const DD_API_HOST = ‘http-intake.logs.datadoghq.com’;
const DD_API_KEY = process.env.DD_API_KEY || ”;
const PULUMI_WEBHOOK_SECRET = process.env.PULUMI_WEBHOOK_SECRET || ”;
interface PulumiEventPayload {
timestamp: number;
name: string; // 例: stack_update
resource: string;
action: string;
status: string; // succeeded, failed, etc.
stackName: string;
organizationName: string;
projectName: string;
// その他Pulumi Cloud特有のペイロード構造
deploy?: {
id: string;
kind: string;
message: string;
author: string;
commit?: string;
};
}
export const handler = async (event: APIGatewayProxyEvent, context: Context): Promise
try {
const signature = event.headers[‘X-Pulumi-Signature’] || event.headers[‘x-pulumi-signature’];
const rawBody = event.body || ”;
// 1. HMAC-SHA256 署名検証によるセキュリティ担保
if (!verifyPulumiSignature(rawBody, signature, PULUMI_WEBHOOK_SECRET)) {
console.error(‘Invalid signature detected.’);
return { statusCode: 401, body: JSON.stringify({ error: ‘Unauthorized’ }) };
}
const payload: PulumiEventPayload = JSON.parse(rawBody);
// 2. Datadog向けのログおよびイベントへの変換
await sendToDatadog(payload);
return { statusCode: 200, body: JSON.stringify({ status: ‘ok’ }) };
} catch (error: any) {
console.error(‘Error processing Pulumi webhook:’, error);
return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
}
};
function verifyPulumiSignature(body: string, signature: string | undefined, secret: string): boolean {
if (!signature) return false;
const hmac = crypto.createHmac(‘sha256’, secret);
const digest = `sha256=${hmac.update(body).digest(‘hex’)}`;
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
async function sendToDatadog(payload: PulumiEventPayload): Promise
const ddLogEntry = {
ddsource: ‘pulumi’,
ddtags: `env:${payload.stackName},project:${payload.projectName},org:${payload.organizationName}`,
hostname: ‘pulumi-cloud’,
message: `Pulumi stack ${payload.stackName} update ${payload.status} by ${payload.deploy?.author || ‘unknown’}`,
pulumi: payload,
};
const data = JSON.stringify(ddLogEntry);
const options = {
hostname: DD_API_HOST,
port: 443,
path: `/v1/input/${DD_API_KEY}`,
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
‘Content-Length’: Buffer.byteLength(data),
},
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(new Error(`Datadog API responded with status code ${res.statusCode}`));
}
});
req.on('error', (e) => reject(e));
req.write(data);
req.end();
});
}
—
3. Pulumi Cloud側のWebhook自動構成(Pulumi自身による管理)
「インフラストラクチャ・アズ・コード」を標榜する我々が、Webhookのエンドポイントを手動でポチポチ設定するなどあってはならない。Pulumi Cloudの組織設定やスタック設定も、Pulumiのプロバイダー(`pulumi-service`)を用いてコードとして完全自動化する。
以下は、組織全体に対してDatadog転送用LambdaのAPI Gateway URLへ飛ばすWebhookを定義するコードの断片である。
import as pulumi from “@pulumi/pulumi”;
import as pulumiservice from “@pulumi/pulumi-service”;
// すでに構築済みのAPI Gatewayのエンドポイント
const config = new pulumi.Config();
const webhookTargetUrl = config.require(“webhookTargetUrl”);
const webhookSecret = config.requireSecret(“webhookSecret”);
// Pulumi Cloud Organization Webhookの定義
const datadogWebhook = new pulumiservice.Webhook(“datadog-integration-webhook”, {
organization: “my-enterprise-org”,
displayName: “Datadog-Audit-Forwarder”,
payloadUrl: webhookTargetUrl,
secret: webhookSecret,
active: true,
format: “json_v1”,
filters: [
“stack_pre_run”,
“stack_post_run”,
“stack_import”,
“stack_destroy”
],
});
export const webhookId = datadogWebhook.id;
このコードを実行することで、PulumiのCI/CDパイプラインや手動デプロイの全イベントが、自動的にDatadogへとルーティングされる基盤がコードベースで担保される。
—
4. 障害発生時の迅速なロールバック判断を支えるDatadogダッシュボード構築
イベントがDatadogに集約されただけでは不十分だ。SREが障害時に「あ、このデプロイが原因だ」と0.1秒で確信し、即座にロールバックの判断を下すための「コマンダーズ・ダッシュボード」を構築する。
DatadogのDashboard APIまたはTerraform/HCL(あるいはDatadog UI)を用いて、以下のウィジェットを配置する。
1. タイムライン・イベントストリーム(Event Stream)
- クエリ: `sources:pulumi`
- 意味: アプリケーションのメトリクス(CPU、メモリ、5xxエラーレート)のグラフと同一のタイム軸上に、Pulumiのデプロイメントマーカー(誰が、どのコミットを適用したか)を縦線(Event Overlay)として重ねて表示する。
2. デプロイメント成否のカウンタ(Query Value)
- クエリ: `sum:pulumi.stack.update.count{status:succeeded}.as_count()` vs `sum:pulumi.stack.update.count{status:failed}.as_count()`
- 意味: 組織全体のデプロイ成功率をリアルタイムで可視化。
3. アプリケーション・パフォーマンストラッキングとの相関
- APM Service Stat(例: `api-gateway` / `p99 latency`) のグラフに対し、Pulumiの `stack_post_run` イベントをオーバーレイさせる。
> 現場のエキスパート知見:
> 「インフラ変更がアプリのP99レイテンシーに与える影響」を測定する際、AWS側やK8s側のメトリクス変動遅延(Metrics Lag)を考慮せよ。Pulumiのデプロイ完了イベント(`stack_post_run`)のタイムスタンプから±2分間の窓を作り、DatadogのAPMトレース側でエラーのスパイクが発生しているかを機械的に相関させることが、迅速なロールバック(`pulumi stack cancel` や直前のコミットへの巻き戻し)の要となる。
—
5. 高度な最適化と運用ハック
大規模なエンタープライズ環境や、数千のスタックを運用する超高速CI/CDパイプラインにおいて、この連携をさらに堅牢にするためのハックを授ける。
Webhookの再送耐性(Idempotency)とリトライ制御
Pulumi CloudのWebhook配信は、ネットワーク一時断などによりリトライされることがある。Lambda側で処理の冪等性を担保するため、ペイロードに含まれる `deploy.id` をキーとして、DynamoDB等に処理済みフラグ(TTL付き)を書き込むロジックを挟むと、Datadog側への重複ログや重複イベントの氾濫を防げる。
コールドスタート対策
イベント駆動の特性上、デプロイ頻度が低い環境ではLambdaのコールドスタートが発生し、Webhookの応答遅延やタイムアウト(Pulumi Cloud側は通常10秒程度でタイムアウト判定する)を引き起こす恐れがある。
- Node.js/TypeScriptランタイムを選択し、バンドルサイズを最小限にする(esbuild等を使用)。
- 必要に応じてプロビジョンド同時実行数(Provisioned Concurrency)を設定する。
—
結びにかえて:可観測性の最終形態へ
インフラストラクチャ・アズ・コードとオブザーバビリティは、かつて別のサイロに分かれていた。IaCは「構築の道具」であり、Datadogは「運用の道具」であった。
しかし、Pulumi CloudのWebhookを基軸としたこの統合アプローチにより、「コードの変更」が「稼働中のメトリクス」に与える影響のフィードバックループが極限まで加速する。障害発生時、もはや勘や推測に頼る必要はない。画面をひと目見れば、「どのインフラ変更が、どのメトリクスをどう揺らしたか」が残酷なまでの透明性をもって浮かび上がる。
このアーキテクチャを導入した瞬間から、あなたのチームのMTTR(平均修復時間)は劇的に短縮され、真の意味での「攻めのSRE」が完成する。さあ、コードを書き、パイプラインを繋ぎ、システムを完全に掌握せよ。