Antrea Multi-clusterを試してみる

## はじめに

[Tanzu Community Edition(TCE)](https://tanzucommunityedition.io/)Antrea Multi-clusterを試してみました。Antrea Multi-clusterは[KEP-1645: Multi-Cluster Services API](https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api)に対するAntreaの実装です。

## Antrea Multi-clusterのアーキテクチャ

Antrea Multi-clusterはLeaderクラスターとMemberクラスターで構成され、Memberクラスターで作成されたServiceリソースに対してServiceExportを作成すると、LeaderクラスターにServiceリソースがExportされ、他のMemberクラスターはLeaderクラスターにExportされたServiceリソースをImportします。LeaderクラスターはMemberと併用可能ですが、必ず一つのクラスターがLeaderとして機能する必要があります。(参考 : [Antrea Multi-cluster Architecture](https://antrea.io/docs/v1.6.0/docs/multicluster/architecture/))

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/af8a1e58-2b3a-ff94-84ab-b824eab18ccf.png)


## TCEクラスターの作成

TCE v0.11.0に含まれるAntreaは[v1.2.3](https://github.com/antrea-io/antrea/releases/tag/v1.2.3)ですが、Antrea Multi-clusterは[v1.5.0](https://github.com/antrea-io/antrea/releases/tag/v1.5.0)からサポートされた機能であるためTCEがインストールするAntreaの環境では利用できません。今回は最新のAntrea [v1.6.0](https://github.com/antrea-io/antrea/releases/tag/v1.6.0)を利用するため、TCEクラスター作成時のCNI構成を無効化し、手動でAntreaをインストールします。また、現在のMulti-cluster機能は各クラスター間のServiceリソースの同期のみ行い、データプレーンには関与しません。クラスターを跨るPod間通信を可能にするため、Antreaのカプセル化とSNAT機能を無効化し[Routable Pod構成](https://qiita.com/masanara/items/551f51939ac5a2ecb9f0)として以下のような3つのTCEクラスターを作成し、cl1をLeader専用、cl2/cl3をMemberとして構成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/c39429bd-24d5-81fd-1b5b-bf55c6fdc77b.png)

TCEで3つのクラスターを作成します。TCEクラスター作成時のCNIの構成を無効化するために`CNI=none`として、各クラスターには異なるAPIのIPアドレス(`VSPHERE_CONTROL_PLANE_ENDPOINT`)と、Pod/Service向けCIDR(`CLUSTER_CIDR`、`SERVICE_CIDR`)を割り当てます。

```bash
CNI=none VSPHERE_CONTROL_PLANE_ENDPOINT=192.168.201.104 CLUSTER_CIDR=172.16.0.0/21 SERVICE_CIDR=172.16.64.0/21 tanzu cl create cl1 -f tce.yaml
```

クラスターの作成が開始され、CNIの構成が完了しないためクラスターの作成がWaiting状態になります。

```
Validating configuration...
Creating workload cluster 'cl1'...
Waiting for cluster to be initialized...
cluster control plane is still being initialized: WaitingForControlPlane
cluster control plane is still being initialized: Cloning @ Machine/cl1-control-plane-kwmvx
cluster control plane is still being initialized: PoweringOn @ Machine/cl1-control-plane-kwmvx
Waiting for cluster nodes to be available...
Waiting for addons installation...
Waiting for packages to be up and running...
```

tanzu CLIで確認すると、`creating`状態となっています。(このまましばらく放置すると`tancu cl create`コマンドはタイムアウトで終了し、`createStalled`という状態になりますが、CNIを正しく設定することでクラスターは`running`状態となり正常に機能します。)

```bash
$ tanzu cl list
  NAME   NAMESPACE  STATUS    CONTROLPLANE  WORKERS  KUBERNETES        ROLES   PLAN
  cl1    default    creating  0/1           1/1      v1.22.8+vmware.1  <none>  dev
```

作成中のクラスターにCNIを手動でインストールするため、tanzu CLIでクラスターのクレデンシャルを取得して、接続先として設定します。

```bash
$ tanzu cl kubeconfig get cl1 --admin
Credentials of cluster 'cl1' have been saved
You can now access the cluster by running 'kubectl config use-context cl1-admin@cl1'
$ kubectl config use-context cl1-admin@cl1
```

クラスターのノードの状態を確認すると、`NotReady`状態となっています。

```bash
$ kubectl get node
NAME                      STATUS     ROLES                  AGE   VERSION
cl1-control-plane-9thmg   NotReady   control-plane,master   49s   v1.22.8+vmware.1

$ kubectl get node
NAME                        STATUS     ROLES                  AGE   VERSION
cl1-control-plane-9thmg     NotReady   control-plane,master   57s   v1.22.8+vmware.1
cl1-md-0-7c57d8d6d4-cbbj9   NotReady   <none>                 4s    v1.22.8+vmware.1
```

Podの状態を確認するとcorednsが`Pending`状態となっています。

```bash
$ kubectl get pod -A
NAMESPACE     NAME                                                     READY   STATUS    RESTARTS   AGE
kube-system   coredns-67c8559bb6-89hmw                                 0/1     Pending   0          51s
kube-system   coredns-67c8559bb6-x9fbg                                 0/1     Pending   0          51s
kube-system   etcd-cl1-control-plane-9thmg                             1/1     Running   0          59s
kube-system   kube-apiserver-cl1-control-plane-9thmg                   1/1     Running   0          58s
kube-system   kube-controller-manager-cl1-control-plane-9thmg          1/1     Running   0          59s
kube-system   kube-proxy-94l4n                                         1/1     Running   0          14s
kube-system   kube-proxy-cnd49                                         1/1     Running   0          52s
kube-system   kube-scheduler-cl1-control-plane-9thmg                   1/1     Running   0          59s
kube-system   kube-vip-cl1-control-plane-9thmg                         1/1     Running   0          66s
tkg-system    kapp-controller-6ccc86f49f-9l8gk                         1/1     Running   0          34s
tkg-system    tanzu-capabilities-controller-manager-798c4d557c-pw46l   0/1     Pending   0          51s
```

ノードのIPアドレスと各ノードの`podCIDR`を確認して、ルータにスタティックルートを追加します。

```bash
$ kubectl get node -o wide
NAME                        STATUS     ROLES                  AGE   VERSION            INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                 KERNEL-VERSION   CONTAINER-RUNTIME
cl1-control-plane-9thmg     NotReady   control-plane,master   87s   v1.22.8+vmware.1   192.168.201.63   <none>        VMware Photon OS/Linux   4.19.232-2.ph3   containerd://1.5.9
cl1-md-0-7c57d8d6d4-cbbj9   NotReady   <none>                 34s   v1.22.8+vmware.1   192.168.201.8    <none>        VMware Photon OS/Linux   4.19.232-2.ph3   containerd://1.5.9

$ kubectl get node -o=jsonpath='{range .items[*]}{.spec.podCIDR}{"\n"}{end}'
172.16.0.0/24
172.16.1.0/24
```

[antreaをインストールするためのマニフェスト](https://github.com/antrea-io/antrea/releases/download/v1.6.0/antrea.yml)のantrea-config ConfigMapの`antrea-agent.conf`に含まれる`trafficEncapMode`を`noEncap`、`noSNAT`を`true`として設定し、Podのすべての通信をEncap/SNATしないように設定します。

```shell
--- antrea.yml.org      2022-03-29 16:57:20.000000000 +0000
+++ antrea.yml  2022-05-09 00:01:39.071536349 +0000
@@ -2811,14 +2811,14 @@
     # networkPolicyOnly: Antrea enforces NetworkPolicy only, and utilizes CNI chaining and delegates Pod
     #                    IPAM and connectivity to the primary CNI.
     #
-    #trafficEncapMode: encap
+    trafficEncapMode: noEncap

     # Whether or not to SNAT (using the Node IP) the egress traffic from a Pod to the external network.
     # This option is for the noEncap traffic mode only, and the default value is false. In the noEncap
     # mode, if the cluster's Pod CIDR is reachable from the external network, then the Pod traffic to
     # the external network needs not be SNAT'd. In the networkPolicyOnly mode, antrea-agent never
     # performs SNAT and this option will be ignored; for other modes it must be set to false.
-    #noSNAT: false
+    noSNAT: true

     # Tunnel protocols used for encapsulating traffic across Nodes. If WireGuard is enabled in trafficEncryptionMode,
     # this option will not take effect. Supported values:
```

変更したマニフェストをapplyしてCNIを構成します。

```bash
$ kubectl apply -f antrea.yaml
```

しばらくすると、antreaが有効になり、corednsがRunning状態となります。

```bash
$ kubectl get po -A
NAMESPACE      NAME                                                     READY   STATUS    RESTARTS      AGE
kube-system    antrea-agent-vhvmv                                       2/2     Running   0             40s
kube-system    antrea-agent-wfhls                                       2/2     Running   0             40s
kube-system    antrea-controller-6b69877d5c-9qxh4                       1/1     Running   0             40s
kube-system    coredns-67c8559bb6-8l5z9                                 1/1     Running   0             3m4s
kube-system    coredns-67c8559bb6-m69fg                                 1/1     Running   0             3m4s
kube-system    etcd-cl1-control-plane-9thmg                             1/1     Running   0             3m12s
kube-system    kube-apiserver-cl1-control-plane-9thmg                   1/1     Running   0             3m11s
kube-system    kube-controller-manager-cl1-control-plane-9thmg          1/1     Running   0             3m12s
kube-system    kube-proxy-66gpx                                         1/1     Running   0             2m27s
kube-system    kube-proxy-fg79m                                         1/1     Running   0             3m5s
kube-system    kube-scheduler-cl1-control-plane-9thmg                   1/1     Running   0             3m12s
kube-system    kube-vip-cl1-control-plane-9thmg                         1/1     Running   0             3m19s
kube-system    metrics-server-5766bd59cb-jv6gs                          1/1     Running   0             49s
kube-system    vsphere-cloud-controller-manager-t2b58                   1/1     Running   0             76s
kube-system    vsphere-csi-controller-5c4c85bbff-6rjkr                  6/6     Running   0             47s
kube-system    vsphere-csi-node-qmdm2                                   3/3     Running   2 (23s ago)   47s
kube-system    vsphere-csi-node-wvwfg                                   3/3     Running   2 (24s ago)   47s
tanzu-system   secretgen-controller-555567956c-bkcj7                    1/1     Running   0             75s
tkg-system     kapp-controller-55fb69d57d-gxk4k                         1/1     Running   0             2m47s
tkg-system     tanzu-capabilities-controller-manager-798c4d557c-bpqqp   1/1     Running   0             3m4s
```

同じ手順で2つのクラスターを追加して、各クラスターでCNIを構成します。

```bash
CNI=none VSPHERE_CONTROL_PLANE_ENDPOINT=192.168.201.105 CLUSTER_CIDR=172.16.8.0/21 SERVICE_CIDR=172.16.72.0/21 tanzu cl create cl2 -f tce.yaml
CNI=none VSPHERE_CONTROL_PLANE_ENDPOINT=192.168.201.106 CLUSTER_CIDR=172.16.16.0/21 SERVICE_CIDR=172.16.80.0/21 tanzu cl create cl3 -f tce.yaml
```

作成した3つのクラスターのkubeconfigをファイルに出力します。

```bash
tanzu cl kubeconfig get cl1 --admin --export-file cl1.yaml
tanzu cl kubeconfig get cl2 --admin --export-file cl2.yaml
tanzu cl kubeconfig get cl3 --admin --export-file cl3.yaml
```

## Leaderの設定

Leaderクラスターに対してLeader向けのCRDを作成します。

```bash
$ kubectl --kubeconfig=cl1.yaml apply -f https://github.com/antrea-io/antrea/releases/download/v1.6.0/antrea-multicluster-leader-global.yml
customresourcedefinition.apiextensions.k8s.io/clusterclaims.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/clustersets.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/memberclusterannounces.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceexportfilters.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceexports.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceimportfilters.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceimports.multicluster.crd.antrea.io created
```

Common Areaとなる`antrea-mcs-ns`ネームスペースを作成し、Leaderに必要なコンポーネントをデプロイします。Leader上のController(`deployment.apps/antrea-mc-controller`)は`antrea-mcs-ns`ネームスペースで起動します。githubで公開されているマニフェストではNamespaceが`changeme`となっているため、Common Areaとして利用するNamespace名(`antrea-mcs-ns`)に置換する必要があります。

```bash
$ kubectl --kubeconfig=cl1.yaml create ns antrea-mcs-ns
$ curl -L https://github.com/antrea-io/antrea/releases/download/v1.6.0/antrea-multicluster-leader-namespaced.yml > antrea-multicluster-leader-namespaced.yml
$ sed 's/changeme/antrea-mcs-ns/g' antrea-multicluster-leader-namespaced.yml | kubectl --kubeconfig=cl1 apply -f -
serviceaccount/antrea-mc-controller created
serviceaccount/antrea-mc-member-access-sa created
role.rbac.authorization.k8s.io/antrea-mc-controller-role created
role.rbac.authorization.k8s.io/antrea-mc-member-cluster-role created
clusterrole.rbac.authorization.k8s.io/antrea-mcs-ns-antrea-mc-controller-webhook-role created
rolebinding.rbac.authorization.k8s.io/antrea-mc-controller-rolebinding created
rolebinding.rbac.authorization.k8s.io/antrea-mc-member-cluster-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/antrea-mcs-ns-antrea-mc-controller-webhook-rolebinding created
configmap/antrea-mc-controller-config-49875k4ht8 created
service/antrea-mc-webhook-service created
deployment.apps/antrea-mc-controller created
mutatingwebhookconfiguration.admissionregistration.k8s.io/antrea-mcs-ns-antrea-mc-mutating-webhook-configuration created
validatingwebhookconfiguration.admissionregistration.k8s.io/antrea-mcs-ns-antrea-mc-validating-webhook-configuration created
```

Member(cl2)がLeaderにアクセスする際にに利用するServiceAccountをLeaderクラスターのantrea-mcs-nsに作成します。

```bash
cat <<EOF | kubectl --kubeconfig=cl1.yaml apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: member-cl2-access-sa
  namespace: antrea-mcs-ns
---
apiVersion: v1
kind: Secret
metadata:
    name: member-cl2-access-token
    namespace: antrea-mcs-ns
    annotations:
      kubernetes.io/service-account.name: member-cl2-access-sa
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: member-cl2-access-rolebinding
  namespace: antrea-mcs-ns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: antrea-mc-member-cluster-role
subjects:
  - kind: ServiceAccount
    name: member-cl2-access-sa
    namespace: antrea-mcs-ns
EOF
```

Member(cl3)がLeaderにアクセスする際にに利用するServiceAccountをLeaderクラスターのantrea-mcs-nsに作成します。

```bash
cat <<EOF | kubectl --kubeconfig=cl1.yaml apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: member-cl3-access-sa
  namespace: antrea-mcs-ns
---
apiVersion: v1
kind: Secret
metadata:
    name: member-cl3-access-token
    namespace: antrea-mcs-ns
    annotations:
      kubernetes.io/service-account.name: member-cl3-access-sa
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: member-cl3-access-rolebinding
  namespace: antrea-mcs-ns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: antrea-mc-member-cluster-role
subjects:
  - kind: ServiceAccount
    name: member-cl3-access-sa
    namespace: antrea-mcs-ns
EOF
```

作成したServiceAccountの証明書とTokenをSecretをMemberのSecretとして利用できるようマニフェストを作成します。

```bash
kubectl --kubeconfig=cl1.yaml get secret member-cl2-access-token -n antrea-mcs-ns -o yaml | grep -w -e '^apiVersion' -e '^data' -e '^metadata' -e '^ *name:' -e '^kind' -e '  ca.crt' -e '  token:' -e '^type' -e '  namespace' | sed -e 's/kubernetes.io\/service-account-token/Opaque/g' -e 's/antrea-mcs-ns/kube-system/g' > cl2-token.yaml
```

```bash
kubectl --kubeconfig=cl1.yaml get secret member-cl3-access-token -n antrea-mcs-ns -o yaml | grep -w -e '^apiVersion' -e '^data' -e '^metadata' -e '^ *name:' -e '^kind' -e '  ca.crt' -e '  token:' -e '^type' -e '  namespace' | sed -e 's/kubernetes.io\/service-account-token/Opaque/g' -e 's/antrea-mcs-ns/kube-system/g' > cl3-token.yaml
```

LeaderにClusterSetリソースを定義します。Memberとしてcl2とcl3を設定します。

```bash
cat <<EOF | kubectl --kubeconfig cl1.yaml apply -f -
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterSet
metadata:
    name: test-clusterset
    namespace: antrea-mcs-ns
spec:
    leaders:
      - clusterID: test-cluster-cl1
    members:
      - clusterID: test-cluster-cl2
        serviceAccount: "member-cl2-access-sa"
      - clusterID: test-cluster-cl3
        serviceAccount: "member-cl3-access-sa"
    namespace: antrea-mcs-ns
EOF
```



## Memberの設定

Memberクラスター(cl2)でMulti-cluster CRDを作成しControllerを起動します。Controller(`deployment.apps/antrea-mc-controller`はkube-systemネームスペースで起動します。)

```bash
$ kubectl --kubeconfig=cl2.yaml apply -f https://github.com/antrea-io/antrea/releases/download/v1.6.0/antrea-multicluster-member.yml
customresourcedefinition.apiextensions.k8s.io/clusterclaims.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/clustersets.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceexportfilters.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/resourceimportfilters.multicluster.crd.antrea.io created
customresourcedefinition.apiextensions.k8s.io/serviceexports.multicluster.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/serviceimports.multicluster.x-k8s.io created
serviceaccount/antrea-mc-controller created
clusterrole.rbac.authorization.k8s.io/antrea-mc-controller-role created
clusterrolebinding.rbac.authorization.k8s.io/antrea-mc-controller-rolebinding created
configmap/antrea-mc-controller-config-49875k4ht8 created
service/antrea-mc-webhook-service created
deployment.apps/antrea-mc-controller created
mutatingwebhookconfiguration.admissionregistration.k8s.io/antrea-mc-mutating-webhook-configuration created
validatingwebhookconfiguration.admissionregistration.k8s.io/antrea-mc-validating-webhook-configuration created
```

Leaderに作成したSerivceAccountでアクセスするための証明書とTokenをMember(cl2)にSecretとして作成します。

```bash
kubectl --kubeconfig=cl2 apply -f claim-cl2.yaml
```

Memberクラスター(cl2)にClusterClaim/ClusterSetリソースを作成します。ClsuterSetの`.spec.leaders`にはLeaderの`clusterID`としてcl1を指定し、`server`としてcl1のAPIのアドレスを指定します。

```
cat <<EOF | kubectl --kubeconfig=cl2.yaml apply -f -
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterClaim
metadata:
  name: cl2-membercluster-id
  namespace: kube-system
name: id.k8s.io
value: test-cluster-cl2
---
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterClaim
metadata:
  name: clusterset-id
  namespace: kube-system
name: clusterSet.k8s.io
value: test-clusterset
---
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterSet
metadata:
    name: test-clusterset
    namespace: kube-system
spec:
    leaders:
      - clusterID: test-cluster-cl1
        secret: "member-cl2-access-token"
        server: "https://192.168.201.104:6443"
    members:
      - clusterID: test-cluster-cl2
    namespace: antrea-mcs-ns
EOF
```

cl3でもおなじ様に、Multi-cluster CRDを作成しControllerを起動し、Leaderにアクセスするための証明書とTokenをSecretとして作成します。

```bash
kubectl --kubeconfig=cl3.yaml apply -f https://github.com/antrea-io/antrea/releases/download/v1.6.0/antrea-multicluster-member.yml
kubectl --kubeconfig=cl3.yaml apply -f claim-cl3.yaml
```

Memberクラスター(cl3)にClusterClaim/ClusterSetリソースを作成します。

```bash
cat <<EOF | kubectl --kubeconfig=cl3.yaml apply -f -
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterClaim
metadata:
  name: cl3-membercluster-id
  namespace: kube-system
name: id.k8s.io
value: test-cluster-cl3
---
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterClaim
metadata:
  name: clusterset-id
  namespace: kube-system
name: clusterSet.k8s.io
value: test-clusterset
---
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ClusterSet
metadata:
    name: test-clusterset
    namespace: kube-system
spec:
    leaders:
      - clusterID: test-cluster-cl1
        secret: "member-cl3-access-token"
        server: "https://192.168.201.104:6443"
    members:
      - clusterID: test-cluster-cl3
    namespace: antrea-mcs-ns
EOF
```

## 動作確認

### クラスター間のService同期

cl2とcl3にdemoネームスペースを作成し、各ネームスペースにserver/client Deploymentを作成し、serverに対するServiceを作成します。

```bash
kubectl --kubeconfig=cl2.yaml create ns demo
kubectl --kubeconfig=cl2.yaml create deploy server --image=harbor.tce.example.com/library/demo:1.0 -n demo
kubectl --kubeconfig=cl2.yaml expose deploy/server --port=80 -n demo
```

cl2のdemoネームスペースでpodが起動して、serviceとendpointsが構成されています。

```bash
$ kubectl --kubeconfig=cl2.yaml get pod,svc,ep -o wide -n demo
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE                       NOMINATED NODE   READINESS GATES
pod/server-6d9cc85c58-fkttn   1/1     Running   0          22s   172.16.9.21   cl2-md-0-8ffdf64f7-dpwlv   <none>           <none>

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE    SELECTOR
service/server   ClusterIP   172.16.76.220   <none>        80/TCP    4m4s   app=server

NAME               ENDPOINTS        AGE
endpoints/server   172.16.9.21:80   4m4s
```

cl3のdemoネームスペースにclient用のpodを作成します。

```bash
kubectl --kubeconfig=cl3.yaml create ns demo
kubectl --kubeconfig=cl3.yaml create deploy client --image=harbor.tce.example.com/library/demo:1.0 -n demo
```

cl3でclient podが起動します。serviceとendpointは存在しません。

```bash
$ kubectl --kubeconfig=cl3.yaml get pod,svc,ep -o wide -n demo
NAME                          READY   STATUS    RESTARTS   AGE     IP             NODE                        NOMINATED NODE   READINESS GATES
pod/client-579678cd66-cwnfs   1/1     Running   0          2m23s   172.16.17.16   cl3-md-0-6f9c5f76db-8pghg   <none>           <none>
```

cl2で`ServiceExport`リソースを作成し、Service

```bash
cat <<EOF | kubectl --kubeconfig=cl2.yaml apply -f -
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
  name: server
  namespace: demo
EOF
```

cl2にServiceExportリソースが作成されます。

```bash
kubectl --kubeconfig=cl2.yaml get serviceexport -n demo
NAME     AGE
server   94s
```

cl2のSerivce/Endpontsリソースを確認すると`antrea-mc-server`が追加されています。

```bash
$ kubectl --kubeconfig=cl2.yaml get svc,ep -n demo
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/antrea-mc-server   ClusterIP   172.16.79.15    <none>        80/TCP    4m8s
service/server             ClusterIP   172.16.76.220   <none>        80/TCP    11m

NAME                         ENDPOINTS        AGE
endpoints/antrea-mc-server   <none>           4m8s
endpoints/server             172.16.9.21:80   11m
```

cl3のServiceリソースを確認すると`antrea-mc-server`が追加されています。

```bash
$ kubectl --kubeconfig=cl3.yaml get svc,ep -n demo
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/antrea-mc-server   ClusterIP   172.16.83.110   <none>        80/TCP    4m52s

NAME                         ENDPOINTS        AGE
endpoints/antrea-mc-server   172.16.9.21:80   4m52s
```

Leaderであるcl1にはResourceExport/ResourceImportリソースが作成されています。

```bash
$ kubectl --kubeconfig=cl1.yaml get resourceexport -n antrea-mcs-ns
NAME                                     AGE
test-cluster-cl2-demo-server-endpoints   5m48s
test-cluster-cl2-demo-server-service     5m48s
$ kubectl --kubeconfig=cl1.yaml get resourceimport -n antrea-mcs-ns
NAME                    AGE
demo-server-endpoints   5m54s
demo-server-service     5m55s
```

cl3のclient podからcl3上にImportされた、cl2のService(antrea-mc-server)にアクセスすると、cl2上で起動するpod(172.16.9.21)が応答しています。

```bash
$ kubectl --kubeconfig=cl3.yaml exec -it -n demo client-579678cd66-cwnfs -- curl antrea-mc-server
Server address: 172.16.9.21:80
Server name: server-6d9cc85c58-fkttn
Date: 09/May/2022:13:35:26 +0000
URI: /
Request ID: bd68749472215d86fddd1db5c7ec1854
```

### Serviceの更新

server podを1 > 3にスケールアウトして、ExportされるServiceに状態が反映されるかどうか確認してみます。

Pod数の増加に応じてローカルのEndpointsは更新されました。

```bash
$ kubectl --kubeconfig=cl2.yaml scale deploy server --replicas=3 -n demo
deployment.apps/server scaled

$ kubectl --kubeconfig=cl2.yaml get pod,svc,ep -n demo -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP             NODE                       NOMINATED NODE   READINESS GATES
pod/server-6d9cc85c58-5qx9s   1/1     Running   0          64s   172.16.9.10    cl2-md-0-8ffdf64f7-dpwlv   <none>           <none>
pod/server-6d9cc85c58-dnn7t   1/1     Running   0          64s   172.16.9.11    cl2-md-0-8ffdf64f7-dpwlv   <none>           <none>
pod/server-6d9cc85c58-fkttn   1/1     Running   0          23m   172.16.9.21    cl2-md-0-8ffdf64f7-dpwlv   <none>           <none>

NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/antrea-mc-server   ClusterIP   172.16.79.15    <none>        80/TCP    19m   <none>
service/server             ClusterIP   172.16.76.220   <none>        80/TCP    27m   app=server

NAME                         ENDPOINTS                                        AGE
endpoints/antrea-mc-server   <none>                                           19m
endpoints/server             172.16.9.10:80,172.16.9.11:80,172.16.9.21:80     27m
```

cl3にExportされたEndpointsもLeaderにより同期され、追加されたpodのIPアドレス(172.16.10.10, 172.16.10.11)が追加されています。

```bash
$ kubectl --kubeconfig=cl3.yaml get pod,svc,ep -n demo -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP             NODE                        NOMINATED NODE   READINESS GATES
pod/client-579678cd66-cwnfs   1/1     Running   0          25m   172.16.17.16   cl3-md-0-6f9c5f76db-8pghg   <none>           <none>

NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/antrea-mc-server   ClusterIP   172.16.83.110   <none>        80/TCP    20m   <none>

NAME                         ENDPOINTS                                        AGE
endpoints/antrea-mc-server   172.16.9.10:80,172.16.9.11:80,172.16.9.21:80     20m
```

cl3のclient podからcl3上にImportされた、cl2のService(antrea-mc-server)にアクセスすると、cl2上で起動するpod(172.16.10.10, 172.16.10.11,172.16.9.21,)が応答しています。

```bash
$ kubectl --kubeconfig=cl3.yaml exec -it -n demo client-579678cd66-cwnfs -- curl antrea-mc-server
Server address: 172.16.9.10:80
Server name: server-6d9cc85c58-5qx9s
Date: 09/May/2022:13:49:19 +0000
URI: /
Request ID: ce5084d98b6a24ec8903b047d3f38742
$ kubectl --kubeconfig=cl3.yaml exec -it -n demo client-579678cd66-cwnfs -- curl antrea-mc-server
Server address: 172.16.9.21:80
Server name: server-6d9cc85c58-fkttn
Date: 09/May/2022:13:49:20 +0000
URI: /
Request ID: d747993da6b3db3bd1e71a000a11c3e0
$ kubectl --kubeconfig=cl3.yaml exec -it -n demo client-579678cd66-cwnfs -- curl antrea-mc-server
Server address: 172.16.9.11:80
Server name: server-6d9cc85c58-dnn7t
Date: 09/May/2022:13:49:22 +0000
URI: /
Request ID: 7c7b3d4cb86de00b6497f5925dbba128
```

### Network Policyの同期

[v1.6.0](https://github.com/antrea-io/antrea/releases/tag/v1.6.0)からAntrea ClusterNetworkPolicy(ACNP)の同期もサポートしています。LeaderにResourceExportとしてACNPリソースを作成します。(ネームスペース内の通信と、`app: client2`というラベルが付いたpodからのみアクセスを許可するポリシーです。)

```bash
cat <<EOF | kubectl --kubeconfig=cl1.yaml apply -f -
apiVersion: multicluster.crd.antrea.io/v1alpha1
kind: ResourceExport
metadata:
  name: strict-namespace-isolation-for-test-clusterset
  namespace: antrea-mcs-ns
spec:
  kind: AntreaClusterNetworkPolicy
  name: strict-namespace-isolation
  clusternetworkpolicy:
    priority: 1
    tier: securityops
    appliedTo:
      - namespaceSelector: {}
    ingress:
      - action: Pass
        from:
        - namespaces:
            match: Self
        - podSelector:
            matchLabels:
              app: client2
      - action: Drop
        from:
        - namespaceSelector: {}
EOF
```

LeaderにResourceExportとResourceImportリソースが作成されます。

```bash
$ kubectl --kubeconfig=cl1.yaml get resourceexport -n antrea-mcs-ns
NAME                                             AGE
strict-namespace-isolation-for-test-clusterset   23s
test-cluster-cl2-demo-server-endpoints           25m
test-cluster-cl2-demo-server-service             25m
$ kubectl --kubeconfig=cl1.yaml get resourceimport -n antrea-mcs-ns
NAME                                                    AGE
demo-server-endpoints                                   25m
demo-server-service                                     25m
strict-namespace-isolation-antreaclusternetworkpolicy   35s
```

MemberでACNPを確認すると、各クラスターにリソースが追加されています。

```bash
$ kubectl --kubeconfig=cl2.yaml get acnp
NAME                                   TIER          PRIORITY   DESIRED NODES   CURRENT NODES   AGE
antrea-mc-strict-namespace-isolation   securityops   1          2               2               48s
$ kubectl --kubeconfig=cl3.yaml get acnp
NAME                                   TIER          PRIORITY   DESIRED NODES   CURRENT NODES   AGE
antrea-mc-strict-namespace-isolation   securityops   1          2               2               50s
```

cl2とcl3にdemo2ネームスペースを作成し、それぞれのネームスペースにclient2/client3 deploymentを作成します。

```bash
kubectl --kubeconfig=cl2.yaml create ns demo2
kubectl --kubeconfig=cl2.yaml create deploy client2 --image=harbor.tce.example.com/library/demo:1.0 -n demo2
kubectl --kubeconfig=cl2.yaml create deploy client3 --image=harbor.tce.example.com/library/demo:1.0 -n demo2
```

cl2とcl3でclietn2/clietn3 podが起動します。

```bash
$ kubectl --kubeconfig=cl2.yaml get pod -n demo2
NAME                       READY   STATUS    RESTARTS   AGE
client2-d95b6c978-zrcxf    1/1     Running   0          11s
client3-5f564bd4cc-6t57l   1/1     Running   0          10s
```

ACNPの制御によりcl2のclient2からはserverにアクセスができますが、client2からはアクセスすることができません。

```bash
$ kubectl --kubeconfig=cl2.yaml exec -it -n demo2 client2-d95b6c978-zrcxf -- curl server.demo
Server address: 172.16.9.21:80
Server name: server-6d9cc85c58-fkttn
Date: 09/May/2022:15:11:19 +0000
URI: /
Request ID: 8d77fbe42da9d94573331a9245c6e196
$ kubectl --kubeconfig=cl2.yaml exec -it -n demo2 client3-5f564bd4cc-6t57l -- curl server.demo
curl: (6) Could not resolve host: server.demo
command terminated with exit code 6
```

## まとめ

Antrea Multi-clusterを利用することでクラスター間のSerivceリソースを同期することができました。現在データプレーンには関与していませんが、[データプレーンのサポート](https://github.com/antrea-io/antrea/issues/3502)も検討されているようです。