系統(tǒng)級服務(wù)的無擾動升級(non distruptive upgrade,下文簡稱為熱升級)對服務(wù)的快速迭代開發(fā)有非常重要的意義。虛擬交換機(vSwitch)作為虛擬網(wǎng)絡(luò)的入口,需求多變,但頻繁升級斷網(wǎng)會影響虛機上運行的業(yè)務(wù)。此外,一般每臺宿主機上只有一個虛擬交換機,在架構(gòu)上也不好做主備。因此熱升級技術(shù)對 vSwitch 的快速迭代至關(guān)重要。
本文介紹了我們在 DPDK based Open vSwtich(下簡稱 ovs-dpdk)上的熱更新技術(shù)的實踐,希望和業(yè)界同行共同探討。
現(xiàn)狀
- Open vSwitch(下簡稱 ovs)已有的“熱升級”方案基本是為 ovs-kernel 而實現(xiàn)的。在 ovs-kernel 中,vswitchd 進程是慢路徑,kernel 模塊是快速路徑。升級 vswitchd 進程時可不替換 kernel 模塊,讓大部分流量經(jīng)過 kernel 中 flow cache 轉(zhuǎn)發(fā),減少網(wǎng)絡(luò)擾動。升級 kernel 模塊則可能會有較長的斷網(wǎng)風(fēng)險。這塊的詳細信息可以參考鏈接: http://docs.openvswitch.org/en/latest/intro/install/general/?highlight=hot%20upgrade#hot-upgrading
- ovs-dpdk 中,快速路徑和慢速路徑都集成在 vswitchd 進程中,如果簡單的重啟 vswtichd,由于 DPDK 的初始化(主要是大頁初始化,1G 大頁耗時約 600ms)、網(wǎng)卡的初始化(實測 Mellanox CX5 驅(qū)動初始化耗時接近 1s)都比較耗時,因此會有秒級斷網(wǎng)。
- 為了實現(xiàn)快速的迭代開發(fā),降低升級導(dǎo)致的斷網(wǎng)帶來的業(yè)務(wù)擾動,需要開發(fā) ovs-dpdk 的熱升級特性。
方案與折衷
實現(xiàn)熱更新有很多實現(xiàn)方式:
- 插件式升級(形象的說就是熱補丁):將主要的包處理邏輯變成動態(tài)鏈接庫,通過熱加載插件的方式,避免耗時的 dpdk 初始化和網(wǎng)卡初始化。(DPDK 主從模式也可被認為是這種方式的變體)
優(yōu)點:常規(guī)升級斷網(wǎng)時間非常小,可以做到納秒級。
缺點:框架升級和插件升級成為了兩種升級,框架和插件之間必然有相互調(diào)用的 API 和共享 數(shù)據(jù)結(jié)構(gòu),開發(fā)人員需要保持框架和插件之間的 ABI(Application Binary Interface)一致。一個簡單的例子是流表的結(jié)構(gòu)體可能會在框架和插件之間共享,如 果升級修改了流表的結(jié)構(gòu)體,框架也需要升級,否則會因為不一致而導(dǎo)致內(nèi)存錯誤。
- 雙進程升級(形象的說就是熱替換):通過硬件資源冗余,讓新老 ovs 在升級中并存,老的 ovs 繼續(xù)進行轉(zhuǎn)發(fā),使得流量不會斷,新的 ovs 完成初始化之后,接管流量。
優(yōu)點:新舊 ovs 是兩個進程,不需要做任何兼容,只需要遵守新舊進程之間的信息同步的通 信協(xié)議即可。
缺點:需要資源冗余,需要網(wǎng)卡和內(nèi)存冗余以同時滿足新舊 ovs 的升級需求(2x 內(nèi)存加 2x VF),斷網(wǎng)時間也會更長一些。
業(yè)界一般采用雙進程實現(xiàn)熱升級,這種方式的覆蓋面更廣,工程實現(xiàn)上相對來說也更容易一些。下圖描述了雙進程熱升級的一般流程。

OVS 熱更新的工作原理并不復(fù)雜,就是實現(xiàn)上需要考慮不少瑣碎的工程細節(jié)。主要通過代碼上精細的控制使得升級斷網(wǎng)時間較小。
二段式設(shè)計
我們把熱升級的整個流程分成兩個階段(二段式),這里的設(shè)計有點借鑒虛擬機進行熱遷移的過程。在第一階段,網(wǎng)絡(luò)的轉(zhuǎn)發(fā)服務(wù)不會停止,并且一旦升級出現(xiàn)故障,整個系統(tǒng)是可以自動回滾的。只有到達第二階段,網(wǎng)絡(luò)會出現(xiàn)斷網(wǎng)。在第二階段,如果出現(xiàn)問題,只能斷網(wǎng)重啟服務(wù),系統(tǒng)不再具有回滾能力。在實現(xiàn)上,我們給 ovs 開啟了一個 JSON-RPC 服務(wù)用于熱升級。當(dāng) ovs 進程啟動時,會自動檢測是否有老進程的存在。如果存在,則主動通過 JSON-RPC 發(fā)起熱升級請求。接收到熱升級請求的 ovs 進程則開啟兩階段升級:
- 第一階段:
- 老 ovs 進程釋放各種獨占資源:比如 ovsdb 的數(shù)據(jù)庫鎖,pid 文件改名,unixctl server path 等資源。此時老 ovs 不能通過 ovsdb 獲取最新配置,但是 PMD 線程依舊運行,轉(zhuǎn)發(fā)并未停止。
- 釋放資源之后,新老進程開始進行狀態(tài)同步,主要是一些 OVS 的 megaflow 同步,網(wǎng)絡(luò) tap 設(shè)備的 fd 同步等。
- 同時,老的 ovs 會將所有的 OpenFlow 規(guī)則進行備份。
- 這一切完成之后,老進程會返回 JSON-RPC 的請求的響應(yīng)。如果上述過程無問題,則返回成功,升級繼續(xù)。否則返回失敗,新進程會退出,升級失敗。同時老進程會狀態(tài)回滾,將之前釋放的資源又重新申請回來,回到正常的工作狀態(tài)。
- 新進程獲得各種狀態(tài)信息,開始正常初始化。包括 DPDK 初始化內(nèi)存,ovs 從 ovsdb 中獲取網(wǎng)橋、網(wǎng)卡等各種配置開始初始化等。在這個過程中,如果出現(xiàn)異常,新進程 crash,會導(dǎo)致 JSON-RPC 這個 unix socket 連接中斷。一旦老進程檢測到這個連接中斷,就認為新進程初始化失敗,自動回滾。
- 新進程加載之前備份的 OpenFlow 規(guī)則。這之間的 OpenFlow 規(guī)則變更,會由 local controller 記錄并下發(fā)。
- 新進程發(fā)起第二階段 JSON-RPC 請求。
- 第二階段:
- 老進程退出。
- 新進程開啟 pmd 線程開始轉(zhuǎn)發(fā)。
- 升級結(jié)束。
下圖簡單的描述了升級時序圖:

由升級時序圖可以看出,熱升級第一階段里新 ovs 進程完成了最耗時的初始化工作,同時老 ovs 進程一直在進行轉(zhuǎn)發(fā),并沒有斷網(wǎng)。斷網(wǎng)只是在第二階段老 ovs 退出和新 ovs 啟動 PMD 線程之間發(fā)生。
在升級過程中,我們利用了 MLNX 網(wǎng)卡一個特性:同一個網(wǎng)卡設(shè)備可以被兩個獨立的 dpdk 進程同時打開,并且流量會被同時鏡像到兩個進程中去。如果使用其他的網(wǎng)卡,可以通過多 VF 配合流表規(guī)則切換的方法達到同樣效果。這里就不再展開敘述了。
斷網(wǎng)時間評估
我們考察了兩種虛擬網(wǎng)卡后端的斷網(wǎng)時間:
- 采用 representer + VF 的方式,
- 采用 vhost-user + virtio 的方式。
測試方法
兩臺宿主機上兩臺虛機互相 ping,使用ping -i 0.01。兩次 ping 之間相隔 10ms,最后統(tǒng)計在升級中,ping 包未回的數(shù)量即表示升級斷網(wǎng)時間。 iperf -i 1測試,觀察 TCP 吞吐是否會受到影響。
representer + VF 方式
ping 測試
10 次試驗結(jié)果如下:
1524 packets transmitted, 1524 received, +36 duplicates, 0% packet loss, time 16747ms
623 packets transmitted, 622 received, +29 duplicates, 0% packet loss, time 6830ms
662 packets transmitted, 662 received, +30 duplicates, 0% packet loss, time 7263ms
725 packets transmitted, 724 received, +28 duplicates, 0% packet loss, time 7955ms
636 packets transmitted, 635 received, +28 duplicates, 0% packet loss, time 6973ms
752 packets transmitted, 750 received, +27 duplicates, 0% packet loss, time 8251ms
961 packets transmitted, 961 received, +31 duplicates, 0% packet loss, time 10551ms
737 packets transmitted, 737 received, +29 duplicates, 0% packet loss, time 8084ms
869 packets transmitted, 869 received, +27 duplicates, 0% packet loss, time 9543ms
841 packets transmitted, 840 received, +28 duplicates, 0% packet loss, time 9228ms
發(fā)現(xiàn)最多出現(xiàn) 1 個丟包,說明斷網(wǎng)時間最長 10ms。但是發(fā)現(xiàn)了很多 duplicates 的包,這個是因為 mlnx 網(wǎng)卡在 switchdev 模式下有一個 bug:當(dāng)出現(xiàn)雙進程時,本應(yīng)該流量被鏡像到另一個進程,結(jié)果 mlnx 網(wǎng)卡讓一個進程收到了兩個重復(fù)包,而另一個進程沒有包。目前 mlnx 已經(jīng)確認了這個問題。
iperf 測試
觀測到升級期間,有 1s 速率減半,由滿速率 22Gbps 到 13Gbps,估計是和 MLNX bug 引發(fā)的重傳包和升級斷網(wǎng)有關(guān)。1s 之后迅速回到 22Gbps。
vhost-user + virtio 模式
ping 測試
斷網(wǎng)時間在 70~80ms 左右。通過對日志分析,發(fā)現(xiàn)是老進程在退出時,反復(fù)進行了 vhost 重連導(dǎo)致,這塊通過繼續(xù)優(yōu)化,斷網(wǎng)時間會進一步降低。這里就不再展開了。
iperf 測試
和 VF 方式結(jié)果類似,也是吞吐會有一定的下降,升級完成后立刻恢復(fù)。