はじめに
WINTICKETのバックエンドチームマネージャーの鍛冶(@kj455)です。
WINTICKETのサーバーアプリケーションでは、可用性の向上を目的として約2年前から東京(Tokyo)・大阪(Osaka)2拠点を活用したマルチリージョン構成を採用しています。
今回、新たにIstio(Cloud Service Mesh)を導入し、それに合わせてCDNおよびロードバランサー(LoadBalancer)周りのインフラ構成を刷新しました。本記事では、
-
- どのような変更を行ったのか
- パフォーマンス・可用性がどのように向上したのか
について、アーキテクチャの概要と技術的なポイントを交えて解説します。
なお、Cloud Service Meshの導入については別記事で詳しく解説する予定です。
変更前後のアーキテクチャの比較
まず、変更前(Before)と変更後(After)の全体像を概説します。
Before: CDNレイヤーでリージョン間トラフィック分散
当時は、CDNとして利用しているFastly側でリージョン間のトラフィック分散を行い、例えば Tokyo:Osaka = 97:3 のように固定の重み付けを設定していました。
この構成を採用した背景には以下の理由があります。
-
- シングルリージョンからマルチリージョンへの移行を最小限の工数で実現したかった
- 当時、ロードバランサーのバックエンドに複数のクラスタを選択することができなかった
After: ロードバランサーレイヤーでのリージョン間トラフィック分散
今回のリアーキテクチャにより、Fastlyのバックエンドをグローバルなロードバランサー(LB)に一本化し、LBのバックエンドに各リージョンのIstio ingressgatewayを配置する構成に変更しました。
変更後の各コンポーネントの役割は以下の通りです。
-
- Fastly
- CDNの役割に限定し、バックエンドとしてLBを登録
- Google Cloud Load Balancer(LB)
- 地理的な負荷分散
- 動的なヘルスチェック
- 容量制御によるトラフィックコントロール
- Istio ingressgateway
- GKE上で動作するサービスメッシュ(Istio)のゲートウェイ
- Fastly
リアーキテクチャーによる改善点
ここからは、今回のリアーキテクチャがどのようにパフォーマンスと可用性を改善したのかを技術的な観点から解説します。
パフォーマンスの向上
1. LBレイヤーによる地理ベースでのトラフィックルーティング
以前は、Fastly側で静的な重み付けによるトラフィック分散を行っていたため、ユーザーにとって最適なルーティングではない場合がありました。
しかし、新しい構成では、エッジ (Fastly POP) → GoogleCloud LB → Istio ingressgateway の各ステップでユーザーに最適なリージョンへトラフィックを優先的に振り分けます。エンドツーエンドでのレイテンシ測定はできていませんが、このリアーキテクチャにより若干のレイテンシ低減が期待できます。
2. Tokyoリージョンを優先したスピルオーバー設計
LBの設定を調整し、「主にTokyoリージョンで処理を行い、負荷が大きくなった場合にOsakaリージョンにトラフィックを流す」というトラフィックポリシーを実現しました。
Tokyoリージョンを優先する理由
WINTICKETはデータベースにマルチリージョン構成のCloud Spannerを使用しており、リーダーリージョンをTokyoに設定しています。Spannerの仕組み上、ReadWriteトランザクションを発行する際は常にリーダーリージョンで処理されるため、Tokyoリージョンでアプリケーション処理を行うことでレイテンシを低減できます。
Tokyoリージョンへのトラフィック誘導のための考慮点
LBがTokyoリージョンのIstio Ingressgatewayに対してリクエストを優先的に流すようにするために、以下の2点を考慮しました。
-
- FastlyからGoogle Cloudへのネットワークの特性
- LBのバックエンド選択ロジックの仕様
FastlyからGoogle Cloudへのネットワークの特性
2024年11月現在、FastlyからGoogle Cloudへのリクエストは、大阪(ITM)のPOPで受け付けられた場合でも、Google CloudのTokyoリージョンにあるGFE(Google Front End)に到達する挙動が観測されています。これはインターネットのルーティングメカニズムに基づいて最適だと判断された結果だと推定されます。LBのドキュメントにも
同じグローバル外部 IP アドレスがさまざまな拠点からアドバタイズされ、クライアントのリクエストは、クライアントの最も近い GFE に送信されます。
という記述があり、必ずしも地理的に近い場所に接続されるわけではないことを意味しています。実際にFastlyのサポートエンジニアからも同様の回答を得ています。
LBのバックエンド選択ロジックの仕様
GoogleCloud の外部アプリケーションロードバランサの仕組み上、GFEのリージョンに存在するバックエンドNEG(Network Endpoint Group)がトラフィック振り分け先の第一候補となります。ここで、LBのバックエンド容量設定を適切に調整しておくことで、負荷が閾値に達した場合にのみ自動的に別リージョンへトラフィックを流す(スピルオーバーする)ことが可能です。
ただし、この設定はバックエンドNEGであるIstio IngressgatewayのHPA(Horizontal Pod Autoscaler)の設定と同時に調整する必要があります。HPAのスケールアウト指標が低すぎると、LBのバックエンド容量の閾値を超える前にスケールアウトが起こり、別リージョンにトラフィックを流すのではなく、Istio Ingressgatewayがスケールアウトし続けてしまいます。
WINTICKETの設定とパフォーマンス
上記を踏まえ、現在のWINTICKETでは、以下のような設定を行いました。
-
- バックエンド容量設定:
- 最大RPS: 100
- 容量100%
- Istio Ingressgateway HPA:
- CPU 使用率40%
- バックエンド容量設定:
これらの設定により、2024年の最も負荷がかかるタイミングでも、TokyoとOsakaの両リージョンを活用してレイテンシの悪化なしにトラフィックを処理できました。
可用性の向上
1. リージョン障害時のフェイルオーバーの速度向上
リージョン障害が発生した場合、Istio ingressgateway以降のリソースの挙動が不安定になると想定されます。従来の構成では、フェイルオーバーを制御するFastlyのヘルスチェックと、実際に動作が不安定なIngressgatewayとの間にLBレイヤーが挟まっていたため、フェイルオーバーが僅かに遅れる可能性がありました。
新しい構成では、LBがIstio ingressgatewayに直接ヘルスチェックを行い、その結果を元にトラフィックを制御します。LBのネイティブ機能により、バックエンドのヘルスチェックが失敗した場合は即座にトラフィックのルーティング先から取り除かれるため、迅速なフェイルオーバーが可能です。
実際に検証環境でリージョン障害を再現したところ、ユーザーに500系エラーが返ることはなく即座にフェイルオーバーが完了しました。
2. リージョン間のトラフィック分散の動的化
以前は、一部のトラフィックを大阪で捌いておきたかったため、FastlyでTokyoをメインにOsakaに3%ほどトラフィックを流す設定をしていましたが、これは「静的な重み付け」でした。もしメインのTokyo側の負荷が上昇しても、それに応じてOsaka側のトラフィックを増やすという調節は動的には行えませんでした。
今回のリアーキテクチャでは、先述したLBの地理的負荷分散とバックエンド容量設定を活用することで、リアルタイムの負荷状況に応じたトラフィック制御が可能になっています。これにより、Tokyoリージョン側で処理が詰まっている場合などにOsaka側へとトラフィックを流すことが可能になり、システムの全体的な可用性が向上します。
実際に、2024年の最も負荷が高い時間帯は25000RPSを超える時もありましたが、ユーザーには一件も500系エラーを返すことなくトラフィックを捌き切ることができました。
まとめ
今回のインフラ構成リアーキテクチャによって、CDN(Fastly)とロードバランサーの役割を明確に分離し、マルチリージョン構成での
-
- パフォーマンスの最適化
- 地理的負荷分散でユーザーに最適なリージョンへ優先ルーティング
- Tokyoリージョンを優先しつつ、負荷に応じてOsakaリージョンへと適切に負荷分散
- 可用性の向上
- 迅速なフェイルオーバー
- 動的トラフィック分散による柔軟なリージョン活用
- パフォーマンスの最適化
を実現しました。
まだ導入事例の少ない Cloud Service Mesh を導入しつつ、それに伴って今回のリアーキテクチャを無事故で完遂した @taba2424 と @golemiso には大変感謝しています。
WINTICKETサーバーチームでは引き続き技術的な挑戦を行い、より信頼性の高いシステムを構築していきます。興味のある方は、ぜひ @kj455 までご連絡ください。