开机就夯机

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

系统不能正常启动,是常见的一类问题。关于这类问题,我们经常会遇到用户这样的描述:“我们就升配了一下内存/CPU,就启不来了”,“我们就打了个补丁,就启不来了”。甚至有用户说,“我们什么都没干,就重启了一下,就启不来了”。

处理这类问题,有一个定律,就是认真沟通问题发生的背景,但是千万不要相信用户对问题原因的判断。理由很简单,系统启动不了这件事情,可以和系统重启之前任何有意,或者无意的,对系统的更改有关系。而这些更改,往往都不是用户能感知到的。

曾经就遇到系统中boot目录被移动到home目录下导致系统不能启动的问题,用户还信誓旦旦的说他们什么都没有改。所以处理这类问题,基本上还是具体问题具体分析。今天借助一个实际的案例,跟大家交流一下处理这类问题的方法和心得。

“业务现在停着,优先级提到最高”

这节的标题,就是这个问题的“开场白”。因为系统重启之后不能正常启动,所以这类问题往往都影响很大,比较紧迫。经过简单的沟通,基本了解了问题发生的背景以及症状:升级了系统之后,ECS不能正常启动。

录播启动过程

系统在启动过程中,有很多启动日志会输入到控制台。因为刷新太快,所以只用眼睛盯着看,往往会错过关键信息。所以录播启动过程,可以方便重复观看,而且也避免了重复启动机器可能造成的磁盘数据损坏问题。

下边是这个问题的关键日志。系统启动过程中,在第一行A start job is running for...持续了三分钟,然后输出第二行和第三行的夯机日志。

dffbc52331e462a9e6fede1aca1be9bb04ce1a1a

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秒钟。

3a2ef72dcfed5932d4b2359ffedbdba89a5647cd

系统线程调用栈

看Hang机问题的core dump,有一条很有用的经验,就是快速查看系统里每个线程(大概)都在做什么。使用foreach bt命令,可以输出整个系统里的线程调用栈。而每个调用栈,会告诉你当前这个线程正在处理的什么工作。

快速浏览整个系统的线程调用栈,发现若干和下边这个记录类似的调用栈。这个调用栈显然在处理缺页异常(page fault)。而在调用栈的最上边出现了函数vmpressure。

其实就算我对这个函数具体怎么实现的没有什么概念,就凭这个函数的名字,下一步需要看的也是系统内存的使用情况。

a40197165a2fe1eb7dc37052541df40e359ecc98

备注:大多数情况下,从整个系统里,运行的成百上千个线程调用栈里找到有问题的调用栈其实很简单,就是着重查看调用栈比较“长”,而且调用栈上函数比较“特别”的线程。记住一句话:幸福的“人生”是相似的,不幸的“人生”却各有各的不同。

奇葩的内存使用情况

检查内存分配情况,主要使用crash工具的ps和kmem命令。前者可以用来分析进程内存使用情况,后者可以用来查看物理内存使用情况。

在这个例子中,使用ps命令,并没有发现使用内存过分的进程。但使用kmem查看物理内存的时候,却发现了非常奇葩的状况。

4fe11bb1a108f5bae019a6c63a9049fd7ee2dfdc

上图中,我们可以看到,启动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。这个几乎占用了客户所有的物理内存。

fc564db94428afc58712074c07e2ec3db01d625d

进一步验证这个问题,在测试环境里,把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分析可能是生产环境中最为强大调试的工具。这种调试方法不用重启客户机器,不用在生产环境中部署任何脚本或工具,抓取数据急时,完整,准确。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
6月前
|
存储 弹性计算 运维
开机问候语
【4月更文挑战第29天】
42 0
|
6月前
|
Linux
关机重启命令
【2月更文挑战第27天】关机重启命令。
86 2
|
6月前
|
安全 定位技术 Windows
Win10电脑出现No Bootable Device且无法开机或开机后蓝屏无限重启的多个解决方法
Win10电脑出现No Bootable Device且无法开机或开机后蓝屏无限重启的多个解决方法
1015 1
|
Linux
linux设置开机服务自动启动/关闭自动启动命令 chkconfig
linux设置开机服务自动启动/关闭自动启动命令 chkconfig
349 0
|
网络协议 安全 Linux
2.2.3开机流程中的BIOS与UEFI开机检测程序
2.2.3开机流程中的BIOS与UEFI开机检测程序
143 0
2.2.3开机流程中的BIOS与UEFI开机检测程序
BIOS里如何设置U盘启动?硬盘怎么设置第一启动项?
查找您当前使用的主板品牌或笔记本品牌的U盘启动快捷键,然后重启电脑。(选择热键前,请先插入启动U盘) (注意:苹果笔记本,开机/重启时按住“option”键[即Alt键]不放,进入选择启动方式。有些笔记本得加上FN键)
1434 0
|
NoSQL 关系型数据库 MySQL
设置系统开机服务自动启动 | 学习笔记
快速学习设置系统开机服务自动启动
246 0
|
Shell Linux
linux 开机自动启动任务
centos 开机任务,开机执行命令
497 0
|
关系型数据库 Shell Linux
Linux设置开机服务自动启动
[root@localhost ~]# chkconfig --list     显示开机可以自动启动的服务[root@localhost ~]# chkconfig --add ***  添加开机自动启动***服务[root@localhost ~]# chkconfig --del ***  删...
1663 0
|
Linux
linux中将程序加入到开机自动启动
如果将在linux中将命令或者程序设置为开机自动启动,只需要进入到将对应命令加入到/etc/rc.d/rc.local文件里即可,如下 打开文件,vi /etc/rc.d/rc.local   #!/bin/sh## This script will be executed *after* all the other init scripts.
1335 0