天天看點

了解 OpenStack 高可用(HA):Neutron 分布式虛拟路由(下)

1. 代碼分析

DVR 代碼修改包括幾部分:

  1. DVR Router network namespace 的建立和删除
  2. DVR Router 相關的 flows
  3. DVR Router 的 ARP 表

1.1 DVR Router 相關 network namespace 的建立和删除

1.1.1 qrouter 在計算和網絡節點上的删除和建立

  1. 對于每一個 DVR Router,在每個分布了和 router 連接配接的網段内的虛機的計算節點上,都會有一個 qrouter 執行個體。兩種情況下會将一個 DVR Router 部署到一個 L3 Agent 上:
    • 當一個子網 subnet 被加入到一個 DVR Router 時,DVR Router 會被分布到所有包含在該子網内的虛機的計算節點上。
    • 計算節點上的 L3 Agent 會收到一個通知,它會配置 router

      OVS Agent 會将 router 的端口 plug 到 OVS Bridge 上,并且配置 flows

  2. 當一個虛機被建立,而且虛機所在的計算節點上不存在該虛機所在 subnet 連接配接的 DVR Router 時。
  3. 當與 DVR Router 相關的最後一個虛機被删除時,router namespace 會被從虛機所在的計算節點上删除。

1.1.2 snat 在網絡節點上的建立和删除

建立:當設定 router 的 external gateway 時

删除:當删除 router 的 external gateway 時

1.1.3 fip 在 計算節點上的建立和删除

建立:當一個浮動 IP 被配置設定給一個虛機的時候,如果虛機所在的計算節點上 fip namespace 不存在,則建立它

删除:(1)當計算節點上最後一個使用浮動 IP 的虛機被删除後 (2)所有虛機的浮動 IP 被删除後

1.2 DVR MAC 位址

前面提到過,分布到多個計算節點上的 qrouter 的interface 的 MAC 位址都相同。這在傳統的網絡中是不允許的,在 neutron 網絡中某些時候也會導緻一些問題。Neutron的做法是會向每個計算節點配置設定一個唯一的 DVR Host MAC 位址。當使用了 DVR 的 OVS Agent 啟動的時候,它通過 RPC 去從 neutron server 上申請該 MAC 位址。該 MAC 位址會被儲存在 DB 中,與該計算節點強綁定。比如:

MariaDB [neutron]> select * from dvr_host_macs;
+----------+-------------------+
| host     | mac_address       |
+----------+-------------------+
| network  | fa:16:3f:12:a3:38 |
| compute1 | fa:16:3f:b2:34:82 |
| compute2 | fa:16:3f:db:6f:73 |
+----------+-------------------+
           

當資料包離開 DVR Router 經過 br-tun 時,OVS flows 會将 DVR Router interface 的源 MAC 位址替換成該 MAC 位址。

root@compute1:/home/s1# ovs-ofctl dump-flows br-tun | grep mod_dl_src
 cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:ec:f3:dd actions=mod_dl_src:fa::f:b2::,resubmit(,)
 cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e::b:c actions=mod_dl_src:fa::f:b2::,resubmit(,)
 cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:a9:da:b5 actions=mod_dl_src:fa::f:b2::,resubmit(,)
           

而 src mac 位址分别是 qrouter 上的作為預設各網段的預設網關的 mac 位址:

s1@controller:~$ neutron port-list | grep fa::e:ec:f3:dd
| f849ae46--f5-a805-d4e31951 |      | fa::e:ec:f3:dd | {"subnet_id": "f8841500-b392-4053-bda1-acf419f4a86e", "ip_address": "90.1.180.1"} 
s1@controller:~$ neutron port-list | grep fa::e::b:c
| bdba3-b117-ce-b-bb1d039879dc |      | fa::e::b:c | {"subnet_id": "4ec65731-35a5-4637-a59b-a9f2932099f1", "ip_address": "81.1.180.1"} 
s1@controller:~$ neutron port-list | grep fa::e:a9:da:b5
| e47fca31-dbf6-e5-ccb-0950557a55e3 |      | fa::e:a9:da:b5 | {"subnet_id": "13888749-12b3-462e-9afe-c527bd0a297e", "ip_address": "91.1.180.1"} 
           

是以,這裡假設你不會将多個 DVR Router 連接配接到一個 subnet。當資料包達到該計算節點時,OVS flows 會将其源 MAC 位址替換成 VM gateway 的 MAC 位址。

DVR-MAC-ADDRESS 的更新是 neutron server 通過 RPC Notifier 做的。每當一個新的位址被配置設定後,它通知所有的 L3 Agent 節點做處理。

1.3 DVR OVS flows

使用 DVR Router 的計算節點上,br-int 和 br-tun 中的 flows 會有修改。具體請參見上文的 3.2.4 部分。

1.3.1 br-int flows 的主要修改

table 1: DVR_TO_SRC_MAC

table 0:LOCAL_SWITCHING

[email protected]:/home/s1# ovs-ofctl dump-flows br-int
NXST_FLOW reply (xid=):
#擷取所有的 DVR_MAC_ADDRESS,然後
# 從 patch-tun 進入的 src mac 為 network 節點的 DVR HOST MAC 的網絡幀(也就是從 network 節點來的幀),重新送出 table 1 處理
table=, n_packets=, n_bytes=, idle_age=, priority=,in_port=,dl_src=fa::f::a3: actions=resubmit(,)
# 從 compute 2 節點來的網絡幀,送出到 table 1
table=, n_packets=, n_bytes=, idle_age=, priority=,in_port=,dl_src=fa::f:db:f: actions=resubmit(,)
#本機上的虛機産生的網絡幀,走正常的轉發模式發到虛機或者 br-tun
table=, n_packets=, n_bytes=, idle_age=, priority= actions=NORMAL

#目标網段是 81.1.180.0/24 網段的幀,去掉 vlan tag,修改 src mac 為 qrouter 的 81.1.180.1 interface 的 mac 位址,發到虛機
table=, n_packets=, n_bytes=, idle_age=, priority=,ip,dl_vlan=,nw_dst=/ actions=strip_vlan,mod_dl_src:fa::e::b:c,output:
#目标網段是 90.1.180.0/24 網段的幀,去掉 vlan tag,修改 src mac 為qrouter 的 90.1.180.1 interface 的 mac 位址,發到虛機
table=, n_packets=, n_bytes=, idle_age=, priority=,ip,dl_vlan=,nw_dst=/ actions=strip_vlan,mod_dl_src:fa::e:ec:f3:dd,output:
#dst mac 位址為 vm1,去掉 vlan tag,修改 src mac,發到虛機table=1, n_packets=2321, n_bytes=227458, idle_age=0, priority=4,dl_vlan=2,dl_dst=fa:16:3e:30:ee:23 actions=strip_vlan,mod_dl_src:fa:16:3e:63:3b:4c,output:4
#dst mac 為 vm2,則去掉 vlan tag,修改 src mac,發到虛機table=1, n_packets=0, n_bytes=0, idle_age=2268, priority=4,dl_vlan=1,dl_dst=fa:16:3e:4a:22:ff actions=strip_vlan,mod_dl_src:fa:16:3e:ec:f3:dd,output:8table=1, n_packets=0, n_bytes=0, idle_age=9734, priority=1 actions=drop
table=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
           
了解 OpenStack 高可用(HA):Neutron 分布式虛拟路由(下)

1.3.2 br-tun flows 主要的修改

br-tun flows 的主要修改是增加了 table 1 和 9.

了解 OpenStack 高可用(HA):Neutron 分布式虛拟路由(下)

對于将要離開本機的網絡幀:

  • Table 1 (DVR process Table): 如果網絡幀的 src mac 是本機 qrouter 上的 interface 的 mac 位址(dvr-router-intf-mac),将其修改為 DVR-compute-node-unique-mac,然後交給table 2 處理;其它的幀,交給 table 2.
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:ec:f3:dd actions=mod_dl_src:fa::f:b2::,resubmit(,)
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e::b:c actions=mod_dl_src:fa::f:b2::,resubmit(,)
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:a9:da:b5 actions=mod_dl_src:fa::f:b2::,resubmit(,)
           
  • table 2:單點傳播幀轉 table 20;多點傳播幀轉 table 22
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_dst=:::::/::::: actions=resubmit(,)
 table=, n_packets=, n_bytes=, idle_age=, priority=,dl_dst=:::::/::::: actions=resubmit(,)
           
  • table 20:将 vlan id 轉化為 tunnel id,并根據處理進入本機的網絡幀的時候學習到的 mac 位址和 tunnel port,查找網絡幀的出口 tunnel port
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_dst=fa::e::f8:b8 actions=strip_vlan,set_tunnel:x4,output:table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_dst=fa::e:::d actions=strip_vlan,set_tunnel:x4,output:
           
  • table 22:将 vlan id 轉化為 tunnel id,并泛洪到所有的 tunnel 端口
table=, n_packets=, n_bytes=, idle_age=, hard_age=, dl_vlan= actions=strip_vlan,set_tunnel:,output:,output:table=, n_packets=, n_bytes=, idle_age=, hard_age=, dl_vlan= actions=strip_vlan,set_tunnel:,output:,output:table=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
           

對于進入本機的網絡幀:

  • table 0:交給 table 3 處理
  • table 3:隻允許目的網絡為本機上的虛機所在的網絡的網絡幀,修改其 vlan id,轉 table 9
table=, n_packets=, n_bytes=, idle_age=, priority=,tun_id= actions=mod_vlan_vid:,resubmit(,)  # tunnel id 轉換為 vlan id
table=, n_packets=, n_bytes=, idle_age=, priority=,tun_id= actions=mod_vlan_vid:,resubmit(,) #tunnel id 轉換為 vlan id
table=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
           
  • Table 9 (DVR Learning blocker):如果 src mac 是 DVR-Unique-MAC,不做 mac 學習,轉發到 patch-int;否則,轉到 table 10 做 mac 位址學習

table 10:mac 位址學習,結果存到 table 20

table=, n_packets=, n_bytes=, idle_age=, priority= actions=learn(table=,hard_timeout=,priority=,NXM_OF_VLAN_TCI[],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:
           

注意:在 table 20 中,除了自己通過 mac 位址學習學到的 mac 位址外,還需要借助 l2population 。這就是為什麼 DVR 依賴于 l2population 的緣故。沒有使用的話,網絡包無法發到正确的 tunnel interface。

具體設定 OVS flows 的代碼在 setup_dvr_flows_on_integ_tun_br 函數中。更詳細的說明可以參考官方文章。

1.4 qrouter 中的ARP 表

虛機 vm1 需要通過 ARP 擷取兩種 mac 位址:

  1. 當目标計算機(vm2)不在其同一個網段時,它需要擷取預設網關的 mac 位址,這個将由 qrouter 直接相應 arp 請求。
  2. 當目标計算機(vm2)在其同一個網段時,它需要直接擷取 vm2 的 mac 位址。這個應該仍然是通過 ARP 廣播獲得。簡單的做法是使用 arp responder。

qrouter 在做完 vm1 的網絡包的路由後,将網絡包從 vm2 所在網段的 interface 上發出前,需要擷取 vm2 的 mac 位址。而這個是通過它查詢自身的 ARP table 獲得的。這是 compute 1 上 qrouter netns 中的 ip neighbour 表:

root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip neigh
. dev qr-f849ae46- lladdr fa::e:::0d PERMANENT
. dev qr-bdba3-b1 lladdr fa::e:c0:f:c PERMANENT
. dev qr-e47fca31-db lladdr fa::e::: PERMANENT
           

這裡面可以看到大量的 PERMANENT MAC 位址。這是因為,L3 Agent 配置 DVR Router 的時候,它通過 RPC 從 neturon server 擷取該 router 各 interface 的 subnet 中擷取所有虛機的 MAC 位址。當一個 subnet 被加到 DVR Router 的時候,每個相關的 L3 Agent 都會被通知到,然後它通過 RPC 擷取各 MAC 位址。當一個新的 port 被建立,或者 port 的 MAC 有更新的時候,所有相關的 L3 Agent 會被通知到去更新 ARP 表。

通過該由 L3 Agent 動态維護的 ARP 表,qrouter 就能直接查到它要通信的 interface 的 MAC 位址了,而不需要通過廣播的方式去被動擷取。具體原因是:

Why all of this complexity? Remember that the router’s MAC addresses are present on all hosts and cannot leave the host. If a router scheduled on host 1 generated an ARP request for a port scheduled on host 2, host 2 would receive the message and its virtual switches would suddenly re-learn a MAC they supposedly already know to be connected to br-int. They would see it coming from a tunnel connected to br-tun!

大緻的更新過程為:

  1. 每個L2 Agent 程序循環檢查其管理的 port 的狀态
  2. 當 port 狀态由 down 變為 up 時,它通過 RPC 通知 neutron server 該變化,neutron server 然後發出 fanout 通知其他的 L2 agent 去添加 arp entry (add_arp_entry),再調用 ip neigh replace方法在 qrouter network namespace 中 增加一個 arp entry
  3. 當 port 狀态由 up 變為 down 時,它通過 RPC 通知 neutron server 該變化,neutron server 然後發出 fanout 通知其他的 L2 agent 去添加 delete entry (del_arp_entry),再調用 ip neigh del 方法在 qrouter network namespace 中 删除該 arp entry

1.5 ip rule 和 route 操作

1.5.1 增加一個 internal subnet 時

在 qrouter namespace 上:

(1)計算該 subnet cidr(81.1.180.1/24)的 index 1359066113,作為新增 ip rule 的優先級和路由表的名稱。

(2)增加 default gateway,運作 [‘ip route replace default via 81.1.180.17 dev qr-517bdba3-b1 table 1359066113]。這裡的 81.1.180.17 正是 snat namespace 的 IP。

(3)增加 ip rule, 允許 [ip rule add from 81.1.180.1/24 lookup 1359066113 priority 1359066113]。這樣就将該 subnet 中的虛機的網絡幀轉到 route table

(4)執行 ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 sysctl -w net.ipv4.conf.qr-517bdba3-b1.send_redirects=0

效果如下:

root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
:      from all lookup local
:  from all lookup main
:  from all lookup default
:     from ./ lookup 
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 1359066113
default via . dev qr-bdba3-b1
           

這樣,當虛機還沒有配置浮動IP時,通路外網的話,網絡幀的路線為:vm —- qrouter subnet 1 interface — SNAT —- external port —– pc

是以,當 router 上連接配接有多個 subnet 時,qrouter 中也有相應數量的 ip rule 和 routing table:

[email protected]:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
:      from all lookup local
:  from all lookup main
:  from all lookup default
:     from / lookup 
:     from / lookup 
:     from / lookup 
           

1.5.2 給虛機綁定浮動 IP 時

在 qrouter namespace 中:

(1)增加 ip rule,通過運作 [‘add’, ‘from’, u’81.1.180.18’, ‘lookup’, 16, ‘priority’, 32768],其中,ID 16 為寫死的,其優先級是從 32768 開始到 36768 這個區間内依次配置設定。

(2)在路由表 16 中添加路由項 default via 169.254.31.239 dev rfp-e8f12f7a-6。這使得虛機通路外網的網絡包會通過 rfp-e8f12f7a-6 發到 169.254.31.239。而這個 IP 正是 fip 上 pfr 端口的IP。

在 fip namespace 中:

(1)增加 route:192.168.1.0/24 dev fg-6b744484-88 proto kernel scope link src 192.168.1.119。這使得通路外網機器的網絡包能從 fg-6b744484-88 出去。

(2)增加 route:192.168.1.116 via 169.254.31.238 dev fpr-e8f12f7a-6。使得通路虛機的網絡包會發給 169.254.31.238,進入 qrouter。這個 router 上的每個浮動 IP 有這麼一條 route。

配置了兩個浮動 IP 的情況下是這樣的結果:

root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
: from . lookup  #去 fip 的
:  from . lookup   #去 fip 的
:     from ./ lookup  #去 snat 的
:     from ./ lookup  #去 snat 的
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 16
default via . dev rfp-e8f12f7a- # route 是一樣的

root@compute1:/home/s1# ip netns exec fip-557e9f0c-9c66-46da-b289-218d49c218d2 ip route
default via . dev fg-b744484-
./ dev fpr-e8f12f7a-  proto kernel  scope link  src .
./ dev fg-b744484-  proto kernel  scope link  src .
. via . dev fpr-e8f12f7a- #每個浮動 IP 一個 route item
. via . dev fpr-e8f12f7a-
           

這裡能看到 qroute 的 ip rule 上,針對一個虛機/子網,有兩條 rule,一條查路由表 16 到 fip,另一條查表到 snat。但是,在有浮動 IP 的情況下,前一條政策的優先級數值将小于後後一條的,這就決定了查路由表 16,資料包走 fip。

1.5.3 qrouter 的 main 路由表

main 路由表是為虛拟子網服務的,每個 subnet 對應一條路由規則,使得目的為每個 subnet 的網絡包從指定的 qrouter 的 qr interface 上發出。

root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route
./ dev qr-bdba3-b1  proto kernel  scope link  src . #為到子網 1 中的虛機的網絡包做路由
./ dev qr-f849ae46-  proto kernel  scope link  src . #為到子網 2 中的虛機的網絡包做路由
./ dev qr-e47fca31-db  proto kernel  scope link  src . 
./ dev rfp-e8f12f7a-  proto kernel  scope link  src . #為從 fip 進來的外網通路内部虛機的網絡包做路由
           

2. Neutron 其它服務與 DVR

2.1 FWaas DVR

DVR 與傳統的 FWaas 不相容,因為它作用于neuron 網絡節點上的 virtual router,過濾進出租戶網絡的網絡包。傳統的 FWaas 可以參考我的另一篇文章。

DVR 實作後,FWaas 需要做相應的修改。

官方文檔在這裡:

https://wiki.openstack.org/wiki/Neutron/FWaaS/FWaaS-DVR

Spec:https://review.openstack.org/#/c/106225/9/specs/juno/neutron-dvr-fwaas.rst

目标:FWaas 保持對 南-北流量做防火牆,而不影響東-西流量。

做法:Neutron 網絡節點上的 FWaas Agent 安裝在 SNAT network namespace 中;計算和網絡節點上的 FWaas Agent 安裝在 qrouter network namespace 中。

2.2 VPNaas DVR

Juno 版本中 VPNaas 不支援 DVR,隻支援傳統的 router。Kilo 版本中會實作 VPNaas 對 DVR 的支援。新的 VPN 服務隻會在 dvr_snat 節點上的 snat namespace 上運作。

2.3 LBaas 與 DVR

兩者之間沒有互相依賴關系,是以 DVR 對 LBaas 沒有影響。

總體情況:

了解 OpenStack 高可用(HA):Neutron 分布式虛拟路由(下)

3. 後續版本中 DVR 開發

3.1 Kilo 版本中

  • VPNaaS 對 DVR 的支援
  • 從傳統 router 遷移到 DVR router
  • 網絡節點上 HA + DVR 支援
  • VLAN 支援

3.2 Liberty 版本中

  • L3 Agent 重構
  • 分布式 DHCP
  • 性能調優
  • 分布式 SNAT

從前兩個章節也可以看出,Juno 版本才添加的 DVR 功能還很不完善,難以滿足生産環境的使用要求,主要是因為它還不支援目前實際部署中應該很廣泛的 VLAN 組網模式,以及無法解決 HA 和 DVR 共存的問題。可喜的是這兩個主要問題會在 K 版本中解決,是以 K 版本中的 DVR 至少可以用來做測試用了。到了 L 版本,實作分布式 DHCP 和 SNAT,以及性能優化以後,離生産環境的要求基本就差不多了。

劉世民, 雲架構師,十餘年IT行業從業經驗,在電信軟體、企業軟體、存儲以及雲計算等領域從事過研發、管理和架構設計等方面的工作。從2012年開始從事 OpenStack 研發工作,對其核心子產品、相關開源技術和産品,以及行業等都有着較深的了解。

責編:陳晨 聯系請添加微信:violace95 備注公司 職位 姓名。尋求報道或投稿,請請發送至郵箱:[email protected]

繼續閱讀