段階的デプロイ では、各リクエストは指定した割合に基づき、どちらのバージョンへもランダムに振り分けられます。そのため、同じユーザーでもリクエストのたびに別バージョンのコンテンツが返ることがあり、バージョンスキュー(version skew)の問題が起きることがあります。
バージョンアフィニティは、安定した識別子に基づいてユーザーをバージョンへ決定的に割り当てます。段階的デプロイのあいだ、ページ読み込みやサブリクエストでも同じバージョンへ一貫して到達します。
Worker への受信リクエストに Cloudflare-Workers-Version-Key ヘッダーを設定します。
curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: foo'ある デプロイ では、バージョンキーが foo のリクエストは、すべて同じ Worker バージョンが処理します。プラットフォームがキーをハッシュし、設定した割合と組み合わせてバージョンを決定的に割り当てます。キーがどのバージョンに対応するかは、自分では選びません。
段階的デプロイを進めると(たとえば 10% から 20%、50% へ)、すでに新バージョンへ割り当てられたキーのユーザーはそのまま残ります。旧バージョンのユーザーは割合の上昇に合わせて新バージョンへ移りますが、ロールバックしない限り元には戻りません。
Cloudflare-Workers-Version-Key ヘッダーは、インターネットから Worker への外部リクエストでも、サービスバインディング を使った Worker 間のサブリクエストでも設定できます。
バージョンアフィニティは、Worker がコンテンツハッシュ付きファイル名(index-a1b2c3d4.js など)の 静的アセット を配信する場合に特に重要です。これは、最近のビルドツールやフレームワークの既定の動作です。
段階的ロールアウト中、アプリケーションのバージョンごとにアセットのファイル名が異なります。
- バージョン A の HTML は
assets/index-a1b2c3d4.jsを参照します - バージョン B の HTML は
assets/index-m3n4o5p6.jsを参照します
バージョンアフィニティがないと、ユーザーはバージョン A の HTML を受け取り、ブラウザーが index-a1b2c3d4.js を要求したときに、そのリクエストがバージョン B へ振り分けられることがあります。バージョン B にはそのファイルがないため、404 エラーになり、ページが壊れます。
バージョンキーを選ぶ のいずれかの方法でバージョンアフィニティを設定すると、同一ユーザーからのリクエストはすべて同じバージョンへ振り分けられ、この問題を防げます。
適切なバージョンキーは、アプリケーションで使える安定した識別子によって変わります。Transform Rule をゾーンに設定すると、アプリケーションコードを変えずにリクエストから値を取り出せます。
Cookie またはヘッダーにユーザー識別子がある場合、これが最もよい選択肢です。各ユーザーはバージョンへ決定的に割り当てられ、セッション、デバイス、再読み込みをまたいでもそのバージョンに留まります。
Expression Editor のテキスト:
http.cookie contains "user_id"Modify request header で選ぶ操作: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["user_id"][0]
アプリケーションがセッション Cookie を設定する場合は、セッション識別子を使います。セッションのあいだは一貫した振り分けになります。セッションが期限切れになり新しいセッションが作られると、別のバージョンへ割り当てられることがあります。
Expression Editor のテキスト:
http.cookie contains "session_id"Modify request header で選ぶ操作: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["session_id"][0]
リクエストに安定した識別子がない場合、次の 2 つの方法があります。
オプション 1: クライアントの IP アドレスを使う。 最も単純で、アプリケーションの変更は不要です。同じ NAT または VPN の背後にいるユーザーはまとめて扱われ、ネットワークを切り替えるモバイルユーザーはバージョンが変わることがあります。ただし、ほとんどのアプリケーションでは、リクエストごとのランダム振り分けに比べてバージョンの行き来を大きく減らせます。
Expression Editor のテキスト:
trueModify request header で選ぶ操作: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: ip.src
オプション 2: Worker から長寿命の Cookie を設定する。 最初のリクエスト(ランダムに割り当てられます)で、Worker が安定した識別子を生成して Cookie に設定します。以降のリクエストはその Cookie をバージョンキーとして使います。匿名ユーザーに対して最も一貫した振り分けができます。アプリケーションコードは少し必要です。
export default {
async fetch(request, env) {
const response = await handleRequest(request, env);
// Set a long-lived cookie to use as a version affinity key.
const COOKIE_NAME = "version-key"; // can be any name
const cookieHeader = request.headers.get("Cookie") ?? "";
const hasAffinityCookie = new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=`).test(
cookieHeader,
);
if (!hasAffinityCookie) {
const id = crypto.randomUUID();
response.headers.append(
"Set-Cookie",
`${COOKIE_NAME}=${id}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000`,
);
}
return response;
},
};export default {
async fetch(request: Request, env: Env): Promise<Response> {
const response = await handleRequest(request, env);
// Set a long-lived cookie to use as a version affinity key.
const COOKIE_NAME = "version-key"; // can be any name
const cookieHeader = request.headers.get("Cookie") ?? "";
const hasAffinityCookie = new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=`).test(cookieHeader);
if (!hasAffinityCookie) {
const id = crypto.randomUUID();
response.headers.append(
"Set-Cookie",
`${COOKIE_NAME}=${id}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000`,
);
}
return response;
},
};次に、この Cookie をバージョンキーにする Transform Rule を作成します。
Expression Editor のテキスト:
http.cookie contains "version-key"Modify request header で選ぶ操作: Set dynamic
Header name: Cloudflare-Workers-Version-Key
Value: http.request.cookies["version-key"][0]
同じバージョンキーで複数回リクエストし、同じバージョンが処理することを確認すると、バージョンアフィニティが動いているかを検証できます。
# Both requests should return responses from the same version
curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: test-user-123'
curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: test-user-123'テスト中は、バージョンメタデータのバインディング を使い、Worker のレスポンスにバージョン ID を含めます。
段階的ロールアウト中は、Worker の分析で 404 レスポンス率の上昇を監視します。特にアセットファイル(.js、.css、.png)に注意します。Analytics Engine または Logpush でこれらの指標を追跡し、バージョンスキューの問題を早めに見つけます。問題に気づいたら、前のバージョンへ ロールバック できます。
- 段階的デプロイ - 割合に基づくトラフィック分割の仕組み
- バージョンオーバーライド - ID を指定して特定バージョンへリクエストを送る(スモークテストやデバッグ向け。エンドユーザーの振り分けには使いません)
- バージョンメタデータのバインディング - Worker 内からバージョン ID とタグにアクセスする