系统不能正常启动,是常见的一类问题。关于这类问题,我们经常会遇到用户这样的描述:“我们就升配了一下内存/CPU,就启不来了”,“我们就打了个补丁,就启不来了”。甚至有用户说,“我们什么都没干,就重启了一下,就启不来了”。
处理这类问题,有一个定律,就是认真沟通问题发生的背景,但是千万不要相信用户对问题原因的判断。理由很简单,系统启动不了这件事情,可以和系统重启之前任何有意,或者无意的,对系统的更改有关系。而这些更改,往往都不是用户能感知到的。
曾经就遇到系统中boot目录被移动到home目录下导致系统不能启动的问题,用户还信誓旦旦的说他们什么都没有改。所以处理这类问题,基本上还是具体问题具体分析。今天借助一个实际的案例,跟大家交流一下处理这类问题的方法和心得。
“业务现在停着,优先级提到最高”
这节的标题,就是这个问题的“开场白”。因为系统重启之后不能正常启动,所以这类问题往往都影响很大,比较紧迫。经过简单的沟通,基本了解了问题发生的背景以及症状:升级了系统之后,ECS不能正常启动。
录播启动过程
系统在启动过程中,有很多启动日志会输入到控制台。因为刷新太快,所以只用眼睛盯着看,往往会错过关键信息。所以录播启动过程,可以方便重复观看,而且也避免了重复启动机器可能造成的磁盘数据损坏问题。
下边是这个问题的关键日志。系统启动过程中,在第一行A start job is running for...持续了三分钟,然后输出第二行和第三行的夯机日志。
livecd
系统启动不了,用livecd启动,然后挂载系统盘来检查dmesg和messages日志,做rpm包校验,以及检查系统启动配置选项(臭名昭著的fstab问题),几乎是处理这类问题的最常用方法。
可惜的是,这个问题里,使用以上方法,短时间内没有办法找出有用的信息。fstab没有挂载多余的块设备,dmesg和message没有有用的错误信息(甚至在控制台上出现的softlockup日志,也没有被写入这两个日志)。用rpm做包校验,发现更改不少。在“业务现在停着,优先级提到最高”这样的压力下,逐个分析rpm发现的更改,显然是不现实的。
“怎么样,有发现么”
在排查问题过程中,“any update?”,或者“怎么样,有发现么”,可能是让售后技术支持工程师最心惊胆战的一句话^_^。
“我抓了一个dump,我先拉下来看看”
因为使用livecd静态分析系统不可能快速定位问题,所以这个时候只能抗住压力,抓一个core dump来看一下。
有用的日志
我们在使用crash工具分析core dump的时候,log命令应该是每次必然会用到的。这个命令可以打印出缓存在内存里的启动日志和系统日志。下边是从日志拷贝出来的,和图一中softlockup相对应的调用栈记录。基本上下边的记录,说明一件事情,那就是这个线程在D(uninterruptible)状态等了120秒钟。
系统线程调用栈
看Hang机问题的core dump,有一条很有用的经验,就是快速查看系统里每个线程(大概)都在做什么。使用foreach bt命令,可以输出整个系统里的线程调用栈。而每个调用栈,会告诉你当前这个线程正在处理的什么工作。
快速浏览整个系统的线程调用栈,发现若干和下边这个记录类似的调用栈。这个调用栈显然在处理缺页异常(page fault)。而在调用栈的最上边出现了函数vmpressure。
其实就算我对这个函数具体怎么实现的没有什么概念,就凭这个函数的名字,下一步需要看的也是系统内存的使用情况。
备注:大多数情况下,从整个系统里,运行的成百上千个线程调用栈里找到有问题的调用栈其实很简单,就是着重查看调用栈比较“长”,而且调用栈上函数比较“特别”的线程。记住一句话:幸福的“人生”是相似的,不幸的“人生”却各有各的不同。
奇葩的内存使用情况
检查内存分配情况,主要使用crash工具的ps和kmem命令。前者可以用来分析进程内存使用情况,后者可以用来查看物理内存使用情况。
在这个例子中,使用ps命令,并没有发现使用内存过分的进程。但使用kmem查看物理内存的时候,却发现了非常奇葩的状况。
上图中,我们可以看到,启动Hang机的时候,系统内存确实已经非常紧张了。这与进程没有使用太多内存这个情况显然是矛盾的。那问题就在于,上图中的huge pages。我们可以看到,系统里有7.4G的huge pages内存,而这7.4G的huge pages都是空闲的。
猜测与验证
大胆猜测,快速求证。根据以上的数据分析,大概可以猜到,这个问题可能和huge pages的设置有关。Linux系统可以在sysctl.conf文件内配置内核huge pages的初始值,而对应的变量是vm.nr_hugepages。而在core dump里,我们很容易验证。下图可以看到,客户配置了2M的huge page,一共有3803个pages,总共刚好7.4G。这个几乎占用了客户所有的物理内存。
进一步验证这个问题,在测试环境里,把vm.nr_hugepages调整到一个超过物理内存大小的值,重启系统,也看到系统启不来的症状。这基本验证了我们的猜测。用livecd再一次进入客户系统,把客户的设置改成0,重启系统,问题解决。
一点理论
简单来讲,Huge pages是Linux内核,把大页面(相对于4K小页面)物理内存的使用,输出(export)到用户空间的一种机制,这种机制简称hugetlbfs。大页面有很多好处,比如减少page table数量,节省tlb等等。默认情况下,就算没有hugetlbfs机制,内核也会使用大页面,而这些页面的统计值,可以通过/proc/meminfo里DirectMap*查看。hugetlbfs的作用是把这种能力输出到用户空间,以便进程可以通过api来直接使用大页面。
使用nr_hugepages可以(persistent)预分配huge pages。这个值一旦确定,内核就会预分配固定数量的页面专门给hugetlbfs机制使用,内核不会因为缺少内存而把这里预分配的大页面拿出来另作他用。这是这个问题发生的根本原因。
hugetlbpage详细的解释请参考:
https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
结束语
从启动问题排查的方法论上来讲,我们需要具体问题具体分析,根据不同的状况,采用不用的处理方法。从调试技术上来说,core dump分析可能是生产环境中最为强大调试的工具。这种调试方法不用重启客户机器,不用在生产环境中部署任何脚本或工具,抓取数据急时,完整,准确。