Antrea v1.9.0 - Multi-clusterのPod間接続

# はじめに
Antrea CNIはデータプレーンとしてOpen vSwitchを利用するCNIです。[Antrea v1.5.0](https://github.com/antrea-io/antrea/releases/tag/v1.5.0)からAntrea Multi-clusterがサポートされました。Antrea Multi-clusterはLeaderクラスターとMemberクラスターで構成され、Memberクラスターで作成されたServiceリソースに対してServiceExportを作成すると、他のMenberクラスターからServiceを参照することが可能になります。Antrea v1.5.0はマルチクラスター環境のデータプレーンには関与していませんでした。(参考 : [Antrea Multi-clusterを試してみる](https://qiita.com/masanara/items/ee4f731823c06b70f18e))

[Antrea v1.9.0](https://github.com/antrea-io/antrea/releases/tag/v1.9.0)から、マルチクラスター環境のデータプレンに対応し、Geneveを利用したPod間接続([Support Pod to Pod connectivity in Antrea Multi-cluster](https://github.com/antrea-io/antrea/pull/4219))がサポートされたので試してみます。

# クラスターの作成

今回は、kubeadmを使って以下の３つ異なるネットワークに３つのクラスターを作成してあります。Pod同士が直接通信するため、各クラスターのPod CIDRは重複しないようクラスターを構成する必要があります。

```bash
kubeadm init --pod-network-cidr=10.244.0.0/20 --service-cidr=10.96.0.0/20
kubeadm init --pod-network-cidr=10.244.16.0/20 --service-cidr=10.96.16.0/20
kubeadm init --pod-network-cidr=10.244.32.0/20 --service-cidr=10.96.32.0/20
```

```bash
$ kubectl --kubeconfig=cl1.yaml get node -o wide
NAME         STATUS   ROLES           AGE    VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
cl1-master   Ready    control-plane   169m   v1.25.3   192.168.150.101   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl1-node1    Ready    <none>          167m   v1.25.3   192.168.150.102   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl1-node2    Ready    <none>          167m   v1.25.3   192.168.150.103   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9

$ kubectl --kubeconfig=cl2.yaml get node -o wide
NAME         STATUS   ROLES           AGE    VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
cl2-master   Ready    control-plane   156m   v1.25.3   192.168.200.101   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl2-node1    Ready    <none>          155m   v1.25.3   192.168.200.102   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl2-node2    Ready    <none>          155m   v1.25.3   192.168.200.103   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9

$ kubectl --kubeconfig=cl3.yaml get node -o wide
NAME         STATUS   ROLES           AGE    VERSION   INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
cl3-master   Ready    control-plane   152m   v1.25.3   192.168.210.101   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl3-node1    Ready    <none>          151m   v1.25.3   192.168.210.102   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
cl3-node2    Ready    <none>          151m   v1.25.3   192.168.210.103   <none>        Ubuntu 20.04.5 LTS   5.4.0-131-generic   containerd://1.5.9
```

# Antrea Multi-clusterの構成

## Antreaのインストール

すべてのクラスターでAntreaをインストールします。Multi-cluster機能を有効にするため、インストール用のマニフェストをダウンロードして、ConfigMapのantrea-agent.conの内容を修正します。

```bash
wget https://github.com/antrea-io/antrea/releases/download/v1.9.0/antrea.yml
```
変更部分は`featureGates`で`Multicluster: true`と設定する点と、`multicluster`の設定として`enable: true`とする点です。(v1.6.0ではPod間接続がサポートされていなかったため、`trafficEncapMode`を`noEncap`に変更する必要がありましたが、v1.9.0からはPod関接続がサポートされたため、カプセル化を無効化する必要はありません。)

```patch
--- antrea.yml  2022-10-21 12:36:16.000000000 +0000
+++ antrea.yml.new      2022-10-26 07:49:08.052317477 +0000
@@ -2891,7 +2891,7 @@ data:

     # Enable Antrea Multi-cluster Gateway to support cross-cluster traffic.
     # This feature is supported only with encap mode.
-    #  Multicluster: false
+      Multicluster: true

     # Enable support for provisioning secondary network interfaces for Pods (using
     # Pod annotations). At the moment, Antrea can only create secondary network
@@ -3138,7 +3138,7 @@ data:
     multicluster:
     # Enable Antrea Multi-cluster Gateway to support cross-cluster traffic.
     # This feature is supported only with encap mode.
-      enable: false
+      enable: true
     # The Namespace where Antrea Multi-cluster Controller is running.
     # The default is antrea-agent's Namespace.
       namespace: ""
```

編集したマニフェストにより各クラスターにAntreaをインストールします。

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

Antreaが構成されるとkube-systemネームスペースで、antrea-controllerとantrea-agentが起動します。

```bash
$ kubectl get pod -n kube-system -l app=antrea
NAME                                   READY   STATUS    RESTARTS      AGE
antrea-agent-6rb4b                     2/2     Running   0             93m
antrea-agent-j9dw4                     2/2     Running   0             93m
antrea-agent-rpm2n                     2/2     Running   0             93m
antrea-controller-6b76f4549-lgf2q      1/1     Running   0             92m
```

## Multi-Clusterの構成

v1.6.0では、Multi-cluster環境で利用するantrea-mc-controllerを[マニフェストでインストール](https://qiita.com/masanara/items/ee4f731823c06b70f18e#leader%E3%81%AE%E8%A8%AD%E5%AE%9A)していましたが、v1.7.0から`antctl mc`コマンドで`deploy`サブコマンドを利用してantrea-mc-controllerのデプロイが可能になっています。([Antctl Multi-cluster commands](https://github.com/antrea-io/antrea/blob/v1.7.0/docs/multicluster/antctl.md))

```bash
$ antctl mc deploy
Deploy Antrea Multi-cluster Controller to a leader or member cluster

Usage:
  antctl mc deploy [command]

Available Commands:
  leadercluster Deploy Antrea Multi-cluster to a leader cluster
  membercluster Deploy Antrea Multi-cluster to a member cluster
```

今回は`cl1`をLeader兼Member、`cl2`と`cl3`をMemberとして構成します。

### Leaderの構成

`antrea-multicluster`ネームスペースを作成し、Leaderクラスター向けの`antrea-mc-controller`をインストールします。

```bash
kubectl config use-context cl1
kubectl create namespace antrea-multicluster
antctl mc deploy leadercluster -n antrea-multicluster --antrea-version v1.9.0
```

`antrea-multicluster`ネームスペースにantrea-mc-controller podが作成されます。

```bash
$ kubectl get pod -n antrea-multicluster
NAME                                    READY   STATUS    RESTARTS   AGE
antrea-mc-controller-7c87745c84-nfjxk   1/1     Running   0          1h34m
```

LeaderをMemberとして利用するために、`kube-system`ネームスペースに`antrea-mc-controller`をインストールします。

```bash
antctl mc deploy membercluster -n kube-system --antrea-version v1.9.0
```

`antrea-mc-controller`が`kube-system`と`antrea-multicluster`に作成されます。

```bash
$ kubectl get all -A -l="component=antrea-mc-controller"
NAMESPACE             NAME                                        READY   STATUS    RESTARTS   AGE
antrea-multicluster   pod/antrea-mc-controller-7c87745c84-nfjxk   1/1     Running   0          6h38m
kube-system           pod/antrea-mc-controller-d474cd945-wn5cq    1/1     Running   0          5h46m

NAMESPACE             NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
antrea-multicluster   deployment.apps/antrea-mc-controller   1/1     1            1           6h38m
kube-system           deployment.apps/antrea-mc-controller   1/1     1            1           6h35m

NAMESPACE             NAME                                              DESIRED   CURRENT   READY   AGE
antrea-multicluster   replicaset.apps/antrea-mc-controller-7c87745c84   1         1         1       6h38m
kube-system           replicaset.apps/antrea-mc-controller-d474cd945    1         1         1       6h35m
```

### ClusterSetの初期化

`antctl mc init`コマンドでClusterSetリソースを初期化し、MemberがClusterSetに参加するためのtokenを`join-config.yml`として生成します。

```
antctl mc init --clusterset demo-clusterset --clusterid demo-cluster-leader \
-n antrea-multicluster --create-token -j join-config.yml
```

`join-config.yaml`は`ClusterSetJoinConfig`と`Secret`で構成されており、にはLeaderに登録するための`leaderAPIServer`、`ca.crt`、`token`等が含まれています。

```yaml
---
apiVersion: multicluster.antrea.io/v1alpha1
kind: ClusterSetJoinConfig
clusterSetID: demo-clusterset
leaderClusterID: demo-cluster-leader
leaderNamespace: antrea-multicluster
leaderAPIServer: https://192.168.150.101:6443
#clusterID: ""
#namespace: ""
# Use the pre-created token Secret.
#tokenSecretName: ""
# Create a token Secret with the manifest file.
#tokenSecretFile: ""
# Manifest to create a Secret for an Antrea Multi-cluster  member cluster token.
---
apiVersion: v1
data:
  ca.crt: LS0tLS1...
  namespace: YW50cmVhLW11bHRpY2x1c3Rlcg==
  token: ZXlKaGJHY...
kind: Secret
metadata:
  creationTimestamp: null
  name: default-member-token
  namespace: antrea-multicluster
type: Opaque
```

### Memberとしての参加

生成した`join-config.yaml`を利用してMemberとしてClusterSetに登録します。

```bash
antctl mc join --clusterid demo-cluster-leader -n kube-system --config-file join-config.yml
```

Joinに成功すると`ClsuterSet`のMemberとしてクラスターが登録されます。

```bash
$ kubectl --kubeconfig=cl1.yaml get clusterset -n antrea-multicluster demo-clusterset -o=jsonpath='{.spec.members}' | jq
[
  {
    "clusterID": "demo-cluster-leader"
  }
]
```

### Gatewayの設定

ノードにAnnotationを設定することで、クラスター間の通信に利用するGatewayノードを指定します。

```bash
kubectl annotate node cl1-node1 multicluster.antrea.io/gateway=true
```

Anntationを設定したNodeが他クラスターと通信するためのGatewayとして構成されます。

```bash
$ kubectl get gateway -n kube-system
NAME        GATEWAY IP        INTERNAL IP       AGE
cl1-node1   192.168.150.102   192.168.150.102   3s
```

## Memberの構成

`kube-system`ネームスペースにMemberクラスター用の`antrea-mc-controller`をインストールします。

```bash
kubectl config use-context cl2
antctl mc deploy membercluster -n kube-system --antrea-version v1.9.0
```

Memberクラスター(demo-cluster-member2)として、LeaderのClusterSetにjoinします。

```bash
antctl mc join --clusterid demo-cluster-member2 -n kube-system --config-file join-config.yml
```

Joinに成功するとLeaderクラスターのClsuterSetのMemberとしてクラスターが登録されます。

```bash
$ kubectl --kubeconfig=cl1.yaml get clusterset -n antrea-multicluster demo-clusterset -o=jsonpath='{.spec.members}' | jq
[
  {
    "clusterID": "demo-cluster-leader"
  },
  {
    "clusterID": "demo-cluster-member2"
  }
]
```

Memverクラスター(demo-cluster-member3)もClusterSetにjoinします。

```bash
kubectl config use-context cl3
antctl mc join --clusterid demo-cluster-member3 -n kube-system --config-file join-config.yml
```

Joinに成功するとLeaderクラスターのClsuterSetのMemberとしてクラスターが追加されます。

```bash
$ kubectl --kubeconfig=cl1.yaml get clusterset -n antrea-multicluster demo-clusterset -o=jsonpath='{.spec.members}' | jq
[
  {
    "clusterID": "demo-cluster-leader"
  },
  {
    "clusterID": "demo-cluster-member2"
  },
  {
    "clusterID": "demo-cluster-member3"
  }
]
```

ここまでで、cl1をLeaderとしてcl1、cl2、cl3がMemberとして登録されました。cl2、cl3のnode1にGatewayノード用のAnnotationを設定します。

```bash
kubectl --kubeconfig=cl2.yaml annotate node cl2-node1 multicluster.antrea.io/gateway=true
kubectl --kubeconfig=cl3.yaml annotate node cl3-node1 multicluster.antrea.io/gateway=true
```


## Pod CIDRの設定

各クラスターの`antrea-mc-controller-config`ConfigMapを編集し、`controller_manager_config.yaml`として`podCIDRs`に各クラスターのPod CIDRを指定します。各クラスターの`antrea-mc-controller`を再起動することで、クラスターを跨いだPod間通信が可能になります。

```bash
kubectl --kubeconfig=cl1.yaml edit configmap -n kube-system antrea-mc-controller-config
```

```yaml
apiVersion: v1
data:
  controller_manager_config.yaml: |
    apiVersion: multicluster.crd.antrea.io/v1alpha1
    kind: MultiClusterConfig
    health:
      healthProbeBindAddress: :8080
    metrics:
      bindAddress: "0"
    webhook:
      port: 9443
    leaderElection:
      leaderElect: false
    serviceCIDR: ""
    podCIDRs:
      - "10.244.0.0/20"
    ...
```

cl2、cl3に関してもConfigMapを編集して`podCIDRs`を`10.244.16.0/20`、`10.244.16.0/20`として指定します。

```bash
kubectl --kubeconfig=cl2.yaml edit configmap -n kube-system antrea-mc-controller-config
kubectl --kubeconfig=cl3.yaml edit configmap -n kube-system antrea-mc-controller-config
```

## Pod間通信の確認

各クラスターでテスト用のPodを起動します。

```bash
$ kubectl --kubeconfig=cl1.yaml get pod -n demo -owide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
cl1-demo-7cb744444c-k5f6r   1/1     Running   0          92s   10.244.1.2   cl1-node1   <none>           <none>
$ kubectl --kubeconfig=cl2.yaml get pod -n demo -owide
NAME                        READY   STATUS    RESTARTS   AGE    IP            NODE        NOMINATED NODE   READINESS GATES
cl2-demo-7544655b8b-kh6tk   1/1     Running   0          107s   10.244.18.3   cl2-node2   <none>           <none>
$ kubectl --kubeconfig=cl3.yaml get pod -n demo -owide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
cl3-demo-7c47b7fc97-fjrhn   1/1     Running   0          99s   10.244.33.2   cl3-node1   <none>           <none>
```

cl1-demo-7cb744444c-k5f6r(10.244.2.2)から、cl2-demo-7544655b8b-kh6tk(10.244.18.3)、cl3-demo-7c47b7fc97-fjrhn(10.244.33.2)にpingで疎通確認してみます。

cl1のPodからcl2、cl3のPodに直接通信できています。

```bash
$ kubectl --kubeconfig=cl1.yaml exec -it -n demo cl1-demo-7cb744444c-k5f6r -- bash
bash-5.0# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP
    link/ether 7e:d5:fa:0c:35:80 brd ff:ff:ff:ff:ff:ff
    inet 10.244.1.2/24 brd 10.244.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::7cd5:faff:fe0c:3580/64 scope link
       valid_lft forever preferred_lft forever

bash-5.0# ping -c 3 10.244.18.3
PING 10.244.18.3 (10.244.18.3): 56 data bytes
64 bytes from 10.244.18.3: seq=0 ttl=61 time=0.952 ms
64 bytes from 10.244.18.3: seq=1 ttl=61 time=0.821 ms
64 bytes from 10.244.18.3: seq=2 ttl=61 time=1.066 ms
--- 10.244.18.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.821/0.946/1.066 ms

bash-5.0# ping -c 3 10.244.33.2
PING 10.244.33.2 (10.244.33.2): 56 data bytes
64 bytes from 10.244.33.2: seq=0 ttl=62 time=1.449 ms
64 bytes from 10.244.33.2: seq=1 ttl=62 time=1.096 ms
64 bytes from 10.244.33.2: seq=2 ttl=62 time=0.669 ms
--- 10.244.33.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.669/1.071/1.449 ms
```

## cl1上のPodからcl2上のPodに対する通信の詳細

icmp通信のパケットをキャプチャして、クラスターを跨いだPod間通信がどのように行われているか見てみます。

- 送信元 : `cl1-demo-7cb744444c-k5f6r(10.244.1.2)`は`cl1-node1(192.168.150.102)`で実行されています。
- 送信先 : `cl2-demo-7544655b8b-kh6t(10.244.18.3)`は`cl2-node2(192.168.200.103)`で実行されています。


```bash
$ kubectl --kubeconfig=cl1.yaml get pod -n demo -owide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
cl1-demo-7cb744444c-k5f6r   1/1     Running   0          92s   10.244.1.2   cl1-node1   <none>           <none>

$ kubectl --kubeconfig=cl2.yaml get pod -n demo -owide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
cl2-demo-7544655b8b-kh6tk   1/1     Running   0          17m   10.244.18.3   cl2-node2   <none>           <none>
```

送信元Podが起動している`cl1-node1(192.168.150.102)`でパケットをキャプチャすると、Geneveでカプセル化されたecho-requestが`cl2-node1(192.168.200.102)`宛に送信されており、Geneveでカプセル化される前のオリジナルのパケットの送信元は`192.168.150.102(cl1-node1)`にNATされ、送信先は`10.244.18.3(cl2-demo-7544655b8b-kh6t)`のままとなっています。

```
root@cl1-node1:~# tcpdump -e -n -i ens160 udp | grep -i geneve

21:36:06.644924 00:50:56:ae:c4:69 > 00:50:56:ae:08:c2, ethertype IPv4 (0x0800), length 148: 192.168.150.102.46728 > 192.168.200.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 36:c4:92:4c:a3:9e > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 192.168.150.102 > 10.244.18.3: ICMP echo request, id 22016, seq 
0, length 64
21:36:06.646375 00:50:56:ae:08:c2 > 00:50:56:ae:c4:69, ethertype IPv4 (0x0800), length 148: 192.168.200.102.26391 > 192.168.150.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 6e:c0:4b:8c:53:e2 > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 10.244.18.3 > 192.168.150.102: ICMP echo reply, id 22016, seq 0, length 64
```

送信先となっている`cl2-node1(192.168.200.102)`はcl2の`Gateway`として機能しています。

```bash
$ kubectl --kubeconfig=cl2.yaml get gateway -n kube-system
NAME        GATEWAY IP        INTERNAL IP       AGE
cl2-node1   192.168.200.102   192.168.200.102   7h42m
```

cl2の`Gateway`である`cl2-node1(192.168.200.102)`でパケットをキャプチャすると、echo-requestは`cl1-node1(192.168.150.102)`から着信したあと、`cl2-node2(192.168.200.103)`に転送されています。

```bash
root@cl2-node1:~# tcpdump -e -n -i ens160 udp | grep -i geneve

21:36:06.649105 00:50:56:ae:d1:58 > 00:50:56:ae:23:04, ethertype IPv4 (0x0800), length 148: 192.168.150.102.46728 > 192.168.200.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 36:c4:92:4c:a3:9e > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 192.168.150.102 > 10.244.18.3: ICMP echo request, id 22016, seq 
0, length 64
21:36:06.649400 00:50:56:ae:23:04 > 00:50:56:ae:16:a5, ethertype IPv4 (0x0800), length 148: 192.168.200.102.26391 > 192.168.200.103.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 6e:c0:4b:8c:53:e2 > aa:bb:cc:dd:ee:ff, ethertype IPv4 (0x0800), length 98: 192.168.150.102 > 10.244.18.3: ICMP echo request, id 22016, seq 
0, length 64
21:36:06.649995 00:50:56:ae:16:a5 > 00:50:56:ae:23:04, ethertype IPv4 (0x0800), length 148: 192.168.200.103.14356 > 192.168.200.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 72:f6:49:90:53:e8 > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 10.244.18.3 > 192.168.150.102: ICMP echo reply, id 22016, seq 0, length 64
21:36:06.650114 00:50:56:ae:23:04 > 00:50:56:ae:d1:58, ethertype IPv4 (0x0800), length 148: 192.168.200.102.26391 > 192.168.150.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 6e:c0:4b:8c:53:e2 > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 10.244.18.3 > 192.168.150.102: ICMP echo reply, id 22016, seq 0, length 64
```

`cl2-node2(192.168.200.103)`に着信したecho-requestは宛先Podである`cl2-demo-7544655b8b-kh6tk(10.244.18.3)`に送信され、送信元となっている`cl1-node1(192.168.150.102)`宛にecho-replayを返しています。

```bash
root@cl2-node2:~# tcpdump -e -n -i ens160 udp | grep -i geneve

21:36:06.644299 00:50:56:ae:23:04 > 00:50:56:ae:16:a5, ethertype IPv4 (0x0800), length 148: 192.168.200.102.26391 > 192.168.200.103.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 6e:c0:4b:8c:53:e2 > aa:bb:cc:dd:ee:ff, ethertype IPv4 (0x0800), length 98: 192.168.150.102 > 10.244.18.3: ICMP echo request, id 22016, seq 
0, length 64
21:36:06.644734 00:50:56:ae:16:a5 > 00:50:56:ae:23:04, ethertype IPv4 (0x0800), length 148: 192.168.200.103.14356 > 192.168.200.102.6081: Geneve, Flags [none], vni 
0x0, proto TEB (0x6558): 72:f6:49:90:53:e8 > aa:bb:cc:dd:ee:f0, ethertype IPv4 (0x0800), length 98: 10.244.18.3 > 192.168.150.102: ICMP echo reply, id 22016, seq 0, length 64
```

クラスターまたぎのPod間通信は、送信元NodeでNATされた後、Geneveトンネルでクラスター外のGatewayノードに送信され、Gatewayノードは受信したパケットを、Geneveトンネルでクラスター内ので宛先Podが存在するノードに送信しています。図にするとこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27668/9ac80940-77fe-3431-41bd-59cdbdea692c.png)

送信元ノード(cl1-node1)のantrea-agentでOVSのFlow Tableを確認すると、cl2(10.244.16.0/20)とcl3(10.244.32.0/20)を送信先とする通信に関してはカプセル化するルール(`NXM_NX_TUN_IPV4_DST`)があるようです。

```bash
$ kubectl --kubeconfig=cl1.yaml get pod -o wide -n kube-system -l component=antrea-agent | grep cl1-node1
antrea-agent-6rb4b   2/2     Running   4 (64m ago)   9h    192.168.150.102   cl1-node1    <none>           <none>

$ kubectl --kubeconfig cl1.yaml exec -it antrea-agent-6rb4b -n kube-system -- bash

root@cl1-node1:/# ovs-ofctl dump-flows br-int | grep -i tun | grep 10.244.16.0/20
 cookie=0x1060000000000, duration=6976.970s, table=16, n_packets=6032, n_bytes=591136, idle_age=79, priority=200,ip,nw_dst=10.244.16.0/20 actions=mod_dl_src:36:c4:92:4c:a3:9e,mod_dl_dst:aa:bb:cc:dd:ee:f0,load:0xc0a8c866->NXM_NX_TUN_IPV4_DST[],load:0x1->NXM_NX_REG0[4..7],resubmit(,18)
root@cl1-node1:/# ovs-ofctl dump-flows br-int | grep -i tun | grep 10.244.32.0/20
 cookie=0x1060000000000, duration=6982.103s, table=16, n_packets=69, n_bytes=6762, idle_age=0, priority=200,ip,nw_dst=10.244.32.0/20 actions=mod_dl_src:36:c4:92:4c:a3:9e,mod_dl_dst:aa:bb:cc:dd:ee:f0,load:0xc0a8d266->NXM_NX_TUN_IPV4_DST[],load:0x1->NXM_NX_REG0[4..7],resubmit(,18)
```

# まとめ

Antrea Multi-clusterで、Geneveを利用したクラスター間のPod間通信ができるようになりました。ノードネットワークでPod CIDRのルーティングを構成せずに、Pod間通が可能になるため、`ServiceExport`リソースでサービスをクラスター間で共有し、クラスターを跨いでServiceリソースを利用することが可能になります。