概要
従来のGKEのロードバランサーはNodeに到達後iptablesで再度負荷分散するという2段階ロードバランシングでした。
これによりレイテンシの増加、分散のばらつきといった問題が生じていました。
ref: Google Cloud Blog - News, Features and Announcements
しかしNEG(Network Endpoint Group)という機能を使うことで直接Pod宛てに負荷分散を行うことができ、上記の問題を低減することが可能となっています。
ref: Google Cloud Blog - News, Features and Announcements
今回のそのNEGを実際に利用する方法を紹介します。
環境
- GKE v1.18.12
NEGの種類
NEGは大きく3つ種類があります
- Zonal NEG
- Internet NEG
- Serverless NEG
- GCPのサーバレスサービス(Cloud Functions/Cloud Run)にルーティングしたい
GKEを使う場合は Zonal NEG を利用します。
詳細な比較は Network endpoint groups overview | Load Balancing | Google Cloud で確認できます。
またZonal NEGにも2つあり、ざっくり分けるとIngressを用いるNEGか、手動で各々構築するStandalone NEGです。
ref: Container-native load balancing through standalone zonal NEGs
Standalone NEGの場合はやや構築に手間がかかりますが、その分自由度が高いです。
例えば複数のGKEクラスタにあるServiceをそれぞれ Standalone NEGにすることで、単一のLoad BalancerのBackend Serviceとすることができます。
制限
Zonal NEGを使う上では以下の制限があります。
- レガシーネットワークでは使えない
- 内部TCP/UDPロードバランサ、ネットワーク(外部TCP/UDP)ロードバランサでは使えない
- 1 NEG あたりのエンドポイント数は10,000。上限引き上げ不可。
ref: ゾーン ネットワーク エンドポイント グループの概要 | 負荷分散 | Google Cloud
構築
Ingressを使ったZonal NEGを構築してみます。
VPC ネイティブ クラスタの作成
NEGを使うにはVPCネイティブなクラスタを作成する必要があります。
$ gcloud container clusters create neg-demo-cluster \ --enable-ip-alias \ --create-subnetwork="" \ --network=default \ --zone=asia-northeast1-a
kubectlでアクセスできるようにクレデンシャルを取得しておきます。
$ gcloud container clusters get-credentials neg-demo-cluster --zone asia-northeast1-a
Deployment を作成
次に、クラスタにワークロードをデプロイします。
apiVersion: apps/v1 kind: Deployment metadata: name: neg-deployment spec: replicas: 2 selector: matchLabels: app: sample template: metadata: labels: app: sample spec: containers: - name: sample image: gcr.io/google-samples/hello-app:2.0 ports: - containerPort: 8080
Serviceの作成
次にServiceを作成します。
ここでcloud.google.com/neg: '{"ingress": true}'
アノテーションを付ける必要があります。
apiVersion: v1 kind: Service metadata: name: neg-svc annotations: cloud.google.com/neg: '{"ingress": true}' spec: type: ClusterIP selector: app: sample ports: - port: 80 protocol: TCP targetPort: 8080
※一定の条件下ではNEGがデフォルトなのでアノテーションは不要です。
Ingressの作成
外部からアクセスできるよう、Ingressを用意します。
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: neg-ingress spec: rules: - http: paths: - path: /* backend: serviceName: neg-svc servicePort: 80
しばらく待つとグローバルIPが付与されたIngressが作成されます。
動作検証
NEGが作成されているか確認
上記yamlをapplyするとKubernetes Service に相当する NEG が GCP 側に1つ作られています。
$ gcloud compute network-endpoint-groups list | grep neg-svc k8s1-ad013dee-default-neg-svc-80-4a6a4d8c asia-northeast1-a GCE_VM_IP_PORT 2
WebコンソールではGCE→ネットワーク エンドポイント グループから確認できます。
NEGの中にはPodの個数分、Network Endpointが作られています。
$ gcloud compute network-endpoint-groups list-network-endpoints \ k8s1-ad013dee-default-neg-svc-80-4a6a4d8c \ --zone=asia-northeast1-a INSTANCE IP_ADDRESS PORT FQDN gke-a13156-neg-demo-clus-default-pool-3af9a691-qbpl 10.80.0.5 8080 gke-a13156-neg-demo-clus-default-pool-3af9a691-fc6t 10.80.1.6 8080
疎通確認
curlを叩いてみると、ちゃんと疎通できておりかつバランシングされていることが分かります。
$ curl 35.xxx.yyy.zzz Hello, world! Version: 2.0.0 Hostname: neg-deployment-d64cb5c65-gmhvq $ curl 35.xxx.yyy.zzz Hello, world! Version: 2.0.0 Hostname: neg-deployment-d64cb5c65-vl489
スケールアウトさせてみる
Pod数を2→4にスケールアウトしてみます。
$ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP neg-deployment-d64cb5c65-gmhvq 1/1 Running 0 20m 10.80.1.6 neg-deployment-d64cb5c65-kvqzc 1/1 Running 0 95s 10.80.1.7 neg-deployment-d64cb5c65-pp4fj 1/1 Running 0 95s 10.80.2.4 neg-deployment-d64cb5c65-vl489 1/1 Running 0 20m 10.80.0.5 $ gcloud compute network-endpoint-groups list-network-endpoints \ k8s1-ad013dee-default-neg-svc-80-4a6a4d8c \ --zone=asia-northeast1-a INSTANCE IP_ADDRESS PORT FQDN gke-a13156-neg-demo-clus-default-pool-3af9a691-qbpl 10.80.0.5 8080 gke-a13156-neg-demo-clus-default-pool-3af9a691-fc6t 10.80.1.6 8080 gke-a13156-neg-demo-clus-default-pool-3af9a691-fc6t 10.80.1.7 8080 gke-a13156-neg-demo-clus-default-pool-3af9a691-csrq 10.80.2.4 8080
するとPodの個数分Network Endpointが作られていることが確認できます。
まとめ
NEGというコンテナネイティブな負荷分散機能の紹介と使い方を説明しました。