Standalone NEGを使ってマルチリージョン構成を組んでみる

#はじめに
Standalone NEGを使ってマルチリージョン構成を組んでみます。

##確認した構成

東京：1.17.12-gke.1504
大阪：1.17.12-gke.1504
VPC ネイティブ クラスタが前提となるため、GKEのバージョンは1.16.4 以降である必要があります。

#NEGの種類について
GCPでは、Network Eendpoint Group (NEG) を使用しGCPの各種リソースをグループ化し、ロードバランサーのバックエンドとして使用することができます。
NEGは、以下の３つの種類があります。

- Zonal network endpoint groups
- Internet network endpoint groups
- Serverless network endpoint groups

https://cloud.google.com/load-balancing/docs/negs

Zonal NEGは、GCEのインスタンス、GKEのPodをNetwork Endpointとして追加することができます。
https://cloud.google.com/load-balancing/docs/negs/zonal-neg-concepts

Internet NEGは、Googleの外部でホストされるサービスのFQDNまたは、IPアドレス＋ポートをNetwork Endpointとして追加することができます。
https://cloud.google.com/load-balancing/docs/negs/internet-neg-concepts

Serverless NEGは、Cloud Run fully managed、App EngineとCloud FunctionsをNetwork Endpointとして追加することができます。

https://cloud.google.com/load-balancing/docs/negs/serverless-neg-concepts

GKEの観点では、Ingressとして使用する場合とそれ以外で呼び方が変わります。
Ingressとして使用しない場合は、Standalone NEGという呼び名になります。
https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg

IngressでZonal NEGを使うということは、Container-native load balancingを使うということになります。
https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing

IngressとStandalone NEGとの違いは、自動作成してくれる範囲が異なります。Ingressを使ったZonal NEGは、kubernetesのyamlファイルを記載すると、FrontendやBackend設定含めてHTTP(S) LB作成と、BackendにZonal NEGを紐付けてくれますが、Standalone NEGの場合は、FrontendやBackend設定含めてLB作成と、BackendにZonal NEGの紐付けを手動で実施する必要があります。KubernetesのDeploymentとServiceを作成すると、Zonal NEGのみ作成されます。手動で作成するということは、GKEでサポートされていない構成を実装することができるといことになります。
例えば、今回実施するGlobal TCP LBを使用した構成などです。GKEでServiceをType: LoadBalancerとして作成した場合は、Global TCP LBではなく、Regional TCP LBが作成されます。
管理する範囲については、以下の様にマニュアルに記載があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/dcfc96c6-3800-4aad-6743-7f4c8b36e8ea.png)

NEGの種類について整理すると以下になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/5ecab3c4-f691-41f2-9063-04bd47541b8b.png)

#やってみる
手順としては、以下を参考にHTTPS LBをTCP LBに変えて実施します。
https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg

構成としては、以下になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/cc817001-dad5-8b6b-c0f5-4624b5897c18.png)

##東京リージョン側の構築
GKEクラスタを作成します。
Deploymentを作成します。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: k8s.gcr.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname
```

Serviceを作成します。cloud.google.com/negのアノテーションを指定するとPodがNework Endpointとなり、NEGに追加されます。
Container-native load balancingの場合のアノテーションは、
cloud.google.com/neg: '{"ingress": true}'になります。

```
apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"80":{}}}'
spec:
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
```

NEGの名称を確認します。

```
gcloud compute network-endpoint-groups list
```
Network Endpointを確認します。

```
gcloud compute network-endpoint-groups list-network-endpoints network-endpoint-groups-name
```

Global TCP LBを作成します。

ヘルスチェック用のFirewall ruleを作成します。

```
gcloud compute firewall-rules create fw-allow-health-check-and-proxy \
  --network=network-name \
  --action=allow \
  --direction=ingress \
  --target-tags=gke-node-network-tags \
  --source-ranges=130.211.0.0/22,35.191.0.0/16 \
  --rules=tcp:9376
```

Health checkを作成します。

```
gcloud compute health-checks create http http-basic-check \
  --use-serving-port
```

Backend serviceを作成します。

```
gcloud compute backend-services create my-tcp-lb \
    --global-health-checks \
    --global \
    --protocol TCP \
    --health-checks http-basic-check \
    --timeout 5m \
    --port-name http
```

Backend serviceにStandalone NEGを追加します。

```
gcloud compute backend-services add-backend my-tcp-lb \
    --global \
    --network-endpoint-group=network-endpoint-groups-name \
    --network-endpoint-group-zone=zone-name \
    --balancing-mode CONNECTION \
    --max-connections-per-endpoint 1
```

TCP Proxyを設定します。

```
gcloud compute target-tcp-proxies create my-tcp-lb-target-proxy \
    --backend-service my-tcp-lb \
    --proxy-header NONE
```

Forwarding ruleを作成します。Frontendが作成されます。

```
gcloud beta compute forwarding-rules create my-tcp-lb-ipv4-forwarding-rule \
    --global \
    --target-tcp-proxy my-tcp-lb-target-proxy \
    --ports 443
```

##大阪リージョン側の構築
東京リージョンと同様にDeploymentとServiceを作成します。
Global TCP LBは、同じものを使用するので、Backendのみ作成し、大阪リージョンのStandalone NEGをBackendに追加します。

#確認

Network Endpoint Groupの確認

```
$ gcloud compute network-endpoint-groups list
NAME                                                             LOCATION           ENDPOINT_TYPE   SIZE
k8s1-9bb3eb5e-default-neg-demo-svc-80-d7f43583                   asia-northeast1-a  GCE_VM_IP_PORT  1
k8s1-3425f258-default-neg-demo-svc-80-1e88ad69                   asia-northeast2-a  GCE_VM_IP_PORT  1
```

東京のNetwork Endpointの確認

```
$ gcloud compute network-endpoint-groups list-network-endpoints k8s1-9bb3eb5e-default-neg-demo-svc-80-d7f43583 --zone asia-northeast1-a
INSTANCE                            IP_ADDRESS  PORT  FQDN
gke-asm-default-pool-5455ab9f-l2w3  10.224.2.7  9376
```

Podを確認する

```
$ k get pod -o wide
neg-demo-app-85c645dbf-p9xll         1/1     Running   0          31h    10.224.2.7    gke-asm-default-pool-5455ab9f-l2w3   <none>           1/1
```

大阪のNetwork Endpointの確認

```
$ gcloud compute network-endpoint-groups list-network-endpoints k8s1-3425f258-default-neg-demo-svc-80-1e88ad69 --zone asia-northeast2-a
INSTANCE                              IP_ADDRESS   PORT  FQDN
gke-osaka-default-pool-cda75410-q966  10.116.0.15  9376
```

Podを確認する

```
$ k get pod -o wide
NAME                             READY   STATUS    RESTARTS   AGE    IP            NODE                                   NOMINATED NODE   READINESS GATES
neg-demo-app-85c645dbf-2kbj2     1/1     Running   0          32h    10.116.0.15   gke-osaka-default-pool-cda75410-q966   <none>           <none>
```

東京リージョンのインスタンスから、LBのIPアドレスにcurlを実施する。東京リージョンのBackendであるPodにルーティングされている。

```
$ curl X.X.X.X:443 
neg-demo-app-85c645dbf-p9xll
```

大阪リージョンのインスタンスから、LBのIPアドレスにcurlを実施する。大阪リージョンのBackendであるPodにルーティングされている。

```
$ curl X.X.X.X:443 
neg-demo-app-85c645dbf-2kbj2
```

#落ち穂拾い
Global LBの負荷分散アルゴリズムは、Waterfall by Regionと呼ばれる特別な負荷分散アルゴリズムが使用されます。Global LB配下に２つのリージョンのBackendが設定されている場合、インスタンスとユーザー近接性、受信負荷、各ゾーンとリージョンのバックエンド容量を考慮し、リクエストの処理に最適なバックエンドを決定します。
詳細は以下を参照してください。
https://cloud.google.com/solutions/about-capacity-optimization-with-global-lb