標籤:

VLAN Trunk in OpenStack Neutron and SDN

Neutron 系列文章 Neutron Topic Tree:

好久沒有說OpenStack相關的內容了,這次說說OpenStack對於VLAN Trunk的支持。OpenStack對VLAN Trunk的支持具體是什麼?雖然OpenStack與容器,物理主機也做了集成,但是OpenStack最主要的應用還是虛機管理,而現代的操作系統,不論是Linux還是Windows,都支持將網卡配置成Trunk port。OpenStack對VLAN Trunk的支持就是指對OpenStack所管理的虛機的Trunk port,提供網路支持。

什麼是主機的Trunk port?一般來說VLAN只是對交換機可知,主機不感知VLAN,也就是主機網卡發送出來的數據是不帶VLAN Tag的。當主機將網卡配置成Trunk port,會在主機的Trunk port上添加多個帶有不同VLAN ID的子網卡。通過子網卡發送的數據,會打上VLAN Tag,被發送出去。由於主機發送的網路數據有了變化(帶上了VLAN Tag),對主機連接的網路提出了新的要求。另一方面,由於帶了不同的VLAN Tag,子網卡可以認為連接在VLAN Tag標識的網路中。這樣,不用實際增減主機的網卡,只需要為主機創建帶VLAN ID的子網卡,就可以將主機添加到新的VLAN網路中。

主機的Trunk port有什麼用?一個應用是在NFV(Network Function Virtualization)場景下。在NFV場景下,一個VNF(Virtual Networking Function)可能需要同時連接多個網路。當然最簡單是一個網路對應一塊網卡,但如果網路數量巨大,網卡數量也巨大,一方面這不現實。另一方面,連接的多個網路也是動態增減的,如果每次增減都插拔VNF的網卡,那明顯也是不能接受的。如果用Trunk port,當需要動態連接多個網路時,只需要動態的創建/刪除相應的子網卡即可。

本文將先介紹一下VLAN Trunk的基本概念,之後介紹OpenStack Neutron和OpenFlow based SDN是如何為Trunk port提供網路支持。

VLAN

一個個說,先簡單說一下VLAN。VLAN(Virtual LAN)由IEEE802.1Q定義。要講VLAN要先從廣播域(Broadcast Domain)開始說起。在下圖中,假設PC1-5在一個廣播域中,當PC1發出廣播幀,例如ARP,DHCP,由於目的MAC地址是廣播地址,同一個廣播域內的所有設備都將收到相應的廣播幀。如果PC1-5在一個子網中,那這本身也無可厚非,因為這正是ARP,DHCP的工作原理。

如果PC1-5不在一個子網中呢?例如上圖中,PC1在橙色子網,PC2-3在綠色子網,PC4-5在紅色子網。理論上,PC1發出的廣播幀只應該在橙色子網內傳播。但實際上,是由於PC1-5在一個廣播域中,PC2-5還是會收到PC1的廣播幀。雖然PC2-5會直接丟棄這個廣播幀,但是這裡存在安全問題和性能問題。首先,PC2-5雖然不會處理PC1發來的廣播幀,但是數據還是發送過來了,通過偵聽和偽裝,可以達到劫持的目的,例如ARP spoof。其次,廣播幀本來不用在整個網路傳播,但現在就整個網路傳播,這樣佔用了交換機的帶寬,在大規模網路中,尤其會影響網路性能。解決這些問題的方法就是使用VLAN。

VLAN是一組網路埠的集合。實現了VLAN的交換機,會將二層的單播,組播,廣播都局限在一個VLAN中,這樣不同的VLAN中的主機,就不會收到其他VLAN中主機的數據。並且,只有交換機能看到VLAN,連接交換機的其他設備,例如Server,Router,都感覺不到VLAN的存在。將不同的子網規劃在不同的VLAN當中,可以實現子網的隔離。即使所有的設備都在一個交換機上,不同的VLAN之間通信也需要通過Router

VLAN Trunk

VLAN的劃分並非只能在一個交換機上,多個交換機可以共同組成VLAN。那多個交換機上的VLAN是怎麼連接到一起的?有兩種方法。

第一種方法是把每個交換機上的每個VLAN都連在一起。如下圖所示:

圖中有兩個交換機,各有兩個VLAN,為了讓VLAN能正常工作,將VLAN1和VLAN2分別連接起來。如果是兩個交換機,兩個VLAN還好,如果是100個VLAN,那麼這裡需要有100條線路,200個交換機埠。這麼連接可以嗎?可以,只要有錢,因為這裡的線路和交換機埠都是錢。這種方法,運維和成本支出比較大,不實用。

第二種方法是通過VLAN Trunk連接交換機。VLAN Trunk是一種網路設備間的point-to-point的連接。一條VLAN Trunk線路可以同時傳輸多個甚至所有的VLAN數據。

由於VLAN Trunk可以傳輸多個VLAN數據,Ethernet Frame在VLAN Trunk上傳輸時,需要帶上802.1Q定義的VLAN tag,這樣交換機才能區分Ethernet Frame到底屬於哪個VLAN。VLAN Trunk的具體使用過程如下:

  • 當最左側伺服器想要訪問最右側伺服器,最左側伺服器將Ethernet Frame發送到左側交換機
  • 左側交換機本地沒有目的MAC對應的轉發信息,因此Ethernet Frame發送到了左側交換機的VLAN Trunk port
  • 由於這是來自VLAN100的Ethernet Frame,交換機給Ethernet Frame打上VLAN 100的Tag,從自己的VLAN Trunk port發出,發送到右側交換機的VLAN Trunk port
  • 右側的VLAN Trunk port收到VLAN 100的Ethernet Frame,去除VLAN Tag,再在本地的VLAN 100埠中查找MAC轉發
  • 找到對應的MAC/埠記錄,最終Ethernet Frame發送到最右側的伺服器

如果是VLAN200的數據,過程一樣,只是VLAN Tag從100變成了200。

通過VLAN Trunk port,兩個交換機之間不論需要連接多少個VLAN,只需要一個VLAN Trunk連接(一對Trunk port)即可。

如果有多個交換機需要連接,通常會用另外一個交換機連接它們,如下圖所示,交換機之間都通過Trunk port相連。

紅色框內的交換機的4個port都是Trunk port,任意的VLAN 標籤的Ethernet Frame都可以在這個交換機內傳輸。可以把紅色框內看成是一個Trunk network,一個Trunk network可以傳輸帶VLAN標籤的Ethernet Frame。雖然圖中只有一個交換機,但是Trunk network可以更加複雜,由多個交換機組成。多個VLAN的網路數據共同在Trunk network上傳輸,通過VLAN Tag來識別VLAN。

如果把上圖中下面四個交換機換成主機(虛機),那就是OpenStack VLAN Trunk希望支持的場景。為了支持這個場景,需要為虛機提供一個Trunk Network。

Linux VLAN Trunk

Windows也支持Trunk port,與Linux類似,這裡只說明Linux系統下的Trunk port。

Linux系統可以通過內核模塊8021q支持VLAN Trunk。這裡有什麼不一樣?在一般情況下,主機是不感知VLAN Tag的,也就是說主機發送的網路數據都是不帶VLAN Tag,所有VLAN Tag操作都是由交換機完成。但是實際上交換機也不知道自己連接的是什麼,所以,如果在主機完成VLAN Tag的操作,再發送到交換機,交換機也能處理。基於這個前提,Linux能夠將一塊乙太網卡配置成一個支持802.1q的Trunk port,使得這塊網卡跟前面描述的交換機上的Trunk port一樣,能夠收發多個VLAN的網路數據包。並且通過配置Linux主機的子網卡,可以使得Linux主機內部完成VLAN Tag的操作(打上VLAN Tag,去除VLAN Tag)。有不止一種方法可以配置Linux VLAN Trunk,這裡以ip命令為例,為eth0添加名為eth0.102的子網卡,其VLAN ID為102。

$ sudo ip link add link eth0 name eth0.102 type vlan id 102

就這麼簡單,之後可以看到一個新的網卡在系統的網卡列表中。

Linux VLAN Trunk是這麼個工作過程:

  • 外界傳入的帶VLAN Tag 102的包,到達了eth0,VLAN Tag 102被去除,然後不帶VLAN Tag的Ethernet Frame被重新發往操作系統網路棧,再發送到eth0.102。
  • 從eth0.102發送出來的幀,被打上VLAN102的標籤,再從eth0傳出。

應用層面,收發的還是不帶VLAN Tag的數據,只是經過Linux VLAN Trunk的處理,進出主機的的數據是帶VLAN Tag的數據。

由於eth0配置成了VLAN Trunk port。為了讓帶有VLAN標籤的Ethernet Frame能夠在網路上傳輸。eth0所接入的網路必須是一個Trunk network。否則無法傳輸任意帶VLAN Tag的Ethernet Frame。也就是說,需要將主機的Trunk port連接到紅框內。接下來我們看看OpenStack 是怎麼做的。

Neutron VLAN Transparency

這是OpenStack Kilo版本的特性,由VLAN trunking networks for NFV定義。這個特性為Neutron network增加一個屬性「vlan-transparent」,當vlan-transparent為True時,表明在這個Neutron network是一個Trunk network。在這個Neutron network中的虛機可以使用Linux VLAN Trunk功能。接下來看看Neutron為了實現這個特性做了什麼?

什麼也沒做!Neutron只是提供了這麼一個屬性,具體的實現交給底層的SDN。如果SDN不支持VLAN Transparency,可以在ML2中告知Neutron。那麼用戶在創建Neutron network時,如果指定了vlan-transparent=True,Neutron會返回「Backend does not support VLAN Transparency.」。例如OpenVSwitch,就不支持VLAN Transparency。

所以,並非所有的SDN都支持這個功能,就算實現了,每種SDN的實現方式不一樣。不過本質上,都是將SDN控制的vSwitch配置成Trunk模式。需要注意的是,vSwitch最終還是要連接到Physical Switch,所以Physical Switch相應的埠也需要配置成Trunk port。

Neutron VLAN aware VMs

這是OpenStack Newton版本的特性,由VLAN aware VMs定義。VLAN aware VMs和VLAN Transparency說的其實是一件事情,就是如何傳遞VM發出來的帶VLAN Tag的幀。

先來看看VLAN Transparency的問題,

  • 不支持OpenVSwitch
  • 要求Physical Switch也配置相應的Trunk port

VLAN aware VMs在支持相同功能的前提下,解決了上面兩個問題。首先看看如何使用VLAN aware VMs這個功能。

使用

我用的是最新代碼(Pike 2017-08-08)配合OpenVSwitch完成下面的操作。要使能VLAN aware VMs,只需要在/etc/neutron/neutron.conf中,在service_plugins後面加上trunk。

首先創建Trunk port所在的網路和VLAN子網卡所在的網路。

$ openstack network create parent-net$ openstack subnet create parent-subnet --network parent-net --subnet-range 20.0.0.0/24$ openstack network create sub-net$ openstack subnet create sub-subnet --network sub-net --subnet-range 21.0.0.0/24

注意這裡我並沒有指定網路的類型,網路可以不用是VLAN的,像在我的環境中,網路就是VXLAN類型。為什麼會這樣?在後面會說。

接下來創建Trunk port和subport。

$ openstack port create parent-port1 --network parent-net$ openstack port create sub-port1 --network sub-net$ openstack network trunk create trunk1 --parent-port parent-port1$ openstack network trunk set trunk1 --subport port=sub-port1,segmentation-type=vlan,segmentation-id=102$ openstack port create parent-port2 --network parent-net$ openstack port create sub-port2 --network sub-net$ openstack network trunk create trunk2 --parent-port parent-port2$ openstack network trunk set trunk2 --subport port=sub-port2,segmentation-type=vlan,segmentation-id=102

這裡創建了兩組4個port,並用trunk模型將它們關聯起來了。除此之外,還為Trunk中的subport指定了segmentation-type和segmentation-id。VLAN是目前OpenVSwitch支持的唯一的segmentation type(也是大多數SDN唯一支持的類型),segmentation id就是在虛機中創建subport時指定的VLAN ID。

接下來用parent port創建兩個虛機。

$ nova boot --image ubuntu --flavor 2 --nic port-id=<parent-port1 id> --key-name=<your key name> vm1$ nova boot --image ubuntu --flavor 2 --nic port-id=<parent-port2 id> --key-name=<your key name> vm2

因為Linux VLAN Trunk需要8021q內核模塊的支持,這裡用了一個Ubuntu16.04的image。

到此為止,OpenStack的操作完成了。接下來,在兩個虛機內部,配置Linux VLAN Trunk。在vm1內部,做如下配置:

ubuntu@vm1:~$ sudo ip link add link ens3 name ens3.102 type vlan id 102ubuntu@vm1:~$ sudo ip link set dev ens3.102 address <sub-port1 mac address>ubuntu@vm1:~$ sudo ip link set dev ens3.102 up

這裡給虛機的subport配置的VLAN ID是102,這是之前通過OpenStack命令創建Trunk時指定的segmentation-id。這裡的ens3.102對應之前創建的sub-port1,為了讓ens3.102能使用Neutron DHCP服務,這裡將Neutron分配給sub-port1的MAC地址配置在ens3.102上。Linux子網卡默認會跟父網卡用同一個MAC地址,所以也可以在用OpenStack命令創建subport的時候,指定MAC地址。總之,這裡我們讓虛機內的子網卡的MAC地址與OpenStack Neutron中對應的subport的MAC地址一致。

在vm2內部做同樣的操作,只是MAC地址換成成sub-port2的MAC地址。

驗證

DHCP服務

在兩個虛機內部,通過dhclient命令獲取子網卡IP地址。

ubuntu@vm1:~$ sudo dhclient -v ens3.102 DHCPDISCOVER on ens3.102 to 255.255.255.255 port 67 interval 3 (xid=0x8e2bc124)DHCPREQUEST of 21.0.0.6 on ens3.102 to 255.255.255.255 port 67 (xid=0x24c12b8e)DHCPOFFER of 21.0.0.6 from 21.0.0.2DHCPACK of 21.0.0.6 from 21.0.0.2bound to 21.0.0.6 -- renewal in 42323 seconds.

可以看到,子網卡也可以通過Neutron的DHCP服務獲取IP地址。

VLAN互通

在vm1內部,ping vm2.

ubuntu@vm1:~$ ping 21.0.0.7PING 21.0.0.7 (21.0.0.7) 56(84) bytes of data.64 bytes from 21.0.0.7: icmp_seq=1 ttl=64 time=10.2 ms64 bytes from 21.0.0.7: icmp_seq=2 ttl=64 time=0.979 ms64 bytes from 21.0.0.7: icmp_seq=3 ttl=64 time=0.817 ms

在vm2內部,查看收到的包。前面講過Linux VLAN Trunk的工作過程,Ethernet Frame發送到ens3.102時,已經剝離了VLAN標籤。為了查看VLAN信息,所以我們這裡直接監聽ens3。

ubuntu@vm2:~$ sudo tcpdump -nei ens3 icmp08:42:24.256617 fa:16:3e:61:62:fa > fa:16:3e:da:61:99, ethertype 802.1Q (0x8100), length 102: vlan 102, p 0, ethertype IPv4, 21.0.0.6 > 21.0.0.7: ICMP echo request, id 11731, seq 388, length 64 08:42:25.258424 fa:16:3e:61:62:fa > fa:16:3e:da:61:99, ethertype 802.1Q (0x8100), length 102: vlan 102, p 0, ethertype IPv4, 21.0.0.6 > 21.0.0.7: ICMP echo request, id 11731, seq 389, length 64

可以看到,vm2收到的原始Ethernet Frame是帶VLAN Tag的。

原理

註:這部分內容假設你對OpenStack Neutron已經有一定的了解了。

雖然上面完成了VLAN aware VMs的配置和Linux VLAN Trunk的驗證,甚至虛機內的subport都可以使用Neutron的DHCP服務,但是這裡留下了兩個問題沒有解答。

  • 首先,不用在vSwitch和Physical switch上打開trunk模式,就能支持帶VLAN Tag的幀的傳遞,這中間發生了什麼?
  • 其次,一個傳輸VLAN數據的subport如何能使用定義在VXLAN網路中的DHCP服務?

看完Neutron VLAN aware VMs的架構,這兩個問題自然就能解決。首先我們看看計算節點上的OpenVSwitch網橋。可以發現,對每個Trunk port,對應的新增了一個網橋。

在這個網橋的埠中,qvo埠是用來連接虛機的,剩下的埠中,一個是bridge對應的埠,剩下兩個都是patch port。其中一個還帶了VLAN Tag 102,正是我們配置的segmentation-id。patch port都是成對出現,這裡的patch port peer都掛在br-int上。具體的網橋和埠的關係如下圖所示:

左上角是正常的OpenVSwitch上連接的虛機,為什麼不是虛機直接到OpenVSwitch網橋?這是歷史遺留問題,這裡不做解釋,大家只需要知道qvo是br-int上用來連接虛機的埠。再看虛線框內,上半部分與原來虛機伸出的部分相同,下半部分就是之前展示的新增的網橋。網橋向上通過qvo連接虛機。向下通過patch port pair <tpt-tpi>連接parent port,從虛機出來的不帶VLAN Tag的幀,從這對patch port走到br-int。同時向下通過patch port pair <spt-spi>連接subport,一個subport對應一對spt-spi,注意的是每個spt都帶有相應的VLAN Tag,根據OVS的特性,所有從虛機出來的,帶上相應VLAN Tag的幀都會發送到相應的spt,再通過spi發送到br-int。

所以,從虛機出來的Ethernet Frame,根據所攜帶的VLAN Tag(或者不帶VLAN Tag),在tbr網橋上,可以分配到不同的patch port,進而轉發到br-int上的不同埠。也就是說,在tbr上,從虛機里一塊網卡發出來的Ethernet Frame被分流了。

在br-int上,其實不知道qvo、spi,tpi有什麼區別,這些埠對於br-int來說地位是一樣的。這些埠分別在不同的網路中(最開始為parent port和subport分別創建了網路),br-int根據埠所在的網路,將虛機發出來的Ethernet Frame打上Local network ID(又一個VLAN ID,類似於VTEP中VLAN ID與VXLAN ID對應中的VLAN),發往br-tun。在br-tun根據Local network ID轉換成VXLAN數據發送出去。

在對端,經過br-tun發送到br-int,br-int根據不同的網路,將數據發送到相應的qvo,spi,tpi。這裡看spi這條線路,因為patch port的相連,數據發送到了tbr網橋,再經由qvo發向虛機。由於qvo沒有自己的VLAN Tag,所以它將保留入口埠的VLAN Tag,也就是VLAN 102,最後VLAN 102的幀發送給虛機。

總結一下Ethernet Frame的處理流程。從虛機發出來的VLAN數據,在tbr網橋通過相應的patch port <spt-spi>對,發送到br-int。在patch port對的傳遞過程中,VLAN Tag由虛機內定義的VLAN ID變成了br-int內部的Local network ID。br-int將數據發送給br-tun。br-tun根據自身記錄的VLAN ID與VXLAN ID的對應,將VLAN數據中VLAN Tag去除,並將剩餘的Ethernet Frame封裝成VXLAN數據,在VXLAN tunnel上傳輸。

對端收到VXLAN數據,在br-tun轉換成VLAN數據,VLAN Tag是Local network ID,再發送給br-int。br-int根據Local network ID和目的MAC地址,將VLAN數據從對應的埠(spi)發出。經過patch port <spi-spt>對的傳輸,VLAN Tag由Local network ID變成了spt上的VLAN Tag,最後通過qvo發送給虛機。虛機收到還是VLAN 102的數據,但是數據在中間是通過VXLAN網路傳輸

所以為什麼不需要vSwitch和Physical Switch對Trunk模式進行支持?因為VLAN數據換成了VXLAN數據,傳輸的都不是VLAN了,自然不需要交換機配置Trunk。為什麼子網卡能使用Neutron的DHCP服務,因為數據被換成了Neutron network網路數據(VXLAN),接入到Neutron network,自然就能使用Neutron network內的服務。

SDN實現VLAN aware VMs

Neutron在OpenVSwitch上實現的VLAN aware VMs原理在上面介紹過,不得不說,這個方案比較複雜,希望我已經說清楚了。雖然這個方案不依賴物理網路配置Trunk,但是需要創建額外的網橋和埠,對應的轉換步驟也太多了。這麼實現有Neutron的歷史原因,也有Neutron輕度依賴OpenFlow的因素在裡面。我們接下來看看在OpenFlow中,如何實現VLAN aware VMs。

VLAN aware VMs的核心思想是,用Neutron tenant network的overlay網路,來傳輸Linux VLAN Trunk發出來的各種VLAN數據。像上面的介紹,就是用VXLAN網路來傳輸虛機發出的VLAN數據,在進出虛機的時候再對VLAN Tag做相應的處理。相同的功能在OpenFlow中實現就簡單的多,我們以Dragonflow項目的實現為例,具體介紹在Spec for VLAN aware VMs。只需要在網橋上添加幾條OpenFlow流表,就能識別虛機發出的數據,並分發到不同的Neutron tenant network,進而通過Neutron tenant network傳輸。

在虛機的出口,添加下列流表:

table=0, priority=150,in_port=183,vlan,vlan_tag=102 actions=strip_vlan,load:0x168->NXM_NX_REG6[],load:0x2->OXM_OF_METADATA[],resubmit(,5) table=0, priority=100,in_port=183 actions=load:0x166->NXM_NX_REG6[],load:0x1->OXM_OF_METADATA[],resubmit(,5)

這裡的REG6表明入口埠,METADATA用來識別網路,可見,在table0就已經完成了將虛機中一塊網卡發出的Ethernet Frame根據VLAN Tag分成不同的通道,並識別相應的網路。之後的傳輸不需要區分究竟是不是子網卡的數據,就是當成普通的overlay tenant network傳輸。

在虛機的入口,添加下列流表:

table=115, priority=100,reg7=0x168 actions=mod_vlan_vid:vlan_vid:0x10066,output:183table=115, priority=100,reg7=0x166 actions=output:183

「mod_vlan_vid:vlan_vid:0x10066」就是打上VLAN Tag102,可以看出不同網路的數據,打上不同的VLAN Tag(對於parent port不打VLAN Tag)發向了同一個埠183(虛機網卡)。

沒有多餘的網橋,沒有網橋上換來換去的VLAN ID,僅僅是在入口和出口處去除VLAN Tag和打上VLAN Tag,幾條流表就實現了所有的功能。簡單的實現一般意味著更高的效率和更少的錯誤,因此使用SDN方式實現VLAN aware VMs更具有優勢。

最後

最後我們再看看VLAN Trunk在NFV中的應用吧,這個在最開頭一些簡單的介紹,這裡來說個用例。一個多路復用設備,需要連接多個子設備,子設備的數目是不確定的,且有可能變化,並且要求子設備間網路是隔離的。當然,我也可以給多路復用設備配置多塊網卡,但是由於子設備數目不確定,網卡數目是不確定的。這個時候可以將多路復用設備的一塊Ethernet網卡配置成VLAN Trunk port,在之上創建相應的subport。子設備上的網卡也配置成VLAN Trunk port,並創建對應的subport。由於配置的VLAN不一樣,不同子設備之間的網路是隔離的。並且多路復用設備的subport是可以動態刪減的,這滿足了子設備數據是不確定的要求。具體如下圖所示:

不論是VLAN Transparency還是VLAN aware VMs,都是為了使得OpenStack中的虛機能夠使用Linux VLAN Trunk功能。VLAN Transparency依賴vSwitch和物理交換機的配置,不說實際物理交換機的複雜性,單就是vSwitch也不一定支持Trunk模式。VLAN aware VMs通過一系列的轉換,擺脫了對vSwitch和物理交換機配置的依賴,走在網線上的數據甚至都可以不是VLAN數據,這比VLAN Transparency進了一步。而通過SDN實現VLAN aware VMs可以大大簡化實現過程,又更進了一步。

推薦閱讀:

優雅安裝OpenStack
關於openstack的部署架構的一點兒疑問?
哪裡有Openstack入門到精通視頻教程下載?

TAG:VLAN | OpenStack | SDN |