Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

GKEでマルチクラスター ロードバランシングを構築する方法

By StepanAug 17, 202010 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

GCPロードバランシングの構成要素を整理し、グローバルに利用できるGKEマルチクラスター ロードバランサーの構築手順を順を追って解説します。

GCPの中でも特に気に入っている機能のひとつが、外部 HTTP(S)ロードバランシングです。単一のエニーキャストIPアドレスで利用できるグローバルロードバランサーで、DNSによるロードバランシングが不要なのが嬉しいポイントです。リクエストはユーザーに最も近いGoogleのエッジ拠点(POP)からGoogleのグローバルネットワークに入り、¹ 空き容量のある最寄りのリージョンへプロキシされます。これにより、高可用性・グローバル分散・スケーラブルでフルマネージドなロードバランシング環境が手に入ります。さらにDDoS/WAF対策のCloud Armor、Cloud CDN、Identity-Aware Proxy(IAP)を組み合わせれば、Webアプリケーションへのアクセスを一段と安全に保護できます。

こうした特徴から、自然と思い浮かぶのがGKEでのマルチクラスター ロードバランシングで、お客様からもよくご相談をいただくテーマです。現時点ではGKE/Kubernetesにネイティブのサポートはありませんが、² GCPには自前で構築するための部品がひと通り揃っています。

第1部では、まずGCPロードバランシングの構成要素について理解を深めていきましょう。リクエストがシステムに入ってからどう処理されるのかを追いながら、ロードバランシングを構成する各要素の役割を確認していきます。第2部では、2つのGKEクラスターをまたぐロードバランシングを順を追って構築します。

GCPロードバランシングの概要

図1: GCPロードバランシングの概要

まずはロードバランシングの全体的な流れを俯瞰してみましょう。クライアントからのHTTP(S)接続は、HTTP(S) ターゲットプロキシ転送ルールの設定に基づき、エッジロケーションのGoogle Front End(GFE)で終端されます。³ ターゲットプロキシは紐付くURLマップバックエンドサービスの定義を参照し、トラフィックのルーティング方法を決定します。GFEから新たな接続が確立され、トラフィックはGoogleネットワーク経由で、空き容量のある最寄りの正常なバックエンドへと流れます。リージョン内では、容量に応じて個々のバックエンドエンドポイントにトラフィックが振り分けられます。

GCPロードバランシングの構成要素

図2: GCPロードバランシングの構成要素

  • 転送ルール(Forwarding Rule) — 各ルールは特定のIPとポートに紐付きます。今回はグローバルHTTP(S)ロードバランシングが対象なので、IPはエニーキャストのグローバルIPアドレス(必要に応じて静的IPを予約)です。ポートは、ロードバランサーが外部クライアントからのトラフィックを受け付けるポートを指します。⁴
  • ターゲットHTTP(S)プロキシ — トラフィックはターゲットプロキシの設定に基づいて終端されます。各ターゲットプロキシは1つのURLマップに対してのみリンクされます(N:1の関係)。HTTPSプロキシの場合は、SSL証明書を1枚以上アタッチし、SSLポリシーも設定する必要があります。
  • URLマップ — トラフィック管理の中核となるコンポーネントで、異なるバックエンドサービス(GCSバケットを含む)間で受信トラフィックをルーティングできます。基本はホスト名とパスに基づくルーティングですが、URLリダイレクト、URLの書き換え、ヘッダーやクエリパラメーターに基づくルーティングなど、より高度な制御も可能です。各ルールはトラフィックを1つのバックエンドサービスに振り分けます。
  • バックエンドサービス — 同一サービスに属するバックエンドを論理的にまとめたもので、バックエンド間のトラフィック分散、プロトコル、セッションアフィニティ、Cloud CDN・Cloud Armor・IAPなどの機能といった関連設定を保持します。各バックエンドサービスにはヘルスチェックも紐付けられます。
  • ヘルスチェック — 個々のバックエンドエンドポイントの生存確認方法を定義し、その結果から各バックエンドの全体的な健全性を判定します。作成時にはプロトコルとポートの指定が必須で、チェック間隔、正常/異常のしきい値、タイムアウトなどのオプションも設定できます。重要な点として、内部IP範囲からのヘルスチェック トラフィックを許可するファイアウォールルールを事前に用意しておく必要があります。⁵
  • バックエンド — 特定のロケーションに属するエンドポイントの集合を表します。GKEの場合、バックエンドはNetwork Endpoint Group(NEG)となり、⁶ GKEクラスターのゾーンごとに1つずつ作成されます(GKEのNEGはゾーン単位ですが、バックエンドの種類によってはリージョン単位のものもあります)。
  • バックエンドエンドポイント — IPアドレスとポートの組み合わせで、コンテナネイティブ ロードバランシング⁷ を使うGKEでは個々のPodを指します。

セットアップ

2つのクラスターにまたがってデプロイした2つのサービス(FooとBar)を対象に、マルチクラスター ロードバランシングを構築します(図3)。シンプルなパスベースのルールを使い、/foo/* へのリクエストはFooサービスへ、/bar/* へのリクエストはBarサービスへルーティングします。

図3: GKEマルチクラスター Foo Bar

前提条件

git clone https://github.com/stepanstipl/gke-multi-cluster-native.gitcd gke-multi-cluster-native

アプリケーションとサービスをGKEクラスターにデプロイする

図4: K8sデモアプリ

まずは、シンプルなデモアプリケーションを各クラスターにデプロイしましょう。このアプリは、リクエストを処理したクラスターとリージョンの情報を表示するもので、ソースコードは stepanstipl/k8s-demo-app で公開しています。

以下の手順を、各クラスターで実施してください。

kubectl用の認証情報を取得する

gcloud container clusters get-credentials [cluster] \
  --region [cluster-region]

FooとBarの両アプリケーションをデプロイする

kubectl apply -f deploy-foo.yaml
kubectl apply -f deploy-bar.yaml

両サービスのPodが起動しているかは kubectl get pods で確認できます。

両アプリケーション用のK8sサービスを作成する

kubectl apply -f svc-foo.yaml kubectl apply -f svc-bar.yaml

サービスに付与された cloud.google.com/neg: '{"exposed_ports": {"80":{}}}' というアノテーションに注目してください。これにより、GKEがそのService用のNEGを作成します。

サービスが正しく構成されているかは、kubectl port-forward service/foo 8888:80 でローカルポートをフォワードし、http://localhost:8888/ にアクセスすることで確認できます。

すべてのクラスターで上記の手順を忘れずに繰り返してください。

ロードバランシング(GCLB)コンポーネントのセットアップ

ヘルスチェックを作成する

gcloud compute health-checks create http health-check-foobar \
  --use-serving-port \
  --request-path="/healthz"

バックエンドサービスを作成する

各サービス用のバックエンドサービスに加えて、パスベースのルールに合致しないトラフィック用のデフォルトバックエンドをもう1つ作成します。

gcloud compute backend-services create backend-service-default \
  --globalgcloud compute backend-services create backend-service-foo \
  --global \
  --health-checks health-check-foobargcloud compute backend-services create backend-service-bar \
  --global \
  --health-checks health-check-foobar

URLマップを作成する

gcloud compute url-maps create foobar-url-map \
 --global \
 --default-service backend-service-default

URLマップにパスルールを追加する

gcloud compute url-maps add-path-matcher foobar-url-map \
  --global \
  --path-matcher-name=foo-bar-matcher \
  --default-service=backend-service-default \
  --backend-service-path-rules='/foo/*=backend-service-foo,/bar/*=backend-service-bar'

静的IPアドレスを予約する

gcloud compute addresses create foobar-ipv4 \
  --ip-version=IPV4 \
  --global

DNSを設定する

先ほど予約した静的IPアドレスにDNSを向けます。取得したIPアドレスは次のコマンドで確認できます。

gcloud compute addresses list --global

このIPを指す foobar.[your_domain_name] のAレコードを作成してください。レコード管理にはCloud DNSのほか、お好みのサービスを利用できます。次のステップに進む前に、必ずこの作業を完了させてください。¹⁰

マネージドSSL証明書を作成する

gcloud beta compute ssl-certificates create foobar-cert \
 --domains "foobar.[your_domain_name]"

ターゲットHTTPSプロキシを作成する

gcloud compute target-https-proxies create foobar-https-proxy \
  --ssl-certificates=foobar-cert \
  --url-map=foobar-url-map

転送ルールを作成する

gcloud compute forwarding-rules create foobar-fw-rule \
  --target-https-proxy=foobar-https-proxy \
  --global \
  --ports=443 \
  --address=foobar-ipv4

TLS証明書を確認する

証明書のプロビジョニングには時間がかかる場合があります。次のコマンドでステータスを確認できます。

gcloud beta compute ssl-certificates describe foobar-cert

設定がすべて正しければ、managed.status はおおむね60分以内、多くの場合はもっと早く ACTIVE になります。

K8sサービスをロードバランサーに接続する

GKEは、cloud.google.com/neg アノテーション付きでデプロイした各K8sサービスに対して、すでにNEGをプロビジョニング済みです。これらのNEGを、対応するバックエンドサービスのバックエンドとして追加していきます。

プロビジョニングされたNEGの名前を取得する

kubectl get svc \
  -o custom-columns='NAME:.metadata.name,NEG:.metadata.annotations.cloud\.google\.com/neg-status'

各サービスのNEG名とゾーンを控えておきます。

すべてのGKEクラスターで同じ手順を繰り返してください。

NEGをバックエンドサービスに追加する

両クラスターのすべてのNEGとゾーンに対して、以下を繰り返します。Fooサービスに属するNEGだけを使うよう注意してください。

gcloud compute backend-services add-backend backend-service-foo \
 --global \
 --network-endpoint-group [neg_name] \
 --network-endpoint-group-zone=[neg_zone] \
 --balancing-mode=RATE \
 --max-rate-per-endpoint=100

Barサービスについても同様に、両クラスターのすべてのNEGとゾーンに対して繰り返します。

gcloud compute backend-services add-backend backend-service-bar \
 --global \
 --network-endpoint-group [neg_name] \
 --network-endpoint-group-zone=[neg_zone] \
 --balancing-mode=RATE \
 --max-rate-per-endpoint=100

GCLBトラフィックを許可する

gcloud compute firewall-rules create fw-allow-gclb \
  --network=[vpc_name] \
  --action=allow \
  --direction=ingress \
  --source-ranges=130.211.0.0/22,35.191.0.0/16 \
  --rules=tcp:8080

バックエンドの正常性を確認する

gcloud compute backend-services get-health \
  --global backend-service-foo gcloud compute backend-services get-health \
  --global backend-service-bar

各バックエンドサービスにつき、通常は6つのバックエンド(クラスターごとに3つ、¹¹ 各ゾーンに1つずつ)が healthState: HEALTHY の状態で表示されるはずです。ファイアウォールルールを追加してから、すべてのバックエンドが正常になるまで少し時間がかかる場合があります。

動作確認

DNS名 https://foobar.[your-domain] にcurlを実行してみましょう(またはブラウザで開いてみます)。デフォルトサービスにはバックエンドを追加していないため、ルートへのアクセスでは502が返るはずです。

curl -v "https://foobar.[your-domain]"

続いて、各サービスのパス https://foobar.[your-domain]/foo/https://foobar.[your-domain]/bar/ にcurlを実行すると、200と対応するサービスのコンテンツが返ってくるはずです。

curl -v "https://foobar.[your-domain]/foo/"
curl -v "https://foobar.[your-domain]/bar/"

何度かリトライすると、異なるPodやクラスターでトラフィックが処理されている様子が確認できます。¹²

たとえば、私のお気に入りのCLIツールのひとつ vegeta でトラフィックをシミュレートすると、GCPコンソール上でバックエンド間のトラフィック分散をきれいに観察できます。ネットワーク サービス -> ロード バランシング セクション -> 対象のロードバランサーを選択 -> モニタリング タブから該当のバックエンドを選択してみてください。図5のようなダッシュボードが表示されるはずです。

図5: GKEコンソール — ロードバランシング(両クラスターは同一リージョンにあるため、トラフィックはすべてのバックエンドに均等に分散されています)

ここで少し実験してみるのもおすすめです。クラスターを同一リージョンに置いた場合と、異なるリージョンに置いた場合で挙動がどう変わるか試してみてください。負荷を上げれば、別リージョンへのトラフィックのオーバーフローも観察できます(ヒント:先ほど指定した --max-rate-per-endpoint を思い出してみてください)。クラスターのひとつを停止するとどうなるか、3つ目のクラスターを追加できるかも、ぜひ試してみてください。

(オプション)`gke-autoneg-controller`

K8sサービスに付与された anthos.cft.dev/autoneg アノテーションにお気付きでしょうか。今回のセットアップでは不要ですが、必要に応じて gke-autoneg-controller ¹³ をクラスターにデプロイすると、GKEが作成したNEGを対応するバックエンドサービスに自動で関連付けられます。これで、面倒な手作業を省けます。

お疲れさまでした!

以上で完了です。本記事では、GCLBの各構成要素の役割を解説し、異なるリージョンにある2つ以上のGKEクラスターにデプロイしたサービス間で、マルチクラスター ロードバランシングを構築する方法を紹介しました。実運用ではTerraformなどの構成管理ツールでこのセットアップを自動化することをおすすめします。

この構成は、複数の独立したGKEクラスターでトラフィックを処理することでサービスの可用性を高めると同時に、レイテンシの低減にも貢献します。HTTPSの場合、最初のTLSネゴシエーションがユーザーに近いGFEサーバーで行われるため、最初のバイトが届くまでの時間(TTFB)が短くなります。さらに複数クラスター構成では、リクエストはユーザーに最も近いクラスターで処理されます。

本記事がお役に立てば嬉しいです。ご質問などがあれば、こちらまたは @stepanstipl までお気軽にどうぞ。🚀🚀🚀 高速サービングと共にあれ!

  • [1] 世界中の90か所以上のロケーション — ロードバランシング — ロケーション から、空き容量のある最寄りのリージョンへプロキシされます。
  • [2] ここでは Anthos はいったん除外します。Anthosは、オンプレミスや他のクラウドでもK8sクラスターを実行できるアプリケーション管理プラットフォームで、 マルチクラスターIngressコントローラー などGKEクラスターの機能も拡張します。
  • [3] GFEはエッジPOPに配置された、ソフトウェア定義のスケーラブルな分散システムです。
  • [4] HTTPプロキシをターゲットとする場合のポートは80または8080、HTTPSプロキシの場合は443です。
  • [5] 外部HTTP(S)ロードバランシングの場合、これらの範囲は35.191.0.0/16 130.211.0.0/22 です。
  • [6] Network Endpoint Group概要
  • [7] コンテナネイティブ ロードバランシング には VPCネイティブ モードのクラスターが必要で、ロードバランサーは(クラスターノードではなく)個々のPodを直接ターゲットにできます。
  • [8] 異なるリージョンにクラスターを配置するほうが、より興味深い構成になります。
  • [9] 本記事のコマンドで参照しているファイルはすべて、このリポジトリのルートからの相対パスです。
  • [10] GoogleマネージドSSL証明書のプロビジョニングには、DNSレコードが事前に設定されている必要があります。設定されていないと、証明書は永続的な失敗と見なされ(認証局の署名が失敗するため)、再作成が必要になる可能性があります。
  • [11] 3つのゾーンにまたがるリージョナルクラスターを使用していることを前提としています。それ以外の構成では適宜読み替えてください。
  • [12] クラスターを異なるリージョンに配置している場合、GCLBはクライアントに近いクラスターからトラフィックを処理することを優先します。そのため、リージョン間で均等にロードバランスされるとは期待しないでください。
  • [13] ここではデプロイ方法や使い方の詳細には踏み込みません。 readme を参照してください。基本的には、サービスにNEG名を含むアノテーション、たとえばanthos.cft.dev/autoneg: '{"name":"autoneg_test", "max_rate_per_endpoint":1000}' を追加するだけです。