首頁 > 系統教程 > Linux > 一個困擾了我半年的難題

一個困擾了我半年的難題

PHPz
發布: 2024-01-16 23:33:18
轉載
1333 人瀏覽過

本文將介紹一個困擾筆者近半年的虛擬化環境下的疑難故障,最後排查出來的故障原因和修復手段也讓人啼笑皆非。並非因為這個過程有多複雜,而是分享一個心理歷程,思考在遇到故障時如何兼顧業務和技術,如何正確使用搜尋引擎。

故障現象

我們有一套高性能代理集群,之前內測階段運行穩定,結果等正式上線後不到半個月,提供代理服務的宿主突然接二連三死機,導致宿主上的所有服務全部中斷。

故障分析

故障時宿主直接死機,無法遠端登錄,機房現場敲擊鍵盤業務反應。由於宿主syslog已接入ELK,所以我們採集了當時當機前後的各種syslog。

報錯日誌

#透過查看死機宿主的syslog發現機器死機前有以下kernel報錯:

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
登入後複製

顯示訪問了核心空指標後觸發系統bug,然後引起一系列呼叫堆疊報錯,最後當機。

為進一步分析故障現象,首先需要理解這套高效能代理叢集的架構。

架構介紹

一個困擾了我半年的難題

#單一節點,是在萬兆網路卡的宿主機上執行Docker容器,然後在容器中執行Haproxy實例,每個節點、實例的設定資訊、業務資訊都託管在調度器上。

特別之處在於:宿主使用Linux Bridge直接給Docker容器配置IP位址,所有對外服務的IP,包括宿主自己的外網IP都綁在Linux Bridge上。

應用介紹

每台宿主的作業系統、硬體、Docker版本全部一致,其中作業系統和Docker版本如下:

[操作系统]

System : Linux
Kernel : 3.16.0-4-amd64
Version : 8.5
Arch : x86_64

[Docker版本]

Docker version 1.12.1, build 6b644ec
登入後複製
初步分析

此集群的宿主配置一致,故障現像也一致,疑點有三:

1、Docker版本與宿主核心版本不相容

#三台宿主的環境本來一致,但1台穩定跑服務2個月才死機,1台跑服務1個月後死機,另外1台上線跑服務一周便會死機。
發現每台宿主除了死機的異常日誌,平常也有相同報錯日誌:

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”
登入後複製

根據上面提示,應該是作業系統核心版本對該版本的Docker不支援某些功能所導致。不過在搜尋引擎上搜尋這並不影響Docker的功能,更不加影響系統穩定性。

例如:

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以來就有的問題,1.12.3修復了。

例如Github上有人回覆:

“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.”
登入後複製

但這裡所說的都只是v1.12.2版本就能修復的問題,我們升級Docker版本後發現當機依舊。

於是,我們接著透過各種Google確認了很多與我們存在相同故障現象的問題,初步確認故障與Docker的相關性,又根據官方issue初步確認Docker版本與系統內核版本不相容可引發宕機的關聯性;接著,透過官方的changelog和issue確認宿主所使用Docker版本與系統核心版本不相容問題,出於嘗試心理,我們把Docker版本升級到1.12.2後,未出意外仍出現死機。

2.使用Linux bridge方式改造宿主網路卡可能觸發bug

找了那台宿主跑服務一週就會死機的宿主,停止運作Docker,只改造網絡,穩定跑了一週未發現異常。

3.使用pipework為Docker容器配置IP可能觸發bug

由於給容器分配IP時我們採用了開源的pipework腳本,因此懷疑pipework的工作原理有bug,所以嘗試不使用pipework分配IP位址,發現宿主仍出現當機。

於是初步排查陷入困境,眼看著宿主每月至少死機一次,非常鬱悶。

故障定位

因为还有线上业务在跑,所以没有贸然升级所有宿主内核,而是期望能通过升级Docker或者其它热更新的方式修复问题。但是不断的尝试并没有带来理想中的效果。

直到有一天,在跟一位对Linux内核颇有研究的老司机聊起这个问题时,他三下五除二,Google到了几篇文章,然后提醒我们如果是这个 bug,那是在 Linux 3.18 内核才能修复的。

原因:

从sched: Fix race between task_group and sched_task_group的解析来看,就是parent 进程改变了它的task_group,还没调用cgroup_post_fork()去同步给child,然后child还去访问原来的cgroup就会null。

不过这个问题发生在比较低版本的Docker,基本是Docker 1.9以下,而我们用的是Docker1.11.1/1.12.1。所以尽管报错现象比较相似,但我们还是没有100%把握。

但是,这个提醒却给我们打开了思路:去看内核代码,实在不行就下掉所有业务,然后全部升级操作系统内核,保持一个月观察期。

于是,我们开始啃Linux内核代码之路。先查看操作系统本地是否有源码,没有的话需要去Linux kernel官方网站搜索。

下载了源码包后,根据报错syslog的内容进行关键字匹配,发现了以下内容。由于我们的机器是x86_64架构,所以那些avr32/m32r之类的可以跳过不看。结果看下来,完全没有可用信息。

/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)
登入後複製

最后,我们还是下线了所有业务,将操作系统内核和Docker版本全部升级到最新版。这个过程有些艰难,当初推广这个系统时拉的广告历历在目,现在下线业务,回炉重造,挺考验勇气和决心的。

故障处理

下面是整个故障处理过程中,我们进行的一些操作。

升级操作系统内核

对于Docker 1.11.1与内核4.9不兼容的问题,可以删除原有的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
#启动
nohupdocker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock-s=devicemapper&
登入後複製

这里需要注意的是,Docker安装方式在不同操作系统版本上不尽相同,甚至相同发行版上也有不同,比如原来我们使用以下方式安装Docker:

apt-get install docker-engine
登入後複製

然后在早些时候,还有使用下面的安装方式:

apt-get install lxc-docker
登入後複製

可能是基于原来安装方式的千奇百怪导致问题丛出,所以Docker官方提供了一个脚本用于适配不同系统、不同发行版本Docker安装的问题,这也是一个比较奇怪的地方,所以Docker生态还是蛮乱的。

验证
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内核升级到1.19,Linux内核升级到3.19后,保持运行至今已经2个月多了,都是ok的。

总结

这个故障的处理时间跨度很大,都快半年了,想起今年除夕夜收到服务器死机报警的情景,心里像打破五味瓶一样五味杂陈。期间问过不少研究Docker和操作系统内核的同事,往操作系统内核版本等各个方向进行了测试,但总与正确答案背道而驰或差那么一点点。最后发现原来是处理得不够彻底,比如升级不彻底,环境被污染;比如升级的版本不够新,填的坑不够厚。回顾了整个故障处理过程,总结下来大概如下:

回归运维的本质

运维要具有预见性、长期规划,而不能仅仅满足于眼前:

  1. 緊急應變計畫:針對可能係統上線後可能發生的故障類型進行總結,並提供緊急應變計畫。
  2. 搶通業務:優先搶通業務,再處理故障。
  3. 應用版本選擇等技術選型問題:在環境部署和應用選型時需要特別注意各種版本,最好採用社區通用或公司其他同學已經測試或驗證可行的版本。
  4. 作業系統核心:要合理升級內核,只有定位到確定版本存在的問題,才能有針對性的升級內核版本,不然一切徒勞無功。
  5. 在我們原來的設計中,不同用戶調度器針對同一個容器同時操作沒有加鎖機制,也沒有按照對源判斷原則,也曾出現過遷移失敗的情況。遷移時判斷遷往的目的位址是否為本地位址,如果是本地位址應該拒絕操作的。這個問題不知你是否覺得眼熟。我倒是發現,很多人程式開發過程中,就常常不對輸入來源或操作的來源狀態進行判斷,結果出現了各種bug。
Google的能力

#在處理這個故障的過程中,會發現不同人使用Google搜出來的東西並不一樣,為什麼呢?我覺得這就是搜尋引擎槽點滿滿,或者說靈活之處。像這次的故障,我用Linux Docker Unable to handle kernel NULL pointer dereference去搜索,與別人用”Unable to handle kernel NULL pointer dereference”結果就不同。原因在於增加了””之後,搜尋更加精確了。關於Google的正確開啟方式。

以上是一個困擾了我半年的難題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:linuxprobe.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板