GKEのNEG(Network Endpoint Group)とIG(Instance Group)との違い、メリットを整理しました

2021-07-21GCP,Kubernetes

はじめに

GCP の Cloud Armor について調べていたところ、ドキュメントに出てきた NEG という単語。

「VM インスタンスや NEG に対して WAF 機能を提供する」、とありましたが、NEG とはなんだろう?となってしまったため、 NEG について調べました。

NEG とは?

NEG についての記載があるページを参考にさせていただきました。

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

視覚化すると以下のようになります。

= GCP =====================================

 +-------------+       +-------+       +---+
 |Load Balancer| ----> |NEG    | ----> |NE |+
 +-------------+       +-------+       +---+|+
       ^                   ^            +---+|
       |                   |             +---+
       |                   |             ^
       |                   |             |
= GKE =|===================|=============|=
       |                   |             |
 +------------------+  +-------------------+
 |Ingress Controller|  |NEG Controller     |
 +------------------+  +-------------------+
       |                   |             |
 +-------------+       +-------+       +---+
 |Ingress      | ----> |Service| ----> |Pod|+
 +-------------+       +-------+       +---+|+
                                        +---+|
                                         +---+

GKE 上のリソースとして IngressServicePod がありますが、 GCP 上にも対になるリソースが生成されます。
NE は  Pod に割り当てられている IP + Port のセットで一意となる GCP リソースです。

NEG を作成する NEG Controller

NEG の生成を担当するのは NEG Controller です。

GKE の Master 上で稼働しています。

NEG Controllerある条件 に合致した Service が存在していた場合に、NEG を生成します。

NEG が作成される Service には、アノテーションの設定が必要

Load Balancer は、 GKE (Google Kubernetes Engine) に Ingress を作成したときに自動的に作成されます。

NEG(+NE) は、GKE に Serivce を作成しても ある条件 を満たさないと作成されません。

その条件というのは、Service に cloud.google.com/neg から始まる アノテーション を付けるというものです。

apiVersion: v1
kind: Service
metadata:
  name: my-svc
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
spec:
  type: ClusterIP
  selector:
    app: rails
  ports:
    - port: 80
      protocol: TCP
      targetPort: 3000

NEG Controller がアノテーションの存在を検知して、 NEG を生成してくれます。 ( おそらくですが、 NEG Controller はアノテーションが設定されていても Ingress が存在していなければ NEG を作らない )

GCP 上の NEG を確認したい

GCP 上に NEG が存在するかどうかは gcloud compute network-endpoint-groups list コマンドを実行することで確認できます。 ( kubectl コマンドではないことに注意。 )

試しに所有している GKE プロジェクトに対して実行してみた結果が以下になります。

# 一部情報をマスキングしています
$ gcloud compute network-endpoint-groups list
NAME                                                        LOCATION           ENDPOINT_TYPE   SIZE
k8s1-2c946***-***************************-3000-088839b7     asia-northeast1-b  GCE_VM_IP_PORT  2
k8s1-2c946***-****************************-80-9f8f12d7      asia-northeast1-b  GCE_VM_IP_PORT  2
k8s1-2c946***-******************************-4000-b93a9e5e  asia-northeast1-b  GCE_VM_IP_PORT  2

NEG がグルーピングしている NE を確認するためには gcloud compute network-endpoint-groups list-network-endpoints コマンドを実行します。

$ gcloud compute network-endpoint-groups list-network-endpoints k8s1-2c946***-***************************-3000-088839b7
INSTANCE                                            IP_ADDRESS  PORT  FQDN
gke-***************************-pool-9b7f1a43-245c  10.124.0.5  3000
gke-***************************-pool-9b7f1a43-pnsc  10.124.1.5  3000

間違いなく、3000 ポートで稼働している Pod が 2 つ存在していて、そちらを指し示しています。

Service リソースにアノテーションを設定したつもりがないが設定されている?

所有 GKE クラスタに NEG が存在していることを確認できました。

しかし気になることが。 僕は Service リソースにアノテーションを設定した記憶がありません。

ところが、既存の Service リソースを確認すると確かにアノテーションが設定されています。

# アノテーションが設定されている
$ kubectl describe svc neg-exist-cluster | grep neg
Annotations:              cloud.google.com/neg: {"ingress":true}
                          cloud.google.com/neg-status:

では他のクラスタは?と確認してみたところ、他の GKE クラスタにはアノテーションが設定されていません。

# 何も表示されない
$ kubectl describe svc neg-not-exist-cluster | grep neg

アノテーションが設定される条件があるはずと思って調べてみると、GKE のドキュメントに記載がありました。

コンテナ ネイティブの負荷分散は、次のすべての条件が満たされている場合、デフォルトで Services に対して有効になります。

  • GKE クラスタ 1.17.6-gke.7 以降で作成された Service の場合
  • VPC ネイティブ クラスタの使用
  • 共有 VPC を使用しない
  • GKE ネットワーク ポリシーを使用しない

これらの条件では、Service 内に Pod IP をミラーリングするために
NEG が作成する必要があることを示す cloud.google.com/neg: '{"ingress": true}' が Service に自動的に付けられます。

GKE 1.17.6-gke.7+ より前のバージョンで作成された既存の Service には、
サービス コントローラによって自動的にアノテーションが付けられないことに注意してください。

確かに、NEG が生成されているクラスタは v1.18.17-gke.700 で構築したものであり、 生成されていないクラスタは v1.16 のときに Service リソースを生成しています。

NEG を使うべきかどうかについては、 NEG の利用が推奨されているようです。

NEG がデフォルトではないクラスタの場合でも、
コンテナ ネイティブの負荷分散を使用することを強くおすすめしますが、
Service ごとに明示的に有効にする必要があります。

NEG を利用すると何がよいの?

Service リソース生成時にデフォルトで生成される NEG ですが、どんなメリットが有るのでしょう?

Ingress リソースを生成したときに GCP に作られる Load Balancer が Container Native Load Balancing という手法で Pod に対してリクエストを分散するようになります。

従来の負荷分散では、 Instance Group (IG) を利用していました。IG を利用した場合のネットワークパケットは以下のような経路で転送されます。

                           +- VM Instance --------+
    +-------------+        |+--------+     +---+  |
--> |Load Balancer|---+--> ||iptables|--+->|Pod|+ |
    +-------------+   |    |+--------+  |  +---+|+|
                      |    |            |   +---+||
                      |    |            |    +---+|
                      |    +------------+---------+
                      |                 |
                      |    +- VM Instance --------+
                      |    |+--------+  |  +---+  |
                      +--> ||iptables|  +->|Pod|+ |
                      |    |+--------+     +---+|+|
                      |    |                +---+||
                      |    |                 +---+|
                      |    +----------------------+
                      |
                      |    +- VM Instance --------+
                      +--> |                      |
                           +----------------------+
  1. Load Balancer へリクエスト
  2. Load Balancer から VM Instance ( K8S Node ) に対して転送
  3. K8S Node 内の iptables で Pod へ転送

ここで、 3 の転送処理は自分の Node 内で稼働している Pod だけでなく、 別 Node の Pod へも転送される可能性がある という点に注意が必要です。

iptables をホップする分、また別 Node の Pod へも転送される可能性がある分、ロスがあります。

NEG を利用した Container Native Load Balancing 方式の場合、以下のようになります。

                           +- VM Instance --------+
    +-------------+        |               +---+  |
--> |Load Balancer|---+--> | ------------->|Pod|+ |
    +-------------+   |    |               +---+|+|
                      |    |                +---+||
                      |    |                 +---+|
                      |    +----------------------+
                      |
                      |    +- VM Instance --------+
                      |    |               +---+  |
                      +--> | ------------->|Pod|+ |
                      |    |               +---+|+|
                      |    |                +---+||
                      |    |                 +---+|
                      |    +----------------------+
                      |
                      |    +- VM Instance --------+
                      +--> |                      |
                           +----------------------+

(どういう仕組なのかわかりませんが) Load Balancer は直接 Pod の持つ IP:Port に対してパケットを転送します。

転送効率が良いというわけです。

ひとこと

なかなか長い文章をまとめるのが得意ではないのでわかりにくい、誤りなどあるかと思います。
誤りあれば、お気軽にご指摘ください。

2021-07-21GCP,Kubernetes