Cloudflare ハンズオン教科書|2026-08 版
第 16 章

観測・デプロイ運用・料金

本番で困らないための logs・メトリクス・段階的デプロイ・コスト管理

この章で学ぶこと

観測の基本 — ログ・メトリクス・スタックトレース

Workers Logs — 保存して検索できるログ

Workers Logs は、Worker が出力するログを Cloudflare アカウントに自動収集・保存し、ダッシュボードで検索・分析できる仕組みです。新規に作成した Worker では既定で有効ですが、既存プロジェクトでは wrangler.jsonc に observability 設定を追加して再デプロイする必要があります(Wrangler 3.78.6 以上)。

{
	"observability": {
		"enabled": true,
		"head_sampling_rate": 1 // 省略可。既定 = 1(100% 記録)
	}
}

記録されるのは、リクエストごとに 1 件自動生成される invocation log(リクエスト・レスポンス・メタデータ)と、コード中の console.log によるカスタムログ、そして例外です。カスタムログは文字列連結ではなくオブジェクトをそのまま渡すのが定石です。フィールドが自動でインデックスされ、user_id のようなキーでの絞り込みが高速になります。

// NG: テキストマッチでしか検索できない
console.log("user_id: " + 123);

// OK: user_id キーでフィルタできる
console.log({ user_id: 123, event: "checkout" });

高トラフィックなアプリでは head_sampling_rate を 0〜1 で指定し、記録するリクエストの割合を下げてコストを抑えます(例: 0.01 = 1%)。1 ログの上限は 256 KB で、超過分は切り詰められます。料金と保持期間(2026-08 時点)は次のとおりです。

プランログ書き込み保持期間
Workers Free20 万件/日3 日
Workers Paid月 2,000 万件込み、超過 100 万件ごとに $0.607 日

wrangler tail — リアルタイムログ

デプロイ直後の動作確認には、グローバルのログイベントをほぼリアルタイムで流し込む wrangler tail が便利です。プロジェクトディレクトリで実行するだけで、受信リクエストごとの構造化 JSON が届きます。

npx wrangler tail
# jq と組み合わせて必要なフィールドだけ抽出
npx wrangler tail --format json | jq .event.request.url
# エラーになった呼び出しだけに絞る
npx wrangler tail --status error

--method / --search(console.log 本文のテキストマッチ)/ --ip self / --version-id などのフィルタも使えます。リアルタイムログは保存されないことに注意してください。後から調べるためのログは Workers Logs の役割です。第三者サービスへ送りたい場合は OpenTelemetry エクスポート、Workers Logpush(Paid のみ)、Tail Workers が使えます。

同じ console.log が 2 つの経路に分かれる Worker console.log / 例外 wrangler tail 手元のターミナル ほぼリアルタイム 高トラフィック時は間引き そのまま表示 保存されず消える Workers Logs ダッシュボード フィールドで検索 observability を有効化 設定は再デプロイで反映 保存 3〜7 日
図 16-1 同じ console.log が 2 経路に分かれます。上は端末に流れて消えるだけなので、障害を後から掘り返せるのは observability を有効にして再デプロイした下の経路だけです。下の記録量は head_sampling_rate で間引けます(既定 1 = 100%)。

メトリクスとダッシュボード分析

ダッシュボードの Workers & Pages で Worker を選ぶと、リクエスト成功/失敗数、サブリクエスト数、実行ごとの wall time / CPU 時間 / メモリ使用量(分位点表示)などのメトリクスを確認できます。メトリクスは過去 3 か月まで遡れます。ここで重要なのが invocation status(呼び出しステータス)です。HTTP ステータスコードとは別物で、Worker の実行そのものが成功したかを示します。

Invocation status意味エラーコード
Success正常に実行された—
Client disconnected完了前にクライアントが切断—
Worker threw exception未捕捉の JavaScript 例外1101
Exceeded resourcesCPU 時間などのランタイム上限を超過1102, 1027
Internal errorCloudflare 側の内部エラー(課金対象外)—

ルート(route)にひも付けた Worker 群の集計は、ゾーンごとの Workers Analytics(過去 30 日)で確認します。アプリ固有のカスタム指標を取りたい場合は Workers Analytics Engine が用意されています。

ソースマップでスタックトレースを復元する

本番の Worker はバンドル・ミニファイ済みのため、素のスタックトレースは「1 行に潰れた js の何文字目」しか教えてくれません。wrangler.jsonc に 1 行足すと、デプロイ時にソースマップが自動生成・アップロードされ、未捕捉例外のスタックトレースが元の TypeScript のファイル名・行番号に自動で逆引きされます(Wrangler 3.46.0 以上、上限 15 MB gzip)。

{
	"upload_source_maps": true
}

復元されたトレースはリアルタイムログや Tail Workers で確認できます。逆引きは呼び出し完了後に非同期で行われるため CPU 時間には影響しませんが、Worker 内で error.stack を console.log しても復元済みトレースは得られない点に注意してください。

呼び出し中 — Worker が見るのはミニファイ後の位置 呼び出し完了後 — Cloudflare 側で自動的に逆引き Worker 実行中 ミニファイ後 worker.js:1:24601 .map で逆引き upload_source_maps 元の行番号 src/index.ts:12 例外 throw 非同期で 復元済み error.stack はここ止まり CPU 時間には計上されない
図 16-2 破線が「呼び出しが終わった」境界です。逆引きはこの線の外側で走るので CPU 時間を食わず、その代わり内側にいる Worker から error.stack を console.log しても、破線の手前=ミニファイ後の位置しか見えません。

Versions と gradual deployments — 段階的に出す、即座に戻す

Worker のコードや設定を変更するたびに version(バンドル済みコード・静的アセット・バインディング・互換性設定のスナップショット)が作られます。どの version がトラフィックを受けるかを決めるのが deployment です。npx wrangler deploy はこの 2 つを一度に行い、新 version を即座に 100% のトラフィックへ展開します。

この 2 つを分離すると、gradual deployment(段階的ロールアウト)が可能になります。まず versions upload でデプロイせずに version だけを作り、versions deploy で新旧 2 version 間のトラフィック比率を指定します。

# 1. デプロイせずに新バージョンをアップロード
npx wrangler versions upload --message "checkout flow v2"

# 2. 対話プロンプトで比率を指定してデプロイ(例: 旧 90% / 新 10%)
npx wrangler versions deploy

# 非対話で比率を直接指定することもできる
npx wrangler versions deploy <OLD_VERSION_ID>@90% <NEW_VERSION_ID>@10% -y

<OLD_VERSION_ID> / <NEW_VERSION_ID> は npx wrangler versions list で確認できる UUID に置き換えます。エラー率をメトリクス(--version-id 付きの wrangler tail や Version metadata binding)で観測しながら、新 version の比率を段階的に 100% まで上げるのが基本の運用です。分割中は複数 version が同時に稼働するため、同一ユーザーのリクエストが version 間を行き来する version skew が起きえます。ユーザーを一定期間同じ version に固定したい場合は version affinity を使います。

問題が起きたら rollback です。直近 100 version 以内なら、指定 version を即座に 100% トラフィックの新 deployment として展開できます。

# 1 つ前のバージョンに戻す(ID 指定も可)
npx wrangler rollback
# 現在の稼働状態を確認
npx wrangler deployments status

ただし rollback が戻すのはコードと設定だけです。KV・R2・D1・Durable Objects のデータは戻りませんし、バインディング先のリソースを削除・変更していた場合(例: 対象 version が参照する KV namespace を削除済み)はロールバック自体が拒否されます。

wrangler deploy — アップロードと展開が一体で、新 version が即 100% コード変更 新 version 100% 稼働 deploy 即 100% — 途中に止まれる状態が無い versions upload → versions deploy — 同じ変更を比率で少しずつ出す コード変更 新 version トラフィック 0% 分割デプロイ 旧 90% / 新 10% 100% 稼働 新 version へ昇格 upload deploy 様子を見て 旧 version が 100% wrangler rollback — 直近 100 version から即時に戻す 戻るのはコードと設定だけ — KV / D1 などのデータは戻らない
図 16-3 どちらも出発点は同じコード変更で、違うのは upload と deploy を分けたかどうかだけです。上は長い矢印の区間に箱が無い=立ち止まれる状態そのものが存在せず、下は新旧 2 version が同時に動く「分割デプロイ」が挟まるので、そこで異常に気づけます。強調した戻り矢印(rollback)は分割中でも 100% 後でも使え、直近 100 version から選んで即座に 100% へ戻します。

limits と料金 — 上限を知り、課金を見積もる

Workers の料金は「リクエスト数」と「CPU ミリ秒」の 2 軸だけで決まります。CPU 時間はコードが実際に CPU を使った時間で、fetch() や KV 読み取りなど I/O を待っている時間(wall time との差分)は含まれず、課金もされません。duration・データ転送(egress)・帯域への課金はありません。主要な上限と料金(2026-08 時点)は次のとおりです。

項目Workers FreeWorkers Paid($5/月〜)
リクエスト10 万/日(超過で Error 1027)月 1,000 万込み、超過 100 万ごとに $0.30
CPU 時間(課金)課金なし月 3,000 万 CPU ms 込み、超過 100 万 ms ごとに $0.02
CPU 時間の上限/呼び出し10 ms既定 30 秒、最大 5 分まで引き上げ可
メモリ128 MB / isolate128 MB / isolate
サブリクエスト50/リクエスト10,000/リクエスト
Worker サイズ(gzip 後)3 MB10 MB
Worker 数 / アカウント100500
1 リクエストの時間の内訳(帯の幅は実時間に比例) fetch() の応答待ち 120 ms CPU は使わない KV の読み取り待ち 40 ms 同じく非課金 実行 2 ms 実行 3 ms 実行 2 ms 課金対象は強調した 3 区間の合計 = CPU 7 ms wall time 合計 167 ms — 課金にも CPU 上限にも数えない
図 16-4 帯の幅は実時間そのままです。167 ms のうち課金されるのは細い 3 本= CPU 7 ms だけなので、外部 API がどれだけ遅くなっても請求額は動きません。逆にこの細い区間で重いループや暗号処理を回すと、体感が速くてもそのままコストになります。

試算例: 月 1,500 万リクエスト・平均 CPU 7 ms の Worker なら、基本料 $5.00 + リクエスト超過分 $1.50(500 万 × $0.30/100 万)+ CPU 超過分 $1.50(1 億 500 万 ms − 3,000 万 ms ≒ 7,500 万 ms × $0.02/100 万)= 月 $8.00 です。サブリクエストは課金されず、静的アセットへのリクエストは無料・無制限です。

そのほか、同時に応答待ちできる外部接続は 6、環境変数は 1 つ 5 KB で Free 64 個 / Paid 128 個、グローバルスコープの初期化(startup)は 1 秒以内、という上限もあります。Cron・Queue・Durable Object アラームの呼び出しは wall time 15 分までです。暴走やコスト攻撃への保険として、CPU 時間の上限は Worker ごとに明示設定できます(引き上げは Paid のみ、最大 300,000 ms)。

{
	"limits": {
		"cpu_ms": 300000 // 既定は 30000(30 秒)
	}
}

nodejs_compat と CI/CD — npm パッケージとデプロイ自動化

Node.js 互換(nodejs_compat)

npm パッケージの多くは node:buffer や node:crypto といった Node.js API に依存しています。Workers ランタイムはこれらの API のサブセットを提供しており、nodejs_compat フラグを有効化し、compatibility_date を 2024-09-23 以降にすると使えるようになります。

{
	"compatibility_flags": ["nodejs_compat"],
	"compatibility_date": "2026-08-11"
}

Buffer / Crypto / EventEmitter / File system / HTTP(S) / Net / Path / Process / Stream / Timers / URL / Zlib などはランタイムがネイティブ実装しており、未実装の API は Wrangler が unenv によるポリフィルを注入します。ポリフィルされたモジュールは import は通りますが、未実装メソッドを呼ぶと [unenv] <method name> is not implemented yet! という実行時エラーになります。「バンドルは通ったのに本番で落ちる」パターンの典型なので、依存パッケージの動作確認は必須です。AsyncLocalStorage だけ必要なら軽量な nodejs_als フラグもあります。

CI/CD — Workers Builds と GitHub 連携

手元からの wrangler deploy は、チーム開発では「誰の環境から何がデプロイされたか分からない」問題を生みます。Workers Builds は Cloudflare 内蔵の CI/CD で、GitHub / GitLab リポジトリを Worker に接続すると、対象ブランチへの push のたびに自動でビルド・デプロイされます。設定はダッシュボードで完結します。

  1. 新規なら Workers & Pages → Create application → Import a repository からリポジトリを選択
  2. 既存 Worker なら対象 Worker の Settings → Builds → Connect で接続し、ビルド設定を構成
  3. push するとビルドが走り、成功すると version としてアップロード。deploy command が npx wrangler deploy なら、その version がそのまま Active Deployment に昇格

deploy command を npx wrangler versions upload に変えると「ビルドと version 化は自動、本番反映は手動(gradual deployment で)」という安全側の運用にできます。接続時はダッシュボード上の Worker 名と Wrangler 設定の name が一致している必要があります。self-hosted の Git や他のプロバイダを使う場合は、GitHub Actions など外部 CI/CD から wrangler を実行する構成が推奨されています。

ハンズオン 16-1|Workers Logs を有効化して構造化ログを観測する

デプロイ済みの Worker(第 2 章で作成したもので OK)にログ基盤を追加します。所要 10 分。

  1. wrangler.jsonc に observability 設定を追加する
    {
    	"observability": {
    		"enabled": true
    	}
    }
  2. fetch ハンドラに構造化ログを追加する
    export default {
    	async fetch(request, env, ctx): Promise<Response> {
    		const url = new URL(request.url);
    		console.log({ path: url.pathname, colo: request.cf?.colo });
    		return new Response("Hello with logs!");
    	},
    } satisfies ExportedHandler;
  3. npx wrangler deploy で再デプロイする
  4. 別ターミナルで npx wrangler tail --format pretty を起動し、curl https://<WORKER_NAME>.<SUBDOMAIN>.workers.dev/test を数回実行する(URL は deploy 出力に表示されたものに置き換え)
  5. ダッシュボードで Worker を開き、Observability タブでログを表示。path フィールドで絞り込めることを確認する

tail 側には GET https://.../test の invocation ログと {"path":"/test","colo":"NRT"} のようなカスタムログがリアルタイムに流れ、ダッシュボード側には同じログが検索可能な形で保存されていれば成功です。

ハンズオン 16-2|ソースマップでスタックトレースを復元する

意図的にエラーを起こし、TypeScript の元ソース位置でトレースが読めることを確認します。所要 10 分。

  1. wrangler.jsonc に "upload_source_maps": true を追加する
  2. ハンズオン 16-1 のコードにエラー用のパスを追加する
    if (url.pathname === "/boom") {
    	throw new Error("intentional error for source map test");
    }
  3. npx wrangler deploy で再デプロイする
  4. npx wrangler tail を起動した状態で curl -i https://<WORKER_NAME>.<SUBDOMAIN>.workers.dev/boom を実行する

クライアントには 1101 系のエラーページ(HTTP 500)が返り、tail には例外のスタックトレースが表示されます。ミニファイ後の位置ではなく src/index.ts の実際の行番号を指していれば、ソースマップの逆引きが機能しています。確認後、/boom のコードは削除して再デプロイしておきましょう。

ハンズオン 16-3|段階的デプロイとロールバックを試す

新バージョンを 10% のトラフィックだけに公開し、100% 昇格とロールバックまで一巡します。所要 15 分。

  1. レスポンス文字列を "Hello v2!" などに変更し、デプロイせずにアップロードする
    npx wrangler versions upload --message "v2 response"
    出力される Worker Version ID を控える
  2. npx wrangler versions deploy を実行し、対話プロンプトで旧バージョン 90% / 新バージョン 10% を指定する
  3. トラフィックの分配を確認する
    for i in {1..10}; do curl -s https://<WORKER_NAME>.<SUBDOMAIN>.workers.dev/; echo; done
    おおむね 9 回は旧レスポンス、1 回程度は Hello v2! が返る(毎リクエスト独立に振り分けられるため比率は揺れる)
  4. 問題なければ再度 npx wrangler versions deploy で新バージョンを 100% にする
  5. 障害を想定して npx wrangler rollback を実行し、1 つ前のバージョンに戻す。npx wrangler deployments status で旧バージョンが 100% に戻ったことを確認する

npx wrangler deployments list に「分割デプロイ → 100% 昇格 → ロールバック」の履歴が並んでいれば完了です。

つまずきポイント

observability を書いたのにログが溜まらない — 設定はデプロイに焼き込まれるため、wrangler.jsonc に追記しただけでは有効になりません。必ず再デプロイしてください。Wrangler が 3.78.6 より古い場合も設定が反映されません。wrangler --version で確認を。

wrangler tail のログが欠ける・出ない — 高トラフィック時はサンプリングモードに入り一部のメッセージが破棄されます(--status や --method で絞ると回避しやすい)。また同時に tail できるのはダッシュボード込みで 10 クライアントまで。WebSocket ハンドラ内の console.log は接続がクローズされるまで表示されず、close 時に一括で流れます。

Free プランで Error 1102 / 1027 が出る — 1102 は CPU 時間 10 ms などのリソース超過、1027 は日次 10 万リクエストの超過です。limits.cpu_ms で上限を引き上げられるのは Paid プランのみ。重い処理を Free で動かすなら、処理の分割か Paid への移行を検討してください。

rollback で「全部元に戻る」と思い込む — 戻るのはコードと設定だけで、KV / D1 / Durable Objects のデータはそのままです。新バージョンでデータ構造を変えた後に旧コードへ戻すと、旧コードが新形式のデータを読んで壊れることがあります。スキーマ変更は前方互換を保つのが鉄則です。

理解度チェック

Q1. wrangler tail で見たログを 3 日後に検索できるか?

できません。wrangler tail はリアルタイム専用でログを保存しません。後から検索するには observability.enabled = true で Workers Logs を有効化しておく必要があります(保持期間は Free 3 日 / Paid 7 日。2026-08 時点)。

Q2. 新バージョンをトラフィックの 10% だけに公開したい。使うコマンドは?

npx wrangler versions upload でデプロイせずにバージョンを作り、npx wrangler versions deploy で新旧の比率(90% / 10% など)を指定します。通常の wrangler deploy は即座に 100% へ展開してしまうため段階的ロールアウトには使えません。

Q3. Workers Paid の課金軸を 2 つ挙げよ。fetch() の応答待ち時間は課金されるか?

リクエスト数(月 1,000 万込み、超過 $0.30/100 万)と CPU ミリ秒(月 3,000 万 ms 込み、超過 $0.02/100 万 ms)です(2026-08 時点)。I/O 待ちは CPU 時間に含まれないため課金されません。duration やデータ転送(egress)への課金もありません。

Q4. rollback で元に戻らないものは何か?

ストレージのデータ(KV / R2 / D1 / Durable Objects)とバインディング先のリソースです。rollback が戻すのはコードと設定のスナップショットだけで、対象バージョンが参照するリソースが削除済みの場合は rollback 自体が拒否されます。

Q5. nodejs_compat を完全に効かせるための 2 つの条件は?

compatibility_flags に "nodejs_compat" を追加することと、compatibility_date を 2024-09-23 以降にすることです。この 2 つが揃うとネイティブ実装の Node.js API に加えて未実装 API のポリフィルも注入されます。

まとめと次章へ

これで観測・デプロイ・コストという「本番運用の三種の神器」が揃いました。最終章では、ここまでの全要素を組み合わせてミニ SaaS を丸ごと構築する総合演習に挑みます。