NutaNice Xperience

主にNutanix製品を検証したり触ったりした結果をつづっています。※このブログの内容は個人の見識や見解をもとに作成しています。参考にされる場合は自己責任でご活用ください。実際に製品を使用される場合は、メーカードキュメントの手順に従い実施してください。

Flow Virtual Networkingでノード間のオーバーレイネットワーク通信を確認してみる【AOS 7.0 AHV 10.0/pc.2024.3】

※この記事は「AOS 7.0 AHV10.0 Prism Central pc.2024.3」時点の情報をもとに作成しています。その後の機能アップデートについてはメーカーの公開情報をご確認ください。

NutanixのFlow Virtual Networkingの連載については以下の記事にリンクをまとめています。ほかの記事にもこちらからアクセス可能です。

tomomartin.hateblo.jp

前回の記事では、FVNのVPCでオーバーレイネットワークを作成して、仮想マシンを接続してみました。今回は、オーバーレイネットワーク上に接続された仮想マシン間の通信を確認してみます。

目次

1.今回の環境

3ノードAHVクラスタ
AOS: 7.0.1
AHV: 10.0.1
Prism Central: pc.2024.3.1.1
仮想マシン: Windows Server 2022

環境は一般的なNutanixの3ノードクラスタです。Prism CentralはFVNで推奨となる3台構成としています。

▽Flow Virtual Networkingによる仮想プライベートクラウドのざっくりしたイメージ図は以下の通りです。クラスター上に独立した空間を作成して、そこにオーバーレイネットワークを作成する機能を指します。

2. オーバーレイネットワーク通信の仕組み

▽今回の環境は以下の通りで、先に答えを教えてしまうのですが、VPCのオーバーレイネットワークに接続したVM間での通信は、各ノードのbrAtlasブリッジのFlowルールでトンネル化され、「Geneveポート(ovn-xx)」の出口として設定されているAHVのbr0を経由してノード間で通信をやり取りしています。

3.「brAtlas」のFlowテーブル

VPCやオーバーレイネットワークを作成したあとは、brAtlasブリッジに多くのFlowテーブルが追加されます。すべてを理解しようとすると、1週間くらいかかりそうなので、関係がありそうな部分だけピックアップして紹介します。

▽確認するのは、以下の部分です。

まずは、VM01からの通信部分です。今回の環境では、VM01が接続している「tap」は「4番」のofports番号が付けられています。▽Flowテーブルのtable=0に「4番ポート」からの受信のルールがあるため、VM01からの通信を処理するテーブルが存在することが分かります。

[root@ahv-01 ~]# ovs-ofctl dump-flows brAtlas | grep table=0
~~~省略~~~
 cookie=0xded6f111, duration=18567.937s, table=0, n_packets=530698, n_bytes=41493777, idle_age=0, priority=100,in_port=4 actions=load:0x10->NXM_NX_REG13[0..15],load:0xf->NXM_NX_REG9,load:0x2712->OXM_OF_METADATA,load:0x2->NXM_NX_REG14,load:0->NXM_NX_REG13[16..31],resubmit(,8)
 cookie=0x0, duration=516332.336s, table=0, n_packets=13654904, n_bytes=1334706872, idle_age=0, hard_age=65534, priority=0 actions=drop

途中のルールをすべて確認するのはさすがに大変ですが、最終的にトンネル化されてgeneveポート(ovn-xx)である(2)から出力(output)するテーブル(table=39)が存在するため、geneveポートからトンネル通信をするルールが定義されていることが分かります。

[root@ahv-01 ~]# ovs-ofctl dump-flows brAtlas | grep output
 cookie=0x58f8f607, duration=331924.397s, table=39, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=100,reg13=0/0xffff0000,reg15=0x2,metadata=0x4 actions=load:0x4->NXM_NX_TUN_ID[0..23],set_field:0x2->tun_metadata0,move:NXM_NX_REG14[0..14]->NXM_NX_TUN_METADATA0[16..30],output:2,resubmit(,40)
 cookie=0x1f7b0080, duration=331924.397s, table=39, n_packets=230897, n_bytes=19556548, idle_age=658, hard_age=65534, priority=100,reg13=0/0xffff0000,reg15=0x3,metadata=0x4 actions=load:0x4->NXM_NX_TUN_ID[0..23],set_field:0x3->tun_metadata0,move:NXM_NX_REG14[0..14]->NXM_NX_TUN_METADATA0[16..30],output:2,resubmit(,40)
 cookie=0x208c3d0c, duration=20705.246s, table=39, n_packets=93633, n_bytes=103724295, idle_age=1, priority=100,reg13=0/0xffff0000,reg15=0x3,metadata=0x2712 actions=load:0x2712->NXM_NX_TUN_ID[0..23],set_field:0x3->tun_metadata0,move:NXM_NX_REG14[0..14]->NXM_NX_TUN_METADATA0[16..30],output:2,resubmit(,40)
 cookie=0xd999026b, duration=20705.246s, table=39, n_packets=18, n_bytes=9724, idle_age=20415, priority=100,reg15=0x8000,metadata=0x2712 actions=load:0->NXM_NX_REG6,load:0x1->NXM_NX_REG15,resubmit(,41),load:0x8000->NXM_NX_REG15,load:0x2712->NXM_NX_TUN_ID[0..23],set_field:0x8000->tun_metadata0,move:NXM_NX_REG14[0..14]->NXM_NX_TUN_METADATA0[16..30],output:2,resubmit(,40)
 cookie=0xe504fdf6, duration=20705.246s, table=39, n_packets=0, n_bytes=0, idle_age=20705, priority=100,reg15=0x8004,metadata=0x2712 actions=load:0->NXM_NX_REG6[],load:0x2712->NXM_NX_TUN_ID[0..23],set_field:0x8004->tun_metadata0,move:NXM_NX_REG14[0..14]->NXM_NX_TUN_METADATA0[16..30],output:2,resubmit(,40)

続いて、geneveポート(ovn-xx)(2)から受信のテーブルですが、こちらもtable=0に受信のルールが追加されています。トンネルIDやメタデータを操作している様子が見られます。

[root@ahv-01 ~]# ovs-ofctl dump-flows brAtlas | grep table=0
~~~省略~~~
cookie=0x0, duration=520485.249s, table=0, n_packets=360249, n_bytes=416974965, idle_age=0, hard_age=65534, priority=100,in_port=2 actions=move:NXM_NX_TUN_ID[0..23]->OXM_OF_METADATA[0..23],move:NXM_NX_TUN_METADATA0[16..30]->NXM_NX_REG14[0..14],move:NXM_NX_TUN_METADATA0[0..15]->NXM_NX_REG15[0..15],resubmit(,40)

また、こちらも途中のルールは飛ばしていますが、VM01が接続している「tap」の「4番」ポートに送信しているルール(table=65)も確認できます。

[root@ahv-01 ~]# ovs-ofctl dump-flows brAtlas | grep output
~~~省略~~~
cookie=0xded6f111, duration=22985.840s, table=65, n_packets=709629, n_bytes=1006603648, idle_age=0, priority=100,reg15=0x2,metadata=0x2712 actions=output:4

これらの事から、geneve(ovn-xx)ポートを経路としてトンネル通信をしているルールが入っていそうなことが分かります。ただし、これだけだとちょっとわかりにくいので、別の方法で確認してみたいと思います。

4. データパスフローの確認

AHVでは、OVSのOpenFlowをベースとしたFlowテーブルで、ブリッジの通信を細かく制御しています。それに対して、Flowテーブルのルールをもとに、パケットが処理される際に、頻繁に使用されるFlowをカーネル内でキャッシュしたり、入力ポートやMACアドレスIPアドレス、トンネル通信の情報などを参照したりすることができるコマンドがあります。

詳しい説明は難しいですが、カーネル空間で管理されているポート通信の部分をOVSのデータパスといった呼び方をするようです。(AIに聞いてみた)

▽データパスで管理されているポートを参照するコマンドは以下の通りです。AHVやCVM、仮想マシンのtap、またeth0といった物理インターフェースや各ブリッジのinternalポート、そして今回対象となる「geneve」ポートも確認できます。

[root@ahv-01 ~]# ovs-dpctl show
system@ovs-system:
  lookups: hit:5178242122 missed:36160422 lost:30
  flows: 297
  masks: hit:6089411899 total:21 hit/pkt:1.17
  cache: hit:4816049227 hit-rate:92.36%
  caches:
    masks-cache: size:256
  port 0: ovs-system (internal)
  port 1: br0 (internal)
  port 2: eth1
  port 3: eth0
  port 4: vxlan_sys_4789 (vxlan: packet_type=ptap)
  port 5: tap0
  port 6: vnet0
  port 7: vnet2
  port 8: br.microseg (internal)
  port 9: br.mx (internal)
  port 10: br.dmx (internal)
  port 11: br.nf (internal)
  port 12: tap1
  port 13: br0.local (internal)
  port 14: brSpan (internal)
  port 15: brAtlas (internal)
  port 16: genev_sys_6081 (geneve: packet_type=ptap)
  port 17: tap2

ちなみに、これらのポート番号が、OVSのインターフェースで確認できる「ofports」の番号と異なっているのは、OVSのデータパス(カーネルモジュール)で管理されている番号だから、みたいです。

では、ポート番号が分かったところでデータパスにキャッシュされているFlowルールを確認してみます。なお、ノード間のVM同士はお互いにpingを流し続けておきます。

▽以下コマンドの実行結果の通り、通信のキャッシュが3行確認できました。

[root@ahv-01 ~]# ovs-dpctl dump-flows | grep tunnel
recirc_id(0),tunnel(tun_id=0x2712,src=172.22.1.62,dst=172.22.1.61,geneve({class=0x102,type=0x80,len=4,0x30002/0x7fffffff}),flags(-df+csum+key)),in_port(16),ct_state(-new-rpl-trk),eth(src=50:6b:8d:0a:35:04,dst=50:6b:8d:45:5a:21),eth_type(0x0800),ipv4(proto=1,frag=no),icmp(type=8/0xf8), packets:15179, bytes:1123246, used:0.252s, actions:17
recirc_id(0),in_port(17),eth(src=50:6b:8d:45:5a:21,dst=50:6b:8d:0a:35:04),eth_type(0x0800),ipv4(src=192.168.10.33,dst=192.168.10.27,proto=1,tos=0/0x3,frag=no), packets:30367, bytes:2247246, used:0.196s, actions:set(tunnel(tun_id=0x2712,dst=172.22.1.62,ttl=64,tp_dst=6081,geneve({class=0x102,type=0x80,len=4,0x20003}),flags(df|csum|key))),16
recirc_id(0),tunnel(tun_id=0x2712,src=172.22.1.62,dst=172.22.1.61,geneve({class=0x102,type=0x80,len=4,0x30002/0x7fffffff}),flags(-df+csum+key)),in_port(16),ct_state(-new-rpl-trk),eth(src=50:6b:8d:0a:35:04,dst=50:6b:8d:45:5a:21),eth_type(0x0800),ipv4(proto=1,frag=no),icmp(type=0/0xfe), packets:15186, bytes:1123764, used:0.196s, actions:17

これを簡単に解説すると、

行①
入力ポートが16でトンネルID=0x2712
172.22.1.62: トンネルの送信元IP(AHV02)
172.22.1.61: トンネルの宛先IP(AHV01)
ct_state(-new-rpl-trk): 返信トラッキング
送信元MAC: 50:6b:8d:0a:35:04(AHV02のVM
宛先MAC: 50:6b:8d:45:5a:21(AHV01のVM
ICMPエコー要求(タイプ8)
ポート17に送信

行②
入力ポートが17
送信元MAC: 50:6b:8d:45:5a:21(AHV01のVM
宛先MAC: 50:6b:8d:0a:35:04(AHV02のVM
送信元IP: 192.168.10.33(AHV01のVM
宛先IP: 192.168.10.27(AHV02のVM
プロトコル: ICMP
actions=set(tunnel(...)):
トンネル設定を行い、ポート16に送信
tun_id=0x2712: トンネルID
dst=172.22.1.62: トンネルの宛先IP
tp_dst=6081: Geneveポート番号

行③
入力ポートが16でトンネルID=0x2712
src=172.22.1.62: トンネルの送信元IP(AHV02)
dst=172.22.1.61: トンネルの宛先IP(AHV01)
ct_state(-new-rpl-trk): 返信トラッキング
送信元MAC: 50:6b:8d:0a:35:04(AHV02のVM
宛先MAC: 50:6b:8d:45:5a:21(AHV01のVM
ICMPエコー返信(タイプ0)
ポート17に送信

行①と行③については、AHV02のVMからのエコー要求と返信、行②については、AHV01のVMからの、エコー要求の通信のようですね。ポートも16番(geneveポート)と17番(tap2-VM)でやり取りしていることが分かります。

今回AHV03とのトンネル通信が確認できないのは、AHV#0上に仮想マシンを作成していないので、通信も発生していないためです。

これで、仮想マシンがリモートのAHVのgeneveポートとトンネル通信をしている様子が確認できました。ついでにパケットのキャプチャもしてみたいと思います。

5. geneve通信の出入口となるAHVのbr0の確認

ここまで、オーバーレイネットワークの仮想マシン同士が、geneveでトンネル接続してノード間で通信していることはわかりました。ただ、この通信がほんとにAHVのbr0を流れているのか確認してみます

▽確認のポイントはこちらです。

AHVはRocky Linuxをベースとしおり、「tcpdump」が使えるので「br0」のネットワークトラフィックをキャプチャしてみます。Flow Virtual Networkingにおけるノード間のトンネル通信は、一般的なgeneve通信と同様に6081/UDPポートを使用しています。

tcpdumpで確認した結果は以下の通りです。VM間でping疎通確認している最中の内容です。

[root@ahv-01 ~]# tcpdump -i br0 udp port 6081
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:25:10.420166 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo reply, id 1, seq 17384, length 40
13:25:10.475939 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo request, id 1, seq 17387, length 40
13:25:10.476136 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo reply, id 1, seq 17387, length 40
13:25:11.435557 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo request, id 1, seq 17385, length 40
13:25:11.435775 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo reply, id 1, seq 17385, length 40
13:25:11.491579 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo request, id 1, seq 17388, length 40
13:25:11.491809 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo reply, id 1, seq 17388, length 40
13:25:12.451223 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo request, id 1, seq 17386, length 40
13:25:12.451434 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo reply, id 1, seq 17386, length 40
13:25:12.507220 IP ahv01-ntnx.test.nip > 172.22.1.62.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.33 > 192.168.10.27: ICMP echo request, id 1, seq 17389, length 40
13:25:12.507479 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo reply, id 1, seq 17389, length 40
13:25:13.466800 IP 172.22.1.62.51169 > ahv01-ntnx.test.geneve: Geneve, Flags [C], vni 0x2712, options [8 bytes]: IP 192.168.10.27 > 192.168.10.33: ICMP echo request, id 1, seq 17387, length 40

br0経由でgeneve通信が流れていますね。

以上です。

次回は、NATの外部接続のあたりを確認してみたいと思います。