學習 Neutron 系列文章:
OVS bridge 有兩種模式:“normal” 和 “flow”。“normal” 模式的 bridge 同普通的 Linux 橋,而 “flow” 模式的 bridge 是根據其流表(flow tables) 來進行轉發的。Neutron 使用兩種 OVS bridge:br-int 和 br-tun。其中,br-int 是一個 “normal” 模式的虛拟網橋,而 br-tun 是 “flow” 模式的,它比 br-int 複雜得多。
下面左圖是 Open vSwitch 中流表的結構。右圖這個流程圖較長的描述了資料包流通過一個 OpenFlow 交換機的過程。
Proxy ARP 就是通過一個主機(通常是Router)來作為指定的裝置對另一個裝置作出 ARP 的請求進行應答。
舉個例子:主機A,IP位址是192.168.0.11/24;主機B,IP位址是192.168.1.22/24。主機A和主機B通過路由器R相連接配接,并且路由器R啟用了Proxy ARP,并配置有路由。網絡拓撲如下:
eth0 eth0 eth1 eth0
A------------------------Router R----------------------B
192.168.0.11/24 192.168.0.0/24 eth0 192.168.1.22/24
192.168.1.0/24 eth1
在主機A上執行:ping 192.168.1.22,主機 A 不知道主機 B 的 MAC 位址是多少,首先要發送 ARP 查詢封包,路由器 R 接收到主機 A 發出的 ARP 查詢封包,并代替主機 B 作出應答,應答 ARP 封包中填入的就是路由器 R 的MAC位址。這樣,主機A就會認為路由器R的位址是192.168.1.22。以後所有發往192.168.1.22的封包都發到路由器R,路由器R再根據已配置好的路由表将封包轉發給主機B。
這樣做的好處就是,主機A上不需要設定任何預設網關或路由政策,不管路由器R的IP位址怎麼變化,主機A都能通過路由器B到達主機B,也就是實作了所謂的透明代理。相反,若主機A上設定有預設網關或路由政策時,當主機A向192.168.1.22發送封包,首先要查找路由表,而主機A所在的網段是192.168.0.0/24,主機B所在網段是192.168.1.0/24,主機A隻能通過預設網關将封包發送出去,這樣代理ARP也就失去了作用。
優點:
最主要的一個優點就是能夠在不影響其他router的路由表的情況下在網絡上添加一個新的router,這樣使得子網的變化對主機是透明的。
proxy ARP應該使用在主機沒有配置預設網關或沒有任何路由政策的網絡上
缺點:
1.增加了某一網段上 ARP 流量
2.主機需要更大的 ARP table 來處理IP位址到MAC位址的映射
3.安全問題,比如 ARP 欺騙(spoofing)
4.不會為不使用 ARP 來解析位址的網絡工作
5.不能夠概括和推廣網絡拓撲
OpenStack 中,Neutron 作為 OVS 的 Controller,向 OVS 發出管理 tunnel port 的指令,以及提供流表。
Neutron 定義了多種流表。以下面的配置(配置了 GRE 和 VXLAN 兩種 tunnel types)為例:
其中,10.0.1.31 是計算節點1, 10.0.1.21 是網絡節點, 10.0.1.39 是計算節點2。
計算節點1 上 ML2 Agent 啟動後的 br-tun 的 flows:
表号
用途
例子
table=0, priority=1,in_port=3 actions=resubmit(,4) //從網絡節點來的,轉 4,結果被丢棄
table=0, priority=1,in_port=4 actions=resubmit(,3) //從網絡節點來的,轉 3
table=0, priority=1,in_port=5 actions=resubmit(,3) //從計算節點來的,轉 3
table=0, priority=1,in_port=2 actions=resubmit(,4) //從計算節點來的,轉 4,結果被丢棄
table=0, priority=1,in_port=1 actions=resubmit(,2) //從虛機來的,轉 2
table=0, priority=0 actions=drop //其餘的丢棄
DVR_PROCESS = 1
handle packets coming from patch_int unicasts go to table UCAST_TO_TUN where remote addresses are learnt
用于 DVR
PATCH_LV_TO_TUN = 2
table=2, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20) //單點傳播包,轉 20
GRE_TUN_TO_LV = 3
table=3, priority=1,tun_id=0x4 actions=mod_vlan_vid:1,resubmit(,10) //将 tun_id 為 4 的,修改 vlan id 為1,轉 10 處理
table=3, priority=0 actions=drop //其餘的丢棄
VXLAN_TUN_TO_LV = 4
table=4, priority=0 actions=drop //丢棄
DVR_NOT_LEARN = 9
LEARN_FROM_TUN = 10
學習table
table=10,priority=1 actions=learn(table=20,hard_timeout=300,priority=1,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
UCAST_TO_TUN = 20
//學習到的規則
table=20, priority=2,dl_vlan=1,dl_dst=fa:16:3e:7e:ab:cc actions=strip_vlan,set_tunnel:0x3e9,output:5 //如果vlan 為1,而且目的MAC位址等于 fa:16:3e:7e:ab:cc,設定 tunnel id,從端口 5 發出
table=20,priority=0 actions=resubmit(,22) //直接轉 22
ARP_RESPONDER = 21
ARP table
當使用 arp_responder 和 l2population 時候用到
FLOOD_TO_TUN = 22
Flood table
table=22,dl_vlan=1 actions=strip_vlan,set_tunnel:0x4,output:5,output:4 //對于 dl_vlan 為1的,設定 tunnel id 為 4,從端口4 和 5 轉出
table=22,priority=0 actions=drop
來個圖簡單些:
其中比較有意思的是:
(1)為什麼從 VXLAN 過來的流量都被丢棄了,最後發出去也用的是 GRE 端口。看來同時有 GRE 和 VXLAN 隧道的話,OVS 隻會選擇 GRE。具體原因待查。
(2)MAC 位址學習:Table 10 會将學習到的規則(Local VLAN id + Src MAC Addr => IN_Port)放到 table 20。當表格20 發現一個單點傳播位址是已知的時候,直接從一個特定的 GRE 端口發出;未知的話,視同多點傳播位址從所有 GRE 端口發出。
學習規則:
table=20,hard_timeout=300,priority=1,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[]
table=20:修改 table 20。這是個 MAC 學習流表。
hard_timeout:該 flow 的過期時間。
NXM_OF_VLAN_TCI[0..11] :記錄 vlan tag,是以學習結果中有 dl_vlan=1
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] :将 mac source address 記錄,是以結果中有 dl_dst=fa:16:3e:7e:ab:cc
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:0x3e9
output:NXM_OF_IN_PORT[]:指定發送給哪個port,由于是從 port2 進來的,因而結果中有output:2。
學到的規則:
table=20, n_packets=1239, n_bytes=83620, idle_age=735, hard_age=65534, priority=2,dl_vlan=1,dl_dst=fa:16:3e:7e:ab:cc actions=strip_vlan,set_tunnel:0x3e9,output:2
這裡可以看到,通過 MAC 位址學習機制,Neutron 可以一定程度地優化網絡流向,但是這種機制需要等待從别的節點的流量進來,隻能算是一種被動的機制,效率不高。而且,這種機制隻對單點傳播幀有效,而對于多點傳播群組播依然無效。其結果是網絡成本依然很高。下圖中,A 的廣播包其實隻對 3 和 4 有用,但是 2 和 5 也收到了。
使用 ARP Responder 需要滿足兩個條件:
(1)設定 arp_responder = true 來使用 OVS 的ARP 處理能力 。這需要 OVS 2.1 (運作 ovs-vswitchd --version 來檢視 OVS 版本) 和 ML2 l2population 驅動的支援。當使用隧道方式的時候,OVS 可以處理一個 ARP 請求而不是使用廣播機制。如果 OVS 版本不夠的話,Neutorn 是無法設定 arp responder entry 的,你會在 openvswitch agent 日志中看到 “Stderr: '2015-07-11T04:57:32Z|00001|meta_flow|WARN|destination field arp_op is not writable\novs-ofctl: -:2: actions are invalid with specified match (OFPBAC_BAD_SET_ARGUMENT)\n'”這樣的錯誤,你也就不會在 ”ovs-ofctl dump-flows br-tun“ 指令的輸出中看到相應的 ARP Responder 條目了。
(2)設定 l2_population = true。同時添加 mechanism_drivers = openvswitch,l2population。OVS 需要 Neutron 作為 SDN Controller 向其輸入 ARP Table flows。
殺掉 neutron openvswitch, ovs-* 各種程序
#編譯安裝
去 http://openvswitch.org/download/ 下載下傳最新版本的代碼,解壓,進入解壓後的目錄
安裝依賴包,比如 gcc,make
uname -r
./configure --with-linux=/lib/modules/3.13.0-51-generic/build
make && make install
#檢視安裝的版本
root@compute2:/home/s1# ovs-vsctl --version
ovs-vsctl (Open vSwitch) 2.3.2
Compiled Jul 12 2015 09:09:42
DB Schema 7.6.2
#處理 db
rm /etc/openvswitch/conf.db (老的db要删除掉,否則會報錯)
ovsdb-tool create /etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
#啟動 ovs
cp /usr/local/bin/ovs-* /usr/bin
ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/usr/local/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach --monitor
ovs-vswitchd unix:/usr/local/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach --monitor
#啟動 neutron openvswitch agent,確定log 檔案中 ovs-vsctl 和 ovs-ofctl 調用沒有錯誤
#修改 /usr/share/openvswitch/scripts/ovs-lib 檔案,保證機器重新開機後 OVS 正常運作
将 rundir=${OVS_RUNDIR-'/var/run/openvswitch'} 改為 rundir=${OVS_RUNDIR-'/usr/local/var/run/openvswitch'}
有了 arp_responder 以後,br-tun 的流表增加了幾項和處理:
(1)table 2 中增加一條 flow,是的從本地虛機來的 ARP 廣播幀轉到table 21
(2)在 table 21 中增加一條 flow 将其發發往 table 22
(3)由 L2 population 發來的 entry 來更新 table 21。
table 21 是在新的 l2pop 位址進來的時候更新的。比如說,compute C 上增加了新的虛機 VM3,然後計算節點 A 和 B 收到一條 l2pop 消息說 VM3 (IP 是***,MAC 是 ***) 在 Host C 上,在 network "Z“ 中。然後,Compute A 和 B 會在 table 21 中增加相應的 flows。
table 21 中的每一條 flow,會和進來的幀的資料做比對(ARP 協定,network,虛機的 IP)。如果比對成功,則構造一個 ARP 響應包,其中包括了 IP 和 MAC,從原來的 port 發回到虛機。如果沒有吻合的,那麼轉發到 table 22 做泛洪。
增加的 flow tables 在紅色部分:
l2pop 的原理也不複雜。Neutron 中儲存每一個端口的狀态,而端口儲存了網絡相關的資料。虛機啟動過程中,其端口狀态會從 down 到build 到 active。是以,在每次端口發生狀态變化時,函數 update_port_postcommit 都将會被調用:
在某些狀态變化下:
update_port_postcommit (down to active) -> _update_port_up -> add_fdb_entries -> fdb_add -> fdb_add_tun -> setup_tunnel_port (如果 tunnel port 不存在,則建立 tunnel port), add_fdb_flow -> add FLOOD_TO_TUN flow (如果是 Flood port,則将端口添加到 Flood output ports); setup_entry_for_arp_reply('add'。如果不是 Flood port,那麼 添加 ARP Responder entry (MAC -> IP)) 以及 add UCAST_TO_TUN flow Unicast Flow entry (MAC -> Tunnel port number)。
update_port_postcommit (active to down) -> _update_port_down -> remove_fdb_entries
delete_port_postcommit (active to down) -> _update_port_down -> remove_fdb_entries -> fdb_remove -> fdb_remove_tun -> cleanup_tunnel_port, del_fdb_flow -> mod/del FLOOD_TO_TUN flow; setup_entry_for_arp_reply ('remove'), delete UCAST_TO_TUN flow
update_port_postcommit (fixed ip changed) -> _fixed_ips_changed -> update_fdb_entries
通過這種機制,每個節點上的如下資料得到了實時更新,進而避免了不必要的隧道連接配接和廣播。
Tunnel port
FLOOD_TO_TUN (table 22)flow
ARP responder flow
UCAST_TO_TUN (table 20) flow
有和沒有 l2pop 的效果:
1. def tunnel_sync(self) 函數除了上報自己的 local_ip 外不再自己見 tunnels,一切等 l2pop 的通知。
2. 在 compute1 上添加第一個虛機 81.1.180.8
neutron-server:
通知 compute1: {'segment_id': 6L, 'ports': {u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:87:40:f3', u'81.1.180.1']]}, 'network_type': u'gre'}}
通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']]}, 'network_type': u'gre'}}
compute1:
添加和網絡節點的tunnel options: {df_default="true", in_key=flow, local_ip="10.0.1.31", out_key=flow, remote_ip="10.0.1.21"}
添加到網段網關的 Unicast flow:table=20, n_packets=0, n_bytes=0, idle_age=130, priority=2,dl_vlan=2,dl_dst=fa:16:3e:87:40:f3 actions=strip_vlan,set_tunnel:0x6,output:4
增加網段 81.1.180.1 網關的 ARP flows:table=21, n_packets=0, n_bytes=0, idle_age=130, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.1 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:87:40:f3,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:0xfa163e8740f3->NXM_NX_ARP_SHA[],load:0x5101b401->NXM_OF_ARP_SPA[],IN_PORT
修改 Flood flow
compute 2 節點:因為它上面還沒有運作虛機,是以不做操作。
3. 在 compute 2 上添加一個虛機 81.1.180.9
neutron server:
通知 compute 2 : {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']], u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:87:40:f3', u'81.1.180.1']]}, 'network_type': u'gre'}}
通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.39': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:73:49:41', u'81.1.180.9']]}, 'network_type': u'gre'}
建立 tunnel(ID 5): {df_default="true", in_key=flow, local_ip="10.0.1.31", out_key=flow, remote_ip="10.0.1.39"}
增加 arp responder flow(compute2 上新的虛機 IP -> MAC):table=21, n_packets=0, n_bytes=0, idle_age=79, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.9 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:73:49:41,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:0xfa163e734941->NXM_NX_ARP_SHA[],load:0x5101b409->NXM_OF_ARP_SPA[],IN_PORT
增加 unicast flow (新虛機的 MAC -> Tunnel port):table=20, n_packets=0, n_bytes=0, idle_age=79, priority=2,dl_vlan=2,dl_dst=fa:16:3e:73:49:41 actions=strip_vlan,set_tunnel:0x6,output:5
添加新的 Tunnel port 到 Flood flow:table=22, n_packets=13, n_bytes=1717, idle_age=255, hard_age=78, dl_vlan=2 actions=strip_vlan,set_tunnel:0x6,output:5,output:4
compute2:
建立和計算節點以及compute1的tunnel:options: {df_default="true", in_key=flow, local_ip="10.0.1.39", out_key=flow, remote_ip="10.0.1.21"},options: {df_default="true", in_key=flow, local_ip="10.0.1.39", out_key=flow, remote_ip="10.0.1.31"}
增加 ARP flow(compute 1 上的虛機的 MAC -> IP):table=21, n_packets=0, n_bytes=0, idle_age=268, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.8 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:b3:e7:7a,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:0xfa163eb3e77a->NXM_NX_ARP_SHA[],load:0x5101b408->NXM_OF_ARP_SPA[],IN_PORT
增加 Unicast flow (compute 1 上的虛機 MAC -> Tunnel port):table=20, n_packets=0, n_bytes=0, idle_age=268, priority=2,dl_vlan=2,dl_dst=fa:16:3e:b3:e7:7a actions=strip_vlan,set_tunnel:0x6,output:4
增加 ARP flow(新虛機的網關的 MAC -> IP) table=21, n_packets=0, n_bytes=0, idle_age=268, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.1 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:87:40:f3,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:0xfa163e8740f3->NXM_NX_ARP_SHA[],load:0x5101b401->NXM_OF_ARP_SPA[],IN_PORT
修改 Flood flow(添加到 Compute 1 的 port):table=22, n_packets=13, n_bytes=1717, idle_age=128, dl_vlan=2 actions=strip_vlan,set_tunnel:0x6,output:5,output:4
3. 删除 compute1 上的一個vm(也是唯一的一個)
通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']]}, 'network_type': u'gre'}
compute 1:
因為沒有别的虛機了,删除所有 tunnel ports
修改或者删除 ARP, Unicast 和 Flood flows
compute 2:
删除了 compute1 的 tunnel
删除該虛機對應的 ARP flow
4. 在 compute1 上建立第一個不同網絡的虛機
通知 compute 1: {u'e2022937-ec2a-467a-8cf1-f642a3f777b6': {'segment_id': 4L, 'ports': {u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:90:e5:50', u'91.1.180.1'], [u'fa:16:3e:17:c9:26', u'90.1.180.1'], [u'fa:16:3e:69:92:30', u'90.1.180.3'], [u'fa:16:3e:69:92:30', u'91.1.180.2']]}, 'network_type': u'gre'}}
通知所有 agent:{u'e2022937-ec2a-467a-8cf1-f642a3f777b6': {'segment_id': 4L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:e9:ee:0c', u'91.1.180.9']]}, 'network_type': u'gre'}}
compute 1:建立和網絡節點的 tunnel port;更新 Flood flows;添加 ARP flows
compute 2:沒什麼action,因為該節點上沒有建立虛機的網絡内的虛機
過程的大概說明:
虛機在收到 fannout FDB entries 後,檢查其中每個 port 的 network_id(即 “segment_id”)。如果本機上有該 network 内的 port,那麼就處理 entries 中的 “ports”部分;否則,不處理該 entries。
是以,當計算節點上沒有運作任何虛機時,不會建立任何 tunnel。如果兩個虛機上有相同網絡内的虛機,那麼建立會建立 tunnel。
這種機制能實時建立 tunnel port,Flood entry (建立 Tunnel port 同時添加到 Flood output ports 清單), Unicast flow (虛機和網關 MAC -> Tunnel port) 和 ARP Responder entry (虛機和網關 MAC -> IP)。下圖中的藍色部分的流表都會被及時更新。
Neutron server 在端口建立/删除/修改時,如果是該節點上的第一個虛機,首先發送直接消息;然後發通知消息給所有的計算和網絡節點。
應該說 l2pop 的原理和實作都很直接,但是在大規模部署環境中,這種通知機制(通知所有的 ML2 Agent 節點)可能會給 MQ 造成很大的負擔。一旦 MQ 不能及時處理消息,虛機之間的網絡将受到影響。下面是 l2pop 中通知機制代碼:
不知道這個數目有沒有上限?數目很多的情況下會不會有性能問題?OVS 有沒有處理能力上限?這些問題也許得在實際的生産環境中才能得到證明和答案。
本文轉自SammyLiu部落格園部落格,原文連結:http://www.cnblogs.com/sammyliu/p/4633814.html,如需轉載請自行聯系原作者