dpdk-lvs的一次線上故障排查報告

小米雲技術發表於2018-11-01

本文記錄了dpdk-lvs叢集的一次線上故障排查過程,排查思路可供讀者參考。
上篇文章回顧:SOAR的IDE外掛——您的貼身DBA保鏢

背景

我們內部基於 dpdk 自研的高效能負載均衡器 dpdk-lvs 已經在多個機房部署上線,執行正常,但近期有多個金融相關的業務反饋,服務資料包在經過dpdk-lvs轉發後,會出現hang住的情況。

問題

1、dpdk-lvs 已經在多個機房上線,執行時間已超過半年,為何突然有業務反饋異常

2、反饋問題的業務多與金融區相關(金融區由於其特殊性,會額外增加安全方面的加固策略)

3、為什麼問題表現均為服務hang住

問題排查

首先,我們懷疑與dpdk-lvs或與金融的某些安全策略相關,因此我們做了如下測試(後端上跑的均是相同的測試程式碼,並模擬了服務端邏輯):

1、client < —– > dpdk-lvs < —– > rs(金融區)     不正常

2、client < —– > dpdk-lvs < —– > rs(非金融區)  正常

3、client < —– > lvs < —– > rs(金融區)               正常

4、client < —– > lvs < —– > rs(非金融區)           正常

通過1、2組測試能夠得出結論:該問題與金融區相關且dpdk-lvs轉發正常

通過3、4組測試能夠得出結論:該問題與金融區無關且kernel版lvs轉發正常

通過1、3組測試能夠得出結論:該問題與dpdk-lvs有關,經過dpdk-lvs的請求不正常

通過2、4組測試能夠得出結論:該問題與dpdk-lvs/lvs無關,經過dpdk-lvs/lvs的請求均正常

以上4組結論兩兩衝突,無法定位問題是與dpdk-lvs相關還是與金融區相關,排查一度進入僵局,無法定位故障點。

為了進一步排查,我們在client和後端rs上抓包排查,發現client的請求均能夠正常到達rs,而rs的大部分資料也能夠正常回復給client,但有固定的幾個包總是會被重傳且直至超時,以下是抓包截圖:

dpdk-lvs的一次線上故障排查報告

其中10.128.129.14是rs的ip,10.115.167.0/24是dpdk-lvs的local ip,通過在rs上的抓包結果可以清楚的看出rs發給dpdk-lvs的length為184的包正確傳輸,但length為2的包一直在重傳,且直至超時都沒有成功,同時在client上的抓包顯示,client收到了這個length為2的包,但是由於tcp checksum error被丟掉了,並沒有交給上層應用去處理,這樣就解釋了為什麼異常時的表現是hang住,因為某個資料包一直在重傳,直至timeout。

通過上面的分析,我們又產生了疑問:現在的硬體網路卡一般都具有csum offload的功能,能夠通過網路卡硬體幫我們做checksum,難道是網路卡的checksum offload功能出現了問題?如果真是網路卡硬體的offload功能出現問題,那影響的應該不是某一個特定的資料包,而是所有經過這塊網路卡的資料包才對,因此我們懷疑是網路卡在針對某個特定資料包的計算checksum的時候產生了錯誤,為了驗證這個問題,我們在dpdk-lvs上進行抓包分析,以下是抓包截圖:

dpdk-lvs的一次線上故障排查報告

這個包就是被不斷重傳的包,能夠看到dpdk-lvs確實收到了這個包,並且處理邏輯也完全正常,剩下的步驟只有通過網路卡做checksum並把這個資料包轉發出去,問題似乎確實是出在了計算checksum這裡,我們在分析這個包有什麼特點,可以看到,這個包的初始大小=ethernet header length + ip header length + tcp header length + tcp data = 14 + 20 + 20 + 5 = 59,而我們知道,在網路中傳輸的資料幀最小長度為64位元組,除去FCS的4位元組(這部分也由網路卡自行計算後新增在資料包末尾),最小長度應為60位元組,也就是說,到達網路卡的資料包如果不夠60位元組,那麼網路卡會在動在資料包末尾增加全0的padding來使資料包能夠達到60位元組,所以這個資料包也是需要網路卡硬體來補充1位元組的padding來達到最小傳輸長度。對此rfc894是這樣規定的:

dpdk-lvs的一次線上故障排查報告

因此rs的網路卡在資料包長度不足60位元組時需要做兩件事情:

1、補充1位元組的padding達到最小長度60位元組

2、補充的padding為全0

dpdk-lvs的一次線上故障排查報告

可以看到,在二層頭中,確實有個補充的1位元組的padding:ec,這個padding並沒有按rfc894的規定填充成全0,而是填了非0值,這樣就造成了dpdk-lvs的網路卡在計算tcp checksum時把這個padding誤當成了tcp data而計算了check sum,因此在client接收到這個資料包並根據ip偽頭部和tcp頭部計算出來的checksum與資料包tcp頭部的checksum不一致,因此並沒有把這個資料包交給上層應用處理而是直接drop。

dpdk-lvs的一次線上故障排查報告

(網路卡手冊針對 TCP/UDP checksum部分的說明)

至此,問題的原因已經很明顯了:部分機器的網路卡在做padding時未按照rfc894的規定補充全0而是補充了其他值,導致dpdk-lvs的網路卡在做checksum offload時padding的資料也參與了checksum的計算。

分析正常的rs和不正常的rs在網路卡硬體上的差別,發現:網路卡的硬體型號相同,驅動型號也相同,但不正常的網路卡fireware與正常的網路卡不相同,而fireware我們沒有辦法自行升級或降級。

整個故障的過程可以大概表示為:

client                 dpdk-lvs                rs

          —1— >

                                        < —2—

          < —3—

步驟1:資料包正常,請求資料

步驟2:部分資料包初始長度小於60位元組,需要網路卡補充padding,網路卡先計算checksum填入tcp包頭後補充padding至資料包末尾,此時checksum正常,但padding不為全0

步驟3:dpdk-lvs收到步驟2的包進行正常轉發邏輯處理後轉發至網路卡,由網路卡計算checksum並轉發,但在計算新的checksum時由於padding非全0導致checksum計算錯誤,client收到後丟棄了這個包

ps:以上是rs的網路卡在新增padding時補充的不是全0,另一種場景是client的網路卡在新增padding時補充的不是全0,這兩種情況都會導致上述問題的出現。

問題解決

至此,我們已經能夠解釋最開始提出的三個問題:

1、dpdk-lvs已經在多個機房上線,執行時間已超過半年,為何突然有業務反饋異常
A:該業務是在某個核心機房上線了dpdk-lvs後出現了問題,其他機房很早上線了dpdk-lvs但由於其他機房是改業務的備份機房實際並未啟用,因此半年多來一直沒有發現問題

2、反饋問題的業務多與金融區相關(金融區由於其特殊性,會額外增加安全方面的加固策略)
A:排查發現是金融區的某一批次機器的fireware存在bug導致,與金融區本身的安全策略無關

3、為什麼問題表現均為服務hang住
A:問題的實質是出現丟包,服務在等待響應,因此表現為hang住

接下來我們將解決該問題:

只要讓dpdk-lvs在處理資料包時,忽略資料包以前的padding部分,而由dpdk-lvs的網路卡重新去處理padding(由於網路卡計算checksum是在補充padding之前,因此可以保證此時的checksum一定是正確的)。由於dpdk-lvs是基於dpdk開發的,資料包在dpdk-lvs中是以mbuf的結構儲存和處理的,以下是mbuf的結構:

dpdk-lvs的一次線上故障排查報告

資料幀被儲存在headroom和tailroom之間(與skb類似),pkt_len=data_len=整個資料幀的長度,我們要做的就是將padding從data中去除(放到tailroom中去),因此可以在資料包入口處新增以下程式碼:

int padding_length = mbuf->data_len - (mbuf->l2_len +rte_be_to_cpu_16(ipv4_hdr->total_length));
mbuf->data_len = mbuf->data_len - padding_length;
mbuf->pkt_len = mbuf->data_len;複製程式碼

新增以上程式碼後測試通過,本次故障解決。

參考資料

https://tools.ietf.org/html/rfc894

http://doc.dpdk.org/guides/prog_guide/mbuf_lib.html

https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf

本文首發於公眾號“小米運維”,點選檢視原文

相關文章