一个疑难故障,坑了我半年青春-----知识就是生产力
作者介紹
林偉壕,網(wǎng)易游戲資深運(yùn)維工程師。現(xiàn)任職于網(wǎng)易游戲,從事游戲運(yùn)維相關(guān)工作;曾就職于中國(guó)電信,負(fù)責(zé)數(shù)據(jù)網(wǎng)絡(luò)維護(hù)、網(wǎng)絡(luò)安全防御等工作。深入研究Linux運(yùn)維、虛擬化等,現(xiàn)致力于企業(yè)級(jí)網(wǎng)絡(luò)安全防護(hù)自動(dòng)化體系構(gòu)建。
相對(duì)物理環(huán)境,虛擬化環(huán)境更加錯(cuò)綜復(fù)雜。之前弄KVM虛擬化時(shí)經(jīng)常遇到好多次莫名其妙的網(wǎng)絡(luò)故障,查出來(lái)的原因要么是操作系統(tǒng)內(nèi)核bug,要么是KVM與操作系統(tǒng)內(nèi)核版本不兼容,最后是通過(guò)升級(jí)操作系統(tǒng)內(nèi)核或者KVM版本修復(fù)了。沒(méi)想到,轉(zhuǎn)型到Docker后,又重蹈覆轍了。
本文將介紹一個(gè)困擾筆者近半年的虛擬化環(huán)境下的疑難故障,最后排查出來(lái)的故障原因和修復(fù)手段也讓人啼笑皆非。并非因?yàn)檫@個(gè)過(guò)程有多復(fù)雜,而是分享一個(gè)心理歷程,思考在遇到故障時(shí)如何兼顧業(yè)務(wù)和技術(shù),如何正確使用搜索引擎。
故障現(xiàn)象
我們有一套高性能代理集群,之前內(nèi)測(cè)階段運(yùn)行穩(wěn)定,結(jié)果等正式上線后不到半個(gè)月,提供代理服務(wù)的宿主突然接二連三死機(jī),導(dǎo)致宿主上的所有服務(wù)全部中斷。
故障分析
故障時(shí)宿主直接死機(jī),無(wú)法遠(yuǎn)程登錄,機(jī)房現(xiàn)場(chǎng)敲鍵盤(pán)業(yè)務(wù)反應(yīng)。由于宿主syslog已接入ELK,所以我們采集了當(dāng)時(shí)死機(jī)前后的各種syslog。
報(bào)錯(cuò)日志
通過(guò)查看死機(jī)宿主的syslog發(fā)現(xiàn)機(jī)器死機(jī)前有以下kernel報(bào)錯(cuò):
Nov 12 15:06:31 hello-worldkernel: [6373724.634681] BUG: unable to handle kernel NULL pointer dereferenceat 0000000000000078
Nov 12 15:06:31 hello-world kernel: [6373724.634718] IP: []pick_next_task_fair+0x6b8/0x820
Nov 12 15:06:31 hello-world kernel: [6373724.634749] PGD 10561e4067 PUDffdb46067 PMD 0
Nov 12 15:06:31 hello-world kernel: [6373724.634780] Oops: 0000 [#1] SMP
顯示訪問(wèn)了內(nèi)核空指針后觸發(fā)系統(tǒng)bug,然后引起一系列調(diào)用棧報(bào)錯(cuò),最后死機(jī)。
為進(jìn)一步分析故障現(xiàn)象,首先需要理解這套高性能代理集群的架構(gòu)。
架構(gòu)介紹
單個(gè)節(jié)點(diǎn),是在萬(wàn)兆網(wǎng)卡的宿主機(jī)上跑Docker容器,然后在容器中跑Haproxy實(shí)例,每個(gè)節(jié)點(diǎn)、實(shí)例的配置信息、業(yè)務(wù)信息都托管在調(diào)度器上。
特別之處在于:宿主使用Linux Bridge直接給Docker容器配置IP地址,所有對(duì)外服務(wù)的IP,包括宿主自己的外網(wǎng)IP都綁在Linux Bridge上。
應(yīng)用介紹
每臺(tái)宿主的操作系統(tǒng)、硬件、Docker版本全部一致,其中操作系統(tǒng)和Docker版本如下:
[操作系統(tǒng)]
System : Linux
Kernel : 3.16.0-4-amd64
Version : 8.5
Arch : x86_64
[Docker版本]
Docker version 1.12.1, build 6b644ec
初步分析
該集群的宿主配置一致,故障現(xiàn)象也一致,疑點(diǎn)有三個(gè):
1、Docker版本與宿主內(nèi)核版本不兼容
三臺(tái)宿主的環(huán)境本來(lái)一致,但1臺(tái)穩(wěn)定跑服務(wù)2個(gè)月才死機(jī),1臺(tái)跑服務(wù)1個(gè)月后死機(jī),另外1臺(tái)上線跑服務(wù)一周便會(huì)死機(jī)。
發(fā)現(xiàn)每臺(tái)宿主除了死機(jī)的異常日志,平時(shí)也有相同報(bào)錯(cuò)日志:
time=”2016-09-07T20:22:19.450573015+08:00″level=warning msg=”Your kernel does not support cgroup memory limit”
time=”2016-09-07T20:22:19.450618295+08:00″ level=warningmsg=”Your kernel does not support cgroup cfs period”
time=”2016-09-07T20:22:19.450640785+08:00″ level=warningmsg=”Your kernel does not support cgroup cfs quotas”
time=”2016-09-07T20:22:19.450769672+08:00″ level=warningmsg=”mountpoint for pids not found”
根據(jù)上面提示,應(yīng)該是操作系統(tǒng)內(nèi)核版本對(duì)該版本的Docker不支持某些功能所導(dǎo)致。不過(guò)在搜索引擎上搜索這并不影響Docker的功能,更不加影響系統(tǒng)穩(wěn)定性。
比如:
time=”2017-01-19T18:16:30+08:00″level=error msg=”containerd: notify OOM events” error=”openmemory.oom_control: no such file or directory”
time=”2017-01-19T18:22:41.368392532+08:00″level=error msg=”Handler for POST /v1.23/containers/338016c68da6/stopreturned error: No such container:
338016c68da6″
是Docker 1.9以來(lái)就有的問(wèn)題,1.12.3修復(fù)了。參考https://github.com/docker/docker/?issues/24211
比如Github上有人回復(fù):
“I have been update my docker from 1.11.2 to 1.12.3, This issue is fixed.
BTW, this error message can be ignored, it should really just be a warning.”
但這里所說(shuō)的都只是v1.12.2版本就能修復(fù)的問(wèn)題,我們升級(jí)Docker版本后發(fā)現(xiàn)死機(jī)依舊。
于是,我們接著通過(guò)各種Google確認(rèn)了很多與我們存在相同故障現(xiàn)象的問(wèn)題,初步確認(rèn)故障與Docker的相關(guān)性:
http://serverfault.com/questions/709926/bug-unable-to-handle-kernel-null-pointer-dereference-at-on-google-compute-eng
https://support.mayfirst.org/ticket/10872
又根據(jù)以下官方issue初步確認(rèn)Docker版本與系統(tǒng)內(nèi)核版本不兼容可引發(fā)宕機(jī)的關(guān)聯(lián)性:
https://github.com/docker/docker/issues/19910
接著,通過(guò)官方的changelog和issue確認(rèn)宿主所使用Docker版本與系統(tǒng)內(nèi)核版本不兼容問(wèn)題:
https://github.com/docker/docker/blob/v1.12.2-rc1/CHANGELOG.md
出于嘗試心理,我們把Docker版本升級(jí)到1.12.2后,未出意外仍出現(xiàn)死機(jī)。
2.使用Linux bridge方式改造宿主網(wǎng)卡可能觸發(fā)bug
找了那臺(tái)宿主跑服務(wù)一周就會(huì)死機(jī)的宿主,停止運(yùn)行Docker,只改造網(wǎng)絡(luò),穩(wěn)定跑了一周未發(fā)現(xiàn)異常。
3.使用pipework給Docker容器配置IP可能觸發(fā)bug
由于給容器分配IP時(shí)我們采用了開(kāi)源的pipework腳本,因此懷疑pipework的工作原理存在bug,所以嘗試不使用pipework分配IP地址,發(fā)現(xiàn)宿主仍出現(xiàn)死機(jī)。
于是初步排查陷入困境,眼看著宿主每月至少死機(jī)一次,非常郁悶。
故障定位
因?yàn)檫€有線上業(yè)務(wù)在跑,所以沒(méi)有貿(mào)然升級(jí)所有宿主內(nèi)核,而是期望能通過(guò)升級(jí)Docker或者其它熱更新的方式修復(fù)問(wèn)題。但是不斷的嘗試并沒(méi)有帶來(lái)理想中的效果。
直到有一天,在跟一位對(duì)Linux內(nèi)核頗有研究的老司機(jī)聊起這個(gè)問(wèn)題時(shí),他三下五除二,Google到了幾篇文章,然后提醒我們?nèi)绻沁@個(gè) bug,那是在 Linux 3.18 內(nèi)核才能修復(fù)的。
參考:
- https://lists.gt.net/linux/kernel/2256803
- https://lkml.org/lkml/2014/2/15/217
- https://github.com/docker/docker/issues/21081
- https://github.com/torvalds/linux/commit/eeb61e53ea19be0c4015b00b2e8b3b2185436f2b
原因:
從sched: Fix race between task_group and sched_task_group的解析來(lái)看,就是parent 進(jìn)程改變了它的task_group,還沒(méi)調(diào)用cgroup_post_fork()去同步給child,然后child還去訪問(wèn)原來(lái)的cgroup就會(huì)null。
不過(guò)這個(gè)問(wèn)題發(fā)生在比較低版本的Docker,基本是Docker 1.9以下,而我們用的是Docker1.11.1/1.12.1。所以盡管報(bào)錯(cuò)現(xiàn)象比較相似,但我們還是沒(méi)有100%把握。
但是,這個(gè)提醒卻給我們打開(kāi)了思路:去看內(nèi)核代碼,實(shí)在不行就下掉所有業(yè)務(wù),然后全部升級(jí)操作系統(tǒng)內(nèi)核,保持一個(gè)月觀察期。
于是,我們開(kāi)始啃Linux內(nèi)核代碼之路。先查看操作系統(tǒng)本地是否有源碼,沒(méi)有的話需要去Linux kernel官方網(wǎng)站搜索。
“`
apt-cache search linux-image-3.16.0-4-amd64
apt-get source linux-image-3.16.0-4-amd64
“`
下載了源碼包后,根據(jù)報(bào)錯(cuò)syslog的內(nèi)容進(jìn)行關(guān)鍵字匹配,發(fā)現(xiàn)了以下內(nèi)容。由于我們的機(jī)器是x86_64架構(gòu),所以那些avr32/m32r之類(lèi)的可以跳過(guò)不看。結(jié)果看下來(lái),完全沒(méi)有可用信息。
/kernel/linux-3.16.39#grep -nri “unable to handle kernel NULL pointer dereference” *
arch/tile/mm/fault.c:530:????????????? pr_alert(“Unable to handlekernel NULL pointer dereference\n”);
arch/sparc/kernel/unaligned_32.c:221:??????????????? ? printk(KERN_ALERT “Unable to handle kernel NULL pointerdereference in mna handler”);
arch/sparc/mm/fault_32.c:44:??????? ???“Unable to handle kernel NULL pointer dereference\n”);
arch/m68k/mm/fault.c:47:?????????????????? pr_alert(“Unable tohandle kernel NULL pointer dereference”);
arch/ia64/mm/fault.c:292:??????????? printk(KERN_ALERT “Unable tohandle kernel NULL pointer dereference (address %016lx)\n”, address);
debian/patches/bugfix/all/mpi-fix-null-ptr-dereference-in-mpi_powm-ver-3.patch:20:BUG:unable to handle kernel NULL pointer dereference at?????????? (null)
最后,我們還是下線了所有業(yè)務(wù),將操作系統(tǒng)內(nèi)核和Docker版本全部升級(jí)到最新版。這個(gè)過(guò)程有些艱難,當(dāng)初推廣這個(gè)系統(tǒng)時(shí)拉的廣告歷歷在目,現(xiàn)在下線業(yè)務(wù),回爐重造,挺考驗(yàn)勇氣和決心的。
故障處理
下面是整個(gè)故障處理過(guò)程中,我們進(jìn)行的一些操作。
升級(jí)操作系統(tǒng)內(nèi)核
對(duì)于Docker 1.11.1與內(nèi)核4.9不兼容的問(wèn)題,可以刪除原有的Docker配置,然后使用官方腳本重新安裝最新版本Docker
“`
/proxy/bin#ls /var/lib/dpkg/info/docker-engine.
docker-engine.conffiles? docker-engine.md5sums??? docker-engine.postrm???? docker-engine.prerm
docker-engine.list?????? docker-engine.postinst?? docker-engine.preinst
#Getthe latest Docker package.
$curl -fsSL https://get.docker.com/ | sh
#啟動(dòng)
nohupdocker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock-s=devicemapper&
“`
這里需要注意的是,Docker安裝方式在不同操作系統(tǒng)版本上不盡相同,甚至相同發(fā)行版上也有不同,比如原來(lái)我們使用以下方式安裝Docker:
“`
apt-get install docker-engine
“`
然后在早些時(shí)候,還有使用下面的安裝方式:
“`
apt-get install lxc-docker
“`
可能是基于原來(lái)安裝方式的千奇百怪導(dǎo)致問(wèn)題叢出,所以Docker官方提供了一個(gè)腳本用于適配不同系統(tǒng)、不同發(fā)行版本Docker安裝的問(wèn)題,這也是一個(gè)比較奇怪的地方,所以Docker生態(tài)還是蠻亂的。
驗(yàn)證
16:44:15 up 28 days, 23:41,? 2 users,?load average: 0.10, 0.13, 0.15
docker????30320???? 1? 0 Jan11 ???????? 00:49:56 /usr/bin/docker daemon -p/var/run/docker.pid
Docker內(nèi)核升級(jí)到1.19,Linux內(nèi)核升級(jí)到3.19后,保持運(yùn)行至今已經(jīng)2個(gè)月多了,都是ok的。
總結(jié)
這個(gè)故障的處理時(shí)間跨度很大,都快半年了,想起今年除夕夜收到服務(wù)器死機(jī)報(bào)警的情景,心里像打破五味瓶一樣五味雜陳。期間問(wèn)過(guò)不少研究Docker和操作系統(tǒng)內(nèi)核的同事,往操作系統(tǒng)內(nèi)核版本等各個(gè)方向進(jìn)行了測(cè)試,但總與正確答案背道而馳或差那么一點(diǎn)點(diǎn)。最后發(fā)現(xiàn)原來(lái)是處理得不夠徹底,比如升級(jí)不徹底,環(huán)境被污染;比如升級(jí)的版本不夠新,填的坑不夠厚。回顧了整個(gè)故障處理過(guò)程,總結(jié)下來(lái)大概如下:
回歸運(yùn)維的本質(zhì)
運(yùn)維要具有預(yù)見(jiàn)性、長(zhǎng)期規(guī)劃,而不能僅僅滿足于眼前:
Google的能力
在處理這個(gè)故障的過(guò)程中,會(huì)發(fā)現(xiàn)不同人使用Google搜出來(lái)的東西并不一樣,為什么呢?我覺(jué)得這就是搜索引擎槽點(diǎn)滿滿,或者說(shuō)靈活之處。像這次的故障,我用Linux Docker Unable to handle kernel NULL pointer dereference去搜索,與別人用”Unable to handle kernel NULL pointer dereference”結(jié)果就不同。原因在于增加了””之后,搜索更加精確了。關(guān)于Google的正確打開(kāi)方式,建議參考:
- https://www.zhihu.com/question/20161362?rf=19798921
http://www.yunweipai.com/archives/18950.html
?
轉(zhuǎn)載于:https://www.cnblogs.com/softidea/p/6930441.html
總結(jié)
以上是生活随笔為你收集整理的一个疑难故障,坑了我半年青春-----知识就是生产力的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 二氧化钛TiO2纳米粒子(尺寸80nm-
- 下一篇: CP2K入门教程分享