TF虚拟网络流量排错:在正确的时刻使用正确的工具



  • 我们总是希望一切都能如期进行。不过残酷的现实是,大多数时候,总会出现问题。

    在排除网络故障时,第一个碰到的问题总是这个——“流量在哪里?”

    事情变得有点“复杂”

    对于虚拟网络来说,也依然如此!即使是在Tungsten Fabric集群内部,按理说,我们在故障排除环节的第一步,也会进行某种流量嗅探或流量识别。

    那么……和传统的物理网络有什么不同呢?从概念上讲,没有什么不同……但是,在实践当中,事情会更复杂一些。我们所说的复杂,并不是指难以理解无从下手。所谓复杂,是指有更多的变数在起作用,但这并不一定意味着故障排除会非常难。相反,从另一个角度来看,这意味着我们可以使用更多的工具来更好地了解网络中发生的事情。

    回到Tungsten Fabric集群,或者说是一般的虚拟环境,我们的目标是检查/监控进出某个虚拟机的流量。与传统的物理设备相比,如前所述,环境更加复杂。有了物理设备,就像有了一台一体化设备。但当移动到虚拟环境时,一体化设备就不存在了!你有物理服务器(计算)和连接到DC架构的NIC,你有管理程序,最后,你有虚拟机。虚拟机的流量会经过所有这些层次。在每个层次,都有工具可以用来检查/监控流量。这就是我说的“更复杂的场景”的意思,但也是为什么说,从另一个角度来看,这意味着有很多有用的武器可以满足我们的需求。

    因此,了解在每个层面可以使用哪些工具是很重要的。我们必须掌握复杂性,并利用它!

    让我们用一个例子来解决这个问题。例如有一个IP为192.168.10.3的虚拟机(VM1),这个虚拟机运行在compute1上。在compute2上,有另一个IP为192.168.10.4的虚拟机(VM2)。让我们从VM1 ping到VM2:

    $ ping 192.168.10.4
    PING 192.168.10.4 (192.168.10.4): 56 data bytes
    64 bytes from 192.168.10.4: seq=0 ttl=64 time=44.812 ms
    64 bytes from 192.168.10.4: seq=1 ttl=64 time=32.076 ms
    64 bytes from 192.168.10.4: seq=2 ttl=64 time=6.418 ms
    ^C
    --- 192.168.10.4 ping statistics ---
    3 packets transmitted, 3 packets received, 0% packet loss
    round-trip min/avg/max = 6.418/27.768/44.812 ms
    $
    

    Ping成功了!现在,我们要做的是识别和监控这些流量。

    三个层次,三套工具

    首先,正如之前预想的那样,我们必须掌握复杂性。在处理Tungsten Fabric集群时,我们可以识别3个层次:VNF,hypervisor和vRouter。

    107d6934-d6ff-4c1d-9314-c1302d8edb54-image.png

    现在来看一下整个流程。

    在VNF层面,我们拥有VNF提供的所有工具,可能有类似tcpdum的命令,或者如果VNF是一个防火墙,有一些东西可以查看流表内容。由于这个级别取决于厂商/VNF,这里不打算详细介绍。

    再来看看vRouter。vRouter是Tungsten Fabric解决方案的核心,它提供并管理计算节点内部的所有虚拟网络。此外,它还提供了一系列不错的工具,可以用来了解集群内的流量是如何流动的。

    要访问这套工具,首先要访问vRouter容器,通过连接到计算节点并使用知名的docker命令来实现。

    [root@compute1 ~]# docker ps | grep vrouter
    c1f441949cb5        hub.juniper.net/contrail/contrail-vrouter-agent:1911.31       "/entrypoint.sh /usr…"   8 days ago          Up 8 days                               vrouter_vrouter-agent_1
    4333e1b1cf92        hub.juniper.net/contrail/contrail-nodemgr:1911.31             "/entrypoint.sh /bin…"   8 days ago          Up 8 days                               vrouter_nodemgr_1
    [root@compute1 ~]# docker exec -it vrouter_vrouter-agent_1 bash
    (vrouter-agent)[root@compute1 /]$
    

    一旦我们进行到这里,就可以开始寻找流量了。

    首先,确定VM的虚拟机接口(vif)。通过使用“vif”实用程序,匹配端口IP地址(192.168.10.3)来实现:

    (vrouter-agent)[root@compute1 /]$ vif --list | grep -B 1 -A 1 192.168.10.3
    vif0/3      OS: tapcae84676-cb NH: 33
                Type:Virtual HWaddr:00:00:5e:00:01:00 IPaddr:192.168.10.3
                Vrf:2 Mcast Vrf:2 Flags:PL3L2DEr QOS:-1 Ref:6
    

    在这里,我们学到了很多有用的东西。

    接口属于Vrf 2,这是一种路由表索引。虚拟接口索引是3(vif0/3)。最后,对应的tap接口是tapcae84676-cb。

    下一个工具,我们可以使用rt。记住,我们要ping的是地址为192.168.10.4的远程虚拟机。

    (vrouter-agent)[root@compute1 /]$ rt --get 192.168.10.4/32 --vrf 2
    Match 192.168.10.4/32 in vRouter inet4 table 0/2/unicast
    
    Flags: L=Label Valid, P=Proxy ARP, T=Trap ARP, F=Flood ARP
    vRouter inet4 routing table 0/2/unicast
    Destination           PPL        Flags        Label         Nexthop    Stitched MAC(Index)
    192.168.10.4/32         0           LP         23             20        2:e7:fd:ee:27:78(148824)
    

    上面的输出结果告诉我们流量将被发送到下一跳20。此外,它还说到将使用标签23。这表明,为了到达目的地,流量将不得不离开计算节点,并被封装成一个MPLSoUDP(或MPLSoGRE)包。这个标签就是将要推送的服务标签。

    我们来检查下一跳:

    (vrouter-agent)[root@compute1 /]$ nh --get 20
    Id:20         Type:Tunnel         Fmly: AF_INET  Rid:0  Ref_cnt:6          Vrf:0
                  Flags:Valid, MPLSoUDP, Etree Root,
                  Oif:0 Len:14 Data:56 68 ac c3 28 02 56 68 ac c3 28 04 08 00
                  Sip:192.168.200.3 Dip:192.168.200.4
    

    正如想象的那样,下一跳是一个MPLSoUDP隧道,将流量从compute1(vhost0地址192.168.200.3)发送到compute2(vhost0地址192.168.200.4)。

    请注意输出中的“Vrf:0”字段,这告诉我们,流量将通过VRF 0发送出去(如前所述,VM在VRF 2中)。Vrf 0是“fabric network”,连接计算节点和underlay的网络。此外,发送到下一跳20的流量将通过接口0(Oif 0)发送到Vrf 0(如前所述)。Oif 0是连接计算和底层的物理接口接口:

    (vrouter-agent)[root@compute1 /]$ vif --get 0
    Vrouter Interface Table
    vif0/0      OS: ens3f1 (Speed 1000, Duplex 1) NH: 4
                Type:Physical HWaddr:56:68:ac:c3:28:04 IPaddr:0.0.0.0
                Vrf:0 Mcast Vrf:65535 Flags:TcL3L2VpEr QOS:-1 Ref:7
                RX packets:738213  bytes:47096090 errors:0
                TX packets:808295  bytes:42533041 errors:0
                Drops:30
    

    这是vhost0所在的接口。Oif 0会“看到”封装的数据包。

    综上所述,我们的虚拟机的流量属于VRF 2,在这个VRF里面,发生了查找(lookup)动作。在那里,通过Oif 0接口(物理接口),流量将被封装成MPLSoUDP数据包,然后发送到另一个计算节点。

    下一个有用的工具,是“流(flow)”。vRouter默认是基于流的(可以通过将vmis设置为数据包模式来选择性地禁用每个vmi的流模式)。

    让我们根据目的地来匹配流量:

    (vrouter-agent)[root@compute1 /]$ flow --match 192.168.10.4
    Listing flows matching ([192.168.10.4]:*)
    
        Index                Source:Port/Destination:Port                      Proto(V)
    -----------------------------------------------------------------------------------
       512500518060       192.168.10.3:49409                                  1 (2)
                             192.168.10.4:0
    (Gen: 1, K(nh):33, Action:F, Flags:, QOS:-1, S(nh):33,  Stats:4/392,  SPort 54199,
     TTL 0, Sinfo 3.0.0.0)
    
       518060512500       192.168.10.4:49409                                  1 (2)
                             192.168.10.3:0
    (Gen: 1, K(nh):33, Action:F, Flags:, QOS:-1, S(nh):20,  Stats:4/392,  SPort 65513,
     TTL 0, Sinfo 192.168.200.4)
    

    到目前为止,我们只看到了控制面信息:接口索引、路由表、下一跳。

    这是第一次确认流量真的在虚拟机之间流动。

    我们有2个流,因为每个流都是单向的。

    这个输出有这么多信息!流量是流动的,因为Action被设置为F,也就是转发。

    我们还知道,流量是ICMP,因为proto等于1。Proto旁边有“(V)”。这代表了VRF id,不出意外的话,它等于2!

    最后,看到K(NH)和S(NH):这些是Key和Source(RPF)的下一跳。下一跳20我们已经看到了。此外,我们还看到下一跳33,它指向我们的本地虚拟机(vif 3):

    (vrouter-agent)[root@compute1 /]$ nh --get 33 | grep Oif
                  EncapFmly:0806 Oif:3 Len:14
    

    我们可以从vRouter内部使用的东西就到这里了。

    最后,我们进入到hypervisor层。我所说的“hypervisor层”指的是虚拟机和外界之间的那个中间层。这是虚拟机接口与物理网卡连接的地方。在这个层面我们能做的主要是嗅探流量。

    先退出vRouter容器。

    当使用vif时,我们能够定位到与该端口相关的tap接口“tapcae84676-cb”。

    可以使用标准的tcpdump:

    [root@compute1 ~]# tcpdump -i tapcae84676-cb -nn icmp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on tapcae84676-cb, link-type EN10MB (Ethernet), capture size 262144 bytes
    09:24:55.082119 IP 192.168.10.3 > 192.168.10.4: ICMP echo request, id 49409, seq 688, length 64
    09:24:55.107621 IP 192.168.10.4 > 192.168.10.3: ICMP echo reply, id 49409, seq 688, length 64
    09:24:56.082344 IP 192.168.10.3 > 192.168.10.4: ICMP echo request, id 49409, seq 689, length 64
    09:24:56.092153 IP 192.168.10.4 > 192.168.10.3: ICMP echo reply, id 49409, seq 689, length 64
    ^C
    4 packets captured
    10 packets received by filter
    0 packets dropped by kernel
    

    是的,这里就是我们的流量。

    其次,可以直接在物理接口上嗅探流量。这里,我们将看到封装的流量。

    在这个接口上,我们可能会看到许多不同类型的流量:到计算节点2的流量,到其它计算节点的流量,到控制节点的流量。此外,并非所有的流量都是MPLSoUDP。我们可以有VXLAN(L2虚拟化)流量或普通IP流量(XMPP)。出于这个原因,适当地过滤流量可能是有用的。

    MPLSoUDP的流量可以通过6635端口过滤udp流量来缩小范围;VXLAN可以通过设置4789端口过滤udp流量。此外,我们还可以在目的主机IP上进行过滤,如之前学习的使用“rt”和“nh”,流量被发送到192.168.200.4:

    [root@compute1 ~]# tcpdump -i ens3f1 -nn udp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on ens3f1, link-type EN10MB (Ethernet), capture size 262144 bytes
    09:33:24.394590 IP 192.168.200.3.54199 > 192.168.200.4.6635: UDP, length 102
    09:33:24.395915 IP 192.168.200.4.56128 > 192.168.200.3.6635: UDP, length 102
    09:33:25.395362 IP 192.168.200.3.54199 > 192.168.200.4.6635: UDP, length 102
    09:33:25.398488 IP 192.168.200.4.56128 > 192.168.200.3.6635: UDP, length 102
    09:33:26.395541 IP 192.168.200.3.54199 > 192.168.200.4.6635: UDP, length 102
    09:33:26.399384 IP 192.168.200.4.56128 > 192.168.200.3.6635: UDP, length 102
    ^C
    6 packets captured
    6 packets received by filter
    0 packets dropped by kernel
    

    正如你所看到的,我们在这些计算节点之间有双向的MPLSoUDP流量。无论如何,还不能确定这真的是我们的流量。为了确定这一点,我们可以将捕获到的数据保存到一个文件中,然后用wireshark打开,wireshark能够解码MPLSoUDP。

    根据配置的封装优先级,可能VXLAN是用于计算到计算的流量。事实上,VXLAN是VN内部流量的默认选择,除非MPLSoUDP被配置在优先级列表中的第一位(无论如何,现在这并不重要……)。

    我们只能说,我们必须期望处理不同类型的overlay流量。

    其实VXLAN更方便用户使用,因为tcpdump可以显示内部数据包的内容:

    [root@compute1 ~]# tcpdump -i ens3f1 -nn udp
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on ens3f1, link-type EN10MB (Ethernet), capture size 262144 bytes
    09:36:24.478515 IP 192.168.200.3.54199 > 192.168.200.4.4789: VXLAN, flags [I] (0x08), vni 5
    IP 192.168.10.3 > 192.168.10.4: ICMP echo request, id 49409, seq 1377, length 64
    09:36:24.479659 IP 192.168.200.4.56128 > 192.168.200.3.4789: VXLAN, flags [I] (0x08), vni 5
    IP 192.168.10.4 > 192.168.10.3: ICMP echo reply, id 49409, seq 1377, length 64
    09:36:25.478920 IP 192.168.200.3.54199 > 192.168.200.4.4789: VXLAN, flags [I] (0x08), vni 5
    IP 192.168.10.3 > 192.168.10.4: ICMP echo request, id 49409, seq 1378, length 64
    09:36:25.480214 IP 192.168.200.4.56128 > 192.168.200.3.4789: VXLAN, flags [I] (0x08), vni 5
    IP 192.168.10.4 > 192.168.10.3: ICMP echo reply, id 49409, seq 1378, length 64
    ^C
    4 packets captured
    4 packets received by filter
    0 packets dropped by kernel
    [root@compute1 ~]#
    

    在这里,我们可以看到实际的VM ICMP数据包。这样就可以确定是否为我们的流量。

    只要我们的计算节点是在内核模式下,使用tcpdump就是可能的。如果我们有一个dpdk vRouter,那么就不能在主机上使用tcpdump,因为dpdk会“吃掉接口”,使它们对内核不可见(tcpdump在内核可见的接口上工作)。在这种情况下,hypervisor层消失了,我们必须依靠vRouter层。

    一旦我们进入vRouter容器,一个至今没有见过的工具就成了基础:vifdump。Vifdump就像tcpdump一样,只是它工作在DPDK接口上,只能在vRouter容器内部运行。

    要嗅探一个虚拟机接口:

    vifdump -i vif0/3 -nn icmp
    

    要嗅探一个物理接口:

    vifdump -i vif0/0
    

    总结

    就是这样!让我们总结一下所有的可能性——

    在VNF层面,使用厂商/VNF特定的工具。

    在vRouter层面(工具要在vRouter容器内运行):

    • vif,列出虚拟接口
    • nh,了解流量计算一个特定的nexth-hop索引是在哪里发送的
    • flow,查看vRouter上的活动流量
    • rt,查看vRouter路由表内部

    在hypervisor层面,使用tcpdump来嗅探虚拟接口和物理接口上的数据包。

    最后,如果节点是DPDK节点,那么主机级的tcpdump就没有用了,用vRouter容器里面运行的“vifdump”代替。

    现在没有秘密了吧?一句话,在正确的层面上使用正确的工具~


    作者:Umberto Manferdini 译者:TF编译组
    原文链接:
    https://iosonounrouter.wordpress.com/2020/04/13/troubleshooting-contrail-vms-traffic-the-right-tool-at-the-right-moment/



Log in to reply