Red Hat OpenShift on IBM Cloud(VPC):Service(type:LoadBalancer)で private/zoneアノテーションを試してみる

#はじめに
Red Hat OpenShift on IBM CloudのVPC環境にて、`type:LoadBalancer`のサービスを作成する際のアノテーションを試してみた。利用環境としては、マルチゾーンで構成されており、それぞれのゾーンごとに2台ずつのWorker nodeが配置されています。

```shell-session:ノード
$ ibmcloud oc worker ls --cluster myroksclustervpc --show-pools
OK
ID                                                       Primary IP     Flavor     State    Status   Zone         Version                 Worker Pool
kube-bru1t0nd075uqsfusee0-myroksclust-default-00000147   10.240.0.5     bx2.4x16   normal   Ready    us-south-1   4.3.25_1527_openshift   default
kube-bru1t0nd075uqsfusee0-myroksclust-default-0000026a   10.240.0.4     bx2.4x16   normal   Ready    us-south-1   4.3.25_1527_openshift   default
kube-bru1t0nd075uqsfusee0-myroksclust-default-00000399   10.240.128.5   bx2.4x16   normal   Ready    us-south-3   4.3.25_1527_openshift   default
kube-bru1t0nd075uqsfusee0-myroksclust-default-00000410   10.240.128.4   bx2.4x16   normal   Ready    us-south-3   4.3.25_1527_openshift   default
kube-bru1t0nd075uqsfusee0-myroksclust-default-00000586   10.240.64.5    bx2.4x16   normal   Ready    us-south-2   4.3.25_1527_openshift   default
kube-bru1t0nd075uqsfusee0-myroksclust-default-000006a5   10.240.64.4    bx2.4x16   normal   Ready    us-south-2   4.3.25_1527_openshift   default
```

https://cloud.ibm.com/docs/openshift?topic=openshift-vpc-lbaas#setup_vpc_ks_vpc_lb

※2020/07/04現在、`service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: "proxy-protocol"`は利用できません。これは現在開発中の機能であり、ドキュメントのミスのようです。

#Private指定

- アノテーションを指定しない場合はデフォルトでPublic NW側に公開される。
- `service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: <public_or_private>`はPublic NW側に公開するLoadBalancerなのか、Private NW側に公開するLoadBalancerなのかを指定できる。今回は明示的にPrivate指定をしてみる。

```yaml:hello-world-private.yaml
apiVersion: v1
kind: Service
metadata:
 name: hello-world-private
 namespace: syasuda
 annotations:
   service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private
spec:
 type: LoadBalancer
 selector:
   deploymentconfig: hello-world
 ports:
  - name: http
    protocol: TCP
    port: 8080
    targetPort: 8080

```


```
$ oc apply -f hello-world-private.yaml

$ oc get service hello-world-private
NAME                  TYPE           CLUSTER-IP     EXTERNAL-IP                            PORT(S)          AGE
hello-world-private   LoadBalancer   172.21.34.34   xxxxxxxx-us-south.lb.appdomain.cloud   8080:31711/TCP   6m9s

$ oc describe service hello-world-private
Name:                     hello-world-private
Namespace:                syasuda
Labels:                   <none>
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{"service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type":"private"}...
                          service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private
Selector:                 deploymentconfig=hello-world
Type:                     LoadBalancer
IP:                       172.21.34.34
LoadBalancer Ingress:     xxxxxxxx-us-south.lb.appdomain.cloud
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31711/TCP
Endpoints:                172.17.111.9:8080,172.17.115.137:8080,172.17.123.76:8080 + 2 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type     Reason                           Age                    From                Message
  ----     ------                           ----                   ----                -------
  Warning  CreatingCloudLoadBalancerFailed  4m35s (x5 over 5m48s)  ibm-cloud-provider  Error on cloud load balancer kube-bru1t0nd075uqsfusee0-408ea2409e574f6fbe066a0120aa6efc for service syasuda/hello-world-private with UID 408ea240-9e57-4f6f-be06-6a0120aa6efc: LoadBalancer is busy: offline/create_pending
  Warning  SyncLoadBalancerFailed           4m35s (x5 over 5m48s)  service-controller  Error syncing load balancer: failed to ensure load balancer: Error on cloud load balancer kube-bru1t0nd075uqsfusee0-408ea2409e574f6fbe066a0120aa6efc for service syasuda/hello-world-private with UID 408ea240-9e57-4f6f-be06-6a0120aa6efc: LoadBalancer is busy: offline/create_pending
  Normal   EnsuringLoadBalancer             3m15s (x7 over 6m12s)  service-controller  Ensuring load balancer
  Normal   EnsuredLoadBalancer              3m5s (x2 over 5m57s)   service-controller  Ensured load balancer
```

確かに割り当てられたこのFQDNを名前解決するとPrivate IPになる。
また、VPC Load Balancerインスタンスは異なるZoneに配置されていることが、そのIPアドレス情報からわかる。

```
$ dig b3160ca4-us-south.lb.appdomain.cloud
dig A +noall +answer @1.1.1.1 xxxxxxxx-us-south.lb.appdomain.cloud
xxxxxxxx-us-south.lb.appdomain.cloud. 120 IN A	10.240.64.10
xxxxxxxx-us-south.lb.appdomain.cloud. 120 IN A	10.240.128.9
```

<img width="1279" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/19fdf493-5019-f019-a8d1-a9da997c595f.png">


#Private指定 + Zone指定

- 前節の結果からも分かるように、アノテーションを指定しない場合はデフォルトでVPC Load Balancerは複数Zoneにまたがって配置される。また、このVPC Load Balancerは任意のZoneにあるWorker Nodeに割り振る。
- `service.kubernetes.io/ibm-load-balancer-cloud-provider-zone: "<zone>"`を指定することで、特定のZoneにのみVPC Load Balancerを配置することが可能になる。また、このVPC Load Balancerの割り振り先対象もこの指定したWorker Nodeにのみになる。ただし、Worker NodeのNodePortは、他のZoneに存在するPodにも割り振りを行うことができる。
- [IBM Cloud docs](https://cloud.ibm.com/docs/openshift?topic=openshift-vpc-lbaas#setup_vpc_ks_vpc_lb)より引用
    - `The VPC load balancer is deployed to the same subnet in that zone that your worker nodes are connected to.`
    - `Only worker nodes in your cluster in this zone are configured to receive traffic from the VPC load balancer.`

```yaml:hello-world-us-south-1.yaml
apiVersion: v1
kind: Service
metadata:
 name: hello-world-us-south-1
 namespace: syasuda
 annotations:
   service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private
   service.kubernetes.io/ibm-load-balancer-cloud-provider-zone: "us-south-1"
spec:
 type: LoadBalancer
 selector:
   deploymentconfig: hello-world
 ports:
  - name: http
    protocol: TCP
    port: 8080
    targetPort: 8080
```




```
$ oc apply -f hello-world-us-south-1.yaml

$ oc get service hello-world-us-south-1
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP                            PORT(S)          AGE
hello-world-us-south-1   LoadBalancer   172.21.131.127   yyyyyyyy-us-south.lb.appdomain.cloud   8080:31333/TCP   6m35s

oc describe service hello-world-us-south-1
Name:                     hello-world-us-south-1
Namespace:                syasuda
Labels:                   <none>
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{"service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type":"private",...
                          service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private
                          service.kubernetes.io/ibm-load-balancer-cloud-provider-zone: us-south-1
Selector:                 deploymentconfig=hello-world
Type:                     LoadBalancer
IP:                       172.21.131.127
LoadBalancer Ingress:     yyyyyyyy-us-south.lb.appdomain.cloud
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31333/TCP
Endpoints:                172.17.111.9:8080,172.17.115.137:8080,172.17.123.76:8080 + 2 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                           Age    From                Message
  ----    ------                           ----   ----                -------
  Normal  EnsuringLoadBalancer             7m     service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer              6m49s  service-controller  Ensured load balancer
  Normal  CloudVPCLoadBalancerNormalEvent  51s    ibm-cloud-provider  Event on cloud load balancer hello-world-us-south-1 for service syasuda/hello-world-us-south-1 with UID ea8fd792-5d72-475a-b972-e836654aedc2: The VPC load balancer that routes requests to this Kubernetes LoadBalancer service is currently online/active.

$ oc get pods -o wide|grep -v Completed
NAME                   READY   STATUS      RESTARTS   AGE    IP               NODE           NOMINATED NODE   READINESS GATES
hello-world-1-45m9j    1/1     Running     0          3d2h   172.17.111.9     10.240.128.4   <none>           <none>
hello-world-1-b75zr    1/1     Running     0          3d2h   172.17.115.137   10.240.128.5   <none>           <none>
hello-world-1-gl5sd    1/1     Running     0          3d2h   172.17.74.18     10.240.64.5    <none>           <none>
hello-world-1-j8w4f    1/1     Running     0          3d2h   172.17.67.14     10.240.64.4    <none>           <none>
hello-world-1-rb8nt    1/1     Running     0          3d2h   172.17.123.76    10.240.0.4     <none>           <none>
```

上記のEndpoints情報の結果より、別Zoneに存在する複数のPodにも割り振りは行われている。
Private指定のアノテーションを付けているので、確かに割り当てられたこのFQDNを名前解決するとPrivate IPになっているが、その一方でその連続したIPアドレスからVPC Load Balancerインスタンスは同一Zoneに配置されていることが分かる。

```
$ dig A +noall +answer @1.1.1.1 yyyyyyyy-us-south.lb.appdomain.cloud
yyyyyyyy-us-south.lb.appdomain.cloud. 120 IN A	10.240.0.8
yyyyyyyy-us-south.lb.appdomain.cloud. 120 IN A	10.240.0.9
```


また、以下のように全6台（各Zoneに2台ずつ）Worker Nodeがあるにも関わらず、2台しか割り振り先対象になっていない。
<img width="1279" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/004e6d14-76fe-fc81-33d9-a2c04e4741b6.png">