容器雲平台和容器網絡緊密結合,共同構建了容器(qì)化應用程序的網絡基礎設施,實現了容器之(zhī)間的通信、隔離(lí)和安全性(xìng)。文中(zhōng)容器雲平台采用的容器網絡組(zǔ)件是(shì)calico,這個是業界普遍(biàn)采用的一種方案,性能及安全性在同類產品中都是比較好的。本(běn)篇就筆(bǐ)者在近期項目上所涉及的幾個容器網(wǎng)絡問題,依據真實案例,分享(xiǎng)如何通過網絡抓包進行問題的解析與處理。
實戰一、因基礎網絡(luò)層(céng)MTU不一致引發(fā)的應用訪(fǎng)問(wèn)偶發性超時
MTU,即Maximum Transmission Unit(最大傳輸單元),此值設定(dìng)TCP/IP協議傳輸數據報時的最大傳輸(shū)單元,單位字(zì)節。傳統的網絡模式下,如果此值設置得太小,網絡性能會受影響;如果設置得(dé)太大可能觸發“部分網站打不(bú)開”等問題。
在容器環境下,容器網絡借助於IAAS層網絡傳輸(shū)來達到分布式服務間的網絡通訊。容器(qì)環境下的(de)典型特征(zhēng)是:服務都是多實例的、分布式的、跨網段的、跨多網絡區域、甚至跨VPC的(de)。再加上容器網絡本身也是一層虛擬化,特別(bié)是在overlay模式下,會(huì)額外嵌入一(yī)層(céng)協議,更增加了容器網絡問題分析的複雜性。特別是當IAAS較複雜的組網模式時,如果MTU不合理,可能導致以下問題:
大包阻塞(Jumbo Frames):如果不同網段的設備或路由器的MTU設置不(bú)一致,可能會導致大於某個網絡段MTU的數據包無法通過。這(zhè)可能導致大包阻塞,因為在路徑上的某個點上,數據包將被分片或被丟棄(qì)。
丟包:當數據包在跨越網絡段時被分片,而某個網絡(luò)段無法(fǎ)容納整個分片時,會導致分片的丟失。這可能會(huì)引起通信中的丟包情況,降低網絡性能。
性能下降:分(fèn)片和重組(zǔ)數據包會增加網絡設備的負擔,可能導致性能下(xià)降。特別是在(zài)高負載(zǎi)情況下,這可能會對容器間的通(tōng)信產生(shēng)顯著的影響。
某項目(mù)前端(duān)頁麵上出現個別菜單偶發性超時,但是如果(guǒ)重(chóng)新(xīn)點擊又正常了。這種現象(xiàng)出現多次,且(qiě)近期出現的次數更(gèng)加明顯。
應用的通訊架(jià)構可以抽象(xiàng)為如(rú)下:

前端頁麵上出現(xiàn)個(gè)別菜單偶發性超時(shí)

在前端瀏(liú)覽器上(shàng)F12調試-504

從(cóng)調用鏈(liàn)測發(fā)現調用有失敗(bài)
平台的調用鏈引擎是pinpoint,業界很(hěn)多(duō)廠(chǎng)商也是基於此技術棧實現分布式應用的調用拓撲(pū)、調用性能的分析。我們從調用鏈上可以看到前端與後端的請求中,有少數請(qǐng)求是超時的,可以明確看到Socket("message:"Read timeout..")的網(wǎng)絡接收(shōu)數據超時。從這裏可以初步感受到問題是出(chū)在網絡通訊上。

調(diào)用與被調用的容器間的ping包都是正常的(de);做一些簡單的curl也是正常的。
由於從調(diào)用鏈(liàn)上(shàng)能夠得到的信息是(shì)出在網絡上,而在(zài)容器環境下業務間的通(tōng)訊是容器網絡,於是首先從最基本的檢查做起,理清前端、後端間的相關容器的節點分(fèn)布(bù)情況,寫腳本(běn)探測容器之間的網絡穩定性,看看是不是會出現(xiàn)網(wǎng)絡抖動(dòng)或ping丟包。
最基本檢查可以通過ping腳本來評估網絡的穩定性,選取不同的節點上(shàng)調度(dù)的容器,分別在前、後端(duān)容器內執行長時間(jiān)的ping探測,並收(shōu)集ping結果進行分析(xī),腳本如下:
#長ping腳本,帶時間戳,可以(yǐ)直接塞(sāi)入相(xiàng)關被探測的容器中 #!/bin/bash for i in 100.80.95.XX 100.80.174.YY 100.80.90.ZZ 100.80.76.MM ... ;do ping $i | awk '{ print $0"\t" strftime("%Y-%m-%d %H:%M:%S",systime()); fflush()}' >> ./PingpodTopod.log & done
經過幾個小時的探測(確保這個期間(jiān)業務有偶發性超時問題發生),從ping的結果(guǒ)上來分析,所有(yǒu)ping的req包都有回(huí)包,沒(méi)有(yǒu)丟包,且延(yán)時都非常低,從這裏(lǐ)至少(shǎo)可以說明(míng)網絡鏈路層(céng)是沒(méi)有(yǒu)問題的。
那進一步做tcp層通訊的基本檢查,從業務側獲取一些(xiē)涉(shè)及tcp通訊且不會對生產環境產生任何影響的接口調用,在前端容器內對後端容器實行實施多次curl調用檢查,從多對容(róng)器以及多次的抓取中可以看到所有的curl都是能夠快速應答,且沒有(yǒu)超時發生。
從最基本的鏈路層檢測及tcp通訊的檢測上來看,容器網絡沒有明顯的問題。那麽問題會出現在哪裏?接下來對相關容器(qì)實施網絡抓包,從網絡包上看看能不能發現端倪。
限於篇幅,長言短敘(xù),隨便挑一對發生問題的異常容器,然後實施抓(zhuā)包分析。服務端抓包(bāo)可以采取最常見的基(jī)於ip與port的過濾器來抓(zhuā)網絡包。特別注意要分別(bié)在服務端與客戶端進行抓包,好處是可以基於(yú)時(shí)間點及sequence來相(xiàng)互印證,找出有(yǒu)問(wèn)題的包,然後對比分析,重點查(chá)看發出去(qù)的包有沒有到達(dá)對端,對(duì)端的回包自己有沒有收到、有沒(méi)有重傳等等,從異常中找出問題的線索。
在下圖中:服務端容器100.80.95.126 、客(kè)戶端容器100.80.174.53, 可以很明顯地看到在問題發(fā)生的時(shí)間點有多次重(chóng)傳(TCP Retransmission), 進一步分析發現(xiàn)屬於典型的丟包現象。具(jù)體(tǐ)分析細節見圖注說明。

客戶端抓包分(fèn)析:

從客戶(hù)端與服務端抓包的多個包的對比中,得出(chū)以下結論
客戶端、服務端正常建鏈,客戶端正常發送請(qǐng)求
服務端收到客戶端請求後(hòu),客戶端未收到服務端的回包
未收到的回包屬於(yú)大包,大包(bāo)(長度(dù) >=6000 或(huò)者7000的包,服務端發了,但是客戶端收不到)
再加上客戶端容器、服務端容器主要在跨宿主機網(wǎng)段的的機器上,這些特點符合MTU的問(wèn)題,因此,下一步調整MTU。一般來說,應(yīng)該(gāi)要檢查一(yī)下IAAS層(céng)的網絡設備的MTU的配置,但是IAAS資源都(dōu)是(shì)局方的,而且也分屬(shǔ)於不同部門,協調(diào)起來比較困難。因此,考慮在容器側調整MTU,在經過客戶的允許後,在夜晚調小了(le)線上環境的容器網絡的MTU,問題解決。
再次抓包,可以看到(dào)大的數據包也都可(kě)以正(zhèng)常傳輸了,見(jiàn)下(xià)圖:

從調用鏈上反饋也是(shì)正常的啦。

通過分(fèn)析收發包的特點,找出異常數據包的發送(sòng)情況,主要特征是(shì):多發生在跨網段(duàn)間的主機(jī)上(shàng),核心是小(xiǎo)包能正常(cháng)通訊,大包(bāo)通訊上有(yǒu)問題,大(dà)包無(wú)法到達對(duì)端,典型的MTU配置不當的表現。
知道問題的原因了,那麽解決(jué)問題的辦法就(jiù)很簡單了,可以直接調小一點容器層麵的MTU,從而可以(yǐ)規避此問題。
實戰(zhàn)二、基於協(xié)議回包特點反推服務端的邏輯問題(tí)
在容器場景下,不僅要在容器平麵進行MESH方式的通訊,常常容(róng)器(qì)還需要(yào)與外部通訊,甚至與(yǔ)第3方的廠家進行業務(wù)交互,困難點(diǎn)在於你無法清晰知道第3方服務的邏輯、也無法登錄第3方(fāng)環境,無法知道網絡架構、網絡安(ān)全(quán)配置等。這樣的背景下,當業務出現(xiàn)異常且排除自己業務代(dài)碼的問(wèn)題層麵之後,那麽可(kě)借助網絡抓包來進行分析,通過解析數據的的交互同時比對正常情(qíng)況(kuàng)下的TCP流與異常(cháng)情況下的TCP流的差異、同時基於業務采用具體業務協議下本來(lái)應該是個什麽樣的(de),從而來發現問題。
這種問題本質上不是(shì)網絡(luò)問題,網絡層麵的通訊都是(shì)正常的,是業務(wù)邏(luó)輯上有(yǒu)潛在的問題,比如業務代碼條件判斷(duàn)不夠精確,誤判走了不同的(de)流程,回傳了不需要的數據包。
某項目上線期間發現API發起VC充值業務( CGMMLClient調用(yòng)第三方的的MML接(jiē)口 QUERY VC INFO)偶發性異常。相關業務日誌
APPLICATION|ERROR|192.168.12.243|20|2022-12-15 12:39:38:971||70802003|ConnectorCacheHandler.java:86|postHandle|7083012|245|||Message is timeout, and handled. message [beginTime=2022-12-15 12:39:23.969,timeout=15000,interval=15002 : server channel=[1], channel=[/192.168.12.243:58034], channel=[/10.16.76.5:8124] adapter=[SocketClient-MML],sequence=[SocketClient-00018915], status=[REMOTING_TIMEOUT], lastHandlingObject=[com.ztesoft.zsmart.cg.service.binder.ServiceBindingRuleIntrp@2c881e0], command=[QUERY VC INFO] field list: {"OPTYPE":"1","transId":"000024f3","totalLen":{"_endPos_":8,"_value_":"0068","_startPos_":4},"sessionId":"00000000","terminal":"CCB00000","serviceName":"PPSPHS","version":"1.00","msgBeginFlag":"`SC`","transRemain":"FFFF","checkSum":"AA8A9495","COMMAND":"QUERY VC INFO","transControl":"TXBEG ","Body":{"_endPos_":112,"_startPos_":8},"sessionControl":"DLGCON","CARDNO":"516683732197360540","sessionRemain":"FFFF"} variable list: {"protocol":"MML","reomoteHost":"10.16.76.5","cg":"SocketClient-MML","resultCode":"REMOTING_TIMEOUT","__cost__":0,"command":"QUERY VC INFO","status":"REMOTING_TIMEOUT"} param list: {"arg0":{"opType":1,"class":"com.ztesoft.zsmart.bss.payment.orange.smartfren.bc.service.mml.QueryVcCardRequest","cardNumber":"516683732197360540"}} buffer=[。。。。。。] ]抓包(bāo)分析-通過對比正常(cháng)號碼充值與(yǔ)異常號碼充值的網絡包
既然是偶發性異常,那麽一定是存在正常的業務流程與異常的業務流程;接下來大的思路就是對比正常業務流程下的業務(wù)收/發包與異常流程下收/發包(bāo),然後比較差異。
首先重點從業務日誌中找出關鍵的數據包中必傳輸的關鍵字信(xìn)息(xī),比如本例中充(chōng)值號碼(mǎ):"cardNumber":"516683732197360540"。
容器內基於IP與port端口進行抓包,首先針對異常號碼充(chōng)值(zhí)包分析,在業務容器內進行抓包,同時從(cóng)業務側(cè)獲取問題發生期間的充值異常的號碼,例如:CARDNO":"516683732197360540 ,基於此號碼(mǎ)分析網絡包。
現在的wireshark功能比較強大,可以(yǐ)通(tōng)過match添加過濾(lǜ)條件,從TCP包中找出異常(cháng)號碼"cardNumber":"516683732197360540"對應的通訊(xùn)鏈路,然(rán)後再逐一分析(xī)這個TCP流。


另外(wài),取正(zhèng)常號碼(mǎ)充值包分析 ,比如正常的號碼 CRD 160588272651221

從比對中發現問題-第3方的服務(wù)端
包的主要特點:
1) 長(zhǎng)鏈接;
2) 整個網絡流比較正常,第一(yī)次抓取的(de)包(bāo),是在一(yī)個容器內做的;10萬個包(bāo)中真正發(fā)生重傳的次數也就10來次,比(bǐ)率相當低的;且重傳後都能得到及時響應,不存(cún)在網絡丟包;
3) 問(wèn)題觸發時,並不發(fā)生在包的重傳上;
4) 整個單純(chún)的TCP層(céng)麵的(de)通訊上沒有明顯問題;
5) 根據apig發包以及服務(wù)端的回應來看,收到的不是預想的包-這是重(chóng)點。
從比對中發現(xiàn),每次發生異常時,apig 正常發送了vc query請求包,沒有收到vc qeury的應答包;而正常的充值的號(hào)碼都是收到了應答包。因此服務端存在偶發(fā)性的回包問題,而(ér)服務端(duān)是局方管理的第3方公司提供的;之後經由與局方麵對麵溝通,同時比照務端業務日誌的查看,問題確實在第3方公司的服務端。後經第3方公司核查完自己(jǐ)的(de)服務端代碼,發現一處條件的判斷邏輯有問題,後經處理完服(fú)務端,該(gāi)問題(tí)得到解決。
[要點] 分別找出正常充值時(shí)的TCP數據流與異常(cháng)充值時的數據流,通過對比正常充值與(yǔ)異常充值的數據包,從而(ér)發現差異、介定(dìng)異常。
實戰三、websocket協議斷(duàn)鏈-大量斷鏈包中發現規律+源碼求證
WebSocket是一種在單個TCP連接(jiē)上提供全雙工通(tōng)信的協議,它允許在客戶端和服務器之間(jiān)進行(háng)實時、雙向的(de)數據傳輸。WebSocket通訊的優點很多,以下重點提2個:
全雙工通信:WebSocket允許雙向通信(xìn),客戶端和服務器可以同時發送和接收數據。這(zhè)使得實時性應用程序的開發更為簡便,例如在線聊天、實時協作和(hé)實時更新的Web應(yīng)用程序。
持久連接:WebSocket連接是持久的,一旦建立連接,它可以保持打開狀態,允許任何時(shí)候雙方都能發送數據。這降低了頻繁建立和斷開連接(jiē)相關的開銷。
簡(jiǎn)單地說,基於websocket協議通訊的業務,都傾向於長時通訊(xùn),一般情況下不會去主動斷開;如(rú)果項目上碰到websocket通訊有頻發斷鏈的情況,要(yào)引起注意,請明確這屬於邏輯上(shàng)設置的主動斷鏈,還是異常斷鏈;特(tè)別是在不了解背後實(shí)現邏輯的情況下,可以(yǐ)通過抓包,重點抓(zhuā)取3次握手與4次握手,來發現斷鏈的規律、由誰發起的、屬於主動斷鏈、還是丟包導致(zhì)的鍛煉(liàn),有條件的要將(jiāng)抓包與源碼進行比對分(fèn)析,從而解決問題。
業務網關與k8s APIServer之間(jiān)websocket有多次(cì)的斷鏈,按以(yǐ)往經驗(yàn)來看(kàn),之前沒有(yǒu)發生過斷鏈,都(dōu)是長鏈接。項目上(shàng)關於多次斷鏈有(yǒu)點擔心,怕存在隱患。
相關日誌
00:54:34:463|||WatcherWebSocketListener.java:66|onClose||43||||WebSocket close received. code: 1000, reason: k8s.log:APPLICATION|DEBUG|||2023-05-19 00:54:35:468|||WatchConnectionManager.java:60|closeWebSocket||475||||Closingwebsocket io.fabric8.kubernetes.client.okhttp.OkHttpWebSocketImpl@46de5f80 k8s.log:APPLICATION|DEBUG|||2023-05-19 00:54:35:468|||WatchConnectionManager.java:63|closeWebSocket||475||||Websocket already closed io.fabric8.kubernetes.client.okhttp.OkHttpWebSocketImpl@46de5f80 k8s.log:APPLICATION|DEBUG|||2023-05-19 01:52:47:876|||WatcherWebSocketListener.java:66|onClose||43||||WebSocket close received. code: 1000, reason: k8s.log:APPLICATION|DEBUG|||2023-05-19 01:52:48:880|||WatchConnectionManager.java:60|closeWebSocket||487||||Closingwebsocket io.fabric8.kubernetes.client.okhttp.OkHttpWebSocketImpl@7de2a21a k8s.log:APPLICATION|DEBUG|||2023-05-19 01:52:48:880|||WatchConnectionManager.java:63|closeWebSocket||487||||Websocket already closed ...(略) ...
由於是線上環境,首先要考慮的是對線上(shàng)業務的影響。從(cóng)日誌中評估出每小時發生(shēng)的概率不高,因此(cǐ),可以實施(shī)抓包。
- 初步抓握手包-觀察斷鏈及規律
特別強調一下,為了最大程度地不影響線上環境,隻抓少量的包,且隻抓建鏈、斷鏈的(de)包,即3次握手、4次握手。此次抓包不同於前麵的直(zhí)接(jiē)基於(yú)IP與端口抓(zhuā)包(bāo),會加一些(xiē)稍顯複雜的過濾條件,盡可能減少(shǎo)線上環境的影響,隻抓(zhuā)1000個包,隻在網關節點上抓包
sudo nohuptcpdump -i eth0 host XXX and port 6443 and \('tcp[tcpflags] == tcp-rst' or'tcp[tcpflags] == tcp-syn' or 'tcp[tcpflags] == tcp-fin' or 'tcp[13] & 1 != 0'\)-c 1000 -nn -vv -w ./host195-2-socket-esta-or-close-01.pcap &
基於抓包文件分析-取片段:

在實際的分析中是取了多(duō)個片段(duàn)進行分析的(de),總體發現一(yī)個規律
1)服務端發起的斷鏈
2)每個鏈(liàn)接保(bǎo)持時長都在 (30分鍾 60分鍾)這個區間內,這一點很(hěn)重要(yào),這為我們(men)後麵(miàn)在規定時間內抓取數據包提(tí)供很大的(de)價值。
- 進一(yī)步抓數據包
為了不影響(xiǎng)生產環境,在同版本(běn)的(de)測試環境上進行同樣方式(shì)抓包,同樣具有此特點,後續就直接轉到測試環境(jìng)上進行分析(xī)...
按(àn)前麵的規律,半小時至(zhì)1小時內必然發生斷鏈,因此,考慮抓取數據包。由於websocket是基於TLS的通訊(xùn),為了探索出到底(dǐ)是什麽數據(jù)帶來的影響,更進一步的措施(shī)做TLS數據包的解析。方便解包,仿照(zhào)網(wǎng)關邏輯,寫了(le)一個golang的websocket測試程序,在測試環境上進行(háng)多輪測試。從抓包及websocket TLS解包中來看,是服務端正常的斷開,服務發送了一個TLS Aller_message 21,解包中得出是 Close_notify,由此可以明(míng)確知道問題該從服務端查起。

如果不解(jiě)析包,會是(shì)什麽情況?可以看到(dào)全部是加密的數據,不是太好分析,舉例

- 從k8s master源碼分析
從k8s master源碼分析,發現k8s master有個配置項(xiàng),RequestTimeout,基本(běn)上從來沒有用。限於(yú)篇幅,略去中間的摸索過程,見代碼如下

- 定位原因-版本差異
通過辛苦的版本比對及(jí)調試驗證,最後找到是k8s高版本(běn)做了超時的(de)修複,具體來說是k8s 1.15.0-alpha1,直接上個(gè)圖:

由此可以看(kàn)出,從1.15.0-alpha1版本開始,都是這樣子的。後經與研發一起討論,認為這(zhè)個斷(duàn)鏈是正常的,不會有實質性的影響。
【要點】
從抓包中確認(rèn)有(yǒu)websocket 斷鏈發生,且是(shì)服務端發起的抓取足(zú)夠量(liàng)的建鏈、斷鏈,發(fā)現規律每(30分鍾、60分鍾(zhōng))發生一(yī)次斷(duàn)鏈通過以上2點確認排查方向是從k8s master服(fú)務端做起結(jié)合TLS解包,確認這個(gè)斷鏈(liàn)是TLS 21 消息,屬於正常的斷鏈包,排除了網絡層(céng)麵(miàn)的影響結合k8s master的啟動項及源碼發現高版本的源碼中就是這樣的(de)設計邏輯源(yuán)代碼層麵,通過比(bǐ)對低版本與高版(bǎn)本的websocket處的實現,發現從1.15.0-alpha1版本開始,代碼實現上加入(rù)了超時斷開(kāi)的功能經過(guò)反複測試,斷鏈的問(wèn)題清楚,不用擔心,可以忽略,屬於正常機製