【Pulumi×Cloudflare】エッジの覇者へ贈る:Workers & D1 サーバーレスインフラ完全コード化の極意
テックリードの君なら、もう気づいているはずだ。
コンソールをポチポチ叩いてリソースを作る「クリックOps」や、複雑怪奇で遅いTerraformのHCL(HashiCorp Configuration Language)にフラストレーションを溜める時代は終わった。
エッジコンピューティングのスピードを最大限に活かし、TypeScriptの型安全性をインフラ定義にまで持ち込む。それが Pulumi だ。
今回は、Cloudflare Workers と超高速エッジSQLデータベースである D1、そしてDNSルーティングを Pulumiで完全にコード化(IaC)し、冪等性を完全に担保したサーバーレス環境を構築する手法 を、現場の即戦力となる知見とともに余すところなく伝授しよう。
—
1. エッジ時代におけるIaCとCloudflareプロバイダの深淵
なぜ、Cloudflareワーカーズの管理にPulumiなのか?
Cloudflare Workersは、世界275都市以上のエッジロケーションでコードを実行する。しかし、インフラストラクチャが分散すればするほど、その管理はカオスになりがちだ。
- `wrangler.toml` によるデプロイ管理の限界(複数環境の複雑化、シークレット管理の破綻)
- TerraformのCloudflareプロバイダにおける、Workersのバンドル・デプロイプロセスのまどろっこしさ
Pulumiを使えば、インフラの定義に TypeScript(またはPython, Go)の全パワー を利用できる。関数、ループ、条件分岐、そして何より既存のnpmパッケージを活用したビルドパイプラインの統合が、圧倒的な開発スピードをもたらす。
Cloudflareプロバイダの仕組み
PulumiのCloudflareプロバイダは、CloudflareのAPI(GraphQLおよびREST)を直接叩き、リソースの状態を抽象化する。
ここで重要なのは、「Workersスクリプトのバンドル・アップロード」と「D1データベースのスキーマ適用」というアプリケーションレイヤーに近い処理を、インフラのライフサイクル(Create/Update/Delete)にどう組み込むかという点だ。
—
2. 実践:Workers × D1 × DNS の完全プロビジョニング
百聞は一見に如かず。プロジェクトのディレクトリ構造からコードの隅々まで、プロダクションクオリティの構成を解説する。
ディレクトリ構造のベストプラクティス
チーム開発で破綻しないための、洗練されたレイアウトだ。
.
├── Pulumi.yaml
├── Pulumi.dev.yaml # 開発環境用スタック設定(シークレット含む)
├── Pulumi.prod.yaml # 本番環境用スタック設定
├── package.json
├── tsconfig.json
├── index.ts # インフラのメインエントリーポイント
└── src # Workersのソースコード
├── index.ts
└── schema.sql # D1データベースの初期スキーマ
1. `package.json` と依存関係
最新の型定義と、ビルドに不可欠なパッケージを確実に固定する。
{
“name”: “cloudflare-edge-pulumi”,
“devDependencies”: {
“@pulumi/cloudflare”: “^5.0.0”,
“@pulumi/pulumi”: “^3.100.0”,
“typescript”: “^5.3.0”,
“esbuild”: “^0.19.0”
},
“dependencies”: {
“@cloudflare/workers-types”: “^4.20231218.0”
}
}
2. インフラ定義の核心:`index.ts`
ここが今回のハイライトだ。D1データベースの作成、スキーマのバインド、Workersスクリプトのビルド&デプロイ、そしてカスタムドメインのルーティングまでを1つのトランザクション(のような冪等性)で完結させる。
import as pulumi from “@pulumi/pulumi”;
import as cloudflare from “@pulumi/cloudflare”;
import as esbuild from “esbuild”;
import as fs from “fs”;
import as path from “path”;
// 1. スタック設定の読み込み
const config = new pulumi.Config();
const domain = config.require(“domain”);
const zoneId = config.require(“zoneId”);
const environment = pulumi.getStack();
// 2. D1データベースのプロビジョニング
// エッジに分散するSQLiteデータベースを宣言的に作成
const database = new cloudflare.D1Database(`app-db-${environment}`, {
accountId: config.require(“cloudflareAccountId”),
name: `production-d1-${environment}`,
});
// 3. Workersスクリプトのインメモリビルド(esbuild)
// 開発体験を極限まで高めるため、Pulumiの実行時にコードをバンドルする
const buildWorker = () => {
const entryPoint = path.join(__dirname, “src”, “index.ts”);
const result = esbuild.buildSync({
entryPoints: [entryPoint],
bundle: true,
write: false,
format: “esm”,
target: “esnext”,
minify: true,
});
return result.outputFiles[0].text;
};
// 4. Cloudflare Workers Scriptの定義
const workerScript = new cloudflare.WorkerScript(`edge-worker-${environment}`, {
accountId: config.require(“cloudflareAccountId”),
name: `api-${environment}`,
content: buildWorker(),
// D1データベースをバインディングとしてWorkersに渡す
d1Databases: [{
binding: “DB”,
databaseId: database.id,
}],
compatibilityDate: “2024-01-01”,
});
// 5. カスタムドメイン(Routes)の設定
// 例: api.example.com/ をWorkersに向ける
const workerRoute = new cloudflare.WorkerRoute(`worker-route-${environment}`, {
zoneId: zoneId,
pattern: `api.${domain}/`,
scriptName: workerScript.name,
});
// 6. 出力値の定義(CI/CDやデバッグ用)
export const workerName = workerScript.name;
export const d1DatabaseId = database.id;
export const endpointUrl = pulumi.interpolate`https://api.${domain}`;
—
3. チーム開発の生産性を爆上げするプロの技術
ここからが、凡百のチュートリアルとは一線を画す「テックリードの知見」だ。チーム全体の開発スピードを物理の限界まで加速させる設定と作法を共有しよう。
隠れたキーボードショートカット & IDEプラグイン(VS Code)
VS CodeでPulumiを扱う際、以下のプラグインを入れていないなら今すぐインストールせよ。
1. Pulumi Extension (Official)
- スタックのプレビュー、更新、リソースグラフの視覚化をIDE内で完結させる。
2. Error Lens
- TypeScriptの型エラーやPulumiのConfig未設定エラーをコード行の末尾にインライン表示させ、視線移動をゼロにする。
【神ショートカット】
- `Ctrl + Shift + P` -> `Pulumi: Preview` : 変更を加えた瞬間にインフラの差分(Diff)を非同期で確認。
- `Ctrl + Shift + P` -> `Pulumi: Up` : デプロイの実行。
チーム開発における設定共有化ルール
複数のエンジニアでCloudflareインフラを触るとき、シークレット(APIトークン等)の管理で事故が起きる。以下のルールを鉄則とせよ。
1. プレーンテキストのシークレット禁止
- `Pulumi.
.yaml` に機密情報を直接書かない。必ず `pulumi config set –secret` を使用し、Pulumi Secret Encryption(KMS / AWS Secrets Manager / HashiCorp Vault等、またはPulumi Serviceの暗号化)を利用する。
2. Passphraseの共有
- バックエンドにPulumi Service(SaaS)を使わず、S3/GCSなどをState Backendにする場合は、`PULUMI_CONFIG_PASSPHRASE` 環境変数を1Password等のパスワードマネージャーでチーム共有し、CI/CDおよびローカルの暗号化鍵を統一する。
—
4. ローカル開発から本番デプロイまでのシームレスなワークフロー
エッジ開発の最大のボトルネックは「ローカルと本番の挙動の乖離」だ。
これを打破するため、`Wrangler` のローカルエミュレーションと Pulumi のIaCを組み合わせたハイブリッド・ワークフローを構築する。
1. ローカル開発(Wrangler CLIの活用)
Workersのロジック開発やD1のクエリテストには、Cloudflare公式の `wrangler` をローカルサーバーとして走らせる。
ローカルでのD1マイグレーション実行とWorkers起動
npx wrangler d1 execute production-d1-dev –local –file=./src/schema.sql
npx wrangler dev
2. インフラのプロビジョニング & デプロイ(Pulumi)
ローカルで動作確認が取れたコードとインフラの変更を、Pulumiで一撃でクラウドへ反映する。
依存関係のインストールとログイン
npm install
pulumi stack select dev
プレビューで変更点を入念に確認(ここで恐怖心を取り除く)
pulumi preview
デプロイ実行
pulumi up –yes
このワークフローにより、「インフラの構築」「データベースのプロビジョニング」「Workersのコードデプロイ」「DNSルーティングの設定」のすべてが、ひとつの `pulumi up` コマンドに収束する。
—
結び:インフラをコードで統べる快感
ここまでついてきた君なら、もう従来の重厚長大なインフラ管理に戻ることはできないはずだ。
TypeScriptの型補完が効く快適なエディタの中で、Cloudflare Workersの高速なエッジ演算とD1のデータベースが、Pulumiのコードによって美しく結びつき、一瞬で世界中にデプロイされていく。
この完全自動化されたサーバーレス・パイプラインこそが、モダンな開発チームに圧倒的なアジリティをもたらす。さあ、今すぐターミナルを開き、`pulumi up` を叩いて世界を書き換えよう。