Categories
程式開發

eBay雲計算“網”事|網絡重傳篇


#導讀

在之前的eBay雲計算“網”事|網絡超時篇eBay雲計算“網”事|網絡丟包篇裡,我們針對Linux 主機網絡中常見的延時和丟包問題進行了分析。本篇將關注網絡中另外一個常見的問題: 重傳 。在網絡環境中,重傳率的高低往往直接影響到數據的傳輸效率,因此也是用戶應用比較關心的一個指標。

一、問題描述

某個部署到Kubernetes集群的應用,會有多個SLB(Softare Load Balancer)節點來對接收的數據流量進行分發。這些SLB節點原本部署在物理機上,在將某個業務節點部署到Kubernetes集群後,發現每10秒的重傳包個數,相對於運行在物理機上時更高, 如圖1所示。於是我們著手進行排查。

eBay雲計算“網”事|網絡重傳篇 1

圖1 不同節點的TCP每10秒的重傳個數

經過和應用方溝通並進入容器查看後,我們了解到相關信息:

  • 每個SLB節點,同時會有 25k 左右的TCP鏈接。這些鏈接的客戶端節點,不一定相同,每個客戶端節點可能會跟SLB創建多個鏈接,鏈接的維持時間一般都比較短。
  • 運行在物理機上的SLB節點,每10秒的重傳包是 10k 左右,而運行在物理機上的SLB節點,每10秒的重傳包在 2k 以下。
  • 重傳包個數從netstat -s|grep “segments retransmitted”得出,取10秒前後的差值,就得出每10秒的重傳包數。
  • 每個SLB節點的業務量差別不大。運行在Kubernetes集群中的SLB,業務量會比運行在物理機中的SLB流量多些,但是不至於造成重傳統計上這麼大的差別。

TCP數據包的重傳和網絡鏈接客戶端、網絡路徑強相關。在客戶端和網絡路徑不同的情況下,節點的重傳率可能會不同。因此兩方的重傳數據對比,很難說明重傳率高就是因為業務容器化引起的。但我們仍舊需要對重傳著手進行分析,並給出合理的解釋。

二、問題分析

1. 是否有數據丟包的情況發生

因為涉及到重傳,第一反應就是查看是否有丟包的情況發生。從netstat -s的情況來看,並沒有明顯的丟包情況發生。

2. TCP協議棧配置參數是否一致

然後,我們對比下雙邊TCP配置參數,對比容器Pod network namespace和物理機下的/proc/sys/net的配置參數。兩邊的參數一致,除了TCP的擁塞(congestion)算法,前者是BBR,後者是CUBIC。於是修改了下容器內tcp_congestion_control的算法,一段時間後,當老的鏈接都關閉,新的鏈接擁塞算法都變為BBR後,重傳包減少了一半左右,到了 6k/10秒 ,如圖2所示。儘管指標有了很大的改善,但是重傳率還是比較高。

eBay雲計算“網”事|網絡重傳篇 2

圖2 修改TCP congestion算法為CUBIC

3. 重傳的鏈接

因為鏈接比較多,我們需要查看具體是哪些鏈接的重傳導致的問題。於是運行BCC的tcpretrans.py 工具,來查看具體重傳的鏈接信息。 結果發現,重傳比較多的,都屬於四個固定的虛擬機客戶端。

於是在這些節點上通過tcpdump進行抓包,發現瞭如下現象:

  • 不管數據是來自於Pod還是來自於物理機的SLB,在虛擬機上一直能看到有包亂序的情況。
  • 當接收到亂序包後,VM會發送SACK數據包,而在Pod接收到SACK數據包後,Pod會立刻進行重傳,但物理機的SLB並沒有。

4. 為何數據包會亂序

我們先來定位數據包亂序的原因。

在虛擬機所在的物理機上,對網卡和給虛擬機分配的tap端口上做tcpdump,數據包並沒有亂序,因此可以判斷, 亂序應該是發生在虛擬機內部了。

在虛擬機上kprobe了函數napi_gro_receive(),將接收的數據包進行打印,發現在數據包被該函數處理的時候,也並沒有亂序,但是tcpdump上看到的數據包是亂序的,因此我們懷疑是gro引起的亂序問題。

通過ethtool -K eth0 gro off將虛擬機網卡的gro特性關閉。在gro關閉後,從tcpdump抓到的數據就不存在亂序情況了。因此將有包亂序的節點的gro都關閉,在gro關閉後,可以看到TCP的重傳從 6k/10秒 降到了 4k/10秒

這些虛擬機使用的都是ubuntu 16.04的系統,且為4.4的kernel版本,所以網卡驅動的gro有bug,導致包亂序,而如果使用的是4.15 kernel版本,就不會有亂序的情況發生。

既然了解了包亂序的問題,那麼遺留的問題就是,Pod和物理機對接收到SACK後的行為為何會不一致?

5. 為何收到SACK數據包的行為不一致

從netstat -s的命令輸出來看,可以看出雙方重傳的差別,如圖3所示:

eBay雲計算“網”事|網絡重傳篇 3

圖3 重傳的原因

Pod容器的TCPSackRecovery和TCPLossProbes導致重傳的次數差別不大,而物理機上,絕大部分的重傳都是由TCPLossProbes引起的,TCPSackRecovery只是TCPLossProbes的 1/20 左右。如果我們可以減少Pod容器的TCPSackRecovery比例,那麼就能大大降低數據的重傳。

在TCP協議棧中,通過FACK,SACK,RACK等算法來決定是否對TCP數據進行重傳,這些算法在眾多的RFC文檔中都有詳細定義,而對於開發人員來說,相對便捷的途徑,是從TCP協議棧的實現來更快地了解TCP重傳的多種原因。

從TCP協議棧的實現來看,它會調用tcp_retransmit_skb()函數來進行TCP數據包的重傳,於是大概瀏覽了協議棧代碼,整理出函數的調用關係圖,如圖4所示:

eBay雲計算“網”事|網絡重傳篇 4

圖4 TCP重傳函數調用路徑

如果在調用函數tcp_retransmit_skb()的地方,打印出函數調用的協議棧,那麼可以很清楚地看到是因哪個路徑而引起的重傳。這類數據如果將函數調用棧打印出來,就是圖5顯示的情況:

eBay雲計算“網”事|網絡重傳篇 5

圖5 重傳的函數調用棧

在tcp_ack()中調用tcp_xmit_recovery來進行重傳,期間需要調用多個函數進行TCP鏈接是否要進入recovery狀態的判斷。直接的棧調用關係,體現不出這種判斷的具體細節,也不知道TCP鏈接進入recovery狀態的具體原因。因此需要繼續查看代碼,進行更深入的分析。

在tcp_xmit_recovery()中,會根據rexmit的值來判斷是否會調用tcp_xmit_retransmit_queue()。而決定rexmit值的地方,就在函數tcp_fastretrans_alert()中。該函數也會有多個路徑來決定是否給rexmit賦值。 因此,基於以下的邏輯及eBPF,我們設計了一個重傳的定位工具

  • 如果數據重傳,打印出重傳的棧,就可以判斷出數據包的重傳究竟是由於輸入的數據引起的,還是timer超時引起的。
  • 針對輸入數據引起的重傳,我們需要知道輸入數據的具體信息,以及重傳數據的具體信息。這些信息包含數據的源IP,目的IP,tcp sequence,ack sequence,長度,tcp flag字段等。
  • 在此基礎上,我們需要知道輸入數據包在協議棧中被哪些函數處理。因此利用kprobe,探查了tcp_ack(),tcp_fastretrans_alert()裡面的多個函數。如果某個函數不能被kprobe,那麼就需要kprobe函數調用的子程序。這些kprobe的函數通過tcp的socket和當前的PID為key的eBPF map進行關聯,將調用到的函數存儲到map中。在函數重傳的時候,將該信息通過event發送給用戶態。
  • 針對tcp_fastretrans_alert(),將函數的輸入參數以及當前tcp socket的一些字段信息進行打印,例如prior_snd_una, is_dupack ack_flag, tp->packets_out,tp->sacked_out,icsk->icsk_ca_state,tp->snd_una, tp-> reordering , tp->mss_cache等信息也一併計入到map中。

運行工具後,當數據發生重傳時,我們可以獲取到以下三類信息,詳情如圖6所示:

  • 輸入數據包的信息和重傳數據包的信息。
  • 數據包的函數處理函數。
  • 數據包的處理協議棧。

eBay雲計算“網”事|網絡重傳篇 6

圖6 抓取的數據包重傳信息

根據tcp_fastretrans_alert ()的函數調用信息,以及該數據包調用到的函數,不難推測出該數據因為觸發了tcp_force_fast_retransmit()而引起了重傳。在tcp_force_fast_retransmit()中,會進行圖7的判斷:

eBay雲計算“網”事|網絡重傳篇 7

圖7 tcp_force_fast_retransmit() 函數

該處判斷此TCP鏈接的Highest sacked seq – unacknowledged seq >= 重新排序 * MSS 是否滿足條件,如果滿足條件的話,則會進行重傳操作。可以很明顯看到,tp->reordering就是決定是否進行重傳的關鍵因素。因此通過ss -i,查看了下所有鏈接的tp->reordering值,如圖8所示:

eBay雲計算“網”事|網絡重傳篇 8

圖8 TCP重傳的統計

在ss -i命令的輸出結果中,如果tp->reordering等於默認值3,那麼就不會有值打印出來,而如果tp->reordering不等於3,則會打印出來具體的值。從結果可以看出,因為鏈接的客戶端和網絡環境不同,容器環境內SLB和物理機器環境內SLB的TCP鏈接,相互之間的tp->reordering值差別很大。

從tcp_force_fast_retransmit的判斷條件來看,tp->reordering值越高,越不容易發生重傳,這也是在抓包的時候,為什麼同時收到SACK數據,容器發生重傳,而物理機節點不發生重傳的原因。至於tcp->reodering如何更新,網絡上已經有很多的分析文章,這裡不再贅述。

三、解決方案

在TCP鏈接建立的時候,tp->reordering默認值是從/proc/sys/net/ipv4/tcp_reordering(默認值為3)獲取的。之後根據網絡的亂序情況,進行動態調整,最大可以增長到/proc/sys/net/ipv4/tcp_max_reordering (默認值為300)的大小。

在網卡TSO開啟的情況下,會發送超過3個MSS的數據包,如果採用reordering值為3,稍微的亂序都可能導致重傳,而很多的重傳都是沒有必要的。 因此嘗試修改/proc/sys/net/ipv4/tcp_reordering的默認值為6,以修復因為亂序而導致無謂重傳的問題。 經修改後,從netstat -s的結果來看,TCPSackRecovery的統計明顯降低了,因此重傳率也降低了,如圖9所示。

eBay雲計算“網”事|網絡重傳篇 9

圖9 tcp重傳率在增大reordering值後下降

需要注意的是:

  • 新建的network namespace的/proc/sys/net/ipv4/tcp_reordering默認值是3,不會和host network namespace設置的值保持一致。
  • 監聽端口的socket創建後,該socket上的後續鏈接,都會默認使用該socket的tp->reordering作為初始值,而不會動態從/proc/sys/net/ipv4/tcp_reordering裡讀取,因此,該值需要在監聽端口的socket創建之前進行設置。

四、網絡重傳篇小結

通過netstat -s顯示的MIB統計是該network namespace下所有鏈接統計的總和。在類似SLB這種具有幾萬個TCP鏈接的場景下,還是需要像BCC這樣的tcpretrans.py 工具來尋找重傳率比較高的客戶端,這樣可以將問題收斂到某些鏈路上。

與丟包問題不同,重傳原因在TCP內核協議棧中的MIB記錄點明顯偏少,因此也不容易直接通過netstat -s 顯示的MIB信息來獲取,而這可以通過eBPF工具來抓取內核中對數據包的處理路徑進行針對性分析。除了像本案例場景的參數調優以外,內核處理路徑的抓取還可以輸出數據包在協議棧內的信息,也能進一步幫助我們後續定位協議棧方面的代碼問題。

eBay雲計算“網”事三部曲總結

網絡環境作為雲計算裡面的重要一環,經常會發生各種各樣的問題,也是用戶反饋較多的問題點。本系列的三篇文章,從 網絡延遲,網絡丟包,網絡重傳 三個方面,針對網絡的常見問題進行分析,均為線上真實案例。這些案例並不是我們處理過的最困難的問題,卻是在網絡環境中最典型的問題,因此拿來作為例子詳細講述。現在回頭來看,當初定位問題的思路或方式還有很多可以改進的地方,但是認知和經驗的積累從來不是一步到位的,因此定位的方法也會不斷地修正和改進。

Linux內核相關的問題定位一直都是雲環境中的痛點之一,需要定位人員有豐富的知識積累以及相關經驗。在關於Linux內核的問題定位上,一般都是根據經驗和現場猜測懷疑點,不斷修改參數重試或者藉助搜索引擎來解決。該方式費時費力,並且解決問題的方法方式往往不可重複和擴展。下次碰到同一模塊的問題,往往還是需要重來一遍。

在意識到這樣的問題後,我們希望能夠借助eBPF等手段,力求將內核白盒化,盡量能夠從對代碼的跟踪上找出問題的原因。 該方式在剛開始的時候,可能會比較痛苦,因為需要詳細了解相關問題涉及到的Linux模塊實現,並針對性形成該模塊問題定位的方式以及工具集合,比較耗時耗力,但是只要定位該模塊的方式形成後,就會達到磨刀不誤砍柴工的效果。有時候,最難走的路,往往才是捷徑。

本文轉載自公眾號eBay技術薈(ID:eBayTechRecruiting)。

原文鏈接

eBay雲計算“網”事|網絡重傳篇