TCP協議棧的冒險:發現性能回歸TCP麻袋漏洞修複
2019年9月16日 在工程的博客
上個月,我們宣布磚的平台體驗網絡性能回歸是因為LinuxBeplay体育安卓版本的補丁TCP麻袋漏洞。回歸是觀察不到0.2%的情況下運行時數據磚運行時(DBR)亞馬遜網絡服務(AWS)平台。Beplay体育安卓版本在這篇文章中,我們將會深入研究分析,確定了TCP協議棧是退化的根源。我們將討論我們看到的症狀,穿過我們如何調試TCP連接,並解釋在Linux源代碼的根本原因。
作為一個快速注意在跳之前,規範正在一個Ubuntu 16.04圖像解析這些性能回歸。我們計劃更新磚平台一旦圖像是可用的和已通過我們的回歸測試。Beplay体育安卓版本
一個失敗的基準
我們第一次提醒當我們的基準之一成為6 x慢。回歸出現升級之後我們使用Amazon Machine Image (AMI)將Ubuntu的TCP麻袋漏洞修複。
失敗的基準大DataFrame S3通過寫道磚文件係統(DBFS)使用Apache火花的保存函數。
火花.range (10*億).write.format(“鋪”).mode(“覆蓋”).save (“dbfs:/ tmp /測試”)
Apache火花階段執行保存
操作有極端的任務傾斜。一個流浪者任務花了15分鍾,而其餘完成在不到1秒。
不安全的問題消失當回滾AMI沒有TCP麻袋漏洞修複。
調試TCP連接
為了弄清楚為什麼流浪者任務花了15分鍾,我們需要抓住它的行動。我們重新基準監控火花UI時,知道的任務之一保存
操作會在幾分鍾內完成。在這一階段的任務進行排序狀態列,它沒過多久,那裏隻有一個任務處於運行狀態。我們發現傾斜任務和IP地址主機列指出我們的遺囑執行人經曆回歸。
與IP地址,我們ssh到執行人看到發生了什麼。由於懷疑網絡問題AmazonS3Exception
我們以前見過的錯誤集群日誌,我們跑netstat看活動的網絡連接。
~# netstat活躍的互聯網連接(w/o服務器)原型Recv- - - - - -問發送- - - - - -問當地的地址外國地址狀態tcp6816年99571年知識產權-10年0-144年-224年。你:42464年s3- - - - - -我們- - - - - -西2- - - - - -r- - - - - -w。:https CLOSE_WAIT
一個TCP連接到一個S3服務器被卡住了CLOSE_WAIT
狀態。這種狀態表明服務器傳輸數據和關閉了連接,但客戶仍然有更多的數據發送。的套接字連接有97 kb的數據等待發送。
同時監控,套接字的狀態,直到保存
操作完成後15分鍾,我們注意到發送隊列的大小沒有改變。它保持在97 kb。S3服務器要麼不承認或執行器節點的數據發送數據失敗。我們使用黨衛軍更詳細的套接字。
~# ss - a - t - m - o -信息CLOSE-WAIT816年99571年::飛行符:10.0。144.224:42464年::飛行符:52.218。234.25https計時器:(,1min37sec,13)skmem:(r9792、rb236184 t0、tb46080 f732, w103971, o0, bl0)立方wscale袋:8,7rto:120000年補償:13rtt:0.379/0.055ato:40海量存儲係統(mss)中:1412年cwnd:1ssthresh:23bytes_acked:1398838bytes_received:14186年segs_out:1085年segs_in:817年發送29.8Mbps lastsnd:592672年lastrcv:552644年lastack:550640年pacing_rate4759.3Mbps unacked:80年損失:24開除:56rcv_space:26883年
使用黨衛軍
讓我們看看插座的內存使用量(- m
),TCP特定信息(——信息
)和定時器信息(- o
)。讓我們來看看這個調查的重要的數字:
- 計時器:(1)min37sec, 13)——直到下一個重傳(1分鍾37秒)和完成傳輸嚐試(13)。
- tb46080 -套接字的當前發送緩衝區大小的字節(45 kb)。這地圖
sk_sndbuf
在Linux套接字結構。 - w103971——該套接字所消耗的內存寫緩衝區字節(101 kb)。這地圖
sk_wmem_queued
在Linux套接字結構。 - 海量存儲係統(mss)中:1412——連接最大細分市場大小的字節。
- lastsnd: 591672——自從上次數據包發送時在毫秒(9分51秒)。
這個套接字的狀態的快照表明它已經13失敗重傳和下一個嚐試將嚐試在1分37秒。這是奇怪的,但是,考慮到TCP重發在Linux中之間的最長時間是2分鍾和套接字沒有發送一個數據包在過去9分51秒。服務器不是承認失敗重發;客戶的重發甚至不使它的線!
在套接字被困在這種狀態下,我們觀看了傳輸計數器蜱蟲15(默認設置net.ipv4.tcp_retries2
),直到關閉套接字。發送隊列和寫隊列大小從未改變,自從上次包發送的時候從來沒有下降,表明該套接字繼續發送數據失敗。一旦關閉套接字,S3客戶重試機製踢的和數據傳輸成功。
這種行為匹配和15分鍾的這是我們的基準的流浪者的任務完成。與tcp_retries2
設置為15,TCP_RTO_MIN
設置為200毫秒,TCP_RTO_MAX
設置為120秒,一個指數倒扣在重試之間,需要924.6秒,或超過15分鍾,超時的連接。
袋子在TCP痕跡
與斜解釋為重新傳輸超時15分鍾的任務,我們需要理解為什麼客戶端無法重新發送數據包到S3服務器。我們收集所有的TCP痕跡執行人而調試基準和那裏尋找答案。
sudo tcpdump nns淨52.218.0.0/16 - w awstcp.pcap
的tcpdump以上命令捕獲所有流量與S3子網(52.218.0.0/16)。我們下載了.pcap
文件從傾斜的任務的執行者和分析使用Wireshark。
TCP連接與本地端口42464 -被困在CLOSE_WAIT
S3狀態——客戶端試圖發送1.4 mb的數據。第一個1.3 mb轉移成功地在600毫秒,但隨後S3服務器錯過了一些片段和送一袋。SACK表示,22138個字節是錯過了(ACK基地之間的差異和解雇的左邊緣)。
後客戶端發送第一次傳輸所有價值1.4 mb的外向TCP段,沉默了40秒的連接。第一個7重傳的嚐試失蹤21 kb應該在這段時間內觸發,貸款進一步證明客戶端無法通過電線發送數據包。最後發送的服務器鰭
包袋相同的信息,這表明,目前尚未收到丟失的數據。這使得客戶端進入CLOSE_WAIT
狀態,即使允許客戶端傳輸數據在這個狀態,事實並非如此。這個連接的TCP不再跟蹤顯示數據包。不被承認的22138字節從來沒有重新傳輸。
添加這些77433年解雇22138字節字節(從左邊緣減去右邊緣),我們得到了99571字節。這個數字看起來應該很熟悉。它是套接字的大小發送隊列中列出netstat
輸出。這引出了第二個問題。為什麼沒有解雇了75 kb的數據從套接字的發送隊列中刪除嗎?服務器已經確認接收;客戶端不需要保留它。
不幸的是,TCP跟蹤沒有告訴我們為什麼重發失敗,隻添加了一個額外的問題需要回答。我們打開Ubuntu 16.04源代碼,學習更多的知識。
剖析了TCP協議棧
一個Linux補丁為了解決TCP麻袋漏洞一個if語句添加到tcp_fragment ()
函數。它是為了限製條件TCP協議棧碎片套接字的重傳隊列來防止惡意的手工袋包。首先我們看,因為它已經固定一次。
即使有此修複,我們仍然看到問題。7月底,另一個解決發表新版本的Linux。作為我們的ami使用Ubuntu 16.04規範4.4.0 Linux內核的補丁,補丁,建立了一個定製的Ubuntu內核。我們的性能問題消失了。
仍然忽視問題的根源,因為我們不知道這四個條件的if語句固定我們的問題,我們建立了多個自定義映像,隔離每個條件。圖片隻有第一個條件——修改sk_wmem_queued
檢查,證明足以解決我們的問題。
變更的提交消息說,一個128 kb的開銷了sk_sndbuf
形成的極限,因為“tcp_sendmsg ()
和tcp_sendpage ()
可能會過度sk_wmem_queued
約一個完整TSO skb (64 kb大小)。“這意味著sk_wmem_queued
可以合理地超過sk_sndbuf
高達64 kb。這是我們所看到在我們的套接字。如果你記得黨衛軍
輸出,發送緩衝區的大小是46080字節和寫隊列是103971字節(56 kb)的差異。我們寫一半的隊列(注意正確的轉變條件)大於發送緩衝區,我們滿足原修複的if條件。nstat證實這一點,因為我們看到了TCPWQUEUETOOBIG
數記錄為179。
~# nstat | grep“TcpExtTCPWqueueTooBig”TcpExtTCPWqueueTooBig179年0.0
證實了我們確實是失敗的tcp_fragment ()
,我們想找出這可能導致重傳失敗的原因。查找的堆棧重新發送代碼路徑我們看到,如果一個套接字緩衝區的長度大於當前MSS 1412字節(在我們的例子中),tcp_fragment ()
重傳之前被調用。如果失敗,重新傳輸增量的TCPRETRANSFAIL
計數器(我們是在143年)中止重傳整個隊列的套接字緩衝區。這就是為什麼我們沒有觀察到任何重發的21個kb的數據不被承認的。
這使得解雇了75 kb的數據。為什麼沒有從這些包回收內存嗎?它將降低了寫隊列的大小,使分裂企圖在傳輸成功。唉,也稱解雇處理tcp_fragment ()
當一個套接字緩衝區並不是完全袋內邊界,這樣就可以免費承認部分。當一個分裂失敗時,該處理程序爆發沒有檢查的套接字緩衝區。這可能是為什麼承認的75 kb數據沒有公布。如果不被承認的21個kb共享一個套接字緩衝區被解職的第一部分數據,它需要碎片回收。沒有相同的if條件tcp_fragment ()
,SACK處理程序將會中止。
經驗教訓
反思我們的旅程通過發現、調查和根本原因分析,幾點突出:
- 考慮整個堆棧。我們認為理所當然,Ubuntu提供一個安全的和穩定的平台為我們的集群。Beplay体育安卓版本最初我們的基準測試失敗時,我們很自然地開始尋找線索在最近的自己的變化。隻有在發現隨後Linux TCP補丁,我們開始質疑位降低堆棧。
- 基準是數字;日誌記錄是至關重要的。我們已經知道,但值得重申。我們的基準簡單告訴我們這個平台是6 x慢。Beplay体育安卓版本它沒有告訴我們去哪裏看。火花日誌報告S3套接字超時異常給我們第一次真正的提示。基準,提醒等日誌異常會大大減少最初的調查時間。
- 使用正確的工具。15分鍾的套接字超時難住了我們一段時間。我們知道
netstat
,但後來才知道黨衛軍
。後者提供更多細節關於套接字的狀態和重傳失敗明顯。它也幫助我們更好地理解TCP跟蹤期望的重發也失蹤了。
我們也學到了很多關於Linux TCP協議棧在這深潛水。盡管磚不希望很快開始Linux提交補丁,我們欠我們的客戶和Linux社區來支持我們早些時候聲稱,一個操作係統補丁需要修複我們的性能回歸。beplay体育app下载地址好消息是,最新的4.4.0補丁解決這個問題,一位官員Ubuntu 16.04修複在望。