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を利用するのが手軽です。

Terraformerを利用することで、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クラスタ移行のホワイトペーパーで解説しています。

中馬崇尋
Chuma Takahiro