トラフィックの分割

リージョン ID

REGION_ID は、アプリの作成時に選択したリージョンに基づいて Google が割り当てる省略形のコードです。一部のリージョン ID は、一般的に使用されている国や州のコードと類似しているように見える場合がありますが、このコードは国または州に対応するものではありません。2020 年 2 月以降に作成されたアプリの場合、REGION_ID.r が App Engine の URL に含まれています。この日付より前に作成されたアプリの場合、URL のリージョン ID は省略可能です。

詳しくは、リージョン ID をご覧ください。

トラフィック分割機能を使用すると、トラフィックの配分比率を指定して同じサービスの複数のバージョンにトラフィックを振り分けることができます。トラフィックの分割により、バージョン間で A/B テストを実行することが可能になり、機能を展開する際のペースを制御できます。

トラフィック分割は明示的にバージョンを対象にしない URL に適用されます。たとえば、次の URL は指定されたサービス内で使用可能なすべてのバージョンをターゲットとしているのでトラフィックを分割します。

  • https://PROJECT_ID.REGION_ID.r.appspot.com - トラフィックを default サービスの複数のバージョンに分配します。
  • https://SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com - トラフィックを [SERVICE_ID] サービスの複数のバージョンに分配します。

リクエストがバージョンに到達する仕組みについては、リクエストのルーティング方法をご覧ください。

始める前に

バージョンへのトラフィックを構成する前に、必要な権限がユーザー アカウントに含まれていることをご確認ください。

キャッシュの問題を回避する

トラフィック分割を有効にする前に、潜在的なキャッシュの問題を考慮することをおすすめします。キャッシュの問題はどの App Engine アプリにも存在する可能性があります(特に、新しいバージョンをデプロイする場合)。トラフィック分割を行うと、キャッシュ関連の捉えにくい問題がより顕著になることがよくあります。

たとえば、トラフィックを A と B という 2 つのバージョンに分割しているときに、CSS ファイルなどのキャッシュに保存可能な外部リソースがバージョン間で変更されたとします。この状況で、クライアントがリクエストを送信し、そのレスポンスの中にキャッシュ保存されたファイルへの外部参照があるとします。ローカル HTTP キャッシュでは、キャッシュ保存されているファイルのバージョンや、レスポンスを送信するアプリケーションのバージョンを考慮することなく、キャッシュ内に該当するファイルがあれば、そのファイルがキャッシュから取り出されます。キャッシュに保存されているリソースが、レスポンス内で送信されるデータと適合しない場合もあります。

キャッシュの問題を回避するには、次の操作を行います。

  • 動的リソースの場合は、Cache-ControlExpires のヘッダーを両方とも設定します。これらのヘッダーによって、リソースが動的であることがプロキシに伝えられます。両方のヘッダーを設定することをおすすめします。すべてのプロキシ サーバーが HTTP/1.1 の Cache-Control ヘッダーを適正にサポートしているとは限らないためです。

    キャッシュ全般の詳細については、HTTP/1.1 RFC のヘッダー フィールドに関するサイトウェブの基礎内の HTTP キャッシュの概要に関するページをご覧ください。

  • キャッシュ保存が可能な静的リソースがバージョン間で異なる場合は、リソースの URL をバージョン別に変えます。静的リソースを異なる URL から提供することで、両方のバージョンがプロキシ サーバーとブラウザ キャッシュで問題なく共存できます。

また、アプリで Vary: Cookie ヘッダーを設定すると、リソースの一意性がリクエストの Cookie と URL の組み合わせから計算されますが、この方法ではキャッシュ サーバーへの負荷が増加します。GOOGAPPUID に使用可能な値は 1,000 個存在するため、アプリの各 URL に使用される可能性があるエントリもそれぞれ 1,000 個になります。ユーザーとアプリの間のプロキシにかかる負荷の大きさによっては、アプリがキャッシュ内の結果を提供する頻度が下がる場合があります。さらに、新しいユーザー群が特定のバージョンに追加されてから 24 時間は、そのユーザーに引き続き、キャッシュに保存されたリソースが提示されることがあります。しかし、Vary: Cookie を使用することで、バージョン間で異なる静的リソースの名前の変更が容易になります。

Vary: Cookie の手法はどのような状況でも有効というわけではありません。一般に、他の目的のためにアプリで Cookie を使用する場合は、プロキシ サーバーの負荷に与える影響を考慮する必要があります。codeninja に専用の Cookie があり、取りうる値が 100 個とすると、発生する可能性のあるキャッシュ エントリすべてのための領域はきわめて大きくなります(100 * 1,000 = 100,000)。最悪のケースでは、ユーザーごとに 1 個ずつ、一意の Cookie があります。この一般的な例としては、Google アナリティクス(__utma)と SiteCatalyst(s_vi)の 2 つがあります。このような場合は、ユーザーのそれぞれに個別のコピーが作成されるため、キャッシュのパフォーマンスが大幅に低下するとともに、アプリが消費する課金対象インスタンス時間も増加する可能性があります。

複数のバージョン間のトラフィック分割

分割のために 2 つ以上のバージョンを指定する際に、トラフィック分割で IP アドレスと HTTP Cookie のどちらを使用するかを選択する必要があります。設定が簡単なのは IP アドレス分割ですが、Cookie 分割の方が精度では上回ります。詳細については、IP アドレス分割Cookie 分割をご覧ください。

Console

コンソールでトラフィックを分割するには、[バージョン] ページに移動します。

[バージョン] ページに移動

  1. トラフィックの分割先となる 1 つ以上のバージョンを選択します。
  2. [トラフィックを分割] をクリックし、以下を指定します。
    • トラフィックの分割に使用する方式
    • 各バージョンが受け取るトラフィックの割合

gcloud

Google Cloud CLI をインストールした後、たとえば次のコマンドを実行して、複数のバージョンでトラフィックを分割します。

gcloud app services set-traffic [MY_SERVICE] --splits [MY_VERSION1]=[VERSION1_WEIGHT],[MY_VERSION2]=[VERSION2_WEIGHT] --split-by [IP_OR_COOKIE]

詳細情報と追加のオプションについては、gcloud app services set-traffic リファレンスをご覧ください。

API

プログラムでトラフィックを移行する場合は、Admin API を使用できます。詳細については、トラフィックの移行と分割をご覧ください。

IP アドレス分割

IP アドレスに基づいてアプリケーションへのトラフィックを分割する場合、アプリケーションはリクエストを受け取ると、その IP アドレスを 0~999 の範囲のハッシュ値に変換し、その数値を使用してリクエストをルーティングします。

IP アドレス分割には次の重要な制約が課せられます。

  • IP アドレスは、ある程度は固定されますが、永続的に変化しないわけではありません。ユーザーがスマートフォンで接続している場合は 1 回のセッション中に IP アドレスが変わることがあります。同様に、ノートパソコンで接続しているユーザーが自宅からカフェに移動した場合も、IP アドレスが変わることになります。その結果、IP アドレスの変動に伴ってアプリのユーザー エクスペリエンスが変わってしまう可能性があります。
  • IP アドレスは互いに無関係にバージョンに割り当てられるため、その結果としてのトラフィック分割はデベロッパーが指定したものとは多少異なります。ただし、アプリケーションが受信するトラフィックが増えるにつれて、実際の分割は目標値に近づきます。たとえば、トラフィックの 5% をある代替バージョンに配信するように指定した場合、最初にそのバージョンに送信されるトラフィックの実際の割合は 3~7% ですが、最終的な平均値は目標の 5% 近くになります。
  • アプリ間で内部リクエストを送信する必要がある場合は、代わりに Cookie 分割を使用してください。Google のクラウド インフラストラクチャ上で実行中のアプリの間で送信されるリクエストは、すべて同じバージョンに割り当てられている可能性が高い、少数の IP アドレスから発信します。したがって、すべての内部リクエストは単一の IP アドレスから送信されるリクエストに似た動作をする可能性があります。つまり、それらのリクエストはすべて同じバージョンにルーティングされるということです。その結果、内部リクエストは、IP に基づくトラフィック分割のために設定した割合を厳密には尊重しません。たとえば、アプリに対するすべてのトラフィックの 1% を受け取るようにバージョンを設定し、Google クラウド インフラストラクチャ アドレスが偶然そのバージョンに割り当てられた場合、すべての内部リクエストは割り当てられたバージョンにルーティングされるため、実際の結果は 1% をはるかに超える可能性があります。Google のクラウド インフラストラクチャの外からアプリに送信されたリクエストは、送信元 IP アドレスの分布がさまざまなので、予想どおり処理されます。

Cookie を基準としてアプリケーションへのトラフィックを分割する場合、アプリケーションは HTTP リクエスト ヘッダーの中で GOOGAPPUID という Cookie を探します。この Cookie には 0~999 の範囲の値が格納されています。

  • Cookie が存在する場合、その値はリクエストのルーティングに使用されます。
  • こうした Cookie が存在しない場合、リクエストはランダムにルーティングされます。

レスポンスに GOOGAPPUID Cookie が含まれていない場合、まずアプリが 0~999 の間のランダム値を持つ GOOGAPPUID Cookie を追加してから送信されます。

トラフィック分割に Cookie を使用すると、ユーザーをバージョンに正確に割り当てることが容易になります。トラフィック ルーティングの精度はターゲットの分割に対して 0.1% 近くになることがあります。ただし、Cookie 分割には次の制約があります。

  • モバイルアプリの開発や、デスクトップ クライアントの実行では、GOOGAPPUID Cookie を管理する必要があります。たとえば、Set-Cookie レスポンス ヘッダーが使用される場合、Cookie を保存し、以後の各リクエストに含める必要があります。ブラウザベースのアプリはすでにこのようにして自動的に Cookie を管理しています。

  • 内部リクエストを分割する場合は、追加の作業が必要になります。Google のクラウド インフラストラクチャ内から送信されるすべてのユーザー リクエストでは、各リクエストでユーザーの Cookie を転送する必要があります。たとえば、アプリから送信されたリクエスト内のユーザーの Cookie を別のアプリまたはそれ自体に転送する必要があります。ユーザーから発信されたものでない内部リクエストを送信することは推奨しません。

トラフィック分割の無効化

トラフィック分割を無効にするには、すべてのトラフィックを単一のバージョンに移行します。