記一次線上DPDK-LVS的故障排查 薦
背景
我們內部基於dpdk自研的高效能負載均衡器dpvs已經在多個機房部署上線,執行正常,但近期有多個金融相關的業務反饋,服務資料包在經過dpvs轉發後,會出現hang住的情況。
問題
dpvs已經在多個機房上線,執行時間已超過半年,為何突然有業務反饋異常反饋問題的業務多與金融區相關(金融區由於其特殊性,會額外增加安全方面的加固策略)為什麼問題表現均為服務hang住
問題排查
首先,我們懷疑與dpvs或與金融的某些安全策略相關,因此我們做了如下測試(後端上跑的均是相同的測試程式碼,並模擬了服務端邏輯):
client < ----- > dpvs < ----- > rs(金融區) 不正常
client < ----- > dpvs < ----- > rs(非金融區) 正常
client < ----- > lvs < ----- > rs(金融區) 正常
client < ----- > lvs < ----- > rs(非金融區) 正常
通過1、2組測試能夠得出結論:該問題與金融區相關且dpvs轉發正常
通過3、4組測試能夠得出結論:該問題與金融區無關且kernel版lvs轉發正常
通過1、3組測試能夠得出結論:該問題與dpvs有關,經過dpvs的請求不正常
通過2、4組測試能夠得出結論:該問題與dpvs/lvs無關,經過dpvs/lvs的請求均正常
以上4組結論兩兩衝突,無法定位問題是與dpvs相關還是與金融區相關,排查一度進入僵局,無法定位故障點。
為了進一步排查,我們在client和後端rs上抓包排查,發現client的請求均能夠正常到達rs,而rs的大部分資料也能夠正常回復給client,但有固定的幾個包總是會被重傳且直至超時,以下是抓包截圖:
其中10.128.x.x是rs的ip,10.115.x.0/24是dpvs的local ip,通過在rs上的抓包結果可以清楚的看出rs發給dpvs的length為184的包正確傳輸,但length為2的包一直在重傳,且直至超時都沒有成功,同時在client上的抓包顯示,client收到了這個length為2的包,但是由於tcp checksum error被丟掉了,並沒有交給上層應用去處理,這樣就解釋了為什麼異常時的表現是hang住,因為某個資料包一直在重傳,直至timeout。
通過上面的分析,我們又產生了疑問:現在的硬體網絡卡一般都具有csum offload的功能,能夠通過網絡卡硬體幫我們做checksum,難道是網絡卡的checksum offload功能出現了問題?如果真是網絡卡硬體的offload功能出現問題,那影響的應該不是某一個特定的資料包,而是所有經過這塊網絡卡的資料包才對,因此我們懷疑是網絡卡在針對某個特定資料包的計算checksum的時候產生了錯誤,為了驗證這個問題,我們在dpvs上進行抓包分析,以下是抓包截圖:
這個就是被不斷重傳的包,能夠看到dpvs確實收到了這個包,並且處理邏輯也完全正常,剩下的步驟只有通過網絡卡做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是這樣規定的:
因此rs的網絡卡在資料包長度不足60位元組時需要做兩件事情:
- 補充1位元組的padding達到最小長度60位元組
- 補充的padding為全0
可以看到,在二層頭中,確實有個補充的1位元組的padding:ec,這個padding並沒有按rfc894的規定填充成全0,而是填了非0值,這樣就造成了dpvs的網絡卡在計算tcp checksum時把這個padding誤當成了tcp data而計算了check sum,因此在client接收到這個資料包並根據ip偽頭部和tcp頭部計算出來的checksum與資料包tcp頭部的checksum不一致,因此並沒有把這個資料包交給上層應用處理而是直接drop。
----- 網絡卡手冊針對 TCP/UDP checksum部分的說明
至此,問題的原因已經很明顯了:部分機器的網絡卡在做padding時未按照rfc894的規定補充全0而是補充了其他值,導致dpvs的網絡卡在做checksum offload時padding的資料也參與了checksum的計算。
分析正常的rs和不正常的rs在網絡卡硬體上的差別,發現:網絡卡的硬體型號相同,驅動型號也相同,但不正常的網絡卡fireware與正常的網絡卡不相同,而fireware我們沒有辦法自行升級或降級。
整個故障的過程可以大概表示為:
client dpvs rs
---1--- > < ---2--- < ---3---
步驟1:資料包正常,請求資料
步驟2:部分資料包初始長度小於60位元組,需要網絡卡補充padding,網絡卡先計算checksum填入tcp包頭後補充padding至資料包末尾,此時checksum正常,但padding不為全0
步驟3:dpvs收到步驟2的包進行正常轉發邏輯處理後轉發至網絡卡,由網絡卡計算checksum並轉發,但在計算新的checksum時由於padding非全0導致checksum計算錯誤,client收到後丟棄了這個包
ps:以上是rs的網絡卡在新增padding時補充的不是全0,另一種場景是client的網絡卡在新增padding時補充的不是全0,這兩種情況都會導致上述問題的出現。
問題解決
至此,我們已經能夠解釋最開始提出的三個問題:
dpvs已經在多個機房上線,執行時間已超過半年,為何突然有業務反饋異常
a:該業務是在某個核心機房上線了dpvs後出現了問題,其他機房很早上線了dpvs但由於其他機房是改業務的備份機房實際並未啟用,因此半年多來一直沒有發現問題
反饋問題的業務多與金融區相關(金融區由於其特殊性,會額外增加安全方面的加固策略)
a:排查發現是金融區的某一批次機器的fireware存在bug導致,與金融區本身的安全策略無關
為什麼問題表現均為服務hang住
a:問題的實質是出現丟包,服務在等待響應,因此表現為hang住
接下來我們將解決該問題:
只要讓dpvs在處理資料包時,忽略資料包以前的padding部分,而由dpvs的網絡卡重新去處理padding(由於網絡卡計算checksum是在補充padding之前,因此可以保證此時的checksum一定是正確的)。由於dpvs是基於dpdk開發的,資料包在dpvs中是以mbuf的結構儲存和處理的,以下是mbuf的結構:
資料幀被儲存在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