Nullstead

Cloudflare Workers

2026年版 Cloudflare Workers運用術:AI機能を足しても壊れない「観測性ファースト」設計

Cloudflare Workersの最新ベストプラクティスをもとに、Queues・Workflows・D1・Observabilityを組み合わせた壊れにくいAI機能運用の設計手順を、個人開発目線で実践的に解説します。

約9分

「個人開発で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年時点の公式ガイドから、個人開発でも効くポイントを先に列挙します。

  1. Bindings優先 WorkersからCloudflare製品へはREST API直叩きよりBindings推奨です。ネットワークホップと認証処理を減らせるため、遅延と実装負債の両方が下がります。

  2. Queues / Workflowsで非同期化 リクエスト中に全部終わらせる設計は、AI連携と相性が悪いです。重い処理や再試行前提の処理はQueuesかWorkflowsへ退避します。

  3. Logs/Tracesを先に有効化 「障害が起きたら入れる」では遅いです。平常時からログ・トレースを取得し、変化を比較できる状態が必要です。

  4. 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は分割実行が推奨です
  • インデックスの有無で桁が変わります

やることはシンプルです。

  1. WHEREで頻出する列にインデックスを貼る
  2. 1件取得系はLIMIT 1を明示
  3. メンテ系バッチは「1000件ずつ」など小分け
  4. 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を入れること自体は難しくありません。難しいのは、入れた後に壊れ続けないことです。

そのためには、次の順番が現実的です。

  1. 同期パスを最小化する
  2. Queues/Workflowsに重い処理を逃がす
  3. D1のクエリを計測してボトルネックを潰す
  4. Observabilityを先に整備する

機能追加の速度は、障害時の復旧速度に上限を決められます。逆に言うと、観測性を整えるほど、開発は大胆にできます。

「まず作る」は正しいです。ただし2026年版は「まず作って、同時に観測する」です。ここまでやっておくと、金曜夜の自分が土曜朝の自分に感謝してくれます。たぶん。

参考リンク

Tags #Cloudflare Workers #Observability #D1 #Queues #Workflows #AI運用