傳說中的骨灰級程式設計師是如何排查線上問題的

工匠小豬豬的技術世界發表於2018-12-10

原文地址:

原文作者:左耳朵耗子 陳皓

(本文版權歸原作者所有。轉載文章僅為傳播更多資訊之目的,如有侵權請與我們聯絡,我們將及時處理。)


記一次Kubernetes/Docker網路排障

昨天週五晚上,臨下班的時候,使用者給我們報了一個比較怪異的Kubernetes叢集下的網路不能正常訪問的問題,讓我們幫助檢視一下,我們從下午5點半左右一直跟進到晚上十點左右,在遠端不能訪問使用者機器只能遠端遙控使用者的情況找到了的問題。這個問題比較有意思,我個人覺得其中的調查用到的的命令以及排障的一些方法可以分享一下,所以寫下了這篇文章。

問題的症狀
傳說中的骨灰級程式設計師是如何排查線上問題的

使用者直接在微信裡說,他們發現在Kuberbnetes下的某個pod被重啟了幾百次甚至上千次,於是開啟調查這個pod,發現上面的服務時而能夠訪問,時而不能訪問,也就是有一定機率不能訪問,不知道是什麼原因。而且並不是所有的pod出問題,而只是特定的一兩個pod出了網路訪問的問題。使用者說這個pod執行著Java程式,為了排除是Java的問題,使用者用docker exec -it 命令直接到容器內啟了一個 Python的 SimpleHttpServer來測試發現也是一樣的問題。

我們大概知道使用者的叢集是這樣的版本,Kuberbnetes 是1.7,網路用的是flannel的gw模式,Docker版本未知,作業系統CentOS 7.4,直接在物理機上跑docker,物理的配置很高,512GB記憶體,若干CPU核,上面執行著幾百個Docker容器。

問題的排查傳說中的骨灰級程式設計師是如何排查線上問題的

問題初查

首先,我們排除了flannel的問題,因為整個叢集的網路通訊都正常,只有特定的某一兩個pod有問題。而用telnet ip port 的命令手工測試網路連線時有很大的機率出現connection refused 錯誤,大約 1/4的機率,而3/4的情況下是可以正常連線的。

當時,我們讓使用者抓個包看看,然後,使用者抓到了有問題的TCP連線是收到了SYN 後,立即返回了RST, ACK

傳說中的骨灰級程式設計師是如何排查線上問題的我問一下使用者這兩個IP所在的位置,我們知道了,10.233.14.129 是docker010.233.14.145 是容器內的IP。所以,這基本上可以排除了所有和kubernets或是flannel的問題,這就是本地的Docker上的網路的問題。

對於這樣的情況,在telnet上會顯示connection refused 的錯誤資訊,對於我個人的經驗,這種SYN完直接返回RST, ACK的情況只會有三種情況:

  1. TCP連結不能建立,絕大多數情況都是服務端沒有相關的埠號

  2. TCP連結建錯誤,有可能是因為修改了TCP引數

  3. 有防火牆iptables的設定

因為當時還在開車,在等紅燈的時候,我感覺到有點像 NAT 的網路中服務端開啟了tcp_tw_recycle 和tcp_tw_reuse 的症況(詳細參看《TCP的那些事(上)》),所以,讓使用者檢視了一上TCP引數,發現使用者一個TCP的引數都沒有改,全是預設的,於是我們排除了TCP引數的問題。

然後,我也不覺得容器內還會設定上iptables,所以,我懷疑容器內的埠號沒有偵聽上,但是馬上又好了,這可能會是應用的問題。於是我讓使用者那邊看一下,應用的日誌,並用kublet describe看一下執行的情況,並把宿主機的 iptables 看一下。

然而,我們發現並沒有任何的問題。這時,我們失去了所有的調查線索,感覺不能繼續下去了……

重新梳理

這個時候,回到家,大家吃完飯,和使用者通了一個電話,把所有的細節再重新梳理了一遍,這個時候,使用者提供了一個比較關鍵的資訊—— “抓包這個事,在docker0 上可以抓到,然而到了容器內抓不到容器返回RST, ACK” !然而,根據我的知識,我知道在docker0和容器內的veth網路卡上,中間再也沒有什麼網路裝置了(參看《Docker基礎技術:LINUX NAMESPACE(下)》)!

於是這個事把我們逼到了最後一種情況 —— IP地址衝突了!

Linux下看IP地址衝突還不是一件比較簡單事的,而在使用者的生產環境下沒有辦法安裝一些其它的命令,所以只能用已有的命令,這個時候,我們發現使用者的機器上有arping 於是我們用這個命令來檢測有沒有衝突的IP地址。使用了下面的命令:

$ arping -D -I docker0 -c 2 10.233.14.145
echo$?

因為客戶那邊還在處理一些別的事情,所以,我們在時斷時續的情況下工作,而還一些工作都需要使用者完成,所以,進展有點緩慢,但是也給我們一些時間思考問題。根據文件,-D 引數是檢測IP地址衝突模式,如果這個命令的退狀態是0 那麼就有衝突。結果返回了1 。而且,我們用arping IP的時候,沒有發現不同的mac地址。 這個時候,似乎問題的線索又斷了

柳暗花明

現在我們知道,IP衝突的可能性是非常大的,但是我們找不出來是和誰的IP衝突了。而且,我們知道只要把這臺機器重啟一下,問題一定就解決掉了,但是我們覺得這並不是解決問題的方式,因為重啟機器可以暫時的解決掉到這個問題,而如果我們不知道這個問題怎麼發生的,那麼未來這個問題還會再來。而重啟線上機器這個成本太高了。

於是,我們的好奇心驅使我們繼續調查。我讓使用者kubectl delete 其中兩個有問題的pod,因為本來就服務不斷重啟,所以,刪掉也沒有什麼問題。刪掉這兩個pod後(一個是IP為10.233.14.145 另一個是10.233.14.137),我們發現,kubernetes,在其它機器上重新啟到了這兩個服務的例項。然而,在問題機器上,這兩個IP地址居然還可以ping得通

好了,IP地址衝突的問題可以確認了。因為10.233.14.xxx 這個網段是docker 的,所以,這個IP地址一定是在這臺機器上。所以,我們想看看所有的 network namespace 下的 veth 網路卡上的IP。

在這個事上,我們費了點時間。

  • 首先,我們到/var/run/netns目錄下檢視系統的network namespace,發現沒有。

  • 然後,我們到 /var/run/docker/netns 目錄下檢視Docker的namespace,發現有好些。

  • 於是,我們用指定位置的方式檢視Docker的network namespace裡的IP地址


$ls/var/run/docker/netns|xargs-I {} nsenter --net=/var/run/docker/netns/{} ip addr

10.233.14.145我們查到了,docker的namespace下還有這個IP。我們發現了比較詭異的事情。

  • 10.233.14.137,這個IP沒有在docker的network namespace下查到。

於是我上網查了一下,發現了一個docker的bug – 在docker remove/stop 一個容器的時候,沒有清除相應的network namespace,這個問題被報告到了 Issue#31597 然後被fix在了 PR#31996,並Merge到了 Docker的 17.05版中。而使用者的版本是 17.09,應該包含了這個fix。

要檢視所有network namespace,只有最後一條路了,那就是到/proc/ 目錄下,把所有的pid下的/proc/<pid>/ns 目錄給窮舉出來。好在這裡有一個比較方便的命令可以幹這個事 –lsns

於是我寫下了如下的命令:

$ lsns -t net |awk‘{print $4}' |xargs-t -I {} nsenter -t {} -n ip addr |grep-C 4"10.233.14.137"

lsns -t net 列出所有開了network namespace的程式,其每4列是程式PID解釋一下。

  • 把所有開過network namespace的程式PID拿出來,轉給xargs 命令

  • xargs命令把這些PID 依次傳給nsenter命令,

    • xargs -t 的意思是會把相關的執行命令打出來,這樣我知道是那個PID。

    • xargs -I {} 是宣告一個佔位符來替換相關的PID

最後,我們發現,雖然在/var/run/docker/netns 下沒有找到10.233.14.137 ,但是在lsns 中找到了三個程式,其用了10.233.14.137 這個IP,而且他們的MAC地址全是一樣的!衝突了這麼多。透過ps 命令,可以查到這三個程式,有兩個是java的,還有一個是/pause (這個應該是kubernetes的沙盒)。

我們繼續乘勝追擊,窮追猛打,用pstree命令把整個程式樹打出來。發現上述的三個程式的父程式都在多個同樣叫docker-contiane 的程式下!

這明顯還是docker的,但是在docker ps中卻找不道相應的容器,什麼鬼!我快崩潰了……

繼續看程式樹,發現,這些docker-contiane 的程式的父程式不在dockerd 下面,而是在systemd這個超級父程式PID 1下,我靠!進而發現了一堆這樣的野程式(這種野程式或是殭屍程式對系統是有害的,至少也是會讓系統進入亞健康的狀態,因為他們還在佔著資源)。

docker-contiane 應該是dockerd 的子程式,被掛到了pid 1 只有一個原因,那就是父程式“飛”掉了,只能找 pid 1 當養父。這說明,這臺機器上出現了比較嚴重的dockerd 程式退出的問題,而且是非常規的,因為systemd 之所以要成為 pid 1,其就是要監管所有程式的子子孫孫,居然也沒有管理好,說明是個非常規的。(注,關於 systemd,請參看《Linux PID 1 和 Systemd 》,關於父子程式的事,請參看《Unix高階環境程式設計》一書)

接下來就要看看systemddockerd記錄的日誌了……

總結
傳說中的骨灰級程式設計師是如何排查線上問題的

透過這個調查,可以總結一下,

1) 對於問題調查,需要比較紮實的基礎知識,知道問題的成因和範圍。

2)如果走不下去了,要生新梳理,回頭看一下過的一些蛛絲馬跡,但認真推敲每一個細節。

3) 各種診斷工具要比較熟悉,這會讓你事半功倍。

4)系統維護和做清潔比較類似,你需要經黨看看系統中是否有一些殭屍程式或是一些垃圾東西,這些東西要及時清理掉。

最後,多說一下,很多人都說,Docker適合放在物理機內執行,這並不完全對,因為他們只考慮到了效能成本,沒有考慮到運維成本,在這樣512GB中啟動幾百個容器的玩法,其實並不好,因為這本質上是個大單體,因為你一理要重啟某些關鍵程式或是機器,你的影響面是巨大的

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555484/viewspace-2284738/,如需轉載,請註明出處,否則將追究法律責任。

相關文章