容器云平臺重大問題的分析方法

在容器編排技術(shù)之上,我們?yōu)榱烁玫淖岄_發(fā)者使用這個編排系統(tǒng),就有了容器平臺的出現(xiàn),典型開源代表都包括CI/CD、灰度發(fā)布、滾動升級、自動擴縮容、租戶管理、安全認證等等功能模塊。每一個模塊也都涌現(xiàn)出了很多新的技術(shù)代表,這些又構(gòu)成了我們今天大勢宣傳的云原生技術(shù)。

前言

隨著容器技術(shù)的出現(xiàn),我們開發(fā)出的應(yīng)用程序才可以真正實現(xiàn)開發(fā)運維一體化的愿景,因為它比傳統(tǒng)虛擬機定義了更具標準化的打包方式及更輕量級的運行狀態(tài)。當業(yè)務(wù)很多時,如果我們只在一臺主機上部署就不能滿足業(yè)務(wù)的高可用、負載的可擴容性等需求,這時就需要我們在多臺主機上部署,因此出現(xiàn)了集群化、分布式等技術(shù)。

那我們?nèi)绾卧谶@些主機上更高效的管理這些容器,并以某些方式暴露所部署的應(yīng)用服務(wù)?這就需要用到容器編排技術(shù)了,因此從2014年開始,容器領(lǐng)域就誕生了以Mesos、Docker Swarm及Kubernetes為代表的容器編排系統(tǒng)。在過去這幾年間,容器編排技術(shù)已經(jīng)呈現(xiàn)出“三國鼎立”之態(tài)勢,各有個的用戶群體,各有個的活動社區(qū),但2017年至今Kubernetes已經(jīng)成為真正的領(lǐng)導(dǎo)者。一種新的技術(shù)的興起,注定會帶動圍繞其出現(xiàn)的其他新技術(shù)的崛起,這包括了容器存儲、容器網(wǎng)絡(luò)、容器日志管理及監(jiān)控告警、CI/CD(持續(xù)集成/持續(xù)交付)等相關(guān)領(lǐng)域的技術(shù),從而構(gòu)成了我們今天火熱的容器生態(tài)圈。

在容器編排技術(shù)之上,我們?yōu)榱烁玫淖岄_發(fā)者使用這個編排系統(tǒng),就有了容器平臺的出現(xiàn),典型開源代表都包括CI/CD、灰度發(fā)布、滾動升級、自動擴縮容、租戶管理、安全認證等等功能模塊。每一個模塊也都涌現(xiàn)出了很多新的技術(shù)代表,這些又構(gòu)成了我們今天大勢宣傳的云原生技術(shù)。

那這么多新技術(shù)組合在一起,當我們的容器平臺或者部署在其上的業(yè)務(wù)出現(xiàn)問題時,我們該如何分析、定位并最后排除?這就是一個擺在云容器工作者面前的重要問題。本文將分享一些解決問題的思路、方法,由于容器平臺涉及的問題太過寬廣,使用的技術(shù)涉及知識面也很多,不能一一列舉,只能點到為止。

一個問題的解決一般需要經(jīng)過問題的分析、定位、復(fù)現(xiàn)到最后排除與解決等過程。每個過程幾乎都環(huán)環(huán)相扣,只有肯定了前一個過程,才利于后續(xù)過程進行,當然這些過程可能會出現(xiàn)相互否定的時候,但經(jīng)過幾番否定后,最終會形成一個確定的方向,即定位出了問題點。再根據(jù)我們分析與排查中產(chǎn)生的思路、利用相關(guān)的工具及方法等進行問題復(fù)現(xiàn),當問題復(fù)現(xiàn)后,問題就基本解決了70%,接下來就是找到解決該問題的方法以及排除它,這個占了30%,至此一個問題就解決了。

在容器平臺中,我們除了會涉及操作系統(tǒng)層面的知識,還會涉及容器存儲、容器網(wǎng)絡(luò)、容器日志管理及監(jiān)控告警、CI/CD(持續(xù)集成/持續(xù)交付)等相關(guān)領(lǐng)域的技術(shù)。每個領(lǐng)域都有可能是問題的產(chǎn)生點,只有在分析定位出具體點時,我們才好依據(jù)該技術(shù)相關(guān)工具或者閱讀源碼來解決它。

分析問題就是需要了解問題發(fā)生的時間點,問題發(fā)生時的上下文,環(huán)境場景等,并分析問題之間的關(guān)聯(lián)關(guān)系,應(yīng)用“簡單化原則”,這些都是我們分析問題的基礎(chǔ)。

下面我會穿插一個實際案例來分享一下整個排錯過程。

問題背景:容器平臺上線一年多后,總有很少部分租戶稱他們的某個業(yè)務(wù)部署在Kubernetes容器平臺后經(jīng)常會重啟,也很少部分租戶稱某個業(yè)務(wù)在運行一段時間時會產(chǎn)生大量的`CLOSE-WAIT`,還有租戶反饋說某個業(yè)務(wù)跑著就會hang住。剛開始我們都會讓業(yè)務(wù)開發(fā)者先自己找問題,因為這些租戶反映的只是偶偶發(fā)生,大多數(shù)租戶沒有反映類似問題,我們會理所當然的認為是租戶業(yè)務(wù)自身問題,而非平臺問題。當然大多數(shù)情況下,還是租戶業(yè)務(wù)本身程序沒有寫好,或者健康檢查配置不當?shù)纫稹?/p>

1簡單化原則

當我們在排查某個問題時,我們可以回想一下,最近發(fā)生了哪些問題,這些問題是否相互之間有關(guān)聯(lián)關(guān)系,如果可能有關(guān)聯(lián)關(guān)系,那么就可以利用現(xiàn)有解決方案進行修復(fù)。

我要分享的這三個問題,經(jīng)過我們排查后,都是同一個問題引起。像這種情況就需要我們進行初步地關(guān)聯(lián)分析,并按照問題發(fā)生的難易程度進行排查。

比如,我們可以這樣分析,假設(shè)這三個問題有關(guān)聯(lián)的話,那么會不會是服務(wù)hang住了觸發(fā)了其它兩個?即hang住了,服務(wù)自然沒法響應(yīng)客戶端的關(guān)閉連接,此時就會產(chǎn)生CLOSE-WAIT;另外,如果hang住

了,而業(yè)務(wù)在K8S平臺又配置了live probe?;顧C制,那么就會觸發(fā)容器自動重啟。這么看來,我們首要解決的就是hang住的問題,但hang有很多原因引起,其實這里并不好排查,順序可以先調(diào)整一下,因為更有可能是CLOSE-WAIT過多,把業(yè)務(wù)程序打爆,導(dǎo)致程序hang死。所以我們先從CLOSE-WAIT這種比較常見的問題入手,這也是我們遵尋簡單原則的地方。

2時間點

問題發(fā)生的時間點,可以讓我們確定是在低峰還是高峰期發(fā)生的。如果是低峰期發(fā)生,很大可能是軟件自身的重大問題,比如有內(nèi)存泄漏、CPU使用過高、磁盤IO消耗過大等,因為低峰期流量相對比較低,不容易受外在因素的影響。如果是高峰期發(fā)生,很大可能是高并發(fā)有問題,配置有問題(比如緩存池設(shè)置過小)或者有類似死鎖、競爭等產(chǎn)生,甚至涉及到操作系統(tǒng)內(nèi)核參數(shù)的配置問題,這些可能是程序本身的bug。

上面這三個問題,雖不是同一段時間出現(xiàn)的,但我們通過故障事件統(tǒng)計下來,卻是比較多的,這幾個問題在高低峰都有出現(xiàn)過,所以不好排查是否是業(yè)務(wù)程序代碼問題,最多說是業(yè)務(wù)代碼出錯可能性大點兒。

前面我說到了“故障事件統(tǒng)計”這個功能,這里我稍展開一下,這個其實對于容器平臺工作人員來說,是一個很重要的功能,它可以幫助我們分析事件類型及其某類事件的統(tǒng)計值,這樣方便看出在某個時間段哪些類型的事件比較多,也有利于我們定位事件之間的關(guān)聯(lián)性,如果是平臺問題引起,那么就可以迭代平臺,進行改進。事件源可以有很多,比如Kubernetes集群本身的話,Kubernetes核心API提供了各種event事件類型,比如Pod啟動情況,被調(diào)度到哪些物理節(jié)點,ReplicaSet擴縮副本數(shù),Pod Unhealthy原因,Node NotReady信息,及與存儲卷相關(guān)的PV,PVC等眾多事件,這些在默認情況下,只在etcd中保存1小時。我們?yōu)榱朔治觯梢猿志没@些事件信息,比如一周,一個月等,這個可以借助于開源的[eventrouter](https://github.com/heptiolabs/eventrouter)或者阿里的[kube-eventer](https://github.com/AliyunContainerService/kube-eventer),把事件采集到日志系統(tǒng)進行搜索、分析、展示與告警等。在我們的生產(chǎn)環(huán)境中,目前就使用了kube-eventer來收集事件數(shù)據(jù),并發(fā)送到kafka,之后通過日志系統(tǒng)的kafka訂閱功能寫入后臺ElasticSearch和ClickHouse中,并借助現(xiàn)在日志平臺進行事件統(tǒng)計與分析。另外,Zabbix等告警平臺也會提供一些主機層面或者租戶業(yè)務(wù)程序自定義埋點相關(guān)的事件告警,這些我們都可以收集起來,既可以方便查看時間點,也方便做事件統(tǒng)計分析。具體在這里不展開,請自行查閱相關(guān)文獻。

另外,我們也要注意軟件變更時間點,因為很多時候新問題的產(chǎn)生是由于軟件變更引起的,比如容器平臺軟件在添加了新功能后,沒有經(jīng)過完整的集成測試或兼容性等系統(tǒng)測試,就可能會帶來新的問題。當一個問題在某個時間點之后才產(chǎn)生,那么我們需要去看這個時間點之前所提交的程序代碼,及相關(guān)的周邊環(huán)境變動情況等來確定問題是否和這些相關(guān)。

雖然上面這三個問題和軟件變更時間點無關(guān),但我們在生產(chǎn)環(huán)境中就碰到了一次嚴重的“內(nèi)存泄漏”事件。主要的現(xiàn)象就是內(nèi)存由1.5G左右突然激增到35G大小,由于內(nèi)存是不可壓縮資源,在K8S中以Pod方式部署后,會觸發(fā)Pod重啟,并產(chǎn)生告警。我們注意到2020年3月4號之前,并未出現(xiàn)過該問題,之后才出現(xiàn)的,那么解決此問題的方法就是通過定位軟件變更時間點來解決,變更時間點關(guān)聯(lián)了代碼提交點,所以我們只要檢查2020年3月4號該時間點之前commit過了哪些代碼。遺憾的是,剛開始,我們也查看了該時間段提交的代碼,卻沒有發(fā)現(xiàn)什么可疑的代碼。為什么前面"內(nèi)存泄漏"打了雙引號?就是因為我們最終排查下來,發(fā)現(xiàn)其現(xiàn)象是內(nèi)存泄漏,但本質(zhì)上卻不是,這也是我們暫時通過代碼沒有發(fā)現(xiàn)問題的原因。

平臺軟件是用golang開發(fā)的,像內(nèi)存泄漏這種事件,一般可以通過pprof這種內(nèi)存分析工具來定位,網(wǎng)上有很多的教程,這里不對該工具進行展開。我們通過pprof工具獲取了平臺軟件的pprof信息,從heap-pprof的常駐內(nèi)存情況看,Runtime使用的內(nèi)存只有1.5G,而系統(tǒng)看到整個進程的RSS內(nèi)存高達35G,差距非常之大。從發(fā)生問題的時間點看,bond0流量突增,可能跟API請求的各個環(huán)節(jié)有關(guān),涉及到外部系統(tǒng)調(diào)用平臺API,平臺內(nèi)部調(diào)用Kubernetes API,這些請求無論是平臺server主動發(fā)起還是被動接收,都涉及到非常多的類型轉(zhuǎn)換,比如:json encoding/decoding,平臺Server都需要分配相應(yīng)的內(nèi)存。如果請求量非常大,可能就會導(dǎo)致內(nèi)存激增。但是并沒有發(fā)生內(nèi)存泄露,也就是說是正常的內(nèi)存分配,這一點我們從pprof的信息得到了確定,而且被分配的內(nèi)存也沒有被回收,我們看到內(nèi)存在增長到一定階段后非常平穩(wěn)。

那問題來了,為什么會沒有被回收,golang中不是都有自帶內(nèi)存回收機制嗎?之后,我們帶著問題,就去看golang的內(nèi)存回收原理,并查看了網(wǎng)上相關(guān)資料,其中的一篇文章《Go進程的HeapReleased上升,但是RSS不下降造成內(nèi)存泄漏》(知乎)和我們遇到的現(xiàn)象比較像。所以最終將問題定位到GO Runtime的內(nèi)存釋放機制上,在Go 1.12之前,golang runtime會在未使用的內(nèi)存上標志成`MADV_DONTNEED`,標記過的內(nèi)存如果再次使用,會觸發(fā)缺頁中斷,并且操作系統(tǒng)會立即回收未使用的內(nèi)存頁。從Go 1.12開始,該標志已更改為`MADV_FREE`,這告訴操作系統(tǒng)它可以根據(jù)需要回收一些未使用的內(nèi)存頁面,即內(nèi)核會等到內(nèi)存緊張時才會釋放,在釋放之前,這塊內(nèi)存依然可以復(fù)用。這個特性從Linux 4.5版本內(nèi)核開始支持,顯然,`MADV_FREE`是一種用空間換時間的優(yōu)化。此時,我們再去查看該時間段提交的代碼,果然發(fā)現(xiàn)有一個commit是在構(gòu)建平臺軟件的docker鏡像的Dockerfile中修改了Golang的語言版本,從1.11到1.12.9版本。

QQ截圖20200828092910.png

Go 1.12之后,提供了一種方式強制回退使用`MADV_DONTNEED`的方式,在執(zhí)行程序前添加`GODE-BUG=madvdontneed=1`。即解決這個問題的方法是將平臺軟件Pod中的YAML進行修改,添加環(huán)境變量`GODEBUG=madvdontneed=1`,主動使其及時回收未在使用的內(nèi)存,以釋放內(nèi)存空間。

3上下文

問題發(fā)生時,可以通過查看日志記錄,比如某條錯誤的或致命的日志來查找到對應(yīng)的程序執(zhí)行點,這個執(zhí)行點的前后程序段就是這里所說的代碼的上下文,也是軟件的上下文。這個與前面軟件變更的時間還是有點不同,這個是當發(fā)現(xiàn)日志中出現(xiàn)了某條錯誤日志時,根據(jù)該日志對應(yīng)的代碼行來定位的。通過它我們可以分析代碼中執(zhí)行了哪些特定的功能模塊,分析問題產(chǎn)生的時機,觸發(fā)點等,找出一些與問題相關(guān)的蛛絲馬跡。

4環(huán)境場景

環(huán)境場景主要指問題發(fā)生時,相關(guān)的外在環(huán)境,比如操作系統(tǒng)的內(nèi)核版本、docker版本、Kubernetes版本等。如果問題的產(chǎn)生和這些環(huán)境相關(guān),那么就需要考慮程序的兼容性場景,我們一般在開源社區(qū)中,給開源軟件提交bug的時候,也會指定特定的軟件環(huán)境,就是方便作者分析與定位問題。

THEEND

最新評論(評論僅代表用戶觀點)

更多
暫無評論