抽絲剝繭:生產環境中負載均衡產品DPDK問題的解決

ULB4是UCloud自主研發的基於DPDK的高可用四層負載均衡產品,轉發能力接近線速;DPDK則是一個高性能的開源數據面開發套件。ULB4做爲用戶應用的全局入口,在大流量多元化場景下保證用戶業務的持續穩定相當重要,這也是UCloud網絡產品團隊的技術使命。尤爲現網單個ULB集羣承載帶寬已達10G,包量83萬PPS,運行環境複雜,即便面臨突發因素(好比觸發未知BUG),咱們也要設法保證產品正常工做,避免產生嚴重影響。緩存

近期,咱們在ULB4的線上環境中,發現了一個DPDK的發包異常現象,因爲整個ULB產品爲集羣架構,該異常並未致使用戶服務不可用。但爲了任什麼時候刻都能保證用戶服務的足夠穩定,團隊經過GDB、報文導出工具、生產環境流量鏡像等手段,從現網GB級流量中捕獲異常報文,再結合DPDK源碼分析,定位到緣由出自DPDK自己的BUG並修復解決。期間未對用戶業務形成影響,進一步保證了UCloud數萬ULB實例的穩定運行。sass

本文將從問題現象着手,抽絲剝繭,詳述問題定位、分析與解決全過程,但願能爲ULB用戶和DPDK開發者提供參考與啓迪。服務器

問題背景網絡

在12月初一貫穩定的ULB4集羣中忽然出現了容災,某臺ULB4服務器工做異常被自動移出了集羣。當時的現象是:數據結構

轉發面服務監控到網卡接收方向流量正常,可是發送方向流量爲0,重啓轉發面服務後又能夠正常收發,同時集羣其餘機器也會不按期出現異常狀況。對用戶業務而言,會出現少許鏈接輕微抖動,隨後迅速恢復。架構

下面是整個問題的處理過程,咱們在此過程當中作出種種嘗試,最終結合DPDK源碼完成分析和解決,後續也準備將自研的報文導出工具開源共享。負載均衡

問題定位與分析運維

ULB4集羣一直很穩定地工做,忽然陸續在集羣的不一樣機器上出現一樣的問題,而且機器恢復加入集羣后,過了一段時間又再次出現一樣的問題。根據咱們的運營經驗,初步猜想是某種異常報文觸發了程序BUG。可是,面對GB級流量如何捕獲到異常報文?又如何在不影響業務狀況下找出問題呢?tcp

1、GDB調試報文,發現疑點函數

想要知道整個程序爲何不發包,最好的辦法就是可以進入到程序中去看看具體的執行過程。對於DPDK用戶態程序來講,GDB顯然是一個好用的工具。咱們在發包程序邏輯中設置斷點,並經過disassemble命令查看該函數的執行邏輯,反彙編以後足足有七百多行。(該函數中調用的不少函數都使用了inline修飾,致使該函數在彙編以後指令特別多)

結合對應DPDK版本的源碼,單條指令一步步執行。在屢次嘗試以後,發現每次都會在下圖所示的地方直接返回。

大體流程是i40e_xmit_pkts()在發送的時候,發現發送隊列滿了就會去調用i40e_xmit_cleanup()清理隊列。DPDK中網卡在發送完數據包後會去回寫特定字段,代表該報文已經發送,而驅動程序去查看該字段就能夠知道這個報文是否已經被髮過。此處的問題就是驅動程序認爲該隊列中的報文始終未被網卡發送出去,後續來的報文將沒法加入到隊列而被直接丟棄。

至此,直接緣由已經找到,就是網卡由於某種緣由不發包或者沒能正確回寫特定字段,致使驅動程序認爲發送隊列始終處於隊列滿的狀態,而沒法將後續的報文加入發送隊列。

那麼爲何出現隊列滿?異常包是否相關呢?帶着這個疑問,咱們作了第二個嘗試。

2、一鍵還原網卡報文

隊列滿,並且後面的報文一直加不進去,說明此時隊列裏面的報文一直卡在那。既然咱們猜想多是存在異常報文,那麼有沒有可能異常報文還在隊列裏面呢?若是能夠把當前隊列裏面的報文所有導出來,那就能夠進一步驗證咱們的猜想了。

基於對DPDK的深刻研究,咱們根據如下步驟導出報文。

咱們看i40e_xmit_pkts()函數,會發現第一個參數就是發送隊列,因此咱們能夠獲取到隊列的信息。

以下圖所示,在剛進入斷點的時候,查看寄存器信息,以此來得到該函數對應的參數。

當咱們打印該隊列的消息時,卻發現沒有符號信息,此時咱們能夠以下圖所示去加載編譯時候生成的 i40e_rxtx.o 來獲取對應符號信息。

在獲得隊列信息後,咱們使用GDB的dump命令將整個隊列中全部的報文所有按隊列中的順序導出,對每一個報文按序號命名。

此時導出的報文仍是原始的報文,咱們沒法使用wireshark方便地查看報文信息。爲此以下圖所示,咱們使用libpcap庫寫了個簡單的小工具轉換成wireshark能夠解析的pcap文件。

果真,以下圖所示,在導出的全部報文中包含了一個長度爲26字節,但內容爲全0的報文。這個報文看上去十分異常,彷佛初步驗證了咱們的猜想:

爲了提升在排查問題時導出報文的速度,咱們寫了一個報文一鍵導出工具,能夠在異常時一鍵導出全部的報文並轉成pcap格式。

在屢次導出報文後,咱們發現一個規律:每次都會有一個長度爲26字節可是全0的報文,並且在其前面都會有一個一樣長度的報文,且每次源IP地址網段都來自於同一個地區。

3、流量鏡像,確認異常包

第二步結論讓整個排查前進了一大步,可是隊列包是通過一系列程序處理的,並非真正的原始業務報文。不達目的不罷休,關鍵時刻仍是要上鏡像抓包,因而當晚緊急聯繫網絡運維同事在交換機上配置port-mirroring(端口鏡像),將發往ULB4集羣的流量鏡像到一個空閒服務器上進行鏡像抓包。固然,鏡像服務器還須要作特殊配置,以下:

設置網卡混雜模式,用於收取鏡像流量(ifconfig net2 promisc)。 關閉GRO功能(ethtool -K net2 gro off),用於收取最原始的報文,防止Linux的GRO功能提早將報文進行組裝。 根據異常IP的地域特性,咱們針對性抓取了部分源IP段的流量。

參考命令:nohup tcpdump -i net2 -s0 -w %Y%m%d_%H-%M-%S.pcap -G 1800 「proto gre and (((ip[54:4]&0x11223000)==0x11223000) or ((ip[58:4]&0x11223000)==0x11223000))」 &

通過屢次嘗試後,功夫不負有心人,故障出現了,通過層層剝離篩選,找到了以下報文:

這是IP分片報文,可是奇怪的是IP分片的第二片只有IP頭。通過仔細比對,這兩個報文合在一塊兒就是導出隊列報文中的那兩個連在一塊兒的報文。後26字節和全0報文徹底吻合。

咱們知道在TCP/IP協議中,若是發送時一個IP報文長度超過了MTU,將會觸發IP分片,會被拆成多個小的分片報文進行發送。正常狀況下,全部的分片確定都是攜帶有數據的。可是這一個分片報文就很異常,報文的總長度是20,也就是說只有一個IP頭,後面再也不攜帶任何信息,這樣的報文是沒有任何意義的。這個報文還由於長度過短在通過交換機後被填充了26字節的0。

至此,咱們最終找到了這個異常報文,也基本驗證了咱們的猜想。可是還須要去實際驗證是否爲這種異常報文致使。(從整個報文的交互來看,這一片報文原本是設置了不可分片的TCP報文,可是在通過某個公網網關後被強制設定了容許分片,而且分片出了這種異常的形式。)

4、解決方案

若是確實是這個異常報文致使的,那麼只要在收包時對這種異常報文進行檢查而後丟棄就能夠了。因而,咱們修改DPDK程序,丟棄這類報文。做爲驗證,先發布了一臺線上服務器,通過1天運行再也沒有出現異常容災狀況。既然問題根因已經找到,正是這種異常報文致使了DPDK工做異常,後續就能夠按灰度全網發佈了。

5、DPDK社區反饋

本着對開源社區負責任的態度,咱們準備將BUG向DPDK社區同步。對比最新的commit後,找到11月6日提交的一個commit,狀況一模一樣,以下:

ip_frag: check fragment length of incoming packet

DPDK 18.11最新發布的版本中,已對此進行了修復,和咱們處理邏輯一致,也是丟棄該異常報文。

覆盤和總結

處理完全部問題後,咱們開始作總體覆盤。

1、ULB沒法發包的成因總結

ULB4沒法發包的整個產生過程以下:

DPDK收到分片報文中的第一片,將其緩存下來等待後續分片; 第二片只有IP頭的異常分片到來,DPDK按照正常的報文處理邏輯進行處理,並無進行檢查丟棄,因而兩片報文的rte_mbuf結構被鏈在一塊兒,組成了一個鏈式報文返回給ULB4; 這樣的報文被ULB4接收後,由於整個報文的總長度並無達到須要分片的長度,因此ULB4直接調用DPDK的發送接口發送出去; DPDK沒有對這種異常報文進行檢查,而是直接調用相應的用戶態網卡驅動直接將報文發送出去; 用戶態網卡驅動在發送這樣的異常報文時觸發了網卡tx hang; 觸發tx hang後,網卡再也不工做,驅動隊列中報文對應的發送描述符再也不被網卡正確設置發送完成標記; 後續的報文持續到來,開始在發送隊列中積壓,最終將整個隊列佔滿,再有報文到來時將被直接丟棄。

2、爲何異常報文會觸發網卡tx hang

首先咱們看下DPDK中跟網卡發送報文相關的代碼。

從以上的圖中咱們能夠看到,根據網卡的Datasheet對相關字段進行正確設置很是重要,若是某種緣由設置錯誤,將可能會致使不可預知的後果(具體仍是要參考網卡的Datasheet)。

以下圖所示,一般網卡對應的Datasheet中會對相應字段進行相關描述,網卡驅動中通常都會有相應的數據結構與其對應。

在有了基本瞭解後,咱們猜測若是直接在程序中手動構造這種相似的異常報文,是否也會致使網卡異常不發包?

答案是確定的。

以下圖所示,咱們使用這樣的代碼片斷構成異常報文,而後調用DPDK接口直接發送,很快網卡就會tx hang。

3、對直接操做硬件的思考

直接操做硬件是一件須要很是謹慎的事情,在傳統的Linux系統中,驅動程序通常處於內核態由內核去管理,並且驅動程序代碼中可能進行了各類異常處理,所以不多會發生用戶程序操做致使硬件不工做的狀況。而DPDK由於其自身使用用戶態驅動的特色,使得能夠在用戶態直接操做硬件,同時爲了提高性能可能進行了很是多的優化,若是用戶自身程序處理出問題就有可能會致使網卡tx hang這樣的異常狀況發生。

4、工具的價值

咱們編寫了一鍵導出DPDK驅動隊列報文的工具,這樣就能夠在每次出現問題時,快速導出網卡驅動發送隊列中的全部報文,大大提升了排查效率。這個工具再優化下後,準備在UCloud GitHub上開源,但願對DPDK開發者有所幫助。

寫在最後

DPDK做爲開源套件,一般狀況下穩定性和可靠性不存在什麼問題,可是實際的應用場景變幻無窮,一些特殊狀況可能致使DPDK工做異常。雖然發生機率很小,可是DPDK一般在關鍵的網關位置,一旦出現了問題,哪怕是不多見的問題也將會產生嚴重影響。

所以技術團隊理解其工做原理並對其源碼進行分析,同時可以結合具體現象一步步定位出DPDK存在的問題,對提升整個DPDK程序的服務可靠性具備重要意義。值得一提的是,ULB4的高可用集羣架構在本次問題的處理過程當中發揮了重要做用,在一臺不可用的時候,集羣中其餘機器也能夠繼續爲用戶提供可靠服務,有效提高了用戶業務的可靠性。

相關文章
相關標籤/搜索