Worker の障害を診断する

CloudflareBeginner
オンラインで実践に進む

はじめに

依存先の処理が遅いとき、サポート API は役に立たない例外を返します。この実験では、その症状を再現し、リクエスト ID と関連付けて確認します。その後、ハンドラーを修復し、呼び出し元には制限時間内で意味のあるエラーを返しながら、正常なリクエストは引き続き成功するようにします。最後に、実際のクラウドレスポンスと、別のライブログストリームを確認します。

この新しい VM で、自分の学習用アカウントを使って開始します。前提知識として、通常の Wrangler によるデプロイ、service binding、ローカルテストを使用します。以前の Worker や VM は再利用しません。セットアップでは Node.js 22.22.0、Wrangler 4.131.1、Miniflare 4.20260730.0 をインストールし、壊れた呼び出し元と合成 upstream を用意します。upstream は合成データ、制御された 503、または 2.5 秒の遅延を返します。データベース、購入済みドメイン、高負荷の実験は必要ありません。

ランタイム例外、意図的な HTTP 504、実行制限による障害は、それぞれ異なる観測結果です。すべての 5xx レスポンスをプラットフォーム障害として扱わず、それぞれの証拠を確認します。

タイムアウトを再現して関連付ける

このステップでは、依存先の処理が遅い場合に発生する例外をローカルで再現します。呼び出し元と、用意されている upstream のコードを確認してください。呼び出し元には 400 ms の制限時間がありますが、拒否された fetch を捕捉する処理がありません。upstream の slow モードは 2.5 秒待機します。

cd /home/labex/project/failure-diagnostics
cat src/index.js
cat upstream/index.js

一意のリソース名のベースを生成します。最初の引用符で囲まれていない EOF によって、この変数が両方の設定に展開されます。UPSTREAM service binding により fixture は非公開のままです。リクエスト URL のホスト名によって、公開サービスが選択されるわけではありません。

WORKER_NAME="labex-diagnose-$(node -p "require('node:crypto').randomBytes(6).toString('hex')")"
cat > wrangler.jsonc <<EOF
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-07-30",
  "workers_dev": true,
  "preview_urls": false,
  "services": [{"binding": "UPSTREAM", "service": "$WORKER_NAME-upstream"}]
}
EOF
cat > upstream/wrangler.jsonc <<EOF
{
  "name": "$WORKER_NAME-upstream",
  "main": "index.js",
  "compatibility_date": "2026-07-30",
  "workers_dev": false,
  "preview_urls": false
}
EOF

1 つのローカル開発プロセスで、両方の設定を実行します。バックグラウンドジョブによってターミナルを引き続き使用できます。>2>&1 によって、標準出力とエラー出力を dev.log に保存します。リクエストを送る前に、Ready と表示されるまで待ってください。

npx wrangler dev -c wrangler.jsonc -c upstream/wrangler.jsonc --ip 0.0.0.0 --port 8080 > dev.log 2>&1 &
cat dev.log

-H を使って、小さな合成リクエスト ID を付けます。ハンドラーは制約された形式の ID だけを受け付け、それ以外の場合は ID を生成します。--max-time は curl クライアント側の制限時間を設定するもので、ハンドラーの制限時間とは別です。

curl -i --max-time 6 -H "X-Request-ID: healthy-one" "http://127.0.0.1:8080/api/check?mode=healthy"
curl -i --max-time 6 -H "X-Request-ID: slow-one" "http://127.0.0.1:8080/api/check?mode=slow"
cat dev.log

正常なリクエストは、upstream の合成データを含む 200 を返します。slow モードでは、ローカルの 500 エラーが返り、slow-one に対する request_started レコードの後に、捕捉されていないタイムアウト例外が表示されるはずです。正確なローカルエラーページとスタックは環境によって異なります。これにより、制限時間によって処理が拒否されることは確認できますが、有用なエラーレスポンスが存在することまでは確認できません。呼び出し元を変更する前に、検証を実行してください。

エラーレスポンスと診断情報を修復する

このステップでは、制限時間内に発生した upstream 障害を捕捉し、ヘッダーや認証情報をログに記録せずに、役立つ診断情報を維持します。現在のジョブを、実際のジョブ番号を使って停止してください。

jobs
kill %1

以下の完全に修復されたハンドラーで、呼び出し元を置き換えます。引用符で囲まれた区切り文字により、JavaScript の内容がそのまま保持されます。504 は呼び出し元側の依存先の制限時間を示し、502 は upstream のレスポンスまたはプロトコルが失敗したことを示します。成功した呼び出しでは、upstream の結果をそのまま返します。elapsed_ms は CPU 使用量ではなく、実際に経過した時間です。ログとレスポンスでは同じリクエスト ID を使うため、1 つのリクエストをシステム全体で追跡できます。

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname === '/health') return Response.json({status: 'ok'});
    if (url.pathname !== '/api/check') return Response.json({error: 'not_found'}, {status: 404});
    if (request.method !== 'GET') return Response.json({error: 'method_not_allowed'}, {status: 405});
    const mode = url.searchParams.get('mode') || 'healthy';
    if (!['healthy', 'slow', 'fail'].includes(mode)) {
      return Response.json({error: 'invalid_mode'}, {status: 400});
    }
    const suppliedId = request.headers.get('X-Request-ID') || '';
    const requestId = /^[a-z0-9-]{1,64}$/.test(suppliedId) ? suppliedId : crypto.randomUUID();
    const headers = {'X-Request-ID': requestId, 'Cache-Control': 'no-store'};
    const started = Date.now();
    console.log(JSON.stringify({event: 'request_started', request_id: requestId, mode}));
    const upstreamUrl = new URL('https://diagnostic.internal/check');
    upstreamUrl.searchParams.set('mode', mode);
    upstreamUrl.searchParams.set('probe', requestId);
    const signal = AbortSignal.timeout(400);
    const failure = (event, status, detail = {}) => {
      console.error(JSON.stringify({event, request_id: requestId, mode, status,
        elapsed_ms: Date.now() - started, ...detail}));
      return Response.json({error: event, requestId}, {status, headers});
    };
    try {
      const response = await env.UPSTREAM.fetch(upstreamUrl, {signal});
      if (!response.ok) return failure('upstream_status', 502, {upstream_status: response.status});
      const data = await response.json();
      if (data.service !== 'labex-diagnostic-fixture' || data.status !== 'ok' || data.probe !== requestId) {
        return failure('upstream_protocol', 502);
      }
      console.log(JSON.stringify({event: 'request_complete', request_id: requestId,
        mode, status: 200, elapsed_ms: Date.now() - started}));
      return Response.json({status: 'ok', requestId, upstream: data}, {headers});
    } catch {
      return signal.aborted ? failure('upstream_timeout', 504) : failure('upstream_exception', 502);
    }
  }
};
JS
npx wrangler dev -c wrangler.jsonc -c upstream/wrangler.jsonc --ip 0.0.0.0 --port 8080 > dev.log 2>&1 &
cat dev.log

Ready と表示されたら、3 つのモードすべてと、影響を受けていない health ルートを比較します。それぞれの失敗はすぐに終了する必要があります。slow モードで長く待つことは修復ではありません。

curl -i --max-time 6 -H "X-Request-ID: healthy-two" "http://127.0.0.1:8080/api/check?mode=healthy"
curl -i --max-time 6 -H "X-Request-ID: slow-two" "http://127.0.0.1:8080/api/check?mode=slow"
curl -i --max-time 6 -H "X-Request-ID: fail-two" "http://127.0.0.1:8080/api/check?mode=fail"
curl -i http://127.0.0.1:8080/health
cat dev.log

healthy、slow、fail の順に、200/504/502 を確認してください。各レスポンスには、JSON と X-Request-ID の両方でリクエスト ID が含まれます。ログでは、request_startedrequest_completeupstream_timeout、または upstream_status が対応します。最後のカテゴリは、呼び出し元が返す 502 とは別に、upstream の 503 を記録します。捕捉された障害では、HTTP ステータスが 504 または 502 であっても、ハンドラーが正常に完了したため、ランタイムの結果が成功になる場合があります。

用意されている実行制限の例と比較してください。

cat evidence/execution-limit.json

このファイルは明示的に合成された学習用の証拠であり、Worker から取得した記録ではありません。exceededCpu の結果は実行制限による障害を示します。ランタイムが実行を停止した後に、アプリケーション側の catch が実行される保証はありません。この実験で非同期の upstream を待つことは、CPU 時間を消費することとは異なります。制限を検討する前に、高コストな計算やリクエスト処理を調査してください。この例をまねるために、制限時間を削除したり負荷を生成したりしないでください。公式のエラーリファレンス では、例外と制限のカテゴリを説明しています。ランタイムの結果に関するドキュメント では、結果と HTTP ステータスの違いを説明しています。

検証を実行します。独自の fixture とリクエスト ID を持つ分離されたランタイムを起動し、正常時と障害時の契約を確認します。また、合成された Authorization ヘッダーがアプリケーションログに記録されていないことも確認します。学習者のログファイルは、独立した証拠にはなりません。

ライブリクエスト、ログ、メトリクスを確認する

このステップでは、学習用アカウントで修復した動作を確認します。ローカル開発を停止し、前に学習したものと同じスコープ付きデバイスフローを使って、この新しい VM を認証します。

jobs
kill %1
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_tail:read

表示されたリンクを開き、コードを入力して、ブラウザーで対象の学習用アカウントへのアクセスを承認します。Wrangler の標準出力に表示されるアカウント名と ID が実際のものか確認してください。

npx wrangler whoami --json

YOUR_ACCOUNT_ID を実際の ID に置き換えます。この通常の Node コマンドによって、プロジェクトの両方の設定に ID が保存されます。そのため、各デプロイで所有者を明示できます。

node -e 'const fs=require("node:fs");for(const p of ["wrangler.jsonc","upstream/wrangler.jsonc"]){const c=JSON.parse(fs.readFileSync(p));c.account_id="YOUR_ACCOUNT_ID";fs.writeFileSync(p,JSON.stringify(c,null,2)+"\n");}'
cat wrangler.jsonc upstream/wrangler.jsonc
npx wrangler deploy -c upstream/wrangler.jsonc
npx wrangler deploy

fixture には公開エンドポイントがありません。呼び出し元に割り当てられた実際の workers.dev URL を、以下にコピーします。アカウントで初回のサブドメイン登録が必要な場合は、続行する前に「Deploy Your First Cloudflare Worker」で説明されている手順を使ってください。

APP_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"

読みやすいライブストリームを開始します。リクエストを送る前に、events.log に Connected と表示されるまで待ってください。ファイルが存在するだけでは準備完了とは限りません。

npx wrangler tail --format pretty > events.log 2> tail-errors.log &
cat events.log
curl -i --max-time 6 -H "X-Request-ID: cloud-healthy" "$APP_URL/api/check?mode=healthy"
curl -i --max-time 6 -H "X-Request-ID: cloud-slow" "$APP_URL/api/check?mode=slow"
curl -i --max-time 6 -H "X-Request-ID: cloud-fail" "$APP_URL/api/check?mode=fail"
cat events.log

ライブアプリケーションログで、期待される 200/504/502 のレスポンスと、対応する ID を探します。イベントがまだ届いていない場合は、数秒待ってから同じログをもう一度確認してください。イベントを作るためにアプリケーションを変更してはいけません。ストリームが終了していたら、停止して tail-errors.log を確認します。読みやすい出力で、捕捉された 504 の呼び出しが Ok と表示される場合があります。これはランタイムが完了したことを意味し、upstream が正常だったことを意味しません。

Dashboard で、選択したアカウント内の正確な呼び出し元を開き、UPSTREAM binding がこの fixture を対象にしていることを確認してから、Metrics を調べます。利用可能なチャートはリクエスト数と invocation error を集計するため、短いテストの結果が反映されるまで遅延することがあります。すぐにゼロ以外の合計が表示されることを条件にせず、実際に表示された内容を記録してください。個々のリクエストの証拠には、ライブログと HTTP レスポンスを使います。捕捉された 504 は HTTP レスポンスステータスのデータには表示されても、捕捉されていないランタイム例外としては数えられない場合があります。メトリクスリファレンス では、集計と invocation のカテゴリを説明しています。

Compute → Workers & Pages で、正確な呼び出し元を開き、Metrics を選択します。Worker のパンくず、デプロイ済みバージョンのフィルター、リクエストを送った時間帯を含む時間範囲を確認してください。更新ボタンは時間範囲セレクターの横にあります。以下のスクリーンショットは、合成した正常、遅延、upstream 失敗のリクエストを送った直後に撮影したもので、カードには No data と表示されていました。これは分析データの反映が遅れていることを示す有効な観測結果であり、リクエストが実行されなかったことや、修復に失敗したことの証明ではありません。名前、バージョン ID、合計値は環境によって異なります。画像に合わせるためだけに追加の負荷を生成しないでください。

分析データが到着する前の、バージョンと時間範囲のコントロールを表示した Worker Metrics

認証済みの状態で検証を実行します。所有権と binding の状態を確認し、新しい独立したリクエストを送り、別のライブストリームを取得します。およそ 1 分待ってください。ストリームが利用できない、または不完全な場合、その結果だけでは判断できません。接続の準備状態を確認して再試行してください。ログがないことを成功とはみなさないでください。検証に合格したら、現在のジョブ番号を使って学習用の tail を停止します。

jobs
kill %1

診断用 Worker を削除する

このステップでは、認証済みの状態で、この実験に使用した呼び出し元と fixture だけを削除します。2 つの名前とアカウントを確認してから、まず呼び出し元を削除してください。

cat wrangler.jsonc upstream/wrangler.jsonc
npx wrangler delete
npx wrangler delete -c upstream/wrangler.jsonc

一致する名前の確認プロンプトが表示されるたびに、y キーだけを押します。固定された CLI では、Worker の削除後に古い KV クリーンアップ用の認証診断が表示される場合があります。そのために権限を広げたり、任意のエラーが削除失敗の証拠だと判断したりしないでください。Dashboard を更新して検証を実行します。認証済みのインベントリで成功した結果を得るには、両方の名前が存在しない必要があります。アカウント、サブドメイン、無関係なリソースは残してください。

VM の接続を解除する

このステップでは、リソースが削除されたことを確認してから VM の接続を解除します。ターミナルを閉じたりログアウトしたりしても、クラウドリソースは削除されません。

npx wrangler logout
npx wrangler whoami --json

loggedIn=false と表示されることを確認します。認証されていない状態を示すため、このコマンドは 0 以外の終了コードになる場合があります。最後のチェックを実行します。ブラウザーのログイン状態と学習用アカウントは、後で新しい実験に再利用できます。

まとめ

捕捉されていないタイムアウトを再現し、制限時間内で処理する依存先の障害を修復し、レスポンスと構造化ログ間でリクエスト ID を関連付けました。正常な動作は維持されました。また、アプリケーションの HTTP 障害とランタイムの結果、合成された CPU 制限の証拠を区別し、実際のクラウド上の動作を確認したうえで、両方の Worker を削除し、接続を解除しました。