Cloudflare Workers
2026年版 Cloudflare Workers運用術:AI機能を足しても壊れない「観測性ファースト」設計
Cloudflare Workersの最新ベストプラクティスをもとに、Queues・Workflows・D1・Observabilityを組み合わせた壊れにくいAI機能運用の設計手順を、個人開発目線で実践的に解説します。
「個人開発でAIを入れたら便利になったけど、なぜか障害の再現率だけがプロダクション級になった」という経験、ありませんか。私はあります。しかもだいたい金曜の夜です。
2025〜2026年のCloudflare周辺は、Workers本体だけでなくObservability、Queues、Workflows、D1の実運用情報がかなり充実してきました。特に「まず動かす」から「壊れ方をコントロールする」に重心が移ってきた印象です。
この記事では、個人開発でCloudflare Workersを使ってAI機能(要約、分類、通知など)を足すときに、あとで泣かないための設計を、観測性ファーストでまとめます。敬体で書きますが、気分は運用当番のメモです。
なぜ今「観測性ファースト」なのか
Cloudflareの2026年2月の更新で、WorkersのBest PracticesとObservability UIが強化されました。方向性は明快で、速度・信頼性・可観測性を最初からセットで設計することです。
ここで言う観測性(※Observability)は「ログがある」だけではありません。失敗したリクエストに対して、次をすぐ答えられる状態を指します。
- どこで遅くなったか
- どの入力で失敗したか
- 何回リトライしたか
- どこまで処理が完了したか
AI機能を追加すると、外部API呼び出し・トークン制限・タイムアウト・再実行といった不確定要素が増えます。つまり、アプリが賢くなるほど、運用は「勘」から「計測」に寄せないと崩れます。
2026年のWorkers運用で押さえるべき変更点
2026年時点の公式ガイドから、個人開発でも効くポイントを先に列挙します。
Bindings優先 WorkersからCloudflare製品へはREST API直叩きよりBindings推奨です。ネットワークホップと認証処理を減らせるため、遅延と実装負債の両方が下がります。
Queues / Workflowsで非同期化 リクエスト中に全部終わらせる設計は、AI連携と相性が悪いです。重い処理や再試行前提の処理はQueuesかWorkflowsへ退避します。
Logs/Tracesを先に有効化 「障害が起きたら入れる」では遅いです。平常時からログ・トレースを取得し、変化を比較できる状態が必要です。
nodejs_compatと互換日付を更新 互換日付が古いままだと、後でライブラリ導入時に地雷を踏みやすいです。
この4つ、派手さはないですが、後から効く漢方です。運用に効く漢方はだいたい苦いです。
設計のコア:同期パスを細く、非同期パスを太く
私が最近よく使う構成は、次の2レイヤーです。
- 同期パス(ユーザー応答): 入力検証、軽量な保存、ジョブ投入だけ
- 非同期パス(裏側処理): AI推論、外部連携、通知、再試行
これをWorkers + Queues + D1で作る最小例です。
# wrangler.toml(例)
name = "obs-first-app"
main = "src/index.ts"
compatibility_date = "2026-03-12"
compatibility_flags = ["nodejs_compat"]
[[d1_databases]]
binding = "DB"
database_name = "obs-first"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
[[queues.producers]]
queue = "ai-jobs"
binding = "AI_JOBS"
[[queues.consumers]]
queue = "ai-jobs"
max_batch_size = 20
max_batch_timeout = 10
max_retries = 5
dead_letter_queue = "ai-jobs-dlq"
// src/index.ts(概念実装・環境に合わせて要調整)
export interface Env {
DB: D1Database;
AI_JOBS: Queue;
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const body = await req.json<{ userId: string; text: string }>();
const jobId = crypto.randomUUID();
await env.DB.prepare(
`INSERT INTO jobs (id, user_id, text, status, created_at)
VALUES (?1, ?2, ?3, 'queued', datetime('now'))`
).bind(jobId, body.userId, body.text).run();
await env.AI_JOBS.send({ jobId });
return Response.json({ ok: true, jobId }, { status: 202 });
},
async queue(batch: MessageBatch<{ jobId: string }>, env: Env): Promise<void> {
for (const msg of batch.messages) {
try {
// AI推論や外部API連携
// 成功時にステータス更新
await env.DB.prepare(
`UPDATE jobs SET status = 'done', updated_at = datetime('now') WHERE id = ?1`
).bind(msg.body.jobId).run();
msg.ack();
} catch (e) {
// 失敗を記録して再試行へ
await env.DB.prepare(
`UPDATE jobs SET status = 'retrying', updated_at = datetime('now') WHERE id = ?1`
).bind(msg.body.jobId).run();
msg.retry();
}
}
}
};
ポイントは、ユーザーの待ち時間をジョブ投入までに限定することです。AI処理の気まぐれにUXを握らせない、という思想です。
D1でハマりやすい性能罠と回避策
D1は「SQLiteだから軽いでしょ」と油断すると、運用で急に現実を見せてきます。公式の制限情報から、実務で効く要点だけ抜きます。
- D1は基本的に単一DBインスタンス内は逐次処理です
- クエリ時間が長いほどスループットは落ちます
- 長いUPDATE/DELETEは分割実行が推奨です
- インデックスの有無で桁が変わります
やることはシンプルです。
WHEREで頻出する列にインデックスを貼る- 1件取得系は
LIMIT 1を明示 - メンテ系バッチは「1000件ずつ」など小分け
- statusカラムとupdated_atで処理追跡を可能にする
「AIが遅い」の8割は、実は周辺のDB待ちやリトライ渋滞です。モデルのせいにする前にSQLを見ると、平和が訪れます。少なくとも私の心は落ち着きます。
Observabilityの実装:まず最低ラインを作る
2026年のWorkers Observabilityは、可視化・共有リンク・JSON/CSVエクスポートが使えるようになり、障害共有がかなり楽になりました。スクショ職人を卒業しやすいです。
加えてOTel(※OpenTelemetry)エクスポートにより、既存の監視基盤へトレース/ログを送れます(現時点ではメトリクス輸出に制約あり)。
{
"observability": {
"traces": {
"enabled": true,
"destinations": ["honeycomb-traces"],
"head_sampling_rate": 0.1,
"persist": true
},
"logs": {
"enabled": true,
"destinations": ["honeycomb-logs"],
"head_sampling_rate": 0.5,
"persist": true
}
}
}
最初の運用ルールは、次の3つだけで十分です。
- エラー率: 5分平均でしきい値超過を通知
- P95遅延: デプロイ前後で比較
- DLQ件数: 増加したら即確認
この3つがあれば、だいたいの「なんか遅い」「たまに落ちる」を再現可能な問題に変換できます。人間が解ける形にするのが観測性です。
Workflowsは「長い処理の正気」を保つ道具
Queuesはバッファと再試行に強いですが、分岐・待機・承認フローのような長い手順ではWorkflowsが効きます。
- 数時間〜数日単位の処理継続
- 外部イベント待ち(承認、Webhookなど)
- ステップ単位のリトライ
- 状態の永続化
例えば「AIで下書き生成 → 人間承認待ち → 公開」のような流れは、Workflowsと相性が良いです。step.waitForEventを使えば、深夜3時に「承認待ちをwhileループで実装した過去の自分」と決別できます。
失敗パターン集(私の反省入り)
最後に、実際によく起きる事故を3つだけ。
1. 同期処理で全部やる
最初は動くので油断します。アクセス増加でタイムアウト祭りになります。祭りは楽しいですが、運用祭りは楽しくありません。
2. ログが文字列だけ
console.log("error")だけだと追跡不能です。jobId, userId, step, duration_msなど、検索可能なキーを揃えます。
3. 再試行戦略がない
失敗時に即500返す設計は、外部依存があるAI機能と相性最悪です。Queuesのretry()とDLQを前提に設計します。
まとめ:AI機能より先に、壊れ方をデザインする
2026年の個人開発では、AIを入れること自体は難しくありません。難しいのは、入れた後に壊れ続けないことです。
そのためには、次の順番が現実的です。
- 同期パスを最小化する
- Queues/Workflowsに重い処理を逃がす
- D1のクエリを計測してボトルネックを潰す
- Observabilityを先に整備する
機能追加の速度は、障害時の復旧速度に上限を決められます。逆に言うと、観測性を整えるほど、開発は大胆にできます。
「まず作る」は正しいです。ただし2026年版は「まず作って、同時に観測する」です。ここまでやっておくと、金曜夜の自分が土曜朝の自分に感謝してくれます。たぶん。
参考リンク
- https://developers.cloudflare.com/changelog/post/2026-02-15-workers-best-practices/
- https://developers.cloudflare.com/changelog/post/2026-02-06-observability-ui-refresh/
- https://developers.cloudflare.com/workers/observability/exporting-opentelemetry-data/
- https://developers.cloudflare.com/d1/platform/limits/
- https://developers.cloudflare.com/queues/configuration/batching-retries/
- https://developers.cloudflare.com/workflows/