阿里妹導讀:本文是一個理論過分到實踐的典型案例,藉助程序員常常遇到的一個問題——網絡爲何不通,來具體說明怎麼將書本上的死知識真正變成咱們解決問題的能力。程序員
我相信你腦子裏關於網絡基礎知識的概念都在下面這張圖中。知識內容有點亂,感受都認識,又都模模糊糊,更談不上將內容轉化成生產力或是用來解決實際問題了。這是由於知識沒有貫通、沒有實踐、沒有組織。面試
上圖中知識點的做用在RFC1180[1]中講得無比通俗易懂了。看第一遍的時候也許你就看懂了,可是一個月後又忘記了。其實這些東西咱們在大學也學過,但仍是忘了(可以理解,缺乏實操環境和條件),或者碰到問題才發現以前看懂了的東西其實沒懂。docker
因此接下來咱們將示範書本知識到實踐的貫經過程,但願把網絡概念之間的聯繫經過實踐來組織起來。緩存
最近的環境碰到一個網絡ping不通的問題,當時的網絡鏈路是(大概是這樣,略有簡化):tomcat
• 從容器1 ping 物理機2 不通;網絡
• 從物理機1上的 容器2 ping物理機2 通;tcp
• 同時發現即便是通的,有的容器 ping物理機1只須要0.1ms,有的容器須要200ms以上(都在同一個物理機上),不合理;工具
• 全部容器 ping 其它外網IP(好比百度)反而是通的。測試
這個問題扯了一週才解決是由於容器的網絡是咱們本身配置的,交換機咱們沒有權限接觸,由客戶配置。出問題的時候都會以爲本身沒問題對方有問題,另外就是對網絡基本知識認識不夠,因此都以爲本身沒問題而不去找證據。spa
這個問題的答案在你們看完本文的基礎知識後會總結出來。
解決這個問題前你們先想一想,假若有個面試題是:輸入 ping IP 後敲回車,而後發生了什麼?
要解決一個問題你首先要有基礎知識,在知識欠缺的狀況下就算邏輯再好、思路再清晰、智商再高,也不必定有效。
假如你在這臺機器上ping 172.17.0.2 ,根據上面的route表得出 172.17.0.2這個IP符合下面這條路由:
這條路由規則,那麼ping 包會從docker0這張網卡發出去。
可是若是是ping 1.1.4.4 根據路由規則就應該走eth0這張網卡而不是docker0了。接下來就要判斷目標IP是否在同一個子網了。
首先來看看這臺機器的網卡狀況:
這裏有三個網卡和三個IP,三個子網掩碼(netmask)。根據目標路由走哪張網卡,獲得這個網卡的子網掩碼,來計算目標IP是否在這個子網內。
網絡包在物理層傳輸的時候依賴的mac 地址而不是上面的IP地址,也就是根據mac地址來決定把包發到哪裏去。
arp協議就是查詢某個IP地址的mac地址是多少,因爲這種對應關係通常不太變化,因此每一個os都有一份arp緩存(通常15分鐘過時),也能夠手工清理,下面是arp緩存的內容:
有了上面的基礎知識打底,咱們來思考一下 ping IP 到底發生了什麼。
首先 OS 的協議棧須要把ping命令封成一個icmp包,要填上包頭(包括src-IP、mac地址),那麼OS先根據目標IP和本機的route規則計算使用哪一個interface(網卡),肯定了路由也就基本上知道發送包的src-ip和src-mac了。每條路由規則基本都包含目標IP範圍、網關、MAC地址、網卡這樣幾個基本元素。
若是目標IP和本機使用的IP在同一子網
若是目標IP和本機IP是同一個子網(根據本機ifconfig上的每一個網卡的netmask來判斷是不是同一個子網——知識點:子網掩碼的做用),而且本機arp緩存沒有這條IP對應的mac記錄,那麼給整個子網的全部機器廣播發送一個 arp查詢,好比我ping 1.1.3.42,而後tcpdump抓包首先看到的是一個arp請求:
上面就是本機發送廣播消息,1.1.3.42的mac地址是多少?很快1.1.3.42回覆了本身的mac地址。 收到這個回覆後,先緩存起來,下個ping包就不須要再次發arp廣播了。 而後將這個mac地址填寫到ping包的包頭的目標Mac(icmp包),而後發出這個icmp request包,按照mac地址,正確到達目標機器,而後對方正確回覆icmp reply(對方回覆也要查路由規則,arp查發送方的mac,這樣回包才能正確路由回來,略過)。
來看一次完整的ping 1.1.3.43,tcpdump抓包結果:
我換了個IP地址,接着再ping同一個IP地址,arp有緩存了就看不到arp廣播查詢過程了。
若是目標IP不是同一個子網
arp只是同一子網廣播查詢,若是目標IP不是同一子網的話就要通過本IP網關進行轉發(知識點:網關的做用)。若是本機沒有緩存網關mac(通常確定緩存了),那麼先發送一次arp查詢網關的mac,而後流程跟上面同樣,只是這個icmp包發到網關上去了(mac地址填寫的是網關的mac)。
從本機1.1.3.33 ping 11.239.161.60的過程,由於不是同一子網按照路由規則匹配,根據route表應該走1.1.15.254這個網關,以下截圖:
首先是目標IP 11.239.161.60 符合最上面紅框中的路由規則,又不是同一子網,因此查找路由規則中的網關1.1.15.254的Mac地址,arp cache中有,因而將 0c:da:41:6e:23:00 填入包頭,那麼這個icmp request包就發到1.1.15.254上了,雖然包頭的mac是 0c:da:41:6e:23:00,可是IP仍是 11.239.161.60。
看看目標IP 11.239.161.60 真正的mac信息(跟ping包包頭的Mac是不一樣的):
這個包根據Mac地址路由到了網關上。
爲了簡化問題,假設兩個網關直連
網關收到這個包後(由於mac地址是它的),打開一看IP地址是 11.239.161.60,不是本身的,因而繼續查本身的route和arp緩存,發現11.239.161.60這個IP的網關是11.239.163.247,因而把包的目的mac地址改爲11.239.163.247的mac繼續發出去。
11.239.163.247這個網關收到包後,一看 11.239.161.60是本身同一子網的IP,因而該arp廣播找mac就廣播,cache有就拿cache的,而後這個包才最終到達目的11.239.161.60上。
整個過程當中目標mac地址每一跳都在變,IP地址不變,每通過一次MAC變化能夠簡單理解成一跳。
實際上可能要通過多個網關屢次跳躍才能真正到達目標機器。
目標機器收到這個icmp包後的回覆過程同樣,略過。
arp廣播風暴和arp欺騙
廣播風暴:若是一個子網很是大,機器很是多,每次arp查詢都是廣播的話,也容易由於N*N的問題致使廣播風暴。
arp欺騙:一樣若是一個子網中的某臺機器冒充網關或者其餘機器,當收到arp廣播查詢的時候老是把本身的mac冒充目標機器的mac發給你,而後你的包先走到他,再轉發給真正的網關或者目標機器,因此在裏面動點什麼手腳,看看你發送的內容都仍是很容易的。
讀完上面的基礎知識相信如今咱們已經可以回答 ping IP 後發生了什麼。這些已經足夠解決99%的程序員平常網絡中網絡爲何不通的問題了。可是前面的問題比這個要稍微複雜一點,仍是依靠這些基礎知識就能解決——這是基礎知識的威力。
現場網絡同窗所作的一些其它測試:
懷疑不通的IP所使用的mac地址衝突,在交換機上清理了交換機的arp緩存,沒有幫助,仍是不通;
新拿出一臺物理機配置上不通的容器的IP,這是通的,因此負責網絡的同窗堅持是容器網絡的配置致使了問題。
對於1能通,我認爲這個測試不嚴格,新物理機所用的mac不同,而且所接的交換機口也不同,影響了測試結果。
祭出萬能手段——抓包
抓包在網絡問題中是萬能的,可是第一次容易被tcpdump抓包命令的衆多參數嚇暈,不去操做你永遠上不了手,差距也就拉開了,你看差距有時候只是你對一條命令的執行。
在物理機2上抓包:
這個抓包能看到核心證據,ping包有到達物理機2,同時物理機2也正確回覆了(mac、ip都對)。
同時在物理機1上抓包(抓包截圖略掉)只能看到ping包出去,回包沒有到物理機1(因此回包確定不會回到容器裏了)。
到這裏問題的核心在交換機沒有正確地把物理機2的回包送到物理機1上面,同時觀察到的不正常延時都在網關那一跳:
最終的緣由
最後在交換機上分析包沒正確發到物理機1上的緣由跟客戶交換機使用了HSRP(熱備份路由器協議,就是多個交換機HA高可用,也就是同一子網能夠有多個網關的IP),停掉HSRP後全部IP容器都能通了,而且前面的某些容器延時也恢復正常了。
通俗點說就是HSRP把回包拐跑了,有些回包拐跑了又送回來了(延時200ms那些)
至於HSRP爲何會這麼作,要廠家出來解釋了。這裏關鍵在於能讓客戶認同問題出如今交換機上仍是前面的抓包證據充分,無可辯駁。實際中咱們都習慣不給證據就說:個人程序沒問題,就是你的問題。這樣表述沒有一點意義,咱們是要拿着證據這麼說,對方也好就着證據來反駁,這叫優雅地甩鍋。
講這個過程的核心目的是除了真正的網絡不通,有些是服務不可用了也怪網絡。不少現場的同窗根本講不清本身的服務(好比80端口上的tomcat服務)還在不在,網絡通不通,是網絡不通呢仍是服務出了問題。一看到SocketTimeoutException 就想把網絡同窗抓過來羞辱兩句:網絡不通了,網絡抖動致使個人程序異常了(網絡抖動是個萬能的扛包俠)。
實際這裏涉及到四個節點(以兩個網關直連爲例),srcIP -> src網關 -> dest網關 -> destIP。若是ping不通(也有特殊的防火牆限制ping包不讓過的),那麼在這四段中分段ping(二分查找程序員應該最熟悉了)。 好比前面的例子就是網關沒有把包轉發回來。
抓包看ping包有沒有出去,對方抓包看有沒有收到,收到後有沒有回覆。
ping本身網關能不能通,ping對方網關能不能通。
接下來講點跟程序員平常相關的
若是網絡能ping通,服務沒法訪問
那麼嘗試telnet IP port 看看你的服務是否還在監聽端口,在的話再看看服務進程是否能正常響應新的請求。有時候是進程死掉了,端口也沒人監聽了;有時候是進程還在可是假死了,因此端口也不響應新的請求了,還有的是TCP鏈接隊列滿了不能響應新的鏈接。
若是端口還在也是正常的話,telnet應該是好的:
假如我故意換成一個不存在的端口,目標機器上的OS直接就拒絕了這個
鏈接(抓包的話通常是看到reset標識):
一個SocketTimeoutException,程序員首先懷疑網絡丟包的Case
當時的反饋應用代碼拋SocketTimeoutException,懷疑網絡問題:
檢查一下當時的網絡狀態很是好,出問題時間段的網卡的量信息也很是正常:
上圖是經過sar監控到的9號 v24d9e0f23d40 這個網卡的流量,看起來也是正常,流量沒有出現明顯的波動。
爲了監控網絡到底有沒有問題,接着在出問題的兩個容器上各啓動一個http server,而後在對方每1秒鐘互相發一次發http get請求訪問這個http server,基本認識告訴咱們若是網絡丟包、卡頓嚴重,那麼我這個http server的監控日誌時間戳也會跳躍,若是應用是由於網絡出現異常那麼我啓動的http服務也會出現異常——寧願寫個工具都不背鍋(主要是背了鍋也不必定能解決掉問題)。
從實際監控來看,應用出現異常的時候個人http服務是正常的(寫了腳本判斷日誌的連續性):
這也強有力地證實了網絡沒問題,因此寫業務代碼的同窗一門心思集中火力查看應用的問題。後來的實際調查發現是應用假死掉了(內部線程太多,卡死了),服務端口不響應請求了。
若是基礎知識缺少一點那麼甩過來的這個鍋網絡是扛不動的,同時也阻礙了問題的真正發現。
TCP協議通信過程跟前面ping同樣,只是把ping的icmp協議換成TCP協議,也是要先根據route,而後arp。
網絡丟包、卡頓、抖動很容易作扛包俠,只有找到真正的緣由解決問題纔會更快,不然在錯誤的方向上怎麼發力都不對。準確的方向要靠好的基礎知識和正確的邏輯以及證據來支撐,而不是猜想。
• 基礎知識是決定你可否幹到退休的關鍵因素;
• 有了基礎知識不表明你能真正轉化成生產力;
• 越是基礎,越是幾十年不變的基礎越是重要;
• 知識到靈活運用要靠實踐,同時才能把知識之間的聯繫創建起來;
• 簡而言之缺的是融會貫通和運用;
• 作一個有禮有節的甩包俠;
• 在別人不給證據愚昧甩包的狀況下你的機會就來了。
留幾個小問題:
本文做者:蟄劍
本文來自雲棲社區合做夥伴「阿里技術」,如需轉載請聯繫原做者。