NEG とは何か

Yuki Furuyama
google-cloud-jp
Published in
15 min readMar 6, 2020

はじめに

NEG (Network Endpoint Group) というと Container-native Load Balancing の文脈で話されることが多く、パフォーマンスを向上させるためのものという認識が強いと思います。もちろん結果としてパフォーマンス向上が見込まれる場面もあると思いますが、それと同じく個人的に重要だと思うのが、NEG には Kubernetes の世界とその外のプラットフォーム (GCP) を繋ぐ大事な役割があるという点です。

本記事では NEG とは何か、なぜ重要なのかというのを一から説明したいと思います。

尚、NEG には大きく分けて Zonal NEGInternet NEG という2種類がありますが、本記事では Zonal NEG に絞って記載します。また Zonal NEG は VM を表すものとしても使えますが、ここでは GKE の文脈に絞って話したいと思います。

TL;DR

  • NEG の構成要素である Network Endpoint は Kubernetes の Pod に相当する
  • Network Endpoint を複数まとめたグループが NEG であり、Kubernetes の Service に相当する
  • NEG を使うことで Kubernetes の Pod と Service を GCP 側で第一級市民として扱えるようになる

Kubernetes のリソースと GCP のリソース

まず NEG が何であるかを説明する前にクリアにしておきたいのが、Kubernetes のリソースと GCP のリソースの関係性についてです。

Kubernetes には Pod, Service, Ingress のようなリソースがありますが、一部のリソースは GCP などのクラウドプラットフォーム側のリソースを作ることがあります。

例えば、GKE で Ingress リソースを作ると、GCP 側に HTTP Load Balancer を構成するリソースである Forwarding Rule や URL Map などが作られます。これらは GKE の Master Node で動作している Controller が、Kubernetes のリソースに変更が生じたら GCP のリソースもそれに合わせて変更するような作りになっているからです。

今回説明する NEG は GCP 側のリソースとして定義されているものになります。

NEG とは何か

NEG (Network Endpoint Group) とはその名の通り Network Endpoint を複数まとめたものを指します。Network Endpoint というのは IP:Port で一意に表されるエンドポイントです。Network Endpoint というと少し抽象的に聞こえますが、Kubernetes の文脈で言うと Pod に相当します。それと同じく Network Endpoint をまとめた NEG は Kubernetes の Service に相当するものです。

前述したように NEG は GCP のリソースとして定義されているものなので、NEG の操作を行うには gcloud compute network-endpoint-groups コマンドなどで GCP の API を叩く必要がありますが、GKE の場合は Service に cloud.google.com/neg から始まる annotation を付けてあげるだけで、Kubernetes の Service を作った時に NEG が作られたり、Pod が作られた時に Network Endpoint が作られるように制御することが可能になっています。

Kubernetes と NEG の関係性: https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg

試しに GKE で NEG を作ってみましょう。

以下のような Deployment, Service, Ingress を用意し、Service のところに cloud.google.com/neg: '{"ingress": true}' という annotation を付与します。

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
---
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
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: neg-ingress
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: neg-svc
servicePort: 80

これを kubectl apply したのち、 gcloud compute network-endpoint-groups list を実行すると、Kubernetes Service に相当する NEG が GCP 側に1つ作られているのがわかります。

$ gcloud compute network-endpoint-groups list 
NAME LOCATION ENDPOINT_TYPE SIZE
k8s1-62e4828d-default-neg-svc-80-af364d91 us-central1-a GCE_VM_IP_PORT 2

さらにその NEG の中には Pod の個数分 Network Endpoint が作られています。

$ gcloud compute network-endpoint-groups list-network-endpoints k8s1-62e4828d-default-neg-svc-80-af364d91 --zone=us-central1-a
INSTANCE IP_ADDRESS PORT
gke-c07-default-pool-7202fd3e-dnr7 10.72.1.7 8080
gke-c07-default-pool-7202fd3e-nv8j 10.72.2.9 8080

ここで表示されている IP_ADDRESS , PORT はそれぞれ Pod の IP, Container Port と同じものです。

NEG は Kubernetes と GCP を繋ぐもの

今まで見てきたように、NEG は Kubernetes の Service に相当するもの、Network Endpoint は Pod に相当するものです。これは少し固い言葉で言えば、NEG を使うことで Kubernetes の Service と Pod を GCP の世界で第一級市民として扱えるようになることを意味します。

今までは Kubernetes の世界を GCP 側から認識する方法はなく、それらは GCE や Instance Group のなかで覆い隠されていました。 ところが GCE の Alias IP という仕組みで VPC-native な IP が Pod に付与されるようになったことで、GCP 側でネイティブに Pod を認識できる土台が出来上がりました。そして NEG により、それらをグルーピングして扱えるようになった訳です。

GCP が NEG を通して Kubernetes の Service や Pod を認識できると何が出来るようになるのでしょうか?わかりやすい例としては、Load Balancer から Pod にダイレクトにリクエストを送る Container-native Load Balancing や、VM と GKE を組み合わせて Service Mesh を構成できる Traffic Director があると思います。どちらも Kubernetes の外の世界から Service や Pod を扱う必要があるものです。

特に Load Balancer とのインテグレーションには様々な可能性があり、以下のような異なる GKE クラスタの Service を Load Balancer の配下に入れる構成も考えられます。

GCP 側で NEG として認識されていればいいので、片方が実際はオンプレミス環境の Kubernetes クラスタの Service であったとしてもいいのです (NEG を同期するコントローラは必要ですが)。

ちなみにこのような構成は次に説明する Standalone NEG というものを使って構築することができます。

Standalone NEG

前述の YAML で紹介した NEG は cloud.google.com/neg: '{"ingress": true}' という annotation が付いていました。これは Ingress が作られた時に作成される NEG であり、HTTP(S) Load Balancing を構成したい時に使います。

一方で GCP には TCP Proxy のような別の Load Balancer のサービスがあります。それらに相当する Ingress のような Kubernetes リソースはないので、NEG を作る別の方法が用意されています。それが Standalone NEG と呼ばれているものです。

Standalone NEG は以下のように cloud.google.com/neg: ‘{"exposed_ports": {"80":{}}}’ といった annotation を Service オブジェクトに付けることで作ることが出来ます。

apiVersion: v1
kind: Service
metadata:
name: standalone-neg-svc
annotations:
cloud.google.com/neg: '{"exposed_ports": {"80": {}}}'
spec:
type: ClusterIP
selector:
app: sample
ports:
- port: 80
protocol: TCP
targetPort: 8080

作成された NEG の情報は Service オブジェクトの annotation の cloud.google.com/neg-status というフィールドに入っています。

$ kubectl get service standalone-neg-svc -o jsonpath="{.metadata.annotations['cloud\.google\.com/neg-status']}"
{"network_endpoint_groups":{"80":"k8s1-62e4828d-default-standalone-neg-svc-80-fdde8920"},"zones":["us-central1-a"]}

Standalone NEG を使った場合は、GCP 側に自動的に作られるのは NEG のみです。もし Ingress と同等の仕組みを Standalone NEG で作る場合は、以下の図にある GCE Health Check, Backend Service, URL Map, Target HTTP Proxy, Forwarding Rule は自身でセットアップしないといけません。

画像: https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg

Standalone NEG の責務は Kubernetes 内の Service や Pod をきちんと GCP 側の NEG に同期してくれるという点に尽きます。そこからどのようにその NEG を使うかは自由なので、前述したような複数の GKE クラスタにある Service をそれぞれ Standalone NEG にして、それらを単一の Load Balancer の Backend Service に含めるというのが出来る訳です。

Readiness Gate とは

最後に Readiness Gate というのを紹介したいと思います。

GCP の Load Balancer と NEG を組み合わせて使う際に、Deployment の Rolling Update を行うと一つ問題が起きます。

Rolling Update は新しい Pod を生成しつつ古い Pod を削除してくことで行われます。新しく生成した Pod が Ready 状態になったら古い Pod を Terminating 状態にする、というのを maxSurgemaxUnavailable といったパラメータを元に繰り返し行っていく流れです。

一方で GCP 側の世界では Health Check が通った Network Endpoint に対してのみ Load Balancer からリクエストが送られます。Health Check は NEG に追加された Network Endpoint に対して定期的に実施されます。

ここで問題となるのは、Deployment で生成された Pod が Ready 状態になっても、その Pod に相当する Network Endpoint の Health Check がまだ行われていない可能性があることです。もし Deployment Controller が全ての Rolling Update を終えて Kubernetes 側が準備完了になっていたとしても、Health Check が全く通っていなければ Network Endpoint に対してリクエストが送られないので、サービス障害に繋がってしまいます。一部の Health Check が通るのが遅いというだけでも、maxUnavailable で設定した値と乖離が出てキャパシティ不足に陥る可能性があるでしょう。

そこで出てくるのが Readiness Gate という仕組みです。これは Pod が Ready 状態になるための条件を外部から追加できるようにする機能です (Kubernetes v1.14 から GA)。Readiness Gate が Pod に付いていると、Pod 内の各コンテナが Ready になった状態 (ContainersReady)、かつ Readiness Gate が True の状態に限り Pod が Ready 状態とみなされます。

GKE の場合、Rolling Update を行って追加された Pod には cloud.google.com/load-balancer-neg-ready というタイプの Readiness Gate が付きます。

spec:
readinessGates:
- conditionType: cloud.google.com/load-balancer-neg-ready

GKE の Master Node で動いている NEG Controller は各 Network Endpoint の Health Check 状況を定期的に確認します。Health Check が通ったことを確認したら Readiness Gate の状態を True に変更します。ここで Pod 側の readinessProbe も通っていたら、Pod がようやく Ready 状態になります。これで Rolling Update が次に進めるようになる訳です。

まとめると、Pod が Ready 状態になるまでの流れはこのような形です。

  1. (Kubernetes) Rolling Update によって Pod が生成される
  2. (GCP) Network Endpoint が作成される
  3. (Kubernetes) readinessProbe が実行され ContainersReady: True の状態になる
  4. (GCP) Network Endpoint に対して Health Check が実行され、Healthy 状態になる
  5. (Kubernetes) Health Check が通ったので Readiness Gate が True の状態になる
  6. (Kubernetes) Pod を Ready 状態にする

つまり GKE での Readiness Gate というのは、Load Balancer から Pod まで一貫して動作することを保証するために必要なものであることがわかると思います。Kubernetes と GCP が歩調を合わせつつ動いていくのが面白いですね。

ちなみに NEG を使わない場合は Readiness Gate のようなものはいりません。なぜなら、Load Balancer は常に配下の Instance Group に対してリクエストを送ればよく、あとは Instance 上で動いている kube-proxy と iptables によって Ready 状態の Pod のみにリクエストがルーティングされるからです。

まとめ

本記事では NEG の基本と、GKE で使う際に必要になってくる考え方を説明しました。

大事な点をまとめると以下のようになります。

  • NEG の構成要素である Network Endpoint は Kubernetes の Pod に相当する
  • Network Endpoint を複数まとめたグループが NEG であり、Kubernetes の Service に相当する
  • NEG を使うことで Kubernetes の Pod と Service を GCP 側で第一級市民として扱えるようになる

NEG を使うことで GCP の様々な機能とのインテグレーションが可能になるので、是非本記事を参考に NEG を使ってもらえればと思います。

--

--

Yuki Furuyama
google-cloud-jp

Technical Solutions Engineer @Google Cloud. Opinions are my own and not the views of my employer.