網絡
網絡(network)是一個隔離的二層網段,類似於物理網絡世界中的虛擬 LAN (VLAN)。更具體來講,它是為創建它的租戶而保留的一個廣播域,或者被顯式配置為共享網段。端口和子網始終被分配給某個特定的網絡。根據網絡的類型,Neutron network 可以分為:
VLAN network(虛擬局域網) :基於物理 VLAN 網絡實現的虛擬網絡。共享同一個物理網絡的多個 VLAN 網絡是相互隔離的,甚至可以使用重疊的 IP 地址空間。每個支持 VLAN network 的物理網絡可以被視為一個分離的 VLAN trunk,它使用一組獨占的 VLAN ID。有效的 VLAN ID 范圍是 1 到 4094。
Flat network:基於不使用 VLAN 的物理網絡實現的虛擬網絡。每個物理網絡最多只能實現一個虛擬網絡。
local network(本地網絡):一個只允許在本服務器內通信的虛擬網絡,不知道跨服務器的通信。主要用於單節點上測試。
GRE network (通用路由封裝網絡):一個使用 GRE 封裝網絡包的虛擬網絡。GRE 封裝的數據包基於 IP 路由表來進行路由,因此 GRE network 不和具體的物理網絡綁定。
VXLAN network(虛擬可擴展網絡):基於 VXLAN 實現的虛擬網絡。同 GRE network 一樣, VXLAN network 中 IP 包的路由也基於 IP 路由表,也不和具體的物理網絡綁定。
關系:
(1)tenant —- 1:n —– network ——- 1:n ——- subnet (一個 tenant 可以擁有多個 network,一個 network 可以包含多個 subnet)
(2)network ——- 1: n ——- port —— 1:1 — subnet(一個network 可以有多個 port, 每個 port 連接一個 subnet)(若創建虛機時指定的是 net-id,那么虛機將隨機地從該 network 包含的 subnet 中分配 IP)
(3)VM —– 1 : n —- NIC —– 1:1 — port(一個 VM 可以有多個 NIC,每個 NIC 連接一個 port)(可以在創建虛機時指定一個或者多個 port)
(4)Tenant —– 1 : n —- Router —– 1 : n —— subnet/ext-network (一個 tenant 可以擁有多個 router,每個 router 在 Neutron network 節點上使用一個 Linux network namespace,其 ID 就是 neutron router-list 得到的 router 的 ID; 一個 router 連接一個通向外網的 gateway 和多個該 tenant 的 subnet)
(5)network —- 1 : 1 —- Dnamasq —– 1: n —– subnet (一個 network 有一個 Dnsmasq 進程,該進程為多個啟動了 DHCP 的 subnet 服務,分配它們擁有的 IP 給虛機)
Neutron 管理的實體如下:
網絡: 隔離的 L2 域,可以是虛擬、邏輯或交換,同一個網絡中的主機彼此 L2 可見。
子網: IP 地址塊,其中每個虛擬機有一個 IP,同一個子網的主機彼此 L3 可見。
端口: 網絡上虛擬、邏輯或交換端口。
Linux相關技術
Neutron 的設計目標是實現“網絡即服務”,為了達到這一目標,在設計上遵循了基於“軟件定義網絡”實現網絡虛擬化的原則,在實現上充分利用了 Linux 系統上的各種網絡相關的技術。
理解了 Linux 系統上的這些概念將有利於快速理解 Neutron 的原理和實現。
涉及的 Linux 網絡技術
bridge:網橋,Linux中用於表示一個能連接不同網絡設備的虛擬設備,linux中傳統實現的網橋類似一個hub設備,而ovs管理的網橋一般類似交換機。
br-int:bridge-integration,綜合網橋,常用於表示實現主要內部網絡功能的網橋。
br-ex:bridge-external,外部網橋,通常表示負責跟外部網絡通信的網橋。
GRE:General Routing Encapsulation,一種通過封裝來實現隧道的方式。在openstack中一般是基於L3的gre,即original pkt/GRE/IP/Ethernet
VETH:虛擬ethernet接口,通常以pair的方式出現,一端發出的網包,會被另一端接收,可以形成兩個網橋之間的通道。
qvb:neutron veth, Linux Bridge-side
qvo:neutron veth, OVS-side
TAP設備:模擬一個二層的網絡設備,可以接受和發送二層網包。
TUN設備:模擬一個三層的網絡設備,可以接受和發送三層網包。
iptables:Linux 上常見的實現安全策略的防火牆軟件。
Vlan:虛擬 Lan,同一個物理 Lan 下用標簽實現隔離,可用標號為1-4094。
VXLAN:一套利用 UDP 協議作為底層傳輸協議的 Overlay 實現。一般認為作為 VLan 技術的延伸或替代者。
namespace:用來實現隔離的一套機制,不同 namespace 中的資源之間彼此不可見。
GRE網絡, Vlan網絡, Vxlan網絡
舉例GRE網絡
在 VM1 中,虛擬機的網卡實際上連接到了物理機的一個 TAP 設備(即 A,常見名稱如 tap-XXX)上,A 則進一步通過 VETH pair(A-B)連接到網橋 qbr-XXX 的端口 vnet0(端口 B)上,之后再通過 VETH pair(C-D)連到 br-int 網橋上。一般 C 的名字格式為 qvb-XXX,而 D的名字格式為 qvo-XXX。注意它們的名稱除了前綴外,后面的 id 都是一樣的,表示位於同一個虛擬機網絡到物理機網絡的連接上。
br-tun轉發邏輯:
表 10 負責學習。有一條規則,基於 learn 行動來創建反向(內部網包從 gre 端口發出去)的規則。如下所示:
learn 行動並非標准的 openflow 行動,是 openvswitch 自身的擴展行動,這個行動可以根據流內容動態來修改流表內容。這條規則首先創建了一條新的流(該流對應 vm 從 br-tun 的 gre 端口發出的規則):
其中 table=20 表示規則添加在表 20;
NXM_OF_VLAN_TCI[0..11] 表示匹配包自帶的vlan id;
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] 表示 L2 目標地址需要匹配當前包的 L2 源地址;
load:0->NXM_OF_VLAN_TCI[],去掉vlan;
load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],添加 tunnel 號為原始 tunnel 號;
output:NXM_OF_IN_PORT[],發出端口為原始包抵達的端口。
向表 20 添加完規則后,最后將匹配的當前網包從端口 1(即 patch-int)發出。
舉例VLAN網絡:
路由是L3 agent來實現,每個子網在br-int上有一個端口(qr-YYY和qr-ZZZ,已配置IP,分別是各自內部子網的網關),L3 agent綁定到上面。要訪問外部的公共網絡,需要通過L3 agent發出,而不是經過int-br-ex到phy-br-ex(實際上並沒有網包從這個veth pair傳輸)。如果要使用外部可見的floating IP,L3 agent仍然需要通過iptables來進行NAT。
在多租戶情況下
舉例VXLAN網絡:(詳細分析)
當我們部署環境之后(一個網絡節點和兩個計算節點)我們查看計算機節點的狀態如下,此時並沒有vxlan隧道的建立。我們同時發現當創建完成網絡和子網的時候(創建了7.7.7.0/24子網),依然沒有vxlan隧道。
root@com2:~# ovs-vsctl show f4a15348-e3a9-4834-82ee-fa0beb4e9420 Bridge br-tun fail_mode: secure Port br-tun Interface br-tun type: internal Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Bridge br-int fail_mode: secure Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port br-int Interface br-int type: internal
只有當有虛擬機接入該子網時候,我們發現虛擬機所在的計算節點會和網絡節點建立vxlan隧道。
root@com2:~# ovs-vsctl show f4a15348-e3a9-4834-82ee-fa0beb4e9420 Bridge br-tun fail_mode: secure Port br-tun Interface br-tun type: internal Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Port "vxlan-0a0026df" Interface "vxlan-0a0026df" type: vxlan options: {df_default="true", in_key=flow, local_ip="10.0.38.218", out_key=flow, remote_ip="10.0.38.223"} Bridge br-int fail_mode: secure Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port "qvo0fc2bc51-8d" tag: 3 Interface "qvo0fc2bc51-8d" Port br-int Interface br-int type: internal Bridge br-trans Port br-trans Interface br-trans type: internal Port "eth2" Interface "eth2" Port "tra0fc2bc51-8d" Interface "tra0fc2bc51-8d" ovs_version: "2.5.0"
我們查看br-tun的流表如下(開啟了l2 population) :
當數據包由in_port = 1(也就是由br-int發出)到達table 0時候,會轉給table 2;如果是arp廣播請求,因為開啟l2 population,也就是arp_responder之后,會提交給 table 21。此時table21有條規則會處理這個請求,這條規則專門由l2 population發來的 entry 來更新 。
table 21 的更新過程並非網上所講的那么簡單,實際過程如下:假如我們創建了網絡A和網絡B ,有2個計算機點和1個網絡節點。創建一台虛擬機aa加入網絡A,同時落地在計算節點1時候,本計算節點的table21只有dhcp的arp的回應記錄。再創建一虛擬機bb加入網絡B,同時落地在計算節點2時候,本計算節點的table21只有dhcp的arp的回應記錄,計算節點1的table21並沒有bb的arp的回應記錄。創建第三台虛擬機加入網絡A,如果落地在計算節點1時候,兩個計算節點的table21沒有任何更新。當創建第四台虛擬機加入網絡A,同時落地在計算機點2時候,計算機點2的table 21更新了所有在計算節點1的同一網絡的虛擬機的arp信息,同時table 20記錄了所有到達計算機點1同一網絡的虛擬機的規則。計算節點1的table21也更新了計算節點2的同網絡的虛擬機的arp回應,table20也記錄了到達其的規則。
br.add_flow(table=21, priority=1, proto=’arp’, dl_vlan=local_vid, nw_dst= ip, actions=actions) 其中action為:
actions = (‘move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],’ – Place the source MAC address of the request (The requesting VM) as the new reply’s destination MAC address ‘mod_dl_src:%(mac)s,’ – Put the requested MAC address of the remote VM as this message’s source MAC address ‘load:0x2->NXM_OF_ARP_OP[],’ – Put an 0x2 code as the type of the ARP message. 0x2 is an ARP response. ‘move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],’ – Place the ARP request’s source hardware address (MAC) as this new message’s ARP target / destination hardware address ‘move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],’ – Place the ARP request’s source protocol / IP address as the new message’s ARP destination IP address ‘load:%(mac)#x->NXM_NX_ARP_SHA[],’ – Place the requested VM’s MAC address as the source MAC address of the ARP reply ‘load:%(ip)#x->NXM_OF_ARP_SPA[],’ – Place the requested VM’s IP address as the source IP address of the ARP reply ‘in_port’ % {‘mac’: mac, ‘ip’: ip}) – Forward the message back to the port it came in on Here’s the match part: self.tun_br.add_flow(table=constants.ARP_RESPONDER, – Add this new flow to the ARP_RESPONDER table priority=1, – With a priority of 1 (Another, default flow with the lower priority of 0 is added elsewhere in the code) proto=‘arp’, – Match only on ARP messages dl_vlan=lvid, – Match only if the destination VLAN (The message has been locally VLAN tagged by now) matches the VLAN ID / network of the remote VM nw_dst=‘%s‘ % ip, – Match on the IP address of the remote VM in question actions=actions)
如果table21還是無法解決arp的問題就發給table22 去 flood 到所有端口。如果不是arp請求就轉到table 20 ,通過已知學習到的mac地址從相應的口發出,如果不能處理也是發給table 22去flood到所有的端口。
當數據包從in_port=4(也就是由發往br-int)到達table 0時候,會提交給table 4, 它根據tunel號修改為內部vlan號,然后提交給table 10 ,它進行學習更新table 20,然后從patch-int發出。
table 10使用了 openvswitch 的 learn 動作。該動作能根據處理的流來動態修改其它表中的規則。因為此時訪問虛擬機的只有dhcp服務,我們看到table 20有一條到達dhcp的流表,這個流表就是這個learn動作生成的。
table=20 說明是修改表 20 中的規則,后面是添加的規則內容;
NXM_OF_VLAN_TCI[0..11],匹配跟當前流同樣的 VLAN 頭,其中 NXM 是 Nicira Extensible Match 的縮寫 ,dl_vlan=3;
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],將 mac source address 記錄,所以結果中有 dl_dst= fa:16:3e:c0:0e:07;
load:0->NXM_OF_VLAN_TCI[],在發送出去的時候,vlan tag設為0,所以結果中有 actions=strip_vlan;
load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],發出去的時候,設置 tunnul id,所以結果中有set_tunnel: 0x8;
output:NXM_OF_IN_PORT[],指定發送給哪個port,由於是從 port 4進來的,因而結果中有output4。
root@com2:~# ovs-ofctl show br-tun OFPT_FEATURES_REPLY (xid=0x2): dpid:00005e38cc7c8e43 n_tables:254, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(patch-int): addr:ea:ff:57:bb:5f:2a config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max 4(vxlan-0a0026df): addr:02:1c:31:dd:58:08 config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max LOCAL(br-tun): addr:5e:38:cc:7c:8e:43 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 root@com2:~# ovs-ofctl dump-flows br-tun NXST_FLOW reply (xid=0x4): cookie=0xae4f2288fae3c173, duration=75532.472s, table=0, n_packets=14490, n_bytes=1290354, idle_age=0, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2) cookie=0xae4f2288fae3c173, duration=84.773s, table=0, n_packets=2, n_bytes=732, idle_age=73, priority=1,in_port=4 actions=resubmit(,4) cookie=0xae4f2288fae3c173, duration=75532.472s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=846, n_bytes=50760, idle_age=0, hard_age=65534, priority=1,arp,dl_dst=ff:ff:ff:ff:ff:ff actions=resubmit(,21) cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=13547, n_bytes=1230482, idle_age=709, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20) cookie=0xae4f2288fae3c173, duration=75532.471s, table=2, n_packets=97, n_bytes=9112, idle_age=31, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22) cookie=0xae4f2288fae3c173, duration=75532.471s, table=3, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop cookie=0xae4f2288fae3c173, duration=85.471s, table=4, n_packets=2, n_bytes=732, idle_age=73, priority=1,tun_id=0x8 actions=mod_vlan_vid:3,resubmit(,10) cookie=0xae4f2288fae3c173, duration=75532.470s, table=4, n_packets=2, n_bytes=140, idle_age=2866, hard_age=65534, priority=0 actions=drop cookie=0xae4f2288fae3c173, duration=75532.470s, table=6, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop cookie=0xae4f2288fae3c173, duration=75532.470s, table=10, n_packets=12541, n_bytes=1237278, idle_age=73, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xae4f2288fae3c173,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1 cookie=0xae4f2288fae3c173, duration=84.772s, table=20, n_packets=0, n_bytes=0, idle_age=84, priority=2,dl_vlan=3,dl_dst=fa:16:3e:c0:0e:07 actions=strip_vlan,set_tunnel:0x8,output:4 cookie=0xae4f2288fae3c173, duration=73.586s, table=20, n_packets=0, n_bytes=0, hard_timeout=300, idle_age=73, priority=1,vlan_tci=0x0003/0x0fff,dl_dst=fa:16:3e:c0:0e:07 actions=load:0->NXM_OF_VLAN_TCI[],load:0x8->NXM_NX_TUN_ID[],output:4 cookie=0xae4f2288fae3c173, duration=75532.470s, table=20, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=resubmit(,22) cookie=0xae4f2288fae3c173, duration=84.772s, table=21, n_packets=0, n_bytes=0, idle_age=84, priority=1,arp,dl_vlan=3,arp_tpa=7.7.7.2 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:c0:0e:07,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163ec00e07->NXM_NX_ARP_SHA[],load:0x7070702->NXM_OF_ARP_SPA[],IN_PORT cookie=0xae4f2288fae3c173, duration=75532.469s, table=21, n_packets=827, n_bytes=49620, idle_age=0, hard_age=65534, priority=0 actions=resubmit(,22) cookie=0xae4f2288fae3c173, duration=84.758s, table=22, n_packets=84, n_bytes=5614, idle_age=0, dl_vlan=3 actions=strip_vlan,set_tunnel:0x8,output:4 cookie=0xae4f2288fae3c173, duration=75532.462s, table=22, n_packets=31, n_bytes=2778, idle_age=84, hard_age=65534, priority=0 actions=drop
下圖是沒有開啟l2popluation功能,從上述流表看出與開啟l2popluation功能不同。
L2 Population
L2 Population 是用來提高 VXLAN 網絡 Scalability 的,減少廣播風暴。
這是一個包含 5 個節點的 VXLAN 網絡,每個節點上運行了若干 VM。現在假設 Host 1 上的 VM A 想與 Host 4 上的 VM G 通信,VM A 要做的第一步是獲知 VM G 的 MAC 地址。 於是 VM A 需要在整個 VXLAN 網絡中廣播 APR 報文:“VM G 的 MAC 地址是多少?” 如果沒有L2poluation,情況會如下:
L2 Population 的作用是在 VTEP 上提供 Porxy ARP 功能,使得 VTEP 能夠預先獲知 VXLAN 網絡中,包括VM IP – MAC 對應關系和 VM – VTEP 的對應關系。當 Host 1 上的 同網段的VM A 想與 Host 4 上的同網段的 VM G 通信時,Host 1 上的 VTEP 直接響應 VM A 的 APR 請求即可(為什么HOST1 上會響應,參看上文),告訴VM G的MAC地址。當Host 1 上的VMA想與不同網段的VM G 通信時,首先Host 1 上的 VTEP 響應 VMA所在網絡的網關的arp信息(當路由器創建並關聯網絡時候,L2 Population會使得該網絡所跨的所有HOST的VTEP更新該路由器的arp回應信息和如何到達的該路由器的流表,說具體就是table21和table20的更新),數據包會發送給路由器,到達路由器之后,如果路由器同時關聯着VM G 所在的網絡,它就會發送arp廣播請求VM G 的mac,此時網絡節點的table21會響應(L2 Population的功能)VM G的信息,路由器知道了VM G的mac地址后,發送單播,由table0到table2,再由table20匹配相關流表,tunnel發出。數據包到達VM G所在的HOST之后,首先匹配table0,然后轉給table4,table4去tunnel轉給table10, table10 學習完存到table 20之后從patch-int口仍給br-int。br-int相當於一個普通的交換機,將數據包轉發給VM G。
另外我還發現如果開啟了l2population之后,table10的學習可以去掉。
ovs-ofctl add-flow br-tun "table=10, n_packets=0, n_bytes=0, idle_age=62653, priority=1 actions=output:1"