Group Details Private

administrators

Member List

  • 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/


    posted in 博客
  • 如何在SDN GW上汇总虚拟机路由

    在Tungsten Fabric中,每个虚拟网络都不过是vRouter上的一个VRF。这使得vRouter在经典的L3VPN场景中看起来像是一个PE节点。

    每个虚拟网络都被分配了一个CIDR,例如10.10.10.0/24。vRouter有一个/32的路由指向连接到虚拟网络的每个虚拟机。

    通过在虚拟网络上配置一个route-target,这些路由可以被通告到SDN GW。

    这是一个PE-PE交互。那么这意味着什么?

    SDN GW将接收这些路由,并将其存储在bgp.l3vpn.0表中。如果SDN GW有另一个MP-BGP会话,例如朝向骨干(backbone)路由反射器,那么这些路由将被发送到RR,并有可能到达网络中的任何其它远程PE。

    由于通常Tungsten Fabric和SDN GW位于不同的自治系统中,我们处理的是一个经典的Inter-AS option B方案:来自TF的路由会按原样发送到骨干网。

    这样很好,为了将目的地为虚拟机的流量发送到虚拟机运行的确切计算节点,需要拥有/32的路由。想象一下,只有一个/24的网络指向一个随机的计算节点的情况。端到端流量无论如何都会工作,但平均而言,由于虚拟机可能不在通用/24路由指向的计算节点上运行,需要额外的流量跳数。这将产生不必要的东西向流量。在SDN GW上拥有/32路由可以避免这种情况。

    如果我们进一步将这些路由导出到远程PE,那么远程PE也知道正确的目的地来发送数据包。这一切都很好对吗?是,也不是。

    想象一下,一个大规模的TF集群,有许多虚拟机和许多虚拟网络“暴露”在SDN GW上。这将意味着大量的/32在骨干网上旅行。这是可扩展的吗?也许不能!

    另外,所有属于集群上配置的虚拟网络的虚拟机都位于同一个SDN GW的背后,所以拥有所有这些/32路由可能会被视为冗余信息:重要的是到达SDN GW,而SDN GW是唯一需要知道/32细节的。

    其实,这并不完全正确。假设我们的VN有CIDR 10.10.10.0/24。远程PE将向SDN GW发送属于10.10.10.0/24的任何IP的流量,即使不存在具有该特定IP的虚拟机……所以,是的,有一些缺点,但还是可以接受的。

    那么应该如何实现呢?需要SDN GW知道/32,但只通告相应的网络方面(例如/24)的路由。

    0f365f4c-a014-4811-8020-a70a3f177f18-image.png

    这可以通过在SDN GW上配置一个VRF来实现。

    该VRF将从Tungsten Fabric导入路由,匹配正确的路由目标。

    set routing-instances s1 instance-type vrf
    set routing-instances s1 route-distinguisher 2.2.2.100:1
    set routing-instances s1 vrf-import s1-imp
    set routing-instances s1 vrf-export s1-exp
    set policy-options policy-statement s1-imp term contrail from protocol bgp
    set policy-options policy-statement s1-imp term contrail from community s1-vn
    set policy-options policy-statement s1-imp term contrail then accept
    set policy-options community s1-vn members target:64520:100
    

    这将导致/32路由被导入到VRF路由表中。

    root@esto# run show route table s1.inet.0 10.10.10/24
     
    s1.inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
    + = Active Route, - = Last Active, * = Both
     
    10.10.10.10/32     *[BGP/170] 00:47:58, localpref 100, from 1.1.1.100
                          AS path: 64520 64500 I, validation-state: unverified
                        > via gr-0/0/10.0, Push 299856
    10.10.10.11/32     *[BGP/170] 00:47:58, localpref 100, from 1.1.1.100
                          AS path: 64520 64500 I, validation-state: unverified
                        > via gr-0/0/10.0, Push 299856
    10.10.10.12/32     *[BGP/170] 00:47:58, localpref 100, from 1.1.1.100
                          AS path: 64520 64500 I, validation-state: unverified
                        > via gr-0/0/10.0, Push 299856
    

    下一跳是MPLSoGRE隧道,朝向Tungsten Fabric方向。

    现在,我们在VRF上配置一个聚合路由:

    set routing-instances s1 routing-options aggregate route 10.10.10.0/24 discard
    

    最后,需要将该聚合路由朝向路由反射器发布通告。这是通过vrf-export策略完成的:

    set policy-options policy-statement s1-exp term agg from protocol aggregate
    set policy-options policy-statement s1-exp term agg then community add s1-vn
    set policy-options policy-statement s1-exp term agg then accept
    set policy-options policy-statement s1-exp then reject
    

    因此,我们现在向骨干网通告的是聚合路由:

    root@esto# run show route advertising-protocol bgp 3.3.3.3 10.10.10.0/24 exact
    
    s1.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
      Prefix                  Nexthop              MED     Lclpref    AS path
    * 10.10.10.0/24           Self                         100        64520 64500 I
    

    这样就够了吗?还不够!

    我们来查看一下所有的通告路由:

    root@esto# run show route advertising-protocol bgp 3.3.3.3 10.10.10/24
     
    s1.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
      Prefix                  Nexthop              MED     Lclpref    AS path
    * 10.10.10.0/24           Self                         100        64520 64500 I
     
    bgp.l3vpn.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
      Prefix                  Nexthop              MED     Lclpref    AS path
      1.1.1.100:1:10.10.10.10/32
    *                         Self                         100        64520 64500 I
      1.1.1.100:1:10.10.10.11/32
    *                         Self                         100        64520 64500 I
      1.1.1.100:1:10.10.10.12/32
    *                         Self                         100        64520 64500 I
      2.2.2.100:1:10.10.10.0/24
    *                         Self                         100        64520 64500 I
    

    我们也在通告/32路由。为什么会这样?细节决定成败。请记住,这是一个Inter-AS option B的方案,我们收到的/32路由是来自另一个PE,而不是来自CE,它们是作为MP-BGP路由接收的,而不是标准的BGP。

    Vrf-export策略只适用于从CE协议(bgp、bfd、isis、static等)学到的路由。因此,vrf-export策略对/32路由没有任何影响!我们不能在vrf-export策略中停止它们,不能在VRF级别上阻止它们。

    至少有两种方法来解决这个问题。

    第一个办法是在SDN GW和Tungsten Fabric之间的BGP会话上配置一个导入策略。这个导入策略只是根据route target匹配/32路由,并将no-advertise community添加其中:

    root@esto# show policy-options policy-statement imp-contrail | display set
    set policy-options policy-statement imp-contrail term a1-32 from community s1-vn
    set policy-options policy-statement imp-contrail term a1-32 from route-filter 0.0.0.0/0 prefix-length-range /32-/32
    set policy-options policy-statement imp-contrail term a1-32 then community add no-advertise
    set policy-options policy-statement imp-contrail term a1-32 then accept
     
    [edit]
    root@esto# show protocols bgp group contrail import | display set
    set protocols bgp group contrail import imp-contrail
    

    现在的结果是预期的结果:

    root@esto# run show route advertising-protocol bgp 3.3.3.3 10.10.10/24
     
    s1.inet.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
      Prefix                  Nexthop              MED     Lclpref    AS path
    * 10.10.10.0/24           Self                         100        64520 64500 I
     
    bgp.l3vpn.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
      Prefix                  Nexthop              MED     Lclpref    AS path
      2.2.2.100:1:10.10.10.0/24
    *                         Self                         100        64520 64500 I
    

    另一种解决办法是:

    root@esto# show policy-options policy-statement rr | display set
    set policy-options policy-statement rr term s1-32 from community s1-vn
    set policy-options policy-statement rr term s1-32 from route-filter 0.0.0.0/0 prefix-length-range /32-/32
    set policy-options policy-statement rr term s1-32 then reject
    [edit]
    root@esto# show protocols bgp group rr export | display set
    set protocols bgp group rr export rr
    

    这次我们是在SDN GW和RR之间的BGP会话上配置一个导出策略。我们只是根据route target拒绝/32路由。

    两种解决方案得到的结果是一样的,选哪个由你决定。


    作者:Umberto Manferdini 译者:TF编译组
    原文链接:https://iosonounrouter.wordpress.com/2020/05/15/summarizingvm-routes-at-the-sdn-gw/


    posted in 博客
  • 使用Ansible部署器设置一个小型的TF+K8s实验室

    Kubernetes绝对是SDN和虚拟化世界中最热门的趋势之一。简单来说,对于虚拟机我们有OpenStack,而对于容器那就是Kubernetes了(或者RedHat的商业版本OpenShift)。

    和虚拟机一样,容器也需要网络,特别是需要一个软件来管理这个虚拟网络。在Kubernetes的世界里,我们可以把这个所谓的软件称为CNI,即容器网络接口(Container Network Interface)。

    我们不打算描述Kubernetes的基础和基本概念;要想了解什么是K8s,以及对docker容器的一些基本理解,我建议阅读下面这篇伟大的文章(它还谈到了作者工作的一个特定的“模块”,但以一种非常酷的方式解释了基本概念)。
    https://medium.com/@geraldcroes/kubernetes-traefik-101-when-simplicity-matters-957eeede2cf8

    在这里,我们将专注于使用Tungsten Fabric创建一个运行Kubernetes的小型实验室。

    架构

    我们将建立一个这样的实验室:
    7e8a7e23-6926-44dc-bb90-e48a2e8b7047-image.png

    所有的4个元素可能是裸机服务器,也可能是虚拟机;在这个实验室里,我使用了运行CentOS的虚拟机。

    那么这4个元素的实体是什么呢?

    首先,我们有Ansible部署器,它不会成为K8s集群(我们的“云”)的一部分,而只是利用Ansible安装Kubernetes和Tungsten Fabric。

    然后我们有一个控制节点,TF和K8s的控制功能都将在这里运行。用K8s的“语言”来说,这个节点将是master节点。

    另外两个节点将是kubernetes worker,容器将在这里运行。TF将在这里安装并运行vRouter,它将负责虚拟网络。

    TF vRouter并不能与所有的内核一起工作,而是需要一些特定的兼容版本。查看版本说明来了解你所需要的特定TF版本的内核版本。

    例如,我要安装Tungsten Fabric 5.0.3,它需要3.10.0-957内核。

    步骤

    首先,我们需要在master节点和两个节点上安装正确的内核版本:

    yum -y install kernel-3.10.0-957.el7.x86_64.rpm
    yum update
    reboot
    

    接下来,我们连接到Ansible部署器虚拟机并准备环境:

    yum -y install epel-release git ansible net-tools
    yum install -y python-pip
    pip install requests
    

    Ansible部署器以tgz文件的形式出现。

    我们将其解压:

    tar -xvf {contrail-ansible-deployer-vxxx.tgz}
    

    移动到提取的文件夹中:

    cd {contrail-ansible-deployer-vxxx}
    

    现在我们需要建立instances.yaml文件,一个描述我们的设置的文件。

    这个文件在yaml文件中。

    vi config/instances.yaml
    

    下面是一个示例:

    global_configuration:
     CONTAINER_REGISTRY: hub.juniper.net/contrail
     CONTAINER_REGISTRY_USERNAME: xxx
     CONTAINER_REGISTRY_PASSWORD: yyy
    provider_config:
      bms:
       ssh_pwd: Embe1mpls
       ssh_user: root
       domainsuffix: ulab
    instances:
      master:
       provider: bms
       roles:
          config_database:
          config:
          control:
          analytics_database:
          analytics:
          webui: 
          k8s_master:
          kubemanager:
       ip: 10.49.231.32
      w1:
       provider: bms
       roles:
         vrouter:
         k8s_node:
       ip: 10.49.230.206
      w1:
       provider: bms
       roles:
         vrouter:
         k8s_node:
       ip: 10.49.230.205
    contrail_configuration:
      CONTRAIL_CONTAINER_TAG: 5.0.3-0.493
      KUBERNETES_CLUSTER_PROJECT: {}
      CONFIG_NODEMGR__DEFAULTS__minimum_diskGB: 2
      DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      CONFIG_DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      RABBITMQ_NODE_PORT: 5673
    

    详解

    让我们把它分成小块,以便更好地理解它。

    首先,我们有全局配置部分:

    global_configuration:
     CONTAINER_REGISTRY: hub.juniper.net/contrail
     CONTAINER_REGISTRY_USERNAME: xxx
     CONTAINER_REGISTRY_PASSWORD: yyy
    

    这将告诉部署器在哪里获得Tungsten Fabric容器(记住,从5.0版本开始TF控制平面就已经是容器化的了)。你需要一个有效的账户。

    接下来,我们定义访问我们节点的凭证:

    provider_config:
      bms:
       ssh_pwd: Embe1mpls
       ssh_user: root
       domainsuffix: ulab
    

    所有的设备都需要用相同的凭证来配置,我们支持使用相同的凭证,但需要通过在单个节点定义中配置凭证来覆盖这些设置。

    接下来开始定义我们的资源:

    instances:
    

    定义master节点:

    master:
     provider: bms
     roles:
        config_database:
        config:
        control:
        analytics_database:
        analytics:
        webui: 
        k8s_master:
        kubemanager:
     ip: 10.49.231.32
    

    实例是一个bms(即使是虚拟机,也要用bms),那么所有典型的控制角色都会分配给它。

    Worker节点的定义也类似:

    w1:
     provider: bms
     roles:
       vrouter:
       k8s_node:
     ip: 10.49.230.206
    

    很显然,这里的角色是不同的,因为我们必须安装vRouter,并将节点配置为worker。

    我们省略了第二个worker的定义。

    最后,我们有TF配置部分:

    contrail_configuration:
      CONTRAIL_CONTAINER_TAG: 5.0.3-0.493
      KUBERNETES_CLUSTER_PROJECT: {}
      CONFIG_NODEMGR__DEFAULTS__minimum_diskGB: 2
      DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      CONFIG_DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      RABBITMQ_NODE_PORT: 5673
    

    在这里,我们指定了TF容器的具体标签。标签应该与我们要安装的版本相匹配。

    标签可以通过以下方式获取:

    [root@master ~]# curl -u XXX -X GET https://hub.juniper.net/v2/contrail/contrail-analytics-api/tags/list | python -mjson.tool
    Enter host password for user 'JNPR-Customer15':
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100   423  100   423    0     0   1082      0 --:--:-- --:--:-- --:--:--  1087
    {
        "name": "contrail/contrail-analytics-api",
        "tags": [
            "5.0.0-0.40-ocata",
            "5.0.0-0.40",
            "5.0.1-0.214-ocata",
            "5.0.1-0.214-queens",
            "5.0.1-0.214-rhel-queens",
            "5.0.1-0.214",
            "5.0.2-0.360-ocata",
            "5.0.2-0.360-queens",
            "5.0.2-0.360-rhel-queens",
            "5.0.2-0.360",
            "5.0.3-0.493-ocata",
            "5.0.3-0.493-queens",
            "5.0.3-0.493-rhel-queens",
            "5.0.3-0.493",
            "5.1.0-0.38-ocata",
            "5.1.0-0.38-queens",
            "5.1.0-0.38-rhel-queens",
            "5.1.0-0.38-rocky",
            "5.1.0-0.38"
        ]
    }
    

    同样,我们需要一个有效的账户。

    当使用Tungsten Fabric时,你还可以简单地获得最新版本:

    contrail_configuration:
      CONTRAIL_VERSION: latest
    

    回到TF配置部分,将最小磁盘尺寸设置为低值,这样我们的小型实验室虚拟机就绰绰有余了。

    现在我们只需运行这三个Ansible命令(记住你是在Ansible部署器的解压文件夹里):

    ansible-playbook -e orchestrator=kubernetes -i inventory/ playbooks/configure_instances.yml
    ansible-playbook -e orchestrator=kubernetes -i inventory/ playbooks/install_k8s.yml
    ansible-playbook -e orchestrator=kubernetes -i inventory/ playbooks/install_contrail.yml
    

    一切都应该是正常的!

    验证

    现在我们可以连接到master,检查一切工作是否符合预期。

    首先我们列出节点:

    [root@master ~]# kubectl get nodes
    NAME      STATUS     ROLES     AGE       VERSION
    master    NotReady   master    9h        v1.9.2
    w1        Ready          9h        v1.9.2
    w2        Ready          9h        v1.9.2
    

    Worker已经准备好了,Master还没有准备好,但这不是问题。

    接下来,我们检查控制容器:

    [root@master ~]# kubectl get pods --all-namespaces
    NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
    kube-system   etcd-master                             1/1       Running   0          9h
    kube-system   kube-apiserver-master                   1/1       Running   0          9h
    kube-system   kube-controller-manager-master          1/1       Running   0          9h
    kube-system   kube-dns-6f4fd4bdf-nwjkt                3/3       Running   0          9h
    kube-system   kube-proxy-fzvt7                        1/1       Running   0          9h
    kube-system   kube-proxy-tt5nr                        1/1       Running   0          9h
    kube-system   kube-proxy-wvmzh                        1/1       Running   0          9h
    kube-system   kube-scheduler-master                   1/1       Running   0          9h
    kube-system   kubernetes-dashboard-846c4ddc5f-6fzdq   1/1       Running   0          9h
    

    所有的容器都在运行!请注意,K8s是我们用来管理容器及其核心功能的东西,让K8s工作的也是容器。它们运行在一个特殊的命名空间里,叫做kube-system。这些容器在主机上运行,只要运行“docker ps”来验证就可以了。

    我们可以在master和worker上运行经典的contrail-status命令:

    [root@master ~]# contrail-status
    

    最后,验证gui是可以通过https://%5Bmaster_ip}:8143打开。默认凭证(如果没有手动设置)是admin/trail23:
    0cde2458-0d66-4f9f-86e9-30225a9aa0a1-image.png
    就这样,已经设置好了……又快又简单。

    现在剩下的就是学习如何使用Kubernetes了。

    (注:原文出现Contrail的地方,本文都以Tungsten Fabric替代,绝大多数情况下两者功能一致。)


    作者:Umberto Manferdini  译者:TF编译组
    原文链接:
    https://iosonounrouter.wordpress.com/2019/05/30/setting-up-a-small-contrailkubernetes-lab-using-ansible-deployer/


    posted in 博客
  • TF Analytics指南丨TF中的Underlay Overlay映射(下)

    上篇文章介绍了TF中的underlay overlay分析,以及将overlay流映射到其underlay路径的前两个步骤,接下来继续——

    将pRouter信息发送到TF中的SNMP收集器

    在读取和显示全部分析信息之前,必须将物理设备的信息发送到SNMP收集器。通常情况下,pRouter信息是从contrail-config中获取的。

    SNMP收集器从contrail-config获取pRouter信息。

    物理路由器是通过使用Tungsten Fabric用户界面或直接使用API,通过provisioning或其它脚本的方式添加到contrail-config中的。一旦配置在contrail-config中,contrail-snmp-collector就会从contrail-config中获取物理路由器信息。SNMP收集器使用这个列表和其它配置参数来执行SNMP查询,并填充pRouter UVE。

    图2:添加物理路由器窗口
    619b9b74-d10a-49ed-902a-c8969d7d0272-image.png

    pRouter UVE

    pRouter UVE由系统上的REST APIs从contrail-analytics-api访问,使用下面格式的URL:

    http://:8081/analytics/uves/prouters
    

    以下是pRouter REST API的输出示例:

    图3:pRouter REST API的输出示例
    8fe1906a-fd43-4c80-ab8c-cdd43cd7003f-image.png
    pRouter UVE的详细信息可以从你的系统中获得,使用以下形式的URL:

    http://:8081/analytics/uves/prouter/a7-ex3?flat。
    

    以下是pRouter UVE的输出示例。

    图4:来自pRouter UVE的输出样本
    db5bb21a-5e72-4579-8524-5f9fc3b812ea-image.png

    用于Underlay Overlay分析的TF用户界面

    拓扑视图和相关功能可从Tungsten Fabric Web用户界面Monitor > Physical Topology访问。

    在Web用户界面上启用物理拓扑

    要启用Tungsten Fabric Web用户界面中的物理拓扑部分:
    1.在所有contrail-webui节点的/etc/contrail/config.global.js文件中添加以下内容:

    config.optFeatureList = {};
    config.optFeatureList.mon_infra_underlay = true
    

    2.重新启动webui supervisor。

    service supervisor-webui restart
    

    物理拓扑部分现在可以在TF Web UI上使用。

    查看虚拟机级的拓扑

    在Tungsten Fabric用户界面中,可以通过显示的拓扑结构向下钻取到虚拟机级别。下图显示了实例化在a7s36 vRouter上的虚拟机以及与每个虚拟机相关的完整物理拓扑。
    图 5:与 vRouter 相关的物理拓扑结构
    abdffc1b-0ba4-4ce3-9c44-8c3009785c37-image.png

    查看任意链接的流量

    在Monitor > Physical Topology处,双击拓扑上的任意链接,即可显示该链接的流量统计图。下面是一个示例。
    图6:流量统计图
    a4aa9d27-9bdc-4d76-b394-10986238ce34-image.png

    Trace Flow

    单击“Trace Flows”选项卡以查看活动流的列表。要查看流的路径,请单击活动流列表中的某个流,然后单击Trace Flow按钮。显示所选流在underlay的路径。下面是一个示例。
    图7:活动流量列表
    ebdddda5-4294-4e6c-ad8d-b559d5edc123-image.png
    Trace Flow功能的局限性
    由于Trace Flow功能使用ip traceroute来确定流中涉及的两个vRouters之间的路径,因此它具有与ip traceroute相同的限制,比如路径中的2层路由器没有被列出,因此不会出现在拓扑中。

    Search Flow和Map Flow

    单击Search Flows选项卡打开搜索对话框,然后单击Search按钮,列出符合搜索条件的流。你可以从列表中选择一个流,然后单击Map Flow来显示所选流在拓扑中的underlay路径。下面是一个示例。
    图8:Underlay路径
    f98de17b-128a-4513-ab2f-16ce70e0ea3d-image.png

    Overlay到Underlay的流映射模式

    查询overlay流的underlay映射信息的模式是从REST API中获得的,可以通过以下格式的URL在系统中访问:

    http://:8081/analytics/table/OverlayToUnderlayFlowMap/schema
    

    示例:Overlay到Underlay的流映射模式

    {"type": "FLOW",
    
    "columns": [
    
    {"datatype": "string", "index": true, "name": "o_svn", "select": false, "suffixes": ["o_sip"]},
    
    {"datatype": "string", "index": false, "name": "o_sip", "select": false, "suffixes": null},
    
    {"datatype": "string", "index": true, "name": "o_dvn", "select": false, "suffixes": ["o_dip"]},
    
    {"datatype": "string", "index": false, "name": "o_dip", "select": false, "suffixes": null},
    
    {"datatype": "int", "index": false, "name": "o_sport", "select": false, "suffixes": null},
    
    {"datatype": "int", "index": false, "name": "o_dport", "select": false, "suffixes": null},
    
    {"datatype": "int", "index": true, "name": "o_protocol", "select": false, "suffixes": ["o_sport", "o_dport"]},
    
    {"datatype": "string", "index": true, "name": "o_vrouter", "select": false, "suffixes": null},
    
    {"datatype": "string", "index": false, "name": "u_prouter", "select": null, "suffixes": null},
    
    {"datatype": "int", "index": false, "name": "u_pifindex", "select": null, "suffixes": null},
    
    {"datatype": "int", "index": false, "name": "u_vlan", "select": null, "suffixes": null},
    

    在接下来的文章中,我们将进一步深入到TF Analytics配置的部分,包括Alarm报警和Agent模块等内容。


    原文链接:
    https://www.juniper.net/documentation/en_US/contrail20/topics/task/configuration/underlay-overlay-mapping-vnc.html


    posted in 博客
  • TF Analytics指南丨TF中的Underlay Overlay映射(上)

    在上篇文章中,我们概要浏览了TF Analytics的功能,接下来看看如何使用Analytics进行underlay overlay映射。

    概述:使用TF Analytics进行underlay overlay映射

    注意:本主题适用于Tungsten Fabric 2005版及更早的版本。如果你使用Tungsten Fabric 2008之后的版本,可以《在拓扑视图中查看包路径》一文中查看数据包路径:
    https://www.juniper.net/documentation/en_US/contrail20/topics/concept/topology-view-packet-path.html

    如今的云数据中心由大量相互连接的服务器集合组成,这些服务器为运行各种应用提供计算和存储能力。服务器与冗余的ToR交换机相连,而ToR交换机又与spine路由器相连。云部署通常由多个租户共享,每个租户通常需要多个隔离的网络。多个隔离网络可以通过在underlay网络或物理连接上形成隧道(如gre、ip-in-ip、mac-in-mac)建立的overlay网络来提供。

    随着数据在overlay网络中的流动,Tungsten Fabric可以提供underlay网络中流量的统计和可视化。

    TF中的Underlay Overlay分析

    Tungsten Fabric允许在Web用户界面中查看与underlay和overlay流量相关的各种分析。以下是Tungsten Fabric提供的一些用于统计和可视化overlay underlay流量的分析。

    • 查看underlay网络的拓扑结构。
      物理underlay网络的用户界面视图,具有向下钻取机制,以显示连接的服务器(TF计算节点)和服务器上的虚拟机。

    • 查看拓扑结构中任何元素的详细信息。
      你可以查看两个元素之间的 pRouter、vRouter或虚拟机链接的详细信息。还可以在所选元素对应的图形视图中查看流量统计。

    • 查看overlay流的underlay路径。
      给定一个overlay流,你可以获得该流使用的underlay路径,并在拓扑视图中映射该路径。

    架构和数据收集

    累积数据以将overlay流映射到其underlay路径,在Tungsten Fabric模块中分几个步骤进行。基本步骤如下:

    1.SNMP收集器模块会对物理路由器进行轮询。

    SNMP收集器模块利用SNMP协议从TF配置模块接收物理路由器的授权和配置,对所有物理路由器进行轮询。收集器将数据上传至TF分析收集器。SNMP信息存储在pRouter UVEs(物理路由器用户可见实体)中。

    2.IPFIX和sFlow协议用于收集流量统计。

    物理路由器被配置为使用Internet协议流信息导出(IPFIX)或sFlow(2层数据包导出采样流量的行业标准)作为收集协议向收集器发送流量统计信息。。

    3.拓扑模块读取SNMP信息。

    TF拓扑模块从分析API中读取pRouter UVE的SNMP信息,计算邻居列表,并将邻居信息写入pRouter UVE中。这个邻居列表被WebUI用来显示物理拓扑。

    4.TF用户界面读取并显示拓扑和统计数据。

    TF用户界面模块从Tungsten Fabric分析中读取拓扑信息并显示物理拓扑。它还使用分析中存储的信息来显示链路统计的图形,并显示underlay网络上的overlay流映射。

    Underlay Overlay映射的新流程/服务

    contrail-snmp-collector和contrail-topology是新的daemon进程,它们都被添加到contrail-analytics节点。contrail-analytics包中包含了这些新特性及其相关文件。contrail-status用于显示新服务。

    示例:contrail-status

    以下是使用contrail-status显示新流程和服务的状态,用于underlay overlay映射的例子。

    user@host:~# contrail-status
    
    == Contrail Control ==
    
    supervisor-control:      active
    
    contrail-control       active
    
    …
    
    == Contrail Analytics ==
    
    supervisor-analytics:     active
    
    …
    
    contrail-query-engine     active
    
    contrail-snmp-collector    active
    
    contrail-topology       active
    

    示例:Service命令

    Service命令可以用来启动、停止和重新启动新的服务。请看下面的例子。

    user@host:~# service contrail-snmp-collector status
    
    contrail-snmp-collector     RUNNING  pid 12179, uptime 1 day, 14:59:11
    

    物理拓扑结构

    典型的物理拓扑结构包括:

    • 连接到ToR交换机的服务器。
    • ToR交换机与spine交换机相连。
    • spine交换机与core交换机连接。

    以下是WebUI分析中描绘拓扑结构的一个例子。

    图1:分析拓扑结构
    9e46dd0b-812c-4017-899b-44480cb261c2-image.png

    SNMP配置

    在物理设备上配置SNMP,使contrail-snmp-collector能够读取SNMP数据。

    以下是瞻博网络设备的SNMP配置示例。

    set snmp community public authorization read-only
    

    链路层发现协议(LLDP)配置

    在物理设备上配置LLDP,使contrail-snmp-collector能够读取路由器的邻居信息。

    以下是瞻博网络设备上LLDP配置的一个例子。

    set protocols lldp interface all
    
    set protocols lldp-med interface all
    

    IPFIX和sFlow配置

    流量样本由物理设备发送至contrail-collector。由于contrail-collector支持用于接收流量样本的sFlow和IPFIX协议,因此必须将物理设备(如MX系列设备或ToR交换机)配置为使用这些协议之一发送样本。

    示例:sFlow配置

    以下展示了一个sFlow配置的示例。在该例中,IP变量指的是作为sflow源的设备的环回或可到达的IP,另一个IP变量< collector_IP_data>是收集器设备的地址。

    root@host> show configuration protocols sflow | display set
    
    set protocols sflow polling-interval 0
    
    set protocols sflow sample-rate ingress 10
    
    set protocols sflow source-ip 4
    
    set protocols sflow collector   udp-port 6343
    
    set protocols sflow interfaces ge-0/0/0.0
    
    set protocols sflow interfaces ge-0/0/1.0
    
    set protocols sflow interfaces ge-0/0/2.0
    
    set protocols sflow interfaces ge-0/0/3.0
    
    set protocols sflow interfaces ge-0/0/4.0
    

    示例:IPFIX配置

    以下为瞻博网络设备的IPFIX配置示例。IP地址变量代表sflow收集器(控制收集器分析节点),代表路由器/交换机设备上用于向收集器发送流量数据的源(出)接口。如果可以从Tungsten Fabric集群到达,该地址也可以是lo0地址。

    root@host> show configuration chassis | display set
    
    set chassis tfeb slot 0 sampling-instance sample-ins1
    
    set chassis network-services 
    
    
    
    root@host> show configuration chassis tfeb | display set
    
    set chassis tfeb slot 0 sampling-instance sample-ins1
    
    
    
    root@host > show configuration services flow-monitoring | display set
    
    set services flow-monitoring version-ipfix template t1 flow-active-timeout 30
    
    set services flow-monitoring version-ipfix template t1 flow-inactive-timeout 30
    
    set services flow-monitoring version-ipfix template t1 template-refresh-rate packets 10
    
    set services flow-monitoring version-ipfix template t1 ipv4-template
    
    
    
    root@host > show configuration interfaces | display set | match sampling
    
    set interfaces ge-1/0/0 unit 0 family inet sampling input
    
    set interfaces ge-1/0/1 unit 0 family inet sampling input
    
    
    
    root@host> show configuration forwarding-options sampling | display set
    
    set forwarding-options sampling instance sample-ins1 input rate 1
    
    set forwarding-options sampling instance sample-ins1 family inet output flow-server  port 4739
    
    set forwarding-options sampling instance sample-ins1 family inet output flow-server  version-ipfix template t1
    
    set forwarding-options sampling instance sample-ins1 family inet output inline-jflow source-address 
    

    在下一篇文章中,我们将介绍将overlay流映射到其underlay路径的后面两个步骤。


    原文链接:
    https://www.juniper.net/documentation/en_US/contrail20/topics/task/configuration/underlay-overlay-mapping-vnc.html


    posted in 博客
  • 要在数据中心实现快速收敛?你需要一个快速IP Fabric

    先说一句看起来很“傻”,但在我看来并非琐碎的话:如果一切都按预期进行……那么一切就都会顺利进行。

    想象一下这样的网络:你设置链接,配置路由协议,检查端点是否可达。在正常情况下,数据包会如愿以偿地流动,用户也会对自己的体验感到满意。

    但是,如果网络内部出现故障怎么办?我们确实设置了路由协议来交换和传播路由信息,但这还不够。路由协议不一定要简单地提供端点之间的可到达性,它们必须被设计成能够高效地应对网络故障。

    那么“高效”又是什么意思?如果我们是在学校里学习,“高效”的意思就是快,我们确实希望将流量损失降到最低,因为这会导致服务中断,同时我们还希望系统的算法能够快速从故障中恢复。如果我们是在公司工作,“高效”仍然意味着快,压力也会更大,服务中断就意味着“金钱的损失”……

    总之,在这两种情况下,效率都意味着“快”。网络在处理故障的时候,必须要快。我们要尽可能地减少流量损失!

    这就引出了网络设计的一个基本话题:收敛!

    数据中心内部的收敛

    自第一个网络部署以来,网络收敛一直是非常重要的一个方面。

    当然,当你开始学习计算机网络时,一定听说过当一条链路故障时的生成树收敛,OSPF重新计算路径所需的时间,用于实现邻居关系的bgp保持计时器不再活跃等等。

    所有这些考虑因素在创建网络时仍然存在,而且至关重要,现在,我们把它们带到了数据中心里面。

    说到数据中心,我指的是IP Fabric,一个使用BGP作为路由协议的leaf和spine网络,并在leaf层限制L2功能(ERB模型)。有了这些假设,就不难理解很多“老”的快速收敛概念又回来了。

    让我们来看一个示例拓扑:
    16b28aca-a768-4682-a37d-7605c044c8d5-image.png
    我们有一个2×2的IP Fabric(2个leaves,2个spines),一个DC网关连接到两个spine(在真实的网络中,我们很可能有冗余的DC网关)。

    我们有服务器连接到leaves上,这些服务器是多归属连接到Fabric上的(绑定接口,每个leaf上都有一个成员连接)。

    来看看收敛是在哪里成为对话的一部分。

    在服务器和leaf之间,我们有一个LAG,因此,收敛主要是在一个成员故障的情况下,绑定失败的速度。在这里,我们期望亚秒级的故障切换时间,或者最坏的场景下,我们依靠LACP定时器(如果使用快速速率,那么我们期望检测时间为3×1秒)。

    而在IP Fabric内部,事情变得更加复杂……

    VTEPS的可达性取决于底层BGP。这里的收敛,是指BGP收敛。由于直接连接的接口地址之间使用的是eBGP,所以故障转移应该是非常快的。Hold Time不应该出现(除非软件层面的问题);BFD也可以做类似的考虑。

    接下来,对于EVPN驱动的连接,fabric ibgp overlay就变得相关了。在这里,由于在环回地址之间建立了BGP,来自BFD的帮助就成为关键。

    现在,让我来预测一下这个讨论的关键论点吧。2个leaf节点之间有多条路径;这来自于leaf & spine架构在两个leaf节点之间提供的多条ecmp路径。想象一下,从leaf1到leaf2的overlay ibgp数据包穿越spine1。如果leaf1-spine1的链接失效,并不意味着两个leaf不能再连通;它们可以通过spine2的备用路径。

    因此,overlay ibgp bfd定时器的速度不能快于underlay适应故障并提供leaf1和leaf2之间的备用路径所需的时间。由于underlay依赖于直连的接口地址之间配置的eBGP,反应时间应该非常快,因此我们可以设置非常低的BFD定时器。

    此外,由于设备上启用了ecmp,当一条链路发生故障时,leaf和spine已经将替代路径加载到FIB(包转发引擎表)中;这进一步将收敛时间降到最低,因为我们消除了将替代路由从RIB加载到FIB所需的时间(虽然很短,但它仍然存在)。

    这给了我们什么启示呢?

    如果系统A的收敛时间为X,而系统B使用系统A作为传输层,那么系统B的收敛时间不可能比X更快!

    这在后面会很方便。

    出于关闭fabric收敛的考虑,当一个ESI成员失败时,必须发送type 1 withdraw路由,以便让其它leaf知道有些MAC不能再通过发起这些type 1路由的leaf到达。某种程度上,这与ibgp overlay收敛有关。

    最后我们要涉及的部分是DCGW和和spine之间的路径。在这里,我假设了一个设计,有单独的p2p链路连接DCGW和spine(没有ESI LAG),并且eBGP会话通过直连的接口地址建立。在这种情况下,为Fabric eBGP underlay所做的考虑仍然有效,因为我们使用相同的动态路由协议。

    这涵盖了数据中心内部不同的收敛方面。

    南北流量路径收敛场景

    现在,让我们看看当需要在南北流量路径上进行收敛时会发生什么。

    考虑一下下面这个场景:
    5e60bf0b-4ebc-4896-beee-1fa7e94e3ffd-image.png
    我们有一个双归属到两个Leaf节点的VNF虚拟路由器 由于某些原因必须与DCGW对话。这条路径代表了南北流量的一个典型例子。

    现在,想象一下这两个端点通过BGP会话交换路由信息的情形。

    虚拟路由器可能充当DC内部运行的虚拟机的虚拟路由反射器。如果你仔细想想,这种用例并不是虚幻的。设置一个虚拟的RR是有意义的;你可能会通过依靠IP Fabric路由来实现类似的东西,但是很可能的是,你不希望在leaf(或spine)RIB里面带来太多的路由。

    此外,如果考虑到Contrail,Contrail Controller节点将作为路由反射器,并且与DCGW交换路由信息。

    在BGP会话的基础上,我们还配置了BFD,以便有更快的故障检测能力。

    现在,最大的问题是……如何设置BFD定时器?

    让我们看看,在正常情况下,BGP和BFD包可能流向哪里:
    3debfb3d-5c97-43ac-af19-7eab133558c1-image.png
    有四条可能的路径。第一次裂变发生在VNF层,取决于哈希函数的计算来选择一个LAG成员。第二次裂变发生在leaf上,因为DCGW可以通过2条ecmp路径到达。当然,属于同一个流的数据包(相同的5-tuple)将采取相同的路径,因为哈希函数给所有数据包的结果是相同的。

    可能发生的故障推演

    现在,我们来看看可能的故障。

    一个LAG成员可能会发生故障:
    a6652a16-bb4b-4c42-92a1-db93e8683c97-image.png
    如前所述,在这里,收敛时间取决于LAG的反应;假设需要X时间才能收敛。

    另一种可能发生的故障是leaf-spine故障:
    065bdbd8-5a47-4daf-b921-1f47e49f344f-image.png
    在这种情况下,收敛时间取决于underlay BGP(记住,我们是在ERB模型中,所以evpn对端到端路径没有影响)。假设需要Y时间才能收敛。

    回到我们的BFD定时器问题。在这两种故障场景下,BGP端点(虚拟路由器和DCGW)仍然可以通过替代路径到达对方。重要的是,我们必须允许fabric在端到端BGP会话下线之前提供该替代路径。这意味着BFD故障检测时间必须至少是max(X,Y)。

    这告诉我们在谈论数据中心的收敛时一些最基本的东西:每次我们“围绕IP Fabric设计收敛”的时候,都依赖于IP Fabric的收敛时间。

    这对现代架构有很大影响。想一想vEPG的用例:整个移动链将依赖于Fabric收敛。对于任何在数据中心内运行的应用来说,Fabric的恢复速度越快越好。

    虚拟环境下的收敛案例

    让我们简单看一下我们描述的一个例子。

    我在一个虚拟环境中构建了之前展示的拓扑结构。由于是虚拟环境,设备之间并不是通过真实的线缆连接,而是通过中间的虚拟交换机进行连接。
    f8a5012f-1acb-42ef-bae9-669488b7cfc7-image.png
    这就意味着,如果S2接口发生故障,S1接口不会宕机,因为S1-vswitch的链路还在。

    这是我的虚拟实验室环境的一个限制条件。

    这主要意味着两点。第一,如果leaf上的LAG成员被禁用,虚拟路由器会在LACP检测到故障后发现这一情况。第二,fabric BGP underlay必须依赖hold timer。由于这个定时器太长,我把BFD也配置在这些会话上。

    其实,这些虚拟环境给出的限制对我们的目的来说并不坏。由于我们不得不处理以秒为单位的收敛时间,因此将更容易观察到我们感兴趣的事件。

    在fabric内部,我为BGP underlay配置了以下BFD定时器:

    set groups bf_underlay protocols bgp group underlay bfd-liveness-detection minimum-interval 1000
    set groups bf_underlay protocols bgp group underlay bfd-liveness-detection multiplier 5
    

    定时器很大,正如所说,这是选择出来的。

    在虚拟路由器上,我们做如下设置:

    set protocols bgp group dcgw bfd-liveness-detection minimum-interval 300
    set protocols bgp group dcgw bfd-liveness-detection multiplier 3
    

    这些是端到端(虚拟路由器-DCGW)BGP会话的BFD定时器。

    我们有大约1秒与5秒两种情况。

    此时,我们禁用BGP和BFD数据包流动的leaf spine链路。

    大约1秒后,在虚拟路由器上,由于BFD会话关闭,BGP会话被关闭:

    Feb 12 02:14:55  bms bfdd[6205]: BFD Session 1.1.1.1 (IFL 0) state Up -> Down LD/RD(16/17) Up time:00:02:09 Local diag: NbrSignal Remote diag: CtlExpire Reason: Received DOWN from PEER.
    Feb 12 02:14:55  bms bfdd[6205]: BFDD_TRAP_MHOP_STATE_DOWN: local discriminator: 16, new state: down, peer addr: 1.1.1.1
    

    无论如何,虚拟路由器和DCGW仍然可以通过fabric提供的替代路径相连通,我们只需要等待fabric收敛。

    4秒之后发生了什么:

    Feb 12 02:14:59  leaf2 bfdd[1739]: BFDD_STATE_UP_TO_DOWN: BFD Session 192.168.3.1 (IFL 567) state Up -> Down LD/RD(16/17) Up time:00:05:02 Local diag: CtlExpire Remote diag: None Reason: Detect Timer Expiry.
    Feb 12 02:14:59  leaf2 rpd[1728]: bgp_bfd_callback:187: NOTIFICATION sent to 192.168.3.1 (External AS 65511): code 6 (Cease) subcode 9 (Hard Reset), Reason: BFD Session Down
    Feb 12 02:14:59  leaf2 bfdd[1739]: BFDD_TRAP_SHOP_STATE_DOWN: local discriminator: 16, new state: down, interface: xe-0/0/0.0, peer addr: 192.168.3.1
    

    这4秒钟来自于两个BFD会话上配置的检测时间的差异。

    接下来会发生什么?Fabric收敛,端到端BGP会话(虚拟路由器到DCGW)又出现了。结果呢?我们导致了BGP会话flap!

    为了避免这种情况,我们需要在端到端会话上增加BFD定时器的时间;例如增加到3×2.5秒:

    set protocols bgp group dcgw bfd-liveness-detection minimum-interval 2500
    set protocols bgp group dcgw bfd-liveness-detection multiplier 3
    

    在这种情况下,我们给fabric必要的时间来收敛,避免不必要的flapping(考虑到当DCGW将这些路由发送到其它地方,flapping的影响可能会扩展到其它设备,将导致整个网络的不稳定)。

    如果将BGP underlay BFD检测时间降低到2秒呢?在这种情况下,LAG故障切换(LACP 3秒)将成为约束条件,也就是说端到端BFD检测时间应该高于3秒。

    这完美的说明了在数据中心环境中运行的服务,其快速收敛的处理离不开fabric的收敛。

    那么,我的虚拟化服务能快速收敛吗?是的……但可能不会比你的IP Fabric更快!


    原文链接:https://iosonounrouter.wordpress.com/2020/02/13/to-have-fast-convergence-inside-a-dc-you-need-a-fast-ip-fabric/
    作者:Umberto Manferdini 译者:TF编译组


    posted in 博客
  • TF Analytics指南丨“分析”哪些内容?如何发出“警报”?

    Tungsten Fabric是一个由计算节点、控制节点、配置节点、数据库节点、Web UI节点和分析节点组成的分布式系统。

    分析(Analytics)节点负责收集系统所有节点上所有软件模块的系统状态信息、使用统计和调试信息。分析节点将整个系统收集到的数据存储在数据库中,数据库基于Apache Cassandra开源分布式数据库管理系统。该数据库通过类似SQL的语言和表示状态转移(REST)API进行查询。

    分析节点收集到的系统状态信息将汇总到所有的节点上。

    分析节点收集的调试信息包括以下几种类型。

    • 系统日志(syslog)消息——由系统软件组件产生的信息和调试消息。
    • 对象日志消息——记录对系统对象(如虚拟机、虚拟网络、服务实例、虚拟路由器、BGP对等体、路由实例等)的更改。
    • 追踪消息——软件组件在本地收集的活动记录,仅在需要时才发送给分析节点。

    与流量、CPU和内存使用情况等相关的统计信息也由分析节点收集,并可进行查询以提供历史分析和时间序列信息。查询使用REST APIs进行。

    分析数据会被写入到Tungsten Fabric的数据库。数据将在默认的48小时有效时间(TTL)后过期。这个默认的TTL时间可以根据需要通过改变集群配置中的database_ttl值来改变。

    TF警报流(Alert Streaming)

    TF警报(alert)是在基于每个用户可见实体(UVE)提供的。TF分析(analytics)使用Python编码的规则来触发或解除警报,这些规则将检查UVE的内容和对象的配置。一些规则是内置的,其它规则可以使用Python stevedore插件添加。

    本主题介绍了Tungsten Fabric警报功能。

    警报API格式

    TF警报分析API提供以下内容。

    • 作为UVE GET APIs的一部分,读取对警报的访问。
    • 使用POST请求进行警报确认。
    • 使用服务器发送的事件(SSE)进行UVE和警报流。

    例如:
    GET http:// :8081/analytics/alarms。

    {
        analytics-node: [
            {
                name: "nodec40",
                value: {
                    UVEAlarms: {
                        alarms: [
                            {
                                any_of: [
                                    {
                                        all_of: [
                                            {
                                                json_operand1_value: ""PROCESS_STATE_STOPPED"",
                                                rule: {
                                                    oper: "!=",
                                                    operand1: {
                                                        keys: [
                                                            "NodeStatus",
                                                            "process_info",
                                                            "process_state"
                                                        ]
                                                    },
                                                    operand2: {
                                                        json_value: ""PROCESS_STATE_RUNNING""
                                                    }
                                                },
                                                json_vars: {
                                                    NodeStatus.process_info.process_name: "contrail-topology"
                                                }
                                            }
                                        ]
                                    }
                                ],
                                severity: 3,
                                ack: false,
                                timestamp: 1457395052889182,
                                token: "eyJ0aW1lc3RhbXAiOiAxNDU3Mzk1MDUyODg5MTgyLCAiaHR0cF9wb3J0I
    ................................... jogNTk5NSwgImhvc3RfaXAiOiAiMTAuMjA0LjIxNy4yNCJ9",
                                type: "ProcessStatus"
                            }
                        ]
                    }
                }
            }
        ]
    }
    

    在这个例子中:

    • any_of属性包含以[ [rule1 AND rule2 AND ... AND ruleN] ... OR [rule11 AND rule22 AND ... AND ruleNN] ]格式定义的报警(alarm)规则。
    • 警报是在每个UVE的基础上发出的,可以通过在UVE上的GET来检索。
    • ack表示警报是否已被确认。
    • token用于客户端的请求确认。

    用于警报的分析API

    下面的示例显示了用于显示警报(alert)和报警(alarm),以及确认报警(alarm)的API。

    • 检索对名为aXXsYY的控制节点发出的警报列表。
    GET http://:/analytics/uves/control-node/aXXsYY&cfilt=UVEAlarms
    

    这适用于所有UVE表类型。

    • 检索系统中所有报警(alarm)的列表。
    GET http://:/analytics/alarms
    
    • 确认报警(alarm)。
    POST http://:/analytics/alarms/acknowledge
    Body: {“table”: ,“name”: , “type”: , “token”: }
    

    可以使用以下URL查询参数和前面列出的GET操作具体查询已确认和未确认的报警(alarm)。

    ackFilt=True
    ackFilt=False
    

    SSE流的分析API

    下面的例子展示了用于检索全部或部分SE流的API。

    • 检索基于SSE的UVE更新流,用于控制节点报警(alarm)。
    GET http://: /analytics/uve-stream?tablefilt=control-node
    

    这对所有UVE表类型都可用。如果没有提供tablefilt URL查询参数,则会检索所有UVE。

    • 只检索基于SSE的UVE更新流的警报部分,而不是整个内容。
    GET http://: /analytics/alarm-stream?tablefilt=control-node
    

    这对所有UVE表类型都可用。如果没有提供tablefilt URL查询参数,则会检索所有UVE。

    内置节点警报

    可以使用分析API中列出的API来检索以下内置节点警报。

    control‐node: {
    PartialSysinfoControl: "Basic System Information is absent for this node in BgpRouterState.build_info",
    ProcessStatus: "NodeMgr reports abnormal status for process(es) in NodeStatus.process_info",
    XmppConnectivity: "Not enough XMPP peers are up in BgpRouterState.num_up_bgp_peer",
    BgpConnectivity: "Not enough BGP peers are up in BgpRouterState.num_up_bgp_peer",
    AddressMismatch: “Mismatch between configured IP Address and operational IP Address",
    ProcessConnectivity: "Process(es) are reporting non‐functional components in NodeStatus.process_status"
    },
    
    vrouter: {
    PartialSysinfoCompute: "Basic System Information is absent for this node in VrouterAgent.build_info",
    ProcessStatus: "NodeMgr reports abnormal status for process(es) in NodeStatus.process_info",
    ProcessConnectivity: "Process(es) are reporting non‐functional components in NodeStatus.process_status",
    VrouterInterface: "VrouterAgent has interfaces in error state in VrouterAgent.error_intf_list”,
    VrouterConfigAbsent: “Vrouter is not present in Configuration”,
    },
    
    config‐node: {
    PartialSysinfoConfig: "Basic System Information is absent for this node in ModuleCpuState.build_info",
    ProcessStatus: "NodeMgr reports abnormal status for process(es) in NodeStatus.process_info",
    ProcessConnectivity: "Process(es) are reporting non‐functional components in NodeStatus.process_status"
    },
    
    analytics‐node: {
    ProcessStatus: "NodeMgr reports abnormal status for process(es) in NodeStatus.process_info"
    PartialSysinfoAnalytics: "Basic System Information is absent for this node in CollectorState.build_info",
    ProcessConnectivity: "Process(es) are reporting non‐functional components in NodeStatus.process_status"
    },
    
    database‐node: {
    ProcessStatus: "NodeMgr reports abnormal status for process(es) in NodeStatus.process_info",
    ProcessConnectivity: "Process(es) are reporting non‐functional components in NodeStatus.process_status"
    },
    

    分析API服务器和Client服务器之间的加密

    Tungsten Fabric 1910版本支持SSL加密,用于分析(Analytics)API服务器和Client服务器之间的连接。Client服务器是Service Monitor和Contrail Command,后者通过REST API端口连接到分析API服务器。在1910版之前的版本中,分析API服务器和Client服务器之间的连接没有加密,这可能会造成安全威胁。

    只有当Tungsten Fabric与Red Hat OpenStack Platform(RHOSP)一起部署时,1910版才支持SSL加密。在RHOSP部署中,添加了一个全局标志,它决定了SSL加密的状态。

    如果启用了全局标志:

    • 您不必修改配置文件,因为SSL加密是自动启用的。
    • 如果要禁用SSL加密,必须修改配置文件。

    如果全局标志被禁用:

    • 您不必修改配置文件,因为SSL加密是自动禁用的。
    • 即使修改配置文件,也无法启用SSL加密。由于全局标志被禁用,因此在部署期间不会生成证书。

    配置文件有contrail-analytics-api.conf、contrail-svc-monitor.conf和command_servers.yml。在配置文件中,修改下表中的参数,以启用或禁用基于SSL的加密。

    表1:SSL加密参数

    edc2345a-0244-4b9d-a40b-39a981aadc18-image.png

    配置好这些参数后,分析API服务器就会开始使用SSL证书,从而实现对分析API服务器和Client服务器之间连接的SSL加密支持。

    在下篇文章中,我们将继续“游览”TF Analytics的功能,看看如何使用Analytics进行underlay overlay映射。


    原文链接:
    https://www.juniper.net/documentation/en_US/contrail20/topics/concept/analytics-overview-vnc.html
    https://www.juniper.net/documentation/en_US/contrail20/topics/concept/alerts-overview.html
    https://www.juniper.net/documentation/en_US/contrail20/topics/task/configuration/encrypting-connection-analytics-server-and-client-server.html


    posted in 博客
  • 为OpenStack和K8s集群提供无缝虚拟网络

    在数据中心里,同时拥有OpenStack和Kubernetes集群的情况正变得越来越普遍。

    一边是由OpenStack管理的虚拟机,另一边是由K8s控制的容器化工作负载。

    现实情况下,可能会发生虚拟机和容器需要相互“交谈”的情况。如果是这样,我们需要以某种方式实现两个独立集群之间的通信。
    51990e6a-23c8-4247-adb1-5d45a227a2e2-image.png
    作为两个不同的集群,每个集群都有自己的网络平面。OpenStack可能会依赖OVS,而Kubernetes则会部署某个可用的CNI。

    由于每个集群都是独立于其它集群的,因此必须在第三个“元素”上启用通信。这第三个元素可以是IP Fabric。

    虚拟机将通过提供商网络退出计算,并在VLAN内的IP Fabric上现身。容器也会做类似的事情。因此,我们可能最终会有两个VLAN,通过配置IP Fabric叶子节点(或spine节点)作为L2/L3 gw来允许它们相互对话。另外,L3 gw的角色可能会被委托给上层设备,比如SDN gw(在这种情况下,IP Fabric叶子节点/spine节点只会是L2 gw)。

    这里的一个关键要点是,虚拟/容器化的工作负载成为underlay的一部分,使得服务平面(工作负载)和网络平面(IP Fabric)相互交错。

    提供服务也需要在IP Fabric underlay进行配置。我们在探索数据中心使用Tungsten Fabric的优势时,谈到过这个概念,请见下面链接:

    https://iosonounrouter.wordpress.com/2020/04/28/which-sdn-solution-is-better-what-i-learned/

    同样的考虑在这里也适用,另外我们不仅需要连接虚拟机,还需要连接容器。

    我们假设需要部署一个应用程序,其构件既有虚拟机又有容器。这意味着我们需要在OpenStack里部署一些对象,在Kubernetes里部署另外一些对象。从网络的角度来看,我们需要对OpenStack、K8s和IP Fabric进行操作。如果能简化这一点就好了。Tungsten Fabric来了!

    TF可以与OpenStack和K8s一起工作(作为CNI)。当然,代码的某些部分是“编排器专用”的。我的意思是,TF一方面需要与Neutron/Nova进行交互,另一方面需要与K8s API和其它K8s组件进行交互。

    总之,除了这些,解决方案的核心还是一样的!虚拟网络也还是VRF。工作负载也还是接口!不管我们的接口后面是虚拟机还是容器,对于Tungsten Fabric来说,它只是放到VRF中的一个接口。对于计算节点之间和通向SDN网关的overlay(MPLSoUDP、MPLSoGRE、VXLAN)也是一样的。

    因此,让虚拟机与容器进行通信,只是在同一个VRF(虚拟网络)中放置接口的问题。这可以通过让一个实体来“控制”OpenStack和Kubernetes集群的虚拟网络来实现。这个实体就是我们最爱的TF Controller。

    我们的数据中心将会是这样的:
    ccc0ee40-e16e-4c6c-9a9f-0c7ff27c3fa8-image.png
    如你所见,从配置的角度来看,集群还是有区别的。Kubernetes master控制容器,而OpenStack controller负责Nova虚拟机。

    这里的关键变化在于Tungsten Fabric控制器(controller)的存在,它将与OpenStack计算节点和Kubernetes worker节点进行交互(仍然使用XMPP)。这样我们就为集群提供了一个单一的网络平面。

    由于TF在overlay层工作,我们不再需要IP Fabric来“看到”虚拟机和容器。工作负载的流量被隐藏在overlay隧道中(无论是在计算节点之间,还是通向SDN网关……就像任何一个TF集群一样)。

    拥有一个单一的网络平面还会带来其它优势。例如,TF将负责IP地址分配。假设我们有一个地址为192.168.1.0/24的虚拟网络。如果我们在该VN上创建一个虚拟机,TF将分配地址192.168.1.3。之后,如果我们在Kubernetes中创建一个容器,并将其连接到同一个VN,TF将分配地址192.168.1.4,因为它知道.3已经在使用。如果有两个不同的集群,实现这个功能需要额外的“工具”(例如配置静态分配池或让更高级别的编排器充当IPAM管理器角色)。Tungsten Fabric则简化了这种网络操作。

    现在,理论已经说得够多了。让我们来看一个实际的例子!

    我在一个虚拟实验室中构建了上述拓扑。为了部署Tungsten Fabric,我使用了Ansible部署器,它允许我们同时配置OpenStack和Kubernetes集群。我不会去详细介绍如何使用Ansible部署器安装TF(这里是一个部署K8s集群的例子);我假设对这个工具有预先的了解。

    大家知道,Ansible部署器的核心是instances.yaml文件。下面是我使用的一个文件:

    ###FILE USED TO DESCRIBE THE CONTRAIL CLOUD THAT ANSIBLE DEPLOYER HAS TO BUILD
    global_configuration:
      CONTAINER_REGISTRY: hub.juniper.net/contrail
      CONTAINER_REGISTRY_USERNAME: xxx
      CONTAINER_REGISTRY_PASSWORD: yyy
    provider_config:
      bms:
        ssh_user: root
        ssh_pwd: Embe1mpls
        ntpserver: 10.102.255.254
        domainsuffix: multiorch.contrail
    instances:
      cnt-control:
        provider: bms
        ip: 10.102.240.183
        roles:
          config:
          config_database:
          control:
          webui:
          analytics:
          analytics_database:
          analytics_alarm:
      os-control:
        provider: bms
        ip: 10.102.240.178
        roles:
          openstack:
      os-compute:
        provider: bms
        ip: 10.102.240.171
        roles:
          vrouter:
            VROUTER_GATEWAY: 192.168.200.1
          openstack_compute:
      k8s-master:
       provider: bms
       roles:
          k8s_master:
          kubemanager:
       ip: 10.102.240.174
      k8s-worker:
       provider: bms
       roles:
         vrouter:
           VROUTER_GATEWAY: 192.168.200.1
         k8s_node:
       ip: 10.102.240.172
    contrail_configuration:
      CLOUD_ORCHESTRATOR: openstack
      CONTRAIL_CONTAINER_TAG: 2008.121
      CONTRAIL_VERSION: 2008.121
      OPENSTACK_VERSION: queens
      ENCAP_PRIORITY: "VXLAN,MPLSoUDP,MPLSoGRE"
      BGP_ASN: 65100
      CONFIG_NODEMGR__DEFAULTS__minimum_diskGB: 2
      DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      CONFIG_DATABASE_NODEMGR__DEFAULTS__minimum_diskGB: 2
      KUBERNETES_CLUSTER_PROJECT: {}
      KUBERNETES_API_NODES: 192.168.200.12
      KUBERNETES_API_SERVER: 192.168.200.12
      KUBEMANAGER_NODES: 192.168.200.12
      RABBITMQ_NODE_PORT: 5673
      KEYSTONE_AUTH_URL_VERSION: /v3
      VROUTER_GATEWAY: 192.168.200.1
      CONTROLLER_NODES: 10.102.240.183
      CONTROL_NODES: 192.168.200.10
      JVM_EXTRA_OPTS: "-Xms1g -Xmx2g"
      PHYSICAL_INTERFACE: "ens3f1"
    kolla_config:
      kolla_globals:
        enable_haproxy: no
        enable_ironic: no
        enable_swift: no
        enable_barbican: no
      kolla_passwords:
        keystone_admin_password: contrail123
    metadata_secret: meta123
    

    实例部分包括5台服务器:

    • openstack controller
    • kubernetes master
    • contrail controller
    • openstack compute
    • kubernetes worker

    如果你去看“contrail_configuration”部分,就会注意到我们配置TF是为了与openstack controller(KEYSTONE_AUTH_URL_VERSION)和kubernetes master(KUBERNETES_API_NODES)交互。
    一旦所有节点都做好安装准备,从“deployer”节点(可以是contrail controller本身)运行以下命令:

    ansible-playbook -e orchestrator=openstack -i inventory/ playbooks/configure_instances.yml
    ansible-playbook -e orchestrator=openstack -i inventory/ playbooks/install_openstack.yml
    ansible-playbook -e orchestrator=openstack -i inventory/ playbooks/install_k8s.yml
    ansible-playbook -e orchestrator=openstack -i inventory/ playbooks/install_contrail.yml
    

    如果一切顺利的话,我们的集群应该可以运行了。

    让我们连接到Tungsten Fabric GUI:
    f93f1b03-6345-4c45-8705-98245960a45e-image.png

    我们看到两个虚拟路由器:os-compute和k8s-worker!

    我们看一下控制节点:
    c434da04-a779-4028-9c62-4265ec56af9e-image.png

    只有一个控制器!我们“单一网络平面”的关键概念变成了现实。

    接下来,我创建了一个虚拟网络:

    • FQ名称:default-domain:k8s-contrail:seamless
    • CIDR:192.168.1.0/24

    启动一个连接到该VN的虚拟机:

    nova boot --image cirros2 --flavor cirros --nic net-id= vm
    
    (kolla-toolbox)[ansible@os-control /]$ nova list
    +--------------------------------------+------+--------+------------+-------------+------------------------+
    | ID                                   | Name | Status | Task State | Power State | Networks               |
    +--------------------------------------+------+--------+------------+-------------+------------------------+
    | 3cf82185-5261-4b35-87bf-4eaa9de3caaf | vm   | ACTIVE | -          | Running     | seamless=192.168.100.3 |
    +--------------------------------------+------+--------+------------+-------------+------------------------+
    

    接下来,创建一个连接到该VN的容器:

    [root@k8s-master ~]# cat cn.yaml
    ---
    kind: Namespace
    apiVersion: v1
    metadata:
      name: seamless
      annotations:
        'opencontrail.org/network' : '{"domain":"default-domain", "project": "k8s-contrail", "name":"seamless"}'
      labels:
        name: seamless
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: cont
      namespace: seamless
    spec:
      containers:
      - name: cont
        image: alpine
        command: ["tail"]
        args: ["-f", "/dev/null"]
    
    kubectl apply -f vn.yaml
    
    [root@k8s-master ~]# kubectl get pod -n seamless -o wide
    NAME   READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
    cont   1/1     Running   0          74s   192.168.100.4   k8s-worker              
    
    [root@k8s-master ~]# kubectl get -f cn.yaml -o wide
    NAME                 STATUS   AGE
    namespace/seamless   Active   31m
    
    NAME       READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
    pod/cont   1/1     Running   0          31m   192.168.100.4   k8s-worker              
    

    如你所见,由于192.168.100.3已经被VM占用了,pod被分配了192.168.100.4的IP地址。这也是多个集群使用单一网络平面的优势之一。
    baf8bb04-32e2-49a1-8d2f-5b9f631a0305-image.png
    b5add53a-601e-4d02-94a3-8f6b00f6687d-image.png

    让我们从GUI中查看一下路由表:
    37e5b67e-6f59-4308-b949-51b7905e31c0-image.png
    两个IP都有!我们刚刚把一个虚拟机连接到一个工作负载上……而这对underlay是完全透明的!

    让我们再到worker节点,查看一下vRouter代理:

    (vrouter-agent)[root@k8s-worker /]$ vif --get 3
    Vrouter Interface Table
    
    vif0/3      OS: tapeth0-11ccbe NH: 51
                Type:Virtual HWaddr:00:00:5e:00:01:00 IPaddr:192.168.100.4
                Vrf:4 Mcast Vrf:4 Flags:PL3L2DEr QOS:-1 Ref:6
                RX packets:67  bytes:2814 errors:0
                TX packets:87  bytes:3654 errors:0
                Drops:67
    
    (vrouter-agent)[root@k8s-worker /]$ rt --get 192.168.100.3/32 --vrf 4
    Match 192.168.100.3/32 in vRouter inet4 table 0/4/unicast
    
    Flags: L=Label Valid, P=Proxy ARP, T=Trap ARP, F=Flood ARP
    vRouter inet4 routing table 0/4/unicast
    Destination           PPL        Flags        Label         Nexthop    Stitched MAC(Index)
    192.168.100.3/32        0           LP         25             39        2:85:bf:53:6b:67(96024)
    (vrouter-agent)[root@k8s-worker /]$ nh --get 39
    Id:39         Type:Tunnel         Fmly: AF_INET  Rid:0  Ref_cnt:8          Vrf:0
                  Flags:Valid, MPLSoUDP, Etree Root,
                  Oif:0 Len:14 Data:56 68 a6 6f 05 ff 56 68 a6 6f 06 f7 08 00
                  Sip:192.168.200.14 Dip:192.168.200.13
    

    所有的路由信息都在那里!容器可以通过连接Kubernetes worker节点和OpenStack计算节点的MPLSoUPD隧道到达虚拟机。

    这样就可以进行通信了吗?还不行!别忘了安全组还在那里!

    虚拟机属于一个OpenStack项目(这里是管理项目),而容器属于另一个项目(映射到Kubernetes命名空间)。每个项目都有自己的安全组。默认情况下,安全组只允许来自属于同一安全组的入口流量。由于两个工作负载的接口被分配到不同的安全组,因此它们之间不允许对话!

    为了解决这个问题,我们需要对安全组采取行动。

    简单的方法是在两个安全组上都允许来自0.0.0.0/0的入口流量(这是容器安全组,但同样也可以在虚拟机安全组上进行):
    145bb302-b9b3-48ce-a570-888829fd93ff-image.png
    另外,我们也可以在入口方向允许特定的安全组。

    例如,在k8s-seamless-default-sg(容器所属命名空间/项目的安全组)上,我们允许默认安全组(虚拟机所属OpenStack项目的安全组)。
    c629ed30-4733-46ec-9d1d-530fdbf728ae-image.png
    同样地,也可以在分配给虚拟机接口的安全组上进行:
    22418c56-9360-4903-ba85-f3e7bc5a8612-image.png
    现在,让我们访问容器,并且ping一下虚拟机:

    [root@k8s-worker ~]# docker ps | grep seaml
    18c3898a09ac        alpine                                                         "tail -f /dev/null"      9 seconds ago       Up 8 seconds                            k8s_cont_cont_seamless_e4a7ed6d-38e9-11eb-b8fe-5668a66f06f8_0
    6bb1c3b40300        k8s.gcr.io/pause:3.1                                           "/pause"                 17 seconds ago      Up 15 seconds                           k8s_POD_cont_seamless_e4a7ed6d-38e9-11eb-b8fe-5668a66f06f8_0
    [root@k8s-worker ~]# docker exec -it 18c38 sh
    / # ip add
    1: lo:  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
    24: eth0@if25:  mtu 1500 qdisc noqueue state UP
        link/ether 02:e4:e5:52:ce:38 brd ff:ff:ff:ff:ff:ff
        inet 192.168.100.4/24 scope global eth0
           valid_lft forever preferred_lft forever
    / # ping 192.168.100.3
    PING 192.168.100.3 (192.168.100.3): 56 data bytes
    64 bytes from 192.168.100.3: seq=0 ttl=64 time=3.813 ms
    64 bytes from 192.168.100.3: seq=1 ttl=64 time=1.802 ms
    64 bytes from 192.168.100.3: seq=2 ttl=64 time=2.260 ms
    64 bytes from 192.168.100.3: seq=3 ttl=64 time=1.945 ms
    ^C
    --- 192.168.100.3 ping statistics ---
    4 packets transmitted, 4 packets received, 0% packet loss
    round-trip min/avg/max = 1.802/2.455/3.813 ms
    / #
    

    就是这样!虚拟机和容器可以互相对话了!

    无论你的工作负载是虚拟机还是容器……至少从网络的角度来看,一切都是在Tungsten Fabric的机制下进行的。


    作者:Umberto Manferdini 译者:TF编译组
    原文链接:https://iosonounrouter.wordpress.com/2020/12/14/contrail-providing-seamless-virtual-networking-to-openstack-and-kubernetes-clusters/


    posted in 博客
  • 洞察Tungsten Fabric内部的XMPP

    XMPP是一个用于通讯和表示的开放标准协议。从本质上讲,它是一个允许实体交换信息和进行聊天的协议。

    XMPP就在我们身边……比我们想象的要多得多。WhatsApp使用了XMPP的一个变种,Zoom使用XMPP以及一些扩展来实现其聊天功能。XMPP是一些最常用和最著名的应用程序的背后技术。那么,XMPP是如何工作的呢?

    如前所述,它允许实体交换信息和聊天。我说的是实体,因为这种聊天和信息交换可能涉及两个人,也可能是两个服务器/软件之间的对话。

    XMPP利用了TCP。我们可以说XMPP是一个使用TCP作为底层L4协议的L7协议(就像HTTP)。

    到这里为止,XMPP可能看起来就像任何其它协议一样,将消息带入其有效负载。

    围绕XMPP值得注意的是,交换的数据是XML格式的。这就导致了两大优势:第一,XML是一种结构化的标准语言,可以很容易地被编程语言消化/处理;第二,只要用XML来格式化这些信息,就可以很容易地扩展XMPP可以传输哪些信息。

    关于后面一点,请看以下链接,了解这些年发展了多少扩展:

    https://xmpp.org/extensions/

    XMPP与TF如何共同工作

    让我们简单看下XMPP对话是如何工作的吧!详细的解释并不在本文讨论范围内,但我将尝试划出一些相关方面的重点,这将有助于理解Tungsten Fabric中的XMPP是如何工作的。

    访问资源的用户由所谓的JID来识别:

    [ node "@" ] domain [ "/" resource ]

    例如,“iosonounrouter@whatsapp/mobilechat”可能代表我(iosonounrouter)使用whatsapp并访问mobilechat资源。

    资源可以被看成是我想访问的一类聊天室/话题/频道。想象一下,某个软件要从互联网上检索信息,它可能使用XMPP来访问不同的资源:

    • client@infohub/sport

    • client@infohub/finance

    第一个JID可以用来访问资源以查找和检索体育新闻,而第二个JID将相同的功能用于金融数据。

    通常情况下,XMPP遵循客户端-服务器的交互方式(即使p2p也是可能的)。客户端和服务器发送/接收所谓的XMPP节段(stanzas)——节段有不同的用于客户端和服务器的类型。XMPP核心主要有三种类型的节段:

    • 消息(message):顾名思义,它带来消息。采用的是“fire and send”的模式,也就是说,没有可靠的方法来确保消息在另一端被收到。某种程度上,我们可能会把这个节段看作是UDP:发送出去,并希望它能成功!当然,这个节段可以包含XML格式的结构化数据。
    • 表示(presence):用于宣布系统内有某种东西的存在
    • IQ:代表消息(info)/查询(query),允许实现类似于http的请求/响应机制(例如GET)。使用IQ,我们可以获取/设置数据。不同于消息,IQ使用了确认通知,以提供更可靠的通信。

    下图总结了目前为止所看到的概念:
    a1485630-dabf-4530-bcd0-811e97302502-image.png
    比如我们有两个客户(Ivano和Carmela)访问一台服务器(infohub)。服务器上有两个资源(金融和体育)。如前所述,可以将资源看作是聊天室/主题/频道。客户端通过JID访问这两个资源,总共会有四个“对话”。在每一个对话里面,客户端和服务器将使用节段来交换信息,即IQ、MESSAGE、PRESENCE等......

    现在,有一个问题出现了:Tungsten Fabric在这里做了什么?

    如果你有一些Tungsten Fabric的知识,可能知道TF使用XMPP在控制节点和计算节点之间交换数据。控制节点和计算节点会聊天,谈论TF集群内发生的事情。控制节点作为XMPP服务器,而计算节点作为客户端。

    XMPP带来了什么样的信息?大多数时候,我们说XMPP取代BGP进行控制节点-计算节点通信,说明它是用来携带路由信息的。这是事实,但不完全正确。

    XMPP也被用来携带配置信息(我们将在后面看到这意味着什么)。这里的关键概念是,XMPP既是一个信令协议,也是一个配置协议。

    如何转化为XMPP术语呢?很简单,这里有两个资源:一个是配置资源,一个是路由资源。

    把控制节点-计算节点对(pair)看成是两个朋友在互联网上聊天。他们聊的是两个话题:配置和路由。当一个人有关于配置方面的事情要讲时,就会通过配置通道(资源)来讲。另一方面,当有一些路由信息需要沟通时,会使用路由通道。

    下图说明了这种互动关系:
    ebf6e82a-68c2-4df7-bd71-138e6bb2e977-image.png
    节点通过两个不同的通道交换信息,将数据编码成XML格式的负载。

    现在,有关XMPP和Tungsten Fabric如何共同工作所需的最基础知识,我们就都已经掌握了。

    为了学习XMPP,我们将继续进行以下步骤:
    55362672-513e-4cb2-ba74-75f177f6f0d9-image.png

    • 一开始,我们有一个空的计算节点,上面没有虚拟机运行。

    • 接下来,终端用户(通过GUI、Heat……)创建一个虚拟网络和一个连接到该虚拟网络的虚拟机。

    • 因此,控制节点将向虚拟机运行的控制节点发送XMPP消息,以便告诉它配置和路由信息。

    创建虚拟机后,我使用tcpdump捕获计算节点上的XMPP数据包。

    先来看看pcap。首先,我们要告诉Wireshark将5269端口解码为XMPP。
    81c1495d-f269-4e7d-9d17-c1c18a4c1286-image.png
    此时,过滤XMPP数据包,检查信息栏。
    82e97773-549b-4b8c-a3d1-f4981d613a60-image.png
    它们看起来像JID……确实也是!

    • 用户(计算节点)为网络控制(network-control)

    • 服务器(控制节点)为contrailsystems.com

    • 两个资源为:bgp-peer和config

    它们就在这里!就是之前提到的两个通道:一个承载配置信息,一个承载路由信息。

    为什么要两个通道?

    XMPP会带来一些配置信息,因为控制节点要通知vRouter必须创建一些对象:其中包括代表虚拟网络的VRF,以及连接VRF与虚拟机的虚拟机接口。简单地说,config数据包将指示vRouter必须创建的所有这些Tungsten Fabric对象,以便将新的虚拟机整合到虚拟网络生态系统中。

    同时,XMPP也会带来路由信息。新的虚拟机被分配了一个IP地址。关于vnic(虚拟机接口)的IP地址和MAC地址的信息必须传输到计算节点,这样它就可以更新相应的路由表(例如在收到config XMPP消息后刚创建的VRF)。

    正如你所看到的,config和bgp-peer消息经常是相关联的!

    配置信息里都有什么

    现在,让我们来看看这些XMPP信息里面有什么。拿下面这个消息来说:
    86fa3269-9baa-4934-ab9a-47cfc589e56f-image.png
    这是一个IQ“type:set”的消息。使用IQ是很聪明的,因为它需要确认信息;这样Tungsten Fabric可以“验证”计算节点是否真的收到了信息,并且它也避免了计算节点收到路由数据,但没有足够的底层配置对象来使用这些数据的情况。

    IQ数据包应该使用一个ID字段,这样便于跟踪响应。无论如何,Tungsten Fabric不会填充ID属性。

    核心部分已经标蓝。大家可以看到信息是CONFIG UPDATE,意思是“如果存在则更新,否则就创建”。

    接下来,我们有一个要创建的节点和链接列表。可以很容易地发现虚拟网络对象。下面好好观察一下它的内部情况:
    eb44e60e-7bfd-40c3-83f7-d675441d1f9a-image.png
    如果你熟悉Tungsten Fabric,就会发现我们在配置虚拟网络时可以设置的那些设置。例如,VN名称为xmpp_net1,属于juniper-project项目。

    这部分的有效负载基本上是告诉vRouter创建一个新的对象(节点),一个虚拟网络,并告知它的参数和属性。

    对于其它对象(节点)也可以看到类似的输出信息。

    我们看到XMPP数据包中还携带了LINK的信息:
    5829c0be-1c86-457a-aca5-c4319511b47c-image.png
    顾名思义,链接(link)是将两个节点连接起来,以便在配置层代表这些对象之间的“参考”关系。

    例如,在上面的输出内容中,表明要将虚拟路由器与新虚拟机的虚拟机接口“链接”起来。

    这些链接不是随机的,它们与Tungsten Fabric配置模式一致,该模式定义了对象(及其属性)以及对象之间可能的关系,称为链接。这个模式就像Junos XSD模式一样。

    同样,我们希望看到虚拟网络和路由实例对象、实例IP和虚拟网络对象等之间的链接。

    这些信息也可以通过introspect获得。在这里,从Tungsten Fabric GUI中,我“访问”了计算节点introspect web服务器,并请求获得Sandesh Trace Buffer List。
    0ceab37f-7fe6-4a82-813c-d068d0167d61-image.png
    从结果输出中,我们检测到四个相关的轨迹(trace):
    7e2db9c6-9031-4371-a057-8aca556d5ed7-image.png
    其中两个包含接收到的config xmpp消息的信息,其它的是接收到的bgp xmpp消息。每种类型(路由或配置)都有两个轨迹(trace),因为每个计算节点都与两个控制节点进行了XMPP对话(冗余原因)。

    点击“Rx Config Xmpp Message”,选择XSD GRID视图。在那里,我们可以寻找有趣的XMPP消息。
    459522e2-c6b5-4210-a7c6-faa4999656bd-image.png
    例如,这个是指示vRouter必须创建新的虚拟网络对象的消息。如你所见,正文是XML。我们可以将它复制粘贴,并以一种格式友好的方式来查看:
    1f36b68c-232d-40c3-b449-38847169bfa6-image.png
    这些都是我们在Wireshark中看到的信息。

    同样,也可以找到关于链接创建的日志:
    d33d791b-ba31-40ec-b067-ac5fe5be9ed4-image.png
    并提取XML主体:
    28373493-af1c-45ad-88d4-820467417852-image.png
    这是第一个例子,说明XMPP是如何被扩展以携带我们能想到的最多样化的数据集的。鉴于它对XML的原生支持,可以添加想要的信息,只要确保符合XML-syntax即可。同时,作为XML结构的数据,接收端将很容易解析和处理数据。

    扒一扒XMPP路由信息

    接下来进入第二个“通道”:路由。

    虚拟机被分配的地址是172.30.1.3/32。三条XMPP路由消息从控制节点发送到计算节点:
    0aa42e42-692d-429e-8c50-e7d34cff71c9-image.png
    为什么是三条?因为虚拟网络是L3+L2,我们会有一条inet路由,一条evpn mac路由和一条evpn mac:ip路由。
    让我们到内部去看一看:
    064f33b2-d7bc-45f9-a221-1095aaf9b556-image.png
    与以前看到的有一些不同。我们使用的是MESSAGE节段而不是IQ节段。在它里面,BGP路由广告被编码为一个EVENT消息的ENTRY ITEM。

    除了这些方面,上面的输出内容应该是网络工作人员所熟悉的。项目id是路由:172.30.1.3/32。接下来,我们还有NLRI信息。AFI 1 / SAFI 1表示IPv4。然后,是下一跳的信息以及路由所属的虚拟网络名称。

    基本上,我们将BGP UPDATE消息编码成了XMPP有效负载。这又一次证明了XMPP的可扩展性如何使这个协议变得非常灵活。从软件的角度来看,用一个协议(XMPP)就能同时管理配置和路由。除了有些必需的情况(例如与不支持XMPP的SDN网关通信)外,一般不需要有两个协议栈(xmpp和bgp)。

    同样,我们也可以用introspect来提取同样的信息:
    928fe000-7f69-41db-8c4e-a399717e8b3c-image.png
    最后,将虚拟机删除。结果就是,我们又触发了一次XMPP数据包的交换。

    这次它们将不是UPDATE消息,而是DELETE消息:
    f2781451-dd4e-4644-b812-558bea178373-image.png
    因为不再需要,节点和链接都被删除了。

    同样,也会有模仿BGP withdraw消息的routing XMPP消息。我们认识它们是因为RETRACT关键字的存在。这次我们在introspect上看到了这个:
    eb012c7f-7b3c-4c3b-9f5b-3dad24588110-image.png
    2be5fc6a-2d5a-4950-9a6f-02cb46592065-image.png
    就是这样!现在我们知道了XMPP是如何允许Tungsten Fabric节点创建/删除配置对象和路由的。说到底,这就是一个聊天:)


    作者:Umberto Manferdini 译者:TF编译组
    原文链接:https://iosonounrouter.wordpress.com/2020/12/07/a-look-inside-xmpp-in-contrail/


    posted in 博客
  • 通过TF Operator进行统一生命周期管理

    在Linux基金会主办的“LFN技术会议”上,Tungsten Fabric社区进行了一系列演讲,介绍最新的功能和未来发展方向。今天带来第三篇演讲,通过TF Operator实现Tungsten Fabric各方面的部署和生命周期管理

    嗨,大家好!我是Prabhjot Singh Sethi,做架构师已经第八个年头了,今天的主题围绕使用TF Operator进行统一的生命周期管理,同时将演示集成Airship的应用案例。

    (附注:Airship是一组用于自动化云配置和管理的开源工具,它提供了一个声明性框架,用于定义和管理开放式基础架构工具和底层硬件的生命周期。)

    我们从Tungsten Fabric的发布开始,正如很多人已经知道的,Tungsten Fabric目前是作为一个容器镜像进行打包和发布的,所以基本上在部署它们时,最终会定义为基于docker compose的服务或者Kubernetes的原生对象,来真正推出Tungsten Fabric的各种组件。
    2dd1b9a1-0d93-4894-a638-b95205d61fbb-image.png
    当我们这样做的时候,各种模块的生命周期管理要求就变成了首要问题,任何部署系统都必须满足这些依赖关系。例如某些模块被期望按照预定的顺序部署,然后有一些有状态要素的基础设施组件,比如zookeeper,在部署config模块之前这些组件被期望达到某个状态,而这将取决于zookeeper。

    此外,在升级和其它的方面,我们需要确保执行升级时各模块间关系的匹配,某些时候升级到各个版本需要执行不同的方案,而不是遵循类似的方案。

    最后同样重要的是,作为一个网络提供商,我们还需要处理集群规模问题——当你有新的节点要进入系统,或者当你的Tungsten Fabric需要增加水平扩展的配置节点或控制器节点的时候。

    在进入Tungsten Fabric Operator之前,我简单地介绍一下Operators和围绕它的框架。

    回到最初的概念,Operator实际上就是K8s控制器,它的框架就是围绕K8s API提供Operator SDK的开发工具包,这有助于我们建立一个K8s控制器,然后封装其它组件,如Operator生命周期管理器——与Operator互动,并管理系统中所有Operator的生命周期。然后,通过Operator Metering实现使用报告和周围的东西。

    所以,基本上作为K8s控制器,Operator是有目的地建立与其需要执行的知识间的关系,这就是它的工作,通常比任何通用的工具更好、更智能。同样地,它还可以把自动的操作逻辑打包为Operator的一部分,进而允许复杂的自动化任务。

    TF Operator也一样,无非是一个K8s控制器,它的构建目的是解决Tungsten Fabric的生命周期管理需求,所以它本质上是一个TF的生命周期管理器。如果你看一下这些不同的组件在Operator框架中是如何定位的,就会发现Operator本质上是一个生命周期管理器,围绕它有一个封装的OLM,触发Operator实际进行安装和升级。

    8ff60953-24e7-4296-9c37-4308dec91d08-image.png

    围绕Tungsten Fabric生命周期管理的所有需求,目前包括OpenShift、Ansible、Operator Framework、Helm Charts,甚至Juju创建的各种资源库。由于多种安装方案的出现,以及各种集成的解决方案,基本上要把Tungsten Fabric的生命周期管理原生地做进这个方案。

    当你想用Ansible来做的时候,Tungsten Fabric所有的需求期待成为解决方案的一部分。当你想用OpenShift来做的时候,也是作为其中一部分方案来做的,而我们最后会把它们都复制过来。这本质上增加了任何一项工程的维护周期,额外的维护周期又涉及到更多的成本。

    所以我们一开始的目标很简单,就是看如何简化生命周期管理,尽量提高软件的可管理性,以及降低这些成本。而我们需要回答的进一步的问题,就是有没有一种方法来统一应对Tungsten Fabric的LCM处理?如何简化可以允许TF集成到各种部署方案中,而不用担心如何处理每一个独立部署组件之间的版本依赖性问题?当然,集群的扩展也是另一个要考虑的因素。

    这时候TF Operator就起到了一个重要的作用,让我们实现了生命周期管理器的统一。本质上TF Operator可以成为一个基于部署(based deployments)的生命周期管理器。当我们谈论TF Operator时,我们谈论的是K8s的原生控制器,其它组件部分都不在TF Operator的范围内,只与OLM有关。而我们在实现的时候,它所允许的是让我们能够重新使用TF Operator作为部署的一部分,也作为Helm deployment,以及Juju Charms的一部分。本质上是通过引入TF Operator来运行以及触发来做各种部署。

    通过这样的方式,基本上解决了各种K8s部署与TF集成的问题,比如Airship、OpenShift、K8s、kubeadm、Canonical等,甚至是OpenStack Helm部署。

    接下来,我们看下Airship集成的案例,这种整合是如何真正工作的,也将分享一个代码的链接,这是公开的,任何人都可以访问。

    首先从需求开始。无论何时,你想使用任何软件以实现首要的需求,我们要有一个Airship Armada Chart,这只不过是一个Helm Chart的wrapper,加入Airship站点定义。然后,Tungsten Fabric被包装为Helm Chart的形式,实际上可以部署SDN控制器、CNI,甚至是OpenStack Neutron。

    这种方案真正成功的原因,是你可以使用Helm Chart做一个常规的或基于概念验证的安装。目前,Helm Chart还没有足够的能力提供Tungsten Fabric所需要的生命周期管理的所有方面,这就是TF Operator能发挥更大作用的地方。

    6a69a971-d80f-4759-a124-0297cb7c8cf3-image.png

    如果你期待使用TF Operator作为其中的一部分,还要包括Operator本身的Helm Hook,所以Helm Chart不会部署TF本身,而是部署TF Operator,传统意义上它只提供站点定义作为其中的一部分。它将触发基于对象的K8s,本质上是以TF控制器的形式运行,这是由Operator本身管理的。为了完成这个动作循环,我们要确保等待TF控制器运行完成,并触发回Airship Armada Chart。当知道控制器已经运行,现在各种其它组件可以继续部署。把网络提供商看成一个基础设施组件,我们要确保这个触发的工作,否则组件运行会失败,这是非常重要的。

    我们有上述方案的参考实现方案,下面两个代码的链接都是可以公开访问的。

    https://github.com/atsgen/tf-Operator-helm-hook
    https://github.com/atsgen/treasuremap

    一旦我们有社区版TF Operator,我们将迁移这些方案,以使用社区版的TF Operator。以上是我们期待这个统一方案的问题解决方式。关于如何看待这个统一管理的成功,或者它真的可以走多远,欢迎任何人做出讨论、评论,我们要确保能够使用软件的LCM解决几乎所有的问题。


    视频链接:https://v.qq.com/x/page/x3218minifn.html
    文档:https://tungstenfabric.org.cn/assets/uploads/files/unified-life-cycle-management-using-tf-operator.pdf


    posted in 博客