GKE のコンテナネイティブ負荷分散

GKE(Google Kubernetes Engine)は、HTTPS サービスを Kubernetes Gateway/Ingress を用いて公開できます。
非 HTTP のサービスを公開するには、 外部 TCP/UDP ネットワーク負荷分散を別途構築し Service へのルーティングを設定します。

GKE は 1 クラスタで共用できますが、TCP ロードバランサは公開するポートごとに 1 つずつ追加する構成になります。
また、HTTPS サービスであっても GCP の付加機能を利用したい場合には手動構成が必要になる場合があります。

NEG の作成

リクエストを処理する Network Endpoint Group(NEG)までは、kubernetes で作成できます。

Service にcloud.google.com/negアノテーションを追加すると、GCP のスタンドアロン NEG が作成されます。
対応する Deployment などの Pod はごく普通の構成で動作します。

apiVersion: v1
kind: Service
metadata:
  name: mail
  labels:
    name: mail
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"25": {"name": "mail"}}}'
spec:
  type: ClusterIP
  selector:
    name: mail
  ports:
  - port: 25
    targetPort: mail-smtp

リソース状況は、kubectl get ServiceNetworkEndpointGroups [-o yaml <NEG name>]コマンドで確認できます。
NEG が不要になった場合にはkubectl deleteで Service を削除すると同時に削除されます。

exposed_portsアノテーションのポート番号には、ロードバランサーがアクセスする番号を指定する必要があります。
TCP ロードバランサーの場合は公開するポート番号、https ロードバランサーの場合には 443 または 80 です。

exposed_portsにはspec.ports.[].portの番号を指定するため、Pod が公開ポートと異なるポートをサーブしている場合にはtargetPortを用いて変換する必要があります。

NEG 作成エラー

なお、NEG はゾーン内で同名オブジェクトを重複作成できず、ゾーンが異なる場合には重複可能です。ゾーンは k8s クラスタのロケーションで決まります。

重複など作成エラー時には、k8s Service は作成されるものの NEG が追加されない、という分かりづらい挙動になります。
kubectl describe service <service-name>を実行することでエラーログを確認できます。Events欄の具体的なメッセージを解釈することが重要です。NEG の作成に失敗している場合にも、ログレベルはWARNINGにとどまります。

ロードバランサの作成

HTTP サービスには ingress があり、GKE ingress はコンテナネイティブ負荷分散に対応しています。
一方、TCP サービスには対応する Kubernetes サービスがなく、gcloud computeコマンドで手動構築します。

依存関係に沿って health check, backend service, TCP proxy, forwarding rule の順で作成します。

Backend service 作成

$ gcloud compute health-checks create tcp <tcp-basic-check> --use-serving-port

--use-serving-portはサービスのポート設定を参照して監視します。

$ gcloud compute backend-services create <gke-bs> \
  --global --protocol TCP \
  --global-health-checks --health-checks <tcp-basic-check> \
  --timeout 5m 

TCP プロキシが global スコープのサービスであるため、backend service は--globalで作成します。

$ gcloud compute backend-services add-backend <gke-bs> --global \
  --network-endpoint-group=<mail> --network-endpoint-group-zone=asia-northeast1-a \
  --balancing-mode CONNECTION --max-connections-per-endpoint 10

backend service のリージョン(global)に加えて NEG の zone も指定する必要があります。

TCP プロキシ作成

$ gcloud compute target-tcp-proxies create <gke-lb-proxy> --backend-service <gke-bs> [--proxy-header PROXY_V1]

IP 保持のために proxy プロトコルを使いたい場合には、target-tcp-proxies作成オプションでPROXY_V1を指定します。

HTTPS プロキシ作成

HTTPS サービスは Ingress などの k8s Gateway で構築でき、基本的な構成にはgcloudによるネットワーク構築は不要です。

ロードバランサが提供する独自機能を利用したい場合には、target-tcp-proxyに換えて HTTPS 向けにtarget-https-proxy, url-maps, ssl-certificatesのセットを作成します。
HTTPS ロードバランサのオブジェクトは複雑であるため、とくに初回は Web コンソールから作成した方が手軽でしょう。

url-mapsのルーティング先がbackend-servicesを指す構成となり、NEG 経由で各 k8s Service に到達します。
health-checksbackend-services, forwarding-rulesは TCP プロキシと共通です。

フォワーディング・ルール作成

$ gcloud compute forwarding-rules create <tcp-gke-forwarding-rule> --global \
  --target-tcp-proxy <gke-lb-proxy> --ports 25 --address <some-static-ip>

--addressオプションは、事前に確保しておいた静的 IP を指定します。複数の転送ルールを作成する際、ポートが重複しなければ同一 IP に設定できます。

ルーティングオブジェクトの管理

target-tcp-proxy, forwarding-rules, backend-services, health-checksの 4 種は GCP 独自オブジェクトです。
これらを kubernetes のようにコードとして管理するには Terraform を利用するのが手軽です。

OpenTofu のエクスポート機能 を利用することで、gcloudコマンドで作成したオブジェクトをエクスポートできます。

クラスタ移行

上の手順でグローバルの外部 TCP プロキシロードバランサを作成した場合、構築した 4 つの GCP オブジェクトのうちbackend-servicesが参照する NEG を変更するだけでクラスタ移行が完了します。
プロキシはグローバルにサービス提供しているためそのまま流用でき、IP アドレスも変わりません。

  1. k8s の新クラスタを作成し、Service や Pod 群をデプロイ
    • Service マニフェストのアノテーションにより、NEG は新クラスタのゾーンに自動作成
    • クラスタを同一ゾーンに作成すると、NEG の重複エラーになる点に注意。アノテーションの編集が必要
  2. backend-servicesの設定変更
    • gcloud または terraform を用いる
    • health-checksはグローバルオブジェクトであり、同一サービスならゾーンが変わっても流用できる

terraform を使う場合、backendServicesresource.backend.groupの URI を書き換えることで、backendServices が指す NEG を変更できます。
次の例では、zones以下を新クラスタのロケーションに合わせて変更します(該当箇所のみ抜粋)。

resource "google_compute_backend_service" "tfer--mail" {

  backend {
    group                        = "https://www.googleapis.com/compute/v1/projects/<some-project-name>/zones/asia-northeast1-b/networkEndpointGroups/<NEG-name>"

  }

}

編集できたら、標準手順に沿ってterraform planで確認のうえterraform applyで設定できます。 この過程でhealth-checksも流用します。

ただし、構築時のgcloudオプションしだいでリージョン TCP ロードバランサも作成可能であるため、着手前に構成の特定は必須です。サービスのバリエーションについては GCP ロードバランサーの分類で解説しています。

リージョン TCP ロードバランサの場合には、全体的に再構築が必要で、IP アドレスも変わるため最終的に DNS 設定変更で切り替えることになります。

また、ネットワーク以外の総合的な指針は GKE クラスタ移行のホワイトペーパーで解説しています。

⁋ 2021/12/03↻ 2024/04/18
中馬崇尋
Chuma Takahiro