容器云平臺(tái)重大問(wèn)題的分析方法

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

前言

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

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

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

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

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

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

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

下面我會(huì)穿插一個(gè)實(shí)際案例來(lái)分享一下整個(gè)排錯(cuò)過(guò)程。

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

1簡(jiǎn)單化原則

當(dāng)我們?cè)谂挪槟硞€(gè)問(wèn)題時(shí),我們可以回想一下,最近發(fā)生了哪些問(wèn)題,這些問(wèn)題是否相互之間有關(guān)聯(lián)關(guān)系,如果可能有關(guān)聯(lián)關(guān)系,那么就可以利用現(xiàn)有解決方案進(jìn)行修復(fù)。

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

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

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

2時(shí)間點(diǎn)

問(wèn)題發(fā)生的時(shí)間點(diǎn),可以讓我們確定是在低峰還是高峰期發(fā)生的。如果是低峰期發(fā)生,很大可能是軟件自身的重大問(wèn)題,比如有內(nèi)存泄漏、CPU使用過(guò)高、磁盤(pán)IO消耗過(guò)大等,因?yàn)榈头迤诹髁肯鄬?duì)比較低,不容易受外在因素的影響。如果是高峰期發(fā)生,很大可能是高并發(fā)有問(wèn)題,配置有問(wèn)題(比如緩存池設(shè)置過(guò)?。┗蛘哂蓄愃扑梨i、競(jìng)爭(zhēng)等產(chǎn)生,甚至涉及到操作系統(tǒng)內(nèi)核參數(shù)的配置問(wèn)題,這些可能是程序本身的bug。

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

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

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

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

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

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

QQ截圖20200828092910.png

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

3上下文

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

4環(huán)境場(chǎng)景

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

THEEND

最新評(píng)論(評(píng)論僅代表用戶觀點(diǎn))

更多
暫無(wú)評(píng)論