目次

  1. はじめに
  2. 課題と前提
  3. Weighted Backend Service によるルーティング制御
  4. サーバーレス NEG の外れ値検出
  5. 対応の結果と双方の棲み分け
  6. まとめ
  7. 参考資料

この記事で学べること

  • Cloud Load Balancing におけるリージョン障害時のリクエストルーティングの課題
  • リージョン別 Backend Service と重み付けによるルーティング制御(Weighted Backend Service)
  • サーバーレス NEG の外れ値検出機能による異常 NEG の自動切り離し

想定読者

  • Google Cloud の Cloud Run を運用している開発者
  • マルチリージョン構成における可用性・障害対策を検討している開発者

はじめに

株式会社 WinTicket の Web チームでエンジニアをしている安井(@ytaisei_)です。

WINTICKET Web では東京と大阪のリージョンに Cloud Run を配置しています。以前、大阪側の Cloud Run が一時的に落ちたことにより、同リージョンにルーティングされたユーザーがサービスにアクセスできない状況が生じました。この事態を踏まえ、Weighted Backend Service によるルーティング制御とサーバーレス NEG の外れ値検出を導入しました。

本記事では、その背景となる課題と、両対策の内容について紹介します。

課題と前提

WINTICKET Web のリクエストは、Google Cloud の Cloud Load Balancing(URL Map → Backend Service)→ Serverless NetworkEndpointGroup(NEG)→ Cloud Run Service の流れで処理されます。

既存の構成

事象

発生した事象は以下のとおりです。

  • 約 30 分間にわたり、エラーレートが高まっていた
  • 大阪側で一部のリクエストが約 100 件/h ほどエラーになっていた
  • 大阪側の Cloud Run インスタンス数が 0 になっていた
  • 直前までの CPU・メモリ使用率および 5xx エラーレートは正常だった

課題

当時の構成では、特定のリージョンに障害が発生した場合でも、Cloud Load Balancing は該当の NEG にリクエストを送り続けてしまいます。その結果、異常な NEG へトラフィックが流れ、ユーザーにエラーが返される事態となり得ました。

Weighted Backend Service によるルーティング制御

そこで、リージョンを指定した Backend Service を用意し、Backend Service 間の重み付け設定を可能にしました。これにより、障害時に異常な NEG へリクエストが流れ続ける事態を防ぎます。

リージョン指定の Backend Service を用意

通常時は、図内の Backend Service 1 に 100% の重みを設定します。複数 NEG を持つ Backend Service は、Google Cloud 内で内部最適化に基づいてルーティングされるため、従来どおりの挙動となります。

リージョン障害時は、図内の Backend Service 2 か 3 に重みを操作します。例えば、大阪リージョンで障害が発生した場合は、東京を指す Backend Service 2 に対して 100% ルーティングするように設定変更することで、異常なリージョンへのトラフィックを即時に切り離しできます。

Terraform での設定例

google_compute_url_mapdefault_route_actionweighted_backend_services を設定し、3 つの Backend Service とそれぞれの重みを指定します。障害時は、異常リージョンの重みを 0、正常リージョンの重みを 100 に変更することで、トラフィックを切り替えます。

resource "google_compute_url_map" "default" {
  default_route_action {
    weighted_backend_services {
      backend_service = google_compute_backend_service.default.self_link
      weight          = 100
    }
    weighted_backend_services {
      backend_service = google_compute_backend_service.tokyo.self_link
      weight          = 0
    }
    weighted_backend_services {
      backend_service = google_compute_backend_service.osaka.self_link
      weight          = 0
    }
  }
}

この対応により、リージョン障害発生時でも異常リージョンへのトラフィックを即時に切り離し、正常なリージョンへルーティングできるようになりました。

サーバーレス NEG の外れ値検出

Weighted Backend Service の導入により障害時に正常なリージョンへルーティングすることは可能ですが、Backend Service の設定を更新するオペレーションが必要です。しかし本来はリージョン側の異常を自動で検知し、リクエストを最適に振り分けたいところです。そこで、サーバーレス NEG の外れ値検出を導入しました。

概要

サーバーレス NEG の外れ値検出は、HTTP レスポンスのパターンから異常なサーバーレス NEG を検知し、その NEG を一時的に切り離す機能です。異常と判断された NEG にはトラフィックを送らず、正常な NEG へリクエストを振り分けることで、エラー率の低減を図ります。

検出対象

  • 連続する 5xx エラー
  • 連続する Gateway エラー(502、503、504 の HTTP ステータスコードのみ)

各種パラメータと設定値

外れ値検出を有効化する際、以下のパラメータを設定しました。

パラメータ 設定値 補足
base_ejection_time 180 秒 切り離し後トラフィックを送らない時間。約 3 分間。短すぎると異常状態での復帰が多い。長いと正常復帰後の待ち時間が長くなる
interval 10 秒(デフォルト) 外れ値検出の分析間隔
max_ejection_percent 50% 最大で切り離せる NEG の割合。東京・大阪の 2 リージョン運用のため、片方異常時は全てを正常側へルーティングする
consecutive_errors 5 回(デフォルト) 切り離しまでの連続 5xx エラー回数
consecutive_gateway_failure 5 回(デフォルト) 切り離しまでの連続 Gateway エラー回数(502、503、504 のみ)
enforcing_consecutive_gateway_failure 100% 連続 Gateway エラー検知時に実際に切り離す確率。

上記の設定により、10 秒ごとに各 NEG のレスポンスを分析し、5 回連続で 5xx または Gateway エラー(502、503、504)が発生した NEG を切り離します。切り離された NEG は最大 3 分間トラフィックを受けなくなります。

Terraform での設定例

Terraform では google_compute_backend_service リソースに outlier_detection ブロックを追加します。

resource "google_compute_backend_service" "default" {
  outlier_detection {
    base_ejection_time {
      seconds = 180
    }
    interval {
      seconds = 10
    }
    max_ejection_percent              = 50
    consecutive_errors                = 5
    consecutive_gateway_failure       = 5
    enforcing_consecutive_gateway_failure = 100
  }
}

対応の結果と双方の棲み分け

両対策を導入した結果、以下のような構成となりました。

対応後の構成

Cloud Load Balancing の URL Map で Backend Service 1〜3 にトラフィックを振り分け、各 Backend Service は Serverless NEG を経由して東京・大阪の Cloud Run Service に接続します。

Weighted Backend Service により、URL Map で Backend Service 間の重みを設定し、障害時に手動で重みを変更することで異常リージョンへのトラフィックを完全に切り離し、正常なリージョンへルーティング可能になりました。

そして、サーバーレス NEG の外れ値検出により、HTTP レスポンスパターンに基づき異常な Serverless NEG を自動検知し、その NEG を一時的(最大 3 分間)に切り離してエラー率を軽減します。

各対策の制約

一方で、Weighted Backend Service とサーバーレス NEG の外れ値検出のそれぞれに制約があります。

Weighted Backend Service によるルーティング制御の制約

Weighted Backend Service は、異常なリージョンへのトラフィックを完全に切り離すことが可能です。しかし、重みの変更は手動での設定更新が必要であり、切り離し実行までにオペレーションの時間が必要になります。

サーバーレス NEG の外れ値検出の制約

外れ値検出だけでは、完全な切り離しはできません。今回の設定値では、切り離しは最大 3 分間有効であり、3 分経過後には切り離されていた NEG へ再度リクエストが送信されます。ただし、再度外れ値が検出された場合は、その NEG が再び切り離されます。

棲み分け

  • Weighted Backend Service:リージョン全体が落ちている場合に、手動でルーティング割合を変更して完全に切り離す
  • サーバーレス NEG の外れ値検出:自動で検知・切り離ししてエラー率を軽減し、いち早く異常に対応できる

まとめ

本記事では、WINTICKET Web のリージョン障害対策として実施した以下 2 つの対策を紹介しました。

  • Weighted Backend Service:障害時に手動でルーティング重みを変更し、異常リージョンへのトラフィックを即時に切り離しできる
  • サーバーレス NEG の外れ値検出:HTTP レスポンスパターンに基づき異常 NEG を自動検知し、一部のトラフィックを正常側へ転送してエラー率を軽減する

双方を組み合わせることで、リージョン障害時の影響を抑えられる体制を整えました。

参考資料

アバター画像
2024年新卒入社で Web フロントエンドエンジニアをしています。