做者介绍html
林伟壕,网易游戏资深运维工程师。现任职于网易游戏,从事游戏运维相关工做;曾就任于中国电信,负责数据网络维护、网络安全防护等工做。深刻研究Linux运维、虚拟化等,现致力于企业级网络安全防御自动化体系构建。linux
相对物理环境,虚拟化环境更加错综复杂。以前弄KVM虚拟化时常常遇到好屡次莫名其妙的网络故障,查出来的缘由要么是操做系统内核bug,要么是KVM与操做系统内核版本不兼容,最后是经过升级操做系统内核或者KVM版本修复了。没想到,转型到Docker后,又重蹈覆辙了。git
本文将介绍一个困扰笔者近半年的虚拟化环境下的疑难故障,最后排查出来的故障缘由和修复手段也让人哭笑不得。并不是由于这个过程有多复杂,而是分享一个心理历程,思考在遇到故障时如何兼顾业务和技术,如何正确使用搜索引擎。github
咱们有一套高性能代理集群,以前内测阶段运行稳定,结果等正式上线后不到半个月,提供代理服务的宿主忽然连续不断死机,致使宿主上的全部服务所有中断。docker
故障时宿主直接死机,没法远程登陆,机房现场敲键盘业务反应。因为宿主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,而后引发一系列调用栈报错,最后死机。架构
为进一步分析故障现象,首先须要理解这套高性能代理集群的架构。app
单个节点,是在万兆网卡的宿主机上跑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
该集群的宿主配置一致,故障现象也一致,疑点有三个:
一、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修复了。参考https://github.com/docker/docker/ issues/24211
好比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的相关性:
http://serverfault.com/questions/709926/bug-unable-to-handle-kernel-null-pointer-dereference-at-on-google-compute-eng
https://support.mayfirst.org/ticket/10872
又根据如下官方issue初步确认Docker版本与系统内核版本不兼容可引起宕机的关联性:
https://github.com/docker/docker/issues/19910
接着,经过官方的changelog和issue确认宿主所使用Docker版本与系统内核版本不兼容问题:
https://github.com/docker/docker/blob/v1.12.2-rc1/CHANGELOG.md
出于尝试心理,咱们把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官方网站搜索。
“`
apt-cache search linux-image-3.16.0-4-amd64
apt-get source linux-image-3.16.0-4-amd64
“`
下载了源码包后,根据报错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和操做系统内核的同事,往操做系统内核版本等各个方向进行了测试,但总与正确答案背道而驰或差那么一点点。最后发现原来是处理得不够完全,好比升级不完全,环境被污染;好比升级的版本不够新,填的坑不够厚。回顾了整个故障处理过程,总结下来大概以下:
运维要具备预见性、长期规划,而不能仅仅知足于眼前:
在处理这个故障的过程当中,会发现不一样人使用Google搜出来的东西并不同,为何呢?我以为这就是搜索引擎槽点满满,或者说灵活之处。像此次的故障,我用Linux Docker Unable to handle kernel NULL pointer dereference去搜索,与别人用”Unable to handle kernel NULL pointer dereference”结果就不一样。缘由在于增长了””以后,搜索更加精确了。关于Google的正确打开方式,建议参考:
http://www.yunweipai.com/archives/18950.html