一台 CentOS 7.9 的云服务器因为调优内核参数重启后直接 panic,控制台显示 Kernel panic - not syncing: Attempted to kill init!,卡在 emergency mode。
不是磁盘满,也不是 SELinux 挡路。是昨天手痒改了 /etc/sysctl.conf 里的 和 ,sysctl -p 愉快加载,然后顺手 reboot。重启一时爽,修系统火葬场。
云厂商控制台连上 VNC,一眼看到内核日志刷屏:sysctl: Unknown parameter 'net.ipv4.tcp_tw_recycle'。不是语法错误,是那个参数名在当前内核版本(3.10.0-1160)里被彻底移除了。或者更狠——把 设成了 -1,系统直接拒绝加载。
这时候别想用 ssh 连回去,它连网卡都没初始化。得抢在 initramfs 解压完、root 切换前,用 GRUB 调出单用户模式。按 e 编辑启动项,末尾加 rd.break enforcing=0 或 init=/bin/bash,取决于你用 CentOS 还是 Anolis。别抄网上那种加 1 或 s 的旧套路,云环境的 initramfs 已经不是当年的裸机 initrd 了。
单用户模式急救:抢在 rootfs 挂载前动手
GRUB 菜单出来那几秒,手得快。按 e 进编辑模式,找到 linux16 或 linux 开头那行——不同发行版叫法不一样,CentOS 7 是 linux16,Anolis 8 直接是 linux。行尾加 rd.break 加 enforcing=0,然后 Ctrl+x 启动。这招对带 dracut 的系统最稳,能落在 initramfs 的 shell 里,rootfs 还没挂。
有个坑:云厂商的定制内核有时会忽略 rd.break。碰上这种情况,换 init=/bin/bash,但得注意后面补 rw,不然根文件系统是只读的,你连 vi 都改不了。linux16 /vmlinuz-xxx root=/dev/vda1 ro crashkernel=auto init=/bin/bash rw——对,就是把 ro 改成 rw,或者直接在行尾加 rw。Anolis 7 上亲测有效,但 CentOS 8 有些版本会卡在 systemd 那里,得进 rescue target。
进去之后第一件事:(如果没自动挂载读写),然后 。别急着改 /etc/sysctl.conf,先 cp /etc/sysctl.conf /etc/sysctl.conf.bak.$(date +%Y%m%d)。再 ,把最后加的那几行注释掉。如果你记不清改了哪几行,grep -n '^[a-z]' /etc/sysctl.conf | tail -20 通常能锁定最近追加的参数。
有个同事更绝——他改 写成了 net.core.rmem_max = -1,系统直接 panic。单用户模式下 sed -i 's/rmem_max = -1/rmem_max = 212992/' /etc/sysctl.conf 一行搞定。
如果连 GRUB 菜单都看不到(比如某些云主机的串口控制台延迟太高),那就走救援模式。挂载同版本的系统 ISO,选 Rescue installed system,它会扫描磁盘找到现有的 rootfs。这时候 再重复上面的操作。区别是救援模式下网络不一定通,但你只是改个本地文件,不碍事。
改完别急着 reboot。先 sysctl -p 试试,看有没有报错。如果某个参数报 ,那就是值越界了,回头查内核文档里该参数的合法范围。确认无误后 exit 退出 chroot,reboot -f 强制重启。看着内核日志一路刷到 login 提示符,那感觉比修好一块磁盘还踏实。

引导修复:GRUB 命令行和 dracut 重建
改完 /etc/sysctl.conf 后连 GRUB 菜单都不见了?别慌——不是内核没了,是 initramfs 里缺模块,或者 dracut 没把新参数适配进启动流程。CentOS 7/Anolis 7 默认用 dracut,但你手动改过 /etc/default/grub 里的 ,又没跑 ,它就还指着旧配置启动。
GRUB 命令行里临时救火
开机卡黑屏?按 Esc 或 Shift(云控制台常需连按)进 GRUB 命令行。输入:
linux16 /vmlinuz-$(ls /boot/vmlinuz-* | tail -n1 | sed 's/.*vmlinuz-//') root=/dev/vda1 ro rd.break init=/bin/bash rw
initrd16 /initramfs-$(ls /boot/initramfs-* | tail -n1 | sed 's/.*initramfs-//')
boot
注意路径得自己 ls /boot 确认,云服务器常见 /dev/vda1,但阿里云部分实例是 /dev/xvda1。这招能让系统跳过 dracut 的完整校验,直接 drop 到 shell。
dracut 重建不能只靠 -f
进系统后别急着 dracut -f。先 lsinitrd /boot/initramfs-$(uname -r).img | grep sysctl,看有没有 /etc/sysctl.conf 被打进去了。没有?说明 dracut 配置漏了 install_items+=" /etc/sysctl.conf "。补完再 dracut --force --kver $(uname -r),最后 。Anolis 8.8 上试过,不加 --regenerate-all 会沿用缓存的老 initramfs,等于白干。
修完重启,看着 dracut 日志从 变成 dracut: OK,才算真稳了。

稳定性验证:压测跑完日志翻透才算数
系统总算正常启动了,GRUB 菜单没再乱跳,dracut 也闭嘴了。这时候最容易干的事就是长舒一口气,觉得“稳了”,然后关掉终端。慢着。内核参数调优真正的鬼门关从来不在开机那几分钟——等你跑了一小时 stress 测试,系统没翻车,那才算真稳了。
先来最基本的。登录后直接跑 sysctl -p,不带任何参数。这个命令会把 /etc/sysctl.conf 里的每一条参数重新加载一遍,任何格式错误、值越界、不存在的内核参数,都会在这里原形毕露。我见过最坑的是 写成了 net.ipv4.tcp_tw_reuse = 1 后面跟了个看不见的 tab 空格,sysctl -p 直接报“Invalid argument”,排查了二十分钟才发现多了一个不可见字符。
确认没报错后,别急着关终端。看一眼 /proc/sys/ 下面对应的路径,确认值确实写进去了。 看看是不是你设的 1, 是不是 10。sysctl 不会骗你,但手滑写错分区的情况时不时就会发生。
压力不是测一次就够了
这时候要上压力。云服务器的话,我习惯用 stress 和 ab 双管齐下。
# 安装 stress
yum install -y stress # CentOS/Anolis
apt install stress # Ubuntu/Debian
# 跑 CPU + 内存压力,持续 5 分钟
stress --cpu 4 --io 2 --vm 2 --vm-bytes 512M --timeout 300s
另一个窗口开着 dmesg -w,盯着看有没有 OOM killer、soft lockup、hung task 之类的关键字。调优 或者 这类参数时,OOM killer 的触发时机可能完全变了——原本稳定的服务在压力下突然被 kill,而你只顾着看性能数据,忽略了 dmesg 里那一行 kill 日志。
网络相关的参数,用 ab 或者 wrk 压一下本机。比如你调过 和 :
压完后看 ss -s 有没有异常堆积的 TIME_WAIT 或者 CLOSE_WAIT。如果调了 但没配合 (已移除),TIME_WAIT 可能不降反升——那是参数之间的依赖关系没搞明白,压测一跑就暴露了。
注意
sysctl -p 测试,并重启前备份 /etc/sysctl.conf,避免生产事故。说实话,我见过最隐蔽的坑是在 Anolis 8.6 上调了 kernel.randomize_va_space = 0 关闭 ASLR,压测时一切正常,但部署 Java 应用后隔三差五段错误——因为 JVM 的某些内存布局依赖 ASLR 的随机化,关掉后反而触发了 HotSpot 的 bug。这种问题 dmesg 里只有一行 ,你不翻日志根本想不到是内核参数的锅。
压测完别急着切流量。把参数值、压测结果、dmesg 关键行都记下来,跟改之前的基线数据比一比。 从 30 降到 10 可能让应用响应变快了,但如果 swap 使用量降为零而 OOM 频率上升了,那就得不偿失。
参数生效、服务跑稳、日志不再刷红字警告——到这一步,调优才算真正落进生产环境里。别急着关终端,再盯几分钟负载曲线,比啥都踏实。
说到底,内核调优跟修车一样——改完参数不备份、不压测就敢重启,那是等着半夜被告警吵醒。这趟折腾下来,我算是长记性了:/etc/sysctl.conf 改完第一件事是备份,第二件事是 sysctl -p,第三件事才是泡杯咖啡等压力测试跑完。





评论