このページでは、Gateway のトラフィック管理の仕組みについて説明します。
概要
Google Kubernetes Engine(GKE)ネットワーキングは Cloud Load Balancing 上に成り立っています。Cloud Load Balancing では、グローバル トラフィック管理を実現する単一のエニーキャスト IP アドレスが使用されます。Google のトラフィック管理では、グローバルとリージョンのロード バランシング、自動スケーリング、容量管理が提供され、平等で安定した低レイテンシのトラフィックが実現されます。GKE ユーザーは、GKE Gateway Controller を使用して、Google のグローバル トラフィック管理制御を宣言型かつ Kubernetes ネイティブな方法で利用できます。
クラスタ間のトラフィックのスピルオーバーを試すには、容量ベースのロード バランシングのデプロイをご覧ください。トラフィック ベースの自動スケーリングを試すには、ロードバランサのトラフィックに基づく自動スケーリングをご覧ください。
トラフィック管理
ロード バランシング、自動スケーリング、容量管理は、トラフィック管理システムの基盤となります。これらは、連携してシステム負荷を均等にし、安定化を図ります。
- ロード バランシングは、場所、健全性、さまざまなロード バランシング アルゴリズムに従ってバックエンド Pod 間でトラフィックを分散します。
- 自動スケーリングは、ワークロードのレプリカをスケーリングして、より多くのトラフィックを引き受ける容量を作成します。
- 容量管理は、Service の使用状況をモニタリングします。これにより、アプリケーションの可用性やパフォーマンスに影響を及ぼすことなく、トラフィックが容量によってバックエンドへオーバーフローできるようになります。
これらの機能は、目標に応じてさまざまな形に組み合わせることができます。次に例を示します。
- 低コストの Spot VM を活用する場合は、レイテンシを犠牲にして Spot VM 間でトラフィックを均等に分散するように最適化することをおすすめします。ロード バランシングと容量管理を使用すると、GKE は容量に基づいてリージョンをまたいでトラフィックをオーバーフローし、利用可能なすべての場所で Spot VM を最大限活用します。
- オーバー プロビジョニングによってユーザー レイテンシを最適化する場合は、負荷が増加しても多数のリージョンに GKE クラスタをデプロイして容量を動的に増やすことができます。ロード バランシングと自動スケーリングを使用すると、トラフィックが急増したときに Pod の数が自動スケーリングされ、トラフィックが他のリージョンにオーバーフローする必要がなくなります。リージョンでは、可能な限りユーザーの近くで負荷を完全に処理できるように容量が増加します。
次の図では、ロード バランシング、自動スケーリング、容量管理が相互に連携する様子を示します。
図では、gke-us
クラスタのワークロードで障害が発生しています。ロード バランシングとヘルスチェックによって、アクティブな接続がドレインされ、トラフィックは 2 番目に近いクラスタにリダイレクトされます。gke-asia
のワークロードは容量を超えるトラフィックを受信するため、gke-eu
への負荷が取り除かれます。gke-eu
は、gke-us
と gke-asia
のイベントにより、通常より多くの負荷を引き受け、トラフィック容量を増やすために gke-eu
は自動スケーリングされます。
Cloud Load Balancing によるトラフィック管理の扱われ方については、グローバル容量管理をご覧ください。
トラフィック管理機能
Gateway、HTTPRoute、Service、Policy リソースは、GKE でトラフィックを管理するための制御を行います。GKE Gateway コントローラは、これらのリソースをモニタリングするコントロール プレーンです。
GKE で Service をデプロイする場合は、次のトラフィック管理機能を使用できます。
- Service の容量: Pod が自動スケーリングされるか、トラフィックが他の使用可能なクラスタにオーバーフローする前に、Service が受信できるトラフィック容量を指定できます。
- トラフィック ベースの自動スケーリング: 1 秒あたりに受信した HTTP リクエストに基づいて、Service 内の Pod を自動スケーリングします。
- マルチクラスタのロード バランシング: 複数の GKE クラスタや複数のリージョンでホストされている Service にロード バランシングできます。
- トラフィック分割: 明示的な重み付けに基づくバックエンド間でのトラフィック分散。一般提供では単一クラスタ Gateway でトラフィック分割がサポートされています。
トラフィック管理のサポート
利用可能なトラフィック管理機能は、デプロイする GatewayClass によって異なります。機能サポートの一覧については、GatewayClass の機能をご覧ください。次の表は、トラフィック管理に対する GatewayClass のサポートをまとめたものです。
GatewayClass | Service の容量 | トラフィックの自動スケーリング | マルチクラスタのロード バランシング | トラフィック分割1 |
---|---|---|---|---|
gke-l7-global-external-managed |
||||
gke-l7-regional-external-managed |
||||
gke-l7-rilb |
||||
gke-l7-gxlb |
||||
gke-l7-global-external-managed-mc |
||||
gke-l7-regional-external-managed-mc |
||||
gke-l7-rilb-mc |
||||
gke-l7-gxlb-mc |
グローバル、リージョン、ゾーンのロード バランシング
ロードバランサが特定のバックエンドに送信するトラフィック量は、Service の容量、ロケーション、正常性によって決まります。ロード バランシングの決定は、グローバル ロードバランサの場合はグローバル レベルで、リージョン ロードバランサの場合はリージョン レベルで行われます。
- グローバル: トラフィックは、余力があり正常なバックエンドを持つクライアントに最も近い Google Cloud リージョンに送信されます。リージョンに余力がある限り、最も近いトラフィックをすべて受信します。リージョンに余力がない場合、超過したトラフィックは次に最も近いリージョンにオーバーフローします。詳細については、グローバル ロード バランシングをご覧ください。
- リージョン: トラフィックはロードバランサから特定のリージョンに送信されます。トラフィックは、ゾーンの使用可能な容量に比例してゾーン間で負荷分散されます。詳細については、リージョン ロード バランシングをご覧ください。
- ゾーン: 特定のゾーンのトラフィックが決まると、ロードバランサはそのゾーン内のバックエンド全体にトラフィックを均等に分散します。既存の TCP 接続とセッション永続性の設定は維持されるため、将来のリクエストはバックエンド Pod が正常である限り、同じバックエンドに送信されます。詳細については、ゾーン ロード バランシングをご覧ください。
グローバル ロード バランシングとトラフィックのオーバーフロー
独自のクラスタで次のコンセプトを試す場合は、容量ベースのロード バランシングをご覧ください。
通常の条件下では、トラフィックはクライアントに最も近いバックエンドに送信されます。トラフィックは、クライアントに最も近い Google のポイント オブ プレゼンス(PoP)で終端され、Google のバックボーンを通過して、最も近いバックエンド(ネットワーク レイテンシによって決まります)に到達します。リージョンのバックエンドに余力が残っていない場合、トラフィックは、余力のある正常なバックエンドを持つ次に近いクラスタにオーバーフローします。ゾーン内の正常でないバックエンド Pod が 50% 未満の場合、トラフィックは、構成された容量に関係なく他のゾーンまたはリージョンに徐々にフェイル オーバーします。
トラフィックのオーバーフローは、次の状態の場合にのみ発生します。
- マルチクラスタ Gateway を使用している。
- 複数のクラスタに同じ Service をデプロイし、マルチクラスタ Gateway によって処理している。
- トラフィックがある 1 つのクラスタのサービス容量を超えるものの、その他のクラスタのサービス容量は超えないように Service の容量を構成している。
次の図では、グローバル ロード バランシングがトラフィック オーバーフローでどのように機能するかを示します。
図の中で:
- Multi-cluster Gateway は、
store
Service に対してグローバル インターネット ロード バランシングを行います。サービスは、2 つの GKE クラスタ(us-west1
とeurope-west1
)にまたがってデプロイされます。各クラスタでは、2 つのレプリカが実行されています。 - 各 Service は
max-rate-per-endpoint="10"
で構成されています。つまり、各 Service の合計容量は、2 レプリカ * 10 RPS = 20 RPS です。 - Google の北米の PoP は 6 RPS です。すべてのトラフィックは、容量があり最も近く正常なバックエンドである
us-west1
の GKE クラスタに送信されます。 - ヨーロッパの PoP は、30 累積 RPS を受け取ります。最も近いバックエンドは
europe-west1
にありますが、容量は 20 RPS しかありません。us-west1
のバックエンドには過剰な容量があることから、10 RPS がus-west1
にオーバーフローして、合計 16 RPS を受信し、各 Pod に 8 RPS が分散されます。
トラフィック オーバーフローの防止
トラフィックをオーバーフローさせると、パフォーマンスや可用性に影響する可能性のあるアプリケーションの容量超過を防ぐことができます。
ただし、トラフィックをオーバーフローさせる必要がない場合もあります。たとえば、レイテンシの影響を受けやすいアプリケーションの場合、遠いバックエンドへのトラフィックのオーバーフローからメリットは得られません。
トラフィックのオーバーフローは、次のいずれかの方法を使用することで防止できます。
- 単一クラスタでのみ Service をホストできる単一クラスタ Gateway のみを使用します。
- マルチクラスタ Gateway を使用する場合でも、複数のクラスタをまたいでデプロイされるアプリケーションのレプリカは、個別の Service としてデプロイできます。Gateway の観点からは、これによりマルチクラスタ ロード バランシングが有効になりますが、クラスタ間で Service のすべてのエンドポイントが集約されるわけではありません。
- 絶対不可欠な場合を除き、実際に超過することがないように、Service の容量を十分高いレベルに設定します。
リージョン内のロード バランシング
リージョン内では、トラフィックがバックエンドの使用可能な容量に従ってゾーンに分散されます。これはオーバーフローを使用せず、各ゾーンの Service 容量に正比例されてロード バランシングを行います。個々のフローまたはセッションは常に単一のバックエンド Pod に送信され、分割されません。
次の図では、リージョン内でトラフィックがどのように分散されるかを示します。
図の中で:
- Service が GKE のリージョン クラスタにデプロイされます。この Service には 4 つの Pod があり、これらの Pod がゾーン間で均等にデプロイされていません。Pod はゾーン A には 3 個、ゾーン B には 1 個、ゾーン C には 0 個です。
- Service は
maxRatePerEndpoint="10"
で構成されています。ゾーン A の合計容量は 30 RPS、ゾーン B の合計容量は 10 RPS、ゾーン C には Pod がないため、合計容量は 0 RPS です。 - Gateway は、さまざまなクライアントから合計 16 RPS のトラフィックを受信します。このトラフィックは、各ゾーンの残りの容量に比例して、ゾーン間で分散されます。
- 個々のソースまたはクライアントからのトラフィック フローは、セッション永続性設定に従い単一のバックエンド Pod に常にロードバランスされます。トラフィック分割は異なるソースのトラフィック フローに分散されるため、個々のフローが分割されることはありません。そのため、バックエンド間でトラフィックを細かく分散するには、ソースまたはクライアントの多様性を最小限に抑える必要があります。
たとえば、受信トラフィックが 16 RPS から 60 RPS に急増すると、次のいずれかの状況が発生します。
- 単一クラスタ ゲートウェイを使用している場合、このトラフィックがオーバーフローする先のクラスタやリージョンはありません。トラフィックは、受信トラフィックが合計容量を超えても、相対的なゾーン容量に従って引き続き分散されます。その結果、ゾーン A は 45 RPS、ゾーン B は 15 RPS を受信します。
- 複数のクラスタに分散された Service でマルチクラスタ ゲートウェイを使用する場合、グローバル ロード バランシングとトラフィック オーバーフローで説明されているように、トラフィックは他のクラスタと他のリージョンにオーバーフローします。ゾーン A は 30 RPS を受信、ゾーン B は 10 RPS を受信し、20 RPS のオーバーフローで別のクラスタに送られます。
ゾーン内のロード バランシング
トラフィックは、あるゾーンに送信されると、そのゾーン内のすべてのバックエンドに均等に分散されます。HTTP セッションは、セッション アフィニティの設定に応じて持続します。バックエンドが利用不能にならない限り、既存の TCP 接続は別のバックエンドに移動しません。つまり、限られた容量が原因で新しい接続がオーバーフローしても、その前から続いている接続は、同一のバックエンド Pod に対して継続します。ロードバランサでは、新しい接続よりも既存の接続の維持が優先されます。
Service の容量
Service の容量では、Service 内の Pod ごとに 1 秒あたりのリクエスト数(RPS)を定義できます。この値は、Service が受信できる Pod あたりの平均最大 RPS を表します。この値は Service で構成可能で、トラフィックベースの自動スケーリングと容量ベースのロード バランシングを決定するために使用されます。
要件
Service の容量には、次の要件と制限があります。
- トラフィック管理のサポートで定義されている GatewayClass リソースと Ingress タイプでのみサポートされます。
- トラフィック ベースの自動スケーリングまたはマルチクラスタ ゲートウェイを使用している場合にのみ、ロード バランシングに影響します。こうした機能を使用していない場合、Service の容量はネットワーク トラフィックに影響しません。
Service の容量を構成する
単一クラスタ Gateway
GKE クラスタがバージョン 1.31.1-gke.2008000 以降を実行していることを確認します。以前のバージョンでは、[マルチクラスタ Gateway] タブで説明されているように、networking.gke.io/max-rate-per-endpoint
アノテーションを使用できます。
単一クラスタ Gateway を使用して Service の容量を構成するには、Service と関連付けられた GCPBackendPolicy
を作成します。次のマニフェストを使用して Service を作成します。
apiVersion: v1
kind: Service
metadata:
name: store
spec:
ports:
- port: 8080
targetPort: 8080
name: http
selector:
app: store
type: ClusterIP
最大 RPS を指定して maxRatePerEndpoint
フィールドを使用して、GCPBackendPolicy
オブジェクトを構成します。次のマニフェストを使用して、GCPBackendPolicy
オブジェクトを構成します。
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
name: store
spec:
default:
maxRatePerEndpoint: "RATE_PER_SECOND"
targetRef:
group: ""
kind: Service
name: store
複数クラスタ Gateway
マルチクラスタ Gateway を使用して Service の容量を構成するには、networking.gke.io/max-rate-per-endpoint
アノテーションを使用して Service を作成します。次のマニフェストを使用して、最大 RPS を持つ Service を作成します。
apiVersion: v1
kind: Service
metadata:
name: store
annotations:
networking.gke.io/max-rate-per-endpoint: "RATE_PER_SECOND"
spec:
ports:
- port: 8080
targetPort: 8080
name: http
selector:
app: store
type: ClusterIP
RATE_PER_SECOND
は、この Service 内の 1 つの Pod が受信する 1 秒あたりの最大 HTTP / HTTPS リクエストに置き換えます。
maxRatePerEndpoint
値は、Service の Pod 数に基づいて Service の動的な容量を作り出します。Service の容量の合計値は、次の式に示すように、maxRatePerEndpoint
値にレプリカ数を掛けて計算されます。
Total Service capacity = maxRatePerEndpoint * number of replicas
オートスケーラーが Service 内の Pod 数をスケールアップすると、それに応じて Service の合計容量が算出されます。Service が 0 個の Pod にスケールダウンされると、容量はゼロになり、ロードバランサからトラフィックを受信しなくなります。
Service の容量とスタンドアロン NEG
スタンドアロン NEG を使用すると Service の容量を構成することもできますが、maxRatePerEndpoint
設定は使用されません。スタンドアロン NEG を使用する場合、maxRatePerEndpoint
は、NEG をバックエンド サービス リソースに追加するときに手動で構成します。gcloud compute backend-services add-backend
コマンドを使用すると、--max-rate-per-endpoint
フラグで NEG の容量を個別に構成できます。
これは、次のどのワークフローにも有用です。
- スタンドアロン NEG を使用して、手動で内部ロードバランサと外部ロードバランサをデプロイする場合
- スタンドアロン NEG を使用して GKE に Cloud Service Mesh をデプロイする場合
スタンドアロン NEG を使用して Service の容量を構成しても、機能の違いはありません。トラフィックの自動スケーリングとトラフィックの溢れ動作の両方がサポートされています。
Service の容量を確認する
maxRatePerEndpoint
の値を決定するには、アプリケーションのパフォーマンス特性とロード バランシングの目標を理解する必要があります。アプリケーションのパフォーマンス特性を定義するには、次の手法が効果的です。
- Service の容量を設定することなく構成した場合は、テスト環境と本番環境の両方でアプリケーションを確認します。
- Cloud Monitoring を使用して、トラフィック リクエストとパフォーマンスのサービスレベル目標(SLO)との間の対応関係を作成します。
- ロードバランサ指標(RPS レベルをマッピングする
https
やrequest_count
など)を使用します。
- アプリケーションのパフォーマンスに関する SLO を定義します。パフォーマンスを「悪い」または「不安定」と見なすかどうかに応じて、これらは次のいずれかになります。次のすべては、Cloud Monitoring ロードバランサの指標から収集できます。
- レスポンス エラーコード
- レスポンスのレイテンシまたは合計レイテンシ
- バックエンドの非健全性の度合いまたはダウンタイム
- テスト環境と本番環境の両方で、トラフィック負荷がかかった状態のアプリケーションを確認します。テスト環境では、リクエスト負荷が増加する中でアプリケーションに負荷をかけ、トラフィックの増加に伴って各種パフォーマンス指標がどのように影響を受けるかを確認します。本番環境では、現実的なトラフィック パターンのレベルを確認します。
Service のデフォルト容量
アノテーションを使用して明示的に構成されていない場合でも、GKE リソースに接続されているすべての Service には、デフォルトの Service 容量が構成されています。詳細については、Service のデフォルト容量をご覧ください。
次の表に、デフォルトの容量を示します。
ロード バランシングのリソースタイプ | デフォルトの maxRatePerEndpoint |
---|---|
Ingress(内部、外部) | 1 RPS |
Gateway(すべての GatewayClass) | 100,000,000 RPS |
MultiClusterIngress | 100,000,000 RPS |
トラフィック ベースの自動スケーリング
トラフィック ベースの自動スケーリングは、ロードバランサからのトラフィック シグナルをネイティブに統合して Pod を自動スケーリングする GKE の機能です。トラフィック ベースの自動スケーリングは、単一クラスタ Gateway でのみサポートされます。
トラフィック ベースの自動スケーリングを使用するには、ロードバランサのトラフィックに基づく自動スケーリングをご覧ください。トラフィック ベースの自動スケーリングには次の利点があります。
- CPU やメモリを厳密にバインドしていないアプリケーションには、CPU やメモリの使用量に反映されていない容量制限が存在する場合があります。
- トラフィック、すなわち 1 秒あたりのリクエスト数(RPS)は、場合によってはもっと簡単な指標で、ページビューや 1 日のアクティブ ユーザー(DAU)などのアプリの使用状況やビジネス指標に適合します。
- トラフィックは、瞬間需要を表す先行指標です。対して、CPU やメモリは遅行指標になります。
- CPU、メモリ、トラフィックの自動スケーリングの指標を組み合わせることで、複数のディメンションを使用して、容量が適切にプロビジョニングされるように、アプリケーションの自動スケーリングの全体的な方法が提供されます。
次の図では、トラフィック ベースの自動スケーリングの仕組みを示します。
図の中で:
- Service のオーナーは、Deployment の Service の容量とターゲットの使用率を構成します。
- Gateway は、クライアントから
store
Service に送信されるトラフィックを受信します。Gateway は、使用率テレメトリーを GKE Pod Autoscaler に送信します。使用率は、個々の Pod が受信した実際のトラフィックを Pod の構成済み容量で割った値になります。 - GKE Pod Autoscaler は、構成されたターゲット使用率に従って Pod をスケールアップまたはスケールダウンします。
自動スケーリングの動作
次の図では、ロードバランサを介して 10 RPS を受信するアプリケーションで、トラフィック ベースの自動スケーリングがどのように機能するかを示します。
この図で、サービス オーナーはストア Service の容量を 10 RPS に構成しています。つまり、各 Pod は最大 10 RPS を受信できます。HorizontalPodAutoscaler は、averageValue
が 70
に設定され構成されています。つまり、ターゲット使用率が Pod あたり 10 RPS の 70% であることを意味します。
オートスケーラーは、次の式が満たされるようにレプリカをスケーリングしようとします。
replicas = ceiling[ current traffic / ( averageValue * maxRatePerEndpoint) ]
図では、この計算式は次のように求められます。
ceiling[ 10 rps / (0.7 * 10 rps) ] = ceiling[ 1.4 ] = 2 replicas
10 RPS のトラフィックでは、2 つのレプリカになります。各レプリカは 5 RPS を受信しますが、これは目標使用率の 7 RPS を下回ります。
トラフィック分割
トラフィック分割では、Service に送信される HTTP リクエストの割合を定義する明示的な比率(重みと呼ばれます)を使用します。HTTPRoute リソースでは、Service のリストに対する重みを構成できます。Service 間の相対的な重みでは、Service 間のトラフィック分割が定義されます。これは、ロールアウト時、変更のカナリア処理時、または緊急時におけるトラフィック分割に役立ちます。
次の図では、トラフィック分割の構成例を示します。
図の中で:
- サービス オーナーは、トラフィックの 90% を
store-v1
、10% をstore-v2
に分割するルールを使用して、1 つのルートに対して 2 つのサービスを構成します。 - Gateway は、クライアントからストア アプリケーションの URL に送信されるトラフィックを受信し、構成済みのルールに従ってトラフィックが分割されます。トラフィックの 90% が
store-v1
にルーティングされ、10% がstore-v2
にルーティングされます。
トラフィック分割は、同じクラスタ内の Service 間でも、異なるクラスタ内の Service 間でもサポートされます。
Service 間のトラフィック分割: アプリケーション バージョンのロールアウトのトラフィックを分割するために使用します。このトラフィック分割の使用例では、
store-v1
とstore-v2
の 2 つの Deployment があり、それぞれに独自の Service(store-v1
とstore-v2
)があります。store-v2
が完全にロールアウトされるまでトラフィックを徐々にシフトするように、2 つの Service 間で重み付けが構成されます。ServiceImport 間でのトラフィック分割: メンテナンス、移行、緊急時に特定のクラスタ間でトラフィックをずらすために使用されます。ServiceImport は、マルチクラスタ Service を表し、異なるクラスタで異なる Service 間のトラフィック分割を可能にします。ゲートウェイによる Blue/Green マルチクラスタ ルーティングの演習では、クラスタをまたいでトラフィックを分割する方法を説明します。
重み付けと容量
重みと容量の両方で、さまざまな Service に送信されるトラフィックの量を制御します。効果は似ていますが、動作は異なり、ユースケースも異なります。2 つは一緒に使用しますが、目的は異なります。
重み
重み付けは、トラフィックの明示的な制御です。 受信トラフィックやバックエンドの使用状況から独立した、トラフィックの正確な割合を定義します。トラフィック分割の例では、store-v2
が上限に達したか、すべてのレプリカで障害が発生した場合でも、トラフィックの 10% が store-v2
に割り当てられ、トラフィックがドロップされる可能性があります。これは、重みによって使用率や正常性によってトラフィックの割合が変化しないためです。
重みは、次のユースケースに最も適しています。
- ロールアウトのために異なるバージョンの Service 間でトラフィックをシフトする。
- 明示的なトラフィック分割を使用して Service を手動でオンボーディングする。
- 緊急時やメンテナンスの目的で、バックエンドのセットからトラフィックをシフトする。
容量
容量は、暗黙的にトラフィックを制御します。受信トラフィックの量、バックエンド使用率、トラフィックの送信元の場所に応じて、トラフィックの割合を間接的に定義します。容量は Service 固有のプロパティであり、通常、頻繁には更新されません。
容量は、次のユースケースに最も適しています。
- トラフィックの急増時のバックエンドの過剰使用を防止する。
- トラフィックに関連する自動スケーリングのレートを制御する。
トラフィックをオーバーフローするように Service の容量を構成することで、必要な動作が常に得られるとは限りません。グローバル ロード バランシングの例を検討します。Service の容量では、トラフィックをオーバーフローしてバックエンドの使用率が高くなりすぎないようにしていますが、オーバーフローしたリクエストはリモート リージョンに転送されるため、レイテンシが増加する可能性があります。
アプリケーションが過剰な利用からそれほど影響を受けない場合は、トラフィックが別のリージョンにオーバーフローすることがないように、Service 容量を非常に大きく構成することをおすすめします。アプリケーションの可用性やレイテンシが過剰な使用の影響を受けやすい場合は、過剰に使用されるバックエンドで超過トラフィックを吸収するより、他のクラスタやリージョンにトラフィックをオーバーフローさせるほうが良い場合があります。アプリケーションの Service 容量を構成する方法の詳細は、Service の容量を決定するをご覧ください。
次のステップ
- Gateway のデプロイについて確認する。
- 複数クラスタ Gateway のデプロイについて確認する。