☰
CentOS 7.9内核升级:3.10到4.19实战与回滚调优
2026/10/28 3:12:17 网站建设 项目流程

1. 先想清楚:3.10 到 4.19 到底升级了什么,值不值得动

把一台跑着 CentOS 7.9 的机器从 3.10 内核换到 4.19,这件事在运维圈里属于"看起来一句话,干起来一整天"的活。很多人第一次动这个念头,往往不是因为喜欢折腾,而是被现实逼的:新采购的 NVMe 盘认不出来,新网卡插上lspci有、ip a没有,容器跑着跑着 overlayfs 出问题,或者某个业务软件在安装脚本里硬性检查uname -r的主版本号。这些问题在 3.10 上你想修都没地方下手,因为内核太老了。

CentOS 7.9 自带的是 3.10.0-1160 系列内核,基线可以追到 2013 年前后的 3.10 主线。需要注意的是,上游 3.10 和红帽维护的这个 3.10 并不是一回事——他们往里回移了大量 4.x 的特性,比如 tcp_bbr 模块、部分 eBPF 能力、新的文件系统挂载选项,所以你不能简单地拿"3.10 就是老古董"来概括它。但回移终究是回移,接口是残缺的,文档是缺失的,社区案例是搜不到的,你遇到的问题在 Google 上基本只有红帽自己的 KB 有答案。这才是真正难受的地方。

4.19 是 2018 年底发布的一个长期支持版本,它的价值不在于"版本号大",而在于它把 4.x 这一代内核的完整能力一次性给全了:完整的 eBPF 和 bpftool 工具链、overlayfs 的 metadata-only copy-up、更成熟的 cgroup v2、XFS 的在线修复与 reflink、更好的 NVMe 多队列支持、XDP 与 AF_XDP、以及一整套针对侧信道攻击的缓解机制默认开启。对一台还在服役的老机器来说,这些能力很多是用不上的,但只要你的业务沾上容器、高性能网络、新硬件,就至少会撞上其中两三条。

1.1 3.10 上那些绕不过去的具体短板

先说硬件。这是最直接的推动力。3.10 的 PCI ID 表停在 2013 到 2019 年之间,中间新增的设备只能靠回移补丁覆盖,覆盖面有限。你买一块 2020 年之后出的企业级 NVMe,插上之后dmesg里可能只有一句nvme nvme0: I/O 12 QID 0 timeout,或者干脆连nvme控制器都起不来。新网卡同理,芯片在,但驱动的 ID 表里没有,lspci -k显示 "Kernel driver in use: 无"。

再说容器和隔离。3.10 的 cgroup 只有 v1,overlayfs 在很多细节上不完善,最典型的是 copy-up 行为——3.10 的 overlayfs 在修改一个文件时会触发整个文件的数据拷贝,而 4.19 引入了 metadata-only copy-up,只拷贝元数据、数据走原文件引用。这个差异在大镜像、多层构建的场景下能差出几倍的启动时间。如果你的机器上跑着容器平台,这一条本身就值回票价。

网络方面,3.10.0-1160 确实回移了 tcp_bbr 模块,你可以modprobe tcp_bbr然后设net.ipv4.tcp_congestion_control=bbr。但这个 BBR 是早期版本,缺少后来针对浅缓冲、丢包误判的修正,也没有 4.19 里那套和 fq qdisc 配合得更好的实现。另外 3.10 的ss -i输出的信息量比 4.19 少一大截,排查拥塞问题时很不方便。

还有一处容易被忽略:3.10 的 eBPF 只能做很基础的事情,bcc工具集里一大半脚本在上面跑不起来或者跑出来数据不准。你要是想用 eBPF 做无侵入的延迟观测,3.10 基本是死路,4.19 才勉强算能用。

1.2 4.19 换来的实际收益清单

我把这些收益按"能立刻感受到"和"长期受益"分了一下,方便你判断自己的场景该不该动:

收益项具体表现感受强度
新硬件支持新 NVMe、网卡、USB 控制器、CPU 微码识别立刻,插上就用
overlayfs 改进metadata-only copy-up,镜像层操作明显变快立刻,容器密集场景明显
网络栈BBR 完整实现、fq/fq_codel 成熟、XDP 可用中期,压测能看出来
XFS 增强reflink、在线修复、更细的 quota中期,取决于是否用得上
cgroup v2统一层级、更精细的内存控制长期,但受用户态限制
安全缓解KPTI、retpoline、init_on_alloc 等默认开启长期,无感但重要
观测能力perf、bpftrace、ftrace 功能完整中期,排障效率提升

需要泼一盆冷水的是 cgroup v2。4.19 内核里 cgroup v2 的控制器已经比较完整了,但 CentOS 7.9 配的 systemd 是 219 版本,它根本不认识 unified hierarchy。你就算在内核启动参数里加systemd.unified_cgroup_hierarchy=1,systemd 也会直接罢工。所以升级内核并不等于你能用上 cgroup v2,这一条基本可以划掉,除非你连用户态一起换。

1.3 代价盘点:升级前必须认的账

升级内核最怕的不是升不上去,是升上去之后三个月才发现某个东西不对。所以动手前要把代价列清楚。

第一笔账是内核 ABI。原来的 3.10 由发行方维护一套稳定的 kABI,第三方驱动厂商只要对着这套 ABI 编译,就能跨小版本使用。换成 4.19 之后,这套保证没有了,任何依赖 kABI 的闭源模块都必须按新内核重新编译。你机器上装了硬件 RAID 卡管理工具、某些监控 agent、某些安全 agent,它们带的内核模块很可能就是这一类。

第二笔账是用户态工具链的老化。CentOS 7.9 的 glibc 是 2.17,iproute2 是 3.10 时代的版本,util-linux 是 2.23。4.19 内核暴露的一些新接口,老工具根本不认识。比如你想用ip link配置某些新属性,它会报 "RTNETLINK answers: Invalid argument",原因不是内核不支持,是工具太老不会拼这个 netlink 消息。同理,bpftool需要自己编译,perf要从对应版本的源码树里编,系统自带的那个版本对不上。

第三笔账是sysctl 键的变化。3.10 时代的一堆/proc/sys/net/ipv4/conf/*/xxx参数,在 4.19 里有的被移除、有的被重命名、有的语义变了。升级前建议执行一遍sysctl -a > /tmp/sysctl-before.txt,升级后再导一份对比,把消失的键找出来,看看你的调优脚本里有没有引用它们。

第四笔账是排障经验失效。你团队里那套"看这个日志、调这个参数"的肌肉记忆,有一部分要重学。这不算技术成本,但确实是真实成本。

1.4 三条技术路线对比与选型建议

实际可走的路不止一条,我把常见的四种列出来横向比一下:

路线工作量可控性可回滚性长期维护成本适用场景
换成新大版本的发行版最大高差(要重装)最低机器数量多、有统一编排
用 ELRepo 装现成 RPM小中好(grub 切回)中只要 4.x 能力,不锁小版本
源码编译并打 RPM中最高好高(自己跟补丁)必须精确锁 4.19
不动内核,改用容器/用户态方案视方案中好中只是想跑新软件

这里有个关键事实要说明白:ELRepo 这个第三方仓库提供kernel-ml(跟随主线)和kernel-lt(跟随长期支持)两个系列。kernel-lt在不同时期对应的版本是不一样的,历史上长期是 4.4,后来切到过 5.4。也就是说,你现在去装kernel-lt,拿到的很可能不是 4.19。如果你的目标就是精确的 4.19,用yum --showduplicates list翻历史版本、或者去仓库的归档目录里找旧包,都比直接装要靠谱;实在找不到,就走源码编译那条路,可控性最高。

我个人的判断是:如果只是想要 4.x 的能力而不在乎具体小版本,走 ELRepo 最省事;如果是被某个软件硬性要求 4.19,或者需要在几十台机器上保证版本完全一致,那就老老实实自己编 RPM,一次性把 spec 和构建环境固化下来,后面批量分发反而更轻松。

2. 动手前的摸底:把风险摸透再谈变更

很多人栽跟头不是因为技术不够,是因为信息不全。升级前花两小时做摸底,能省掉后面两天的救火。这一节讲的就是怎么在动手前把机器的底细摸清楚。

2.1 主机与业务清单盘点

先建立一份"这台机器上到底跑了什么"的清单。基础信息用几条命令就能拉出来:

uname -r rpm -qa | grep -E '^kernel' | sort cat /etc/centos-release lscpu | head -20 free -g df -hT

重点是/boot的空间。CentOS 7 默认给/boot分 1GB,这个大小在只有一两个 3.10 内核的时候很宽裕,但 4.19 的内核镜像加 initramfs,一套下来 100MB 到 200MB,你要是保留旧内核再加一个新内核,很容易撑爆。装到一半报 "No space left on device" 是很常见的翻车点,而且是在最不该出问题的阶段。

然后是业务侧。把这几类东西单独列出来,逐个确认它们有没有内核态组件:

  • 容器运行时及其存储驱动
  • 数据库和它的 IO 相关配置
  • 监控 agent、日志采集 agent
  • 安全 agent、入侵检测组件
  • 硬件管理工具(RAID 卡、网卡管理套件)
  • 加密加速、国密卡之类的专用硬件驱动

判断方法很直接,看lsmod里那些不在标准内核树里的模块:

lsmod | awk 'NR>1 {print $1}' | while read m; do f=$(modinfo -F filename "$m" 2>/dev/null) case "$f" in */extra/*|*/updates/*|*/weak-updates/*) echo "$m -> $f";; esac done

凡是路径里带extra、updates、weak-updates的,基本都是树外模块,升级后大概率要重装或重编。这份名单一定要在动手前确认清楚,同时把对应的安装包名也记下来,方便升级后重新安装。

2.2 驱动与内核模块排查(这一步最容易翻车)

硬件驱动的排查要具体到每一块网卡、每一个存储控制器。用lspci -k能看到设备和它当前绑定的驱动:

lspci -k | grep -E -A3 'Ethernet|RAID|SCSI|NVMe|VGA'

对每块网卡,再看一眼驱动名和版本:

ethtool -i eth0

输出里的driver、version、firmware-version三项都要记下来。如果version显示的不是内核自带的版本号(比如显示一个厂商自己的编号),说明这是树外驱动,4.19 里很可能没有对应实现,需要提前找厂商要新版本,或者确认该硬件在 4.19 的内核树里有原生支持。

固件也要留意。CentOS 7.9 的固件包是linux-firmware,安装路径是/lib/firmware。这一块在内核升级时通常不用动,因为它是用户态文件,新内核照样读。但如果你的设备需要某个特定版本的固件才能在新内核下正常工作,就得单独确认。

还有一个细节:网卡命名规则。CentOS 7 用的是 systemd/udev 的 predictable network interface names,规则在/etc/udev/rules.d/里,比如70-persistent-net.rules或者基于 ifcfg 文件里的HWADDR。新内核下设备的枚举顺序可能变,导致网卡名字从ens33变成ens34。防这一手的办法是在 grub 参数里加net.ifnames=0 biosdevname=0,退回传统的eth0命名,配置文件不用改。这个参数要写进/etc/default/grub的GRUB_CMDLINE_LINUX里,确保新旧内核都生效。

2.3 备份与回滚方案设计

内核升级的回滚,本质上是 grub 引导项切换,所以只要旧内核和它的 initramfs 还在/boot里,回滚就是改一个默认项然后重启的事。但前提是你没把旧内核删掉,也没把/boot搞坏。

需要备份的东西列一下:

  • /boot整个目录(至少grub2/grub.cfg、vmlinuz-*、initramfs-*)
  • /etc/default/grub
  • /etc/fstab
  • /etc/modprobe.d/和/etc/modules-load.d/
  • /etc/sysconfig/下的网络配置
  • 当前sysctl -a的完整输出

虚拟机的话直接打快照最省事,这是成本最低的回滚手段,有条件的都该用。物理机的话,至少要保证/boot有一份 tar 备份放到别的分区,并且确认你能在救援模式下把它恢复回去。

注意:物理机升级前先确认你能进救援模式。很多机器在 grub 菜单里没有救援入口,得靠安装介质引导。这一步不验证,等于没有回滚方案。

2.4 演练环境的低成本搭法

有条件的话,先在虚拟机上把整套流程走一遍。最贴近真实环境的做法是拿virt-v2v之类的工具把生产机镜像导出来,起一台配置相同的虚拟机。更简单的做法是找一台同型号的测试机,或者在虚拟化平台上按同样的分区结构装一台最小化的 CentOS 7.9,把关键软件包装上,然后跑一遍完整流程。

演练要验证的点不只是"能不能起来",还包括:驱动是否齐全、树外模块能否加载、容器能否启动、业务探针是否正常、监控数据是否还在上报。这些都过了,才轮到生产机。

3. 实操上:4.19 内核的安装与引导切换

准备工作做完,正式进入实操。这里给两条路线,按你的实际需求选。

3.1 路线一:通过 ELRepo 拿现成的 RPM 包

ELRepo 是 CentOS 生态里最常用的第三方内核仓库,配置很简单:

rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm

装完先看看仓库里都有什么:

yum --disablerepo="*" --enablerepo="elrepo-kernel" list available | grep '^kernel'

标准的包名是kernel-lt和kernel-ml,对应的包还有kernel-lt-devel、kernel-lt-headers、kernel-lt-tools。这几个建议一起装,尤其 devel 包,后面装树外驱动、编 DKMS 模块都要用。

安装命令:

yum --enablerepo=elrepo-kernel install -y kernel-lt kernel-lt-devel kernel-lt-headers

装完rpm -qa | grep kernel-lt确认一下版本号。如果出来的不是你想要的 4.19,用yum --showduplicates list kernel-lt看历史版本,指定版本号安装:

yum --enablerepo=elrepo-kernel install -y kernel-lt-4.19.xxx-1.el7.elrepo

找不到的话,就只能走第二条路。

3.2 路线二:从源码编译 4.19 并打成 RPM

这条路可控性最高,也最能保证版本精确。先装编译依赖:

yum groupinstall -y "Development Tools" yum install -y ncurses-devel openssl-devel elfutils-libelf-devel \ bc bison flex perl python3 rsync rpm-build redhat-rpm-config

下载源码。选一个 4.19 系列的稳定小版本,尽量选该系列最后几个版本,补丁更全:

cd /usr/src curl -LO https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.xxx.tar.xz tar -xf linux-4.19.xxx.tar.xz cd linux-4.19.xxx

配置的起点,建议直接拿当前系统的配置做基础:

cp /boot/config-$(uname -r) .config make olddefconfig

olddefconfig会把新版本里多出来的选项按默认值填上。这一步很关键,但不代表可以闭眼过。3.10 到 4.19 跨越了太多个版本,新增的配置项有上千条,默认值不一定符合你的需求。要人工确认的关键项至少包括:

配置项说明建议
CONFIG_EXT4_FS根分区如果用的是 ext4必须为 y
CONFIG_XFS_FSXFS 支持必须为 y
CONFIG_OVERLAY_FS容器存储驱动依赖容器环境必须为 y
CONFIG_BRIDGE / CONFIG_VXLAN虚拟网络有虚拟化或容器网络时必须 y
CONFIG_VIRTIO_*虚拟化平台磁盘网卡虚拟机必选
CONFIG_NF_TABLES新版防火墙后端建议 y,同时保留 iptables
CONFIG_BPF_SYSCALL / CONFIG_BPF_JITeBPF需要观测能力时必选
CONFIG_MODULE_SIG模块签名有安全基线要求时要注意

确认完开始编译。并行数别贪多,一般取核数,但每个编译进程大概吃 1.5 到 2GB 内存,内存不够会直接 OOM:

nproc free -g

8 核 16GB 的机器,-j8是安全线;8GB 内存的机器建议-j4。全量编译 4.19 大概 20 到 40 分钟,看磁盘和 CPU。

编译并直接打成 RPM,后面批量部署就靠这个包:

make -j8 rpm-pkg

产物在~/rpmbuild/RPMS/x86_64/下,会生成kernel-4.19.xxx、kernel-headers、kernel-devel三个包。这个方法的额外好处是:目标机器不需要装编译工具链,直接rpm -ivh就行,而且卸载干净。

如果想先在本机试跑,也可以用传统的安装方式:

make -j8 make modules_install cp arch/x86/boot/bzImage /boot/vmlinuz-4.19.xxx cp System.map /boot/System.map-4.19.xxx cp .config /boot/config-4.19.xxx

3.3 grub2 引导项确认与默认内核设置

内核文件到位之后,要生成 initramfs 并写入 grub。CentOS 7 用的是 dracut,指定内核版本生成:

dracut -f /boot/initramfs-4.19.xxx.img 4.19.xxx

这一步会读取/lib/modules/4.19.xxx/下的模块信息,所以必须保证make modules_install已经跑过。生成的 initramfs 大小通常在 20 到 60MB 之间,如果只有几 MB,说明 hostonly 模式没抓到驱动,得手动加:

dracut -f --add-drivers "megaraid_sas mpt3sas ixgbe" /boot/initramfs-4.19.xxx.img 4.19.xxx

然后把这个内核注册进 grub:

grubby --add-kernel=/boot/vmlinuz-4.19.xxx \ --initrd=/boot/initramfs-4.19.xxx.img \ --title="CentOS 7.9 (4.19.xxx)" \ --copy-default

--copy-default的作用是把当前默认引导项的启动参数复制过来,包括 root=、rd.lvm.lv、crashkernel 这些。不加这个参数,新内核启动时会缺参数,直接 panic。这一步是新手最容易漏的。

如果用的是 RPM 包安装,%post脚本里通常会自动完成上面这些动作,装完grub2-editenv list看一眼就知道。

用 BIOS 引导时,grub 配置文件在/boot/grub2/grub.cfg;用 UEFI 引导时在/boot/efi/EFI/centos/grub.cfg。确认新条目确实写进去了:

awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot/grub2/grub.cfg

设置默认启动项:

grub2-set-default 0 grub2-editenv list

如果命令输出的saved_entry和你选的不一致,说明有别的机制在覆盖,检查/etc/default/grub里的GRUB_DEFAULT。改完之后记得跑一次grub2-mkconfig -o /boot/grub2/grub.cfg让它真正生效。

3.4 重启后的验收清单

重启这一步没什么技巧,但要保证你能接触到控制台,别只靠 SSH。重启之后按这个清单逐项过:

uname -r # 确认内核版本 dmesg -T | head -60 # 看启动早期有没有报错 dmesg -T | grep -iE 'error|fail|warn' | head -30 systemctl --failed # 有没有服务起不来 journalctl -b -p err # 本次启动的所有错误级日志 lsmod | wc -l # 模块数量对比升级前 ip a # 网卡是否齐全、IP 是否正确 df -hT # 文件系统是否正常挂载 cat /proc/mdstat # 软 RAID 状态

还要额外检查几个容易被忽略的点:

  • /proc/sys/kernel/random/boot_id的变化说明确实是新启动
  • cat /proc/cmdline确认启动参数完整
  • sysctl -a > /tmp/sysctl-after.txt,和升级前那份做 diff
  • 如果有 kdump 配置,执行systemctl status kdump确认转储内核能正常加载

sysctl的 diff 建议这么看:

diff <(cut -d= -f1 /tmp/sysctl-before.txt | sort) \ <(cut -d= -f1 /tmp/sysctl-after.txt | sort)

左边有、右边没有的键,就是你原来配置里已经失效的部分,得从调优脚本里清掉,不然每次开机都会报 "cannot stat"。

3.5 旧内核的保留与清理

新内核稳定运行一到两周之前,旧内核一定不能删。这是回滚的唯一依靠。

yum的行为由/etc/yum.conf里的installonly_limit控制,默认是 3,意味着最多保留三个内核包。如果你的 4.19 是通过 RPM 装的,注意它会参与这个计数,可能会把 3.10 挤掉。升级前把installonly_limit改大一点,或者临时用yum install --setopt=installonly_limit=5。

确认新内核稳定之后,再清理:

package-cleanup --oldkernels --count=2

这条命令会保留最新的两个内核。清理之前先看一眼/boot里剩下什么,别把正在跑的那个删了。源码编译方式安装的内核不归 yum 管,需要手动删/boot/vmlinuz-*、/boot/initramfs-*、/lib/modules/*和 grub 条目,删完跑一次grub2-mkconfig。

4. 升完不是结束:关键子系统调优与验证

内核换完只是完成了 60%,剩下的 40% 在调优和验证。3.10 上那套参数直接搬到 4.19,有的还能用,有的会起反作用。

4.1 网络栈:拥塞控制与队列调度

4.19 的 BBR 是完整实现,配合 fq qdisc 效果最好:

cat >> /etc/sysctl.d/99-net-tune.conf <<'EOF' net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 EOF sysctl --system

验证是否生效:

sysctl net.ipv4.tcp_congestion_control sysctl net.core.default_qdisc ss -i | head -20

ss -i的输出里,bbr相关的字段会显示bbr:(bw:...,mrtt:...,pacing_rate ...)。如果没有这些字段,说明拥塞控制没生效,检查是不是模块没加载或者有其他配置覆盖。

这里有个经验:default_qdisc设成fq对 BBR 是必须的,但对高并发短连接的场景,fq_codel在降低尾延迟上表现更稳。如果你机器上跑的是大量短连接的服务,可以先试fq_codel,用压测对比p99延迟再定。

多队列网卡还要看一下队列数。4.19 对 RSS 的支持更完整,中断可以分散到更多核上:

ethtool -l eth0 ethtool -S eth0 | grep -i 'queue\|drop'

如果Combined队列数偏少,可以调:

ethtool -L eth0 combined 8

调完观察/proc/interrupts里的分布是否均匀。这一步在 3.10 上也做,但 4.19 的中断亲和性处理更细致,同样的配置往往能拿到更均匀的分布。

4.2 存储与文件系统:IO 调度器的变化

3.10 时代的调度器是cfq、deadline、noop三选一,4.19 换成了多队列架构下的mq-deadline、kyber、bfq、none。名字变了,/sys/block/sdX/queue/scheduler里的可选项也变了:

cat /sys/block/sda/queue/scheduler

对机械盘,mq-deadline是比较稳的选择;对 SSD 和 NVMe,none(也就是 noop 在多队列架构下的对应项)通常性能最好;bfq适合桌面或者对公平性要求高的场景,服务器上反而容易成为瓶颈。

XFS 在 4.19 上支持 reflink,但这个特性要在格式化时开启,已有的文件系统不会自动获得:

mkfs.xfs -m reflink=1 /dev/sdb1

已有的 XFS 可以查一下当前的特性:

xfs_info /mountpoint

reflink=1显示为reflink=1。如果原来是 0,除非重新格式化,否则改不了。这个特性对快照、容器镜像层、备份去重很有价值,如果机器上有独立的空盘,可以考虑按新特性重新规划。

还有一个容易忽略的变化:4.19 的discard行为。3.10 上挂载参数里加discard会在删除时同步发 TRIM,4.19 上更推荐用fstrim.timer定期批量执行,避免同步 TRIM 带来的写放大。CentOS 7 的fstrim.timer默认状态可以用systemctl status fstrim.timer查,如果没启用,手动开一下:

systemctl enable --now fstrim.timer

4.3 cgroup、systemd 与容器运行时的配合

这一块是升级后问题最集中的地方。4.19 内核支持 cgroup v2,但 CentOS 7.9 的 systemd 219 只认 v1,所以默认还是走 v1 挂载:

mount | grep cgroup

应该能看到一堆cgroup on /sys/fs/cgroup/xxx type cgroup (rw,...,xxx)的挂载,说明是 v1 的分离式层级。这是正常的,不用动。如果有人建议你加systemd.unified_cgroup_hierarchy=1,别听,加了 systemd 起不来。

容器运行时这边,要注意两个点。一是存储驱动,overlayfs 在 4.19 上更稳,但overlay和overlay2是两个不同的驱动名,取决于运行时版本,升完内核之后确认一下docker info或者等价命令里显示的存储驱动有没有变。二是某些运行时会在启动时检查内核版本和/proc里的特性,老版本运行时可能会误判。如果容器起不来,先看运行时日志里有没有 "kernel does not support" 之类的字样。

seccomp 也是一个坑。老版本运行时的 seccomp 白名单是按 3.10 的系统调用表写的,4.19 新增了一批系统调用,如果容器里的程序用了新调用,会被白名单拒绝,报 "Operation not permitted"。这个报错很有迷惑性,看起来像权限问题,实际是 seccomp。排查方法:

dmesg | grep -i seccomp

如果有audit: type=1326之类的记录,基本可以确定。解决办法是升级运行时版本,或者临时用--security-opt seccomp=unconfined验证(只用于排查,不要长期这么跑)。

4.4 安全模块与审计的重新确认

4.19 默认开启了几个安全特性,会改变一些行为。最典型的是kernel.kptr_restrict和kernel.dmesg_restrict,很多发行版在 4.x 时代把默认值调严了,导致普通用户读dmesg会得到 "Operation not permitted"。这不是故障,是设计。如果有脚本依赖读dmesg,要么加 sudo,要么调低限制。

SELinux 的策略本身和内核版本关系不大,但 AVC 日志的格式和字段在 4.19 上有细微变化。检查一下:

sestatus ausearch -m avc -ts recent | head -20

auditd 这边,CentOS 7.9 自带的 audit 2.8.5 在 4.19 上基本能用,但如果你的规则里用到了某些新字段,可能会报 "unknown field"。升级后跑一次auditctl -l确认所有规则都加载成功,然后执行service auditd restart观察日志。

还有一处值得检查:kernel.modules_disabled。有些安全基线会把它设为 1,意味着任何模块都不能再加载。升级内核后如果这个值被继承下来,你后面想modprobe任何东西都会失败,而且报错信息很不直观。升级后第一时间确认一下:

sysctl kernel.modules_disabled

5. 故障现场实录:那些年我们踩过的坑

这一节把实际遇到过的问题按现象归类,前面是现象,后面是原因和排查路径。

5.1 卡在 grub 或直接 panic

最常见的三种 panic 信息,含义各不相同。

"VFS: Unable to mount root fs on unknown-block(0,0)",这个基本就是 initramfs 里缺存储驱动。原因通常是 dracut 生成时用了 hostonly 模式,而新内核的模块路径还没准备好,或者生成的时候/lib/modules/4.19.xxx里的modules.dep还没建好。解决办法是先进旧内核,重新跑一次depmod -a 4.19.xxx,再dracut -f --add-drivers "实际需要的驱动" ...重新生成。

"Kernel panic - not syncing: No working init found",这个通常是 root= 参数不对或者 initramfs 内容不完整。检查grubby --info=ALL里新条目的args是不是复制过来了。

grub 菜单里根本没有新内核,多半是/boot空间不足导致写入失败,或者grub2-mkconfig没重跑。检查df -h /boot和ls -lh /boot/vmlinuz-*。

这里补一个实操技巧:出问题的时候,grub 菜单里选中新内核,按e编辑,把rhgb quiet删掉,加上debug ignore_loglevel,能看到完整的启动日志。再不行就加rd.break进 initramfs 的 shell 里手动检查。

5.2 起来了,网卡没了

系统能起来,ip a只有 lo,这个现象背后可能是三个原因。

一是驱动不在 4.19 的内核树里。这种情况lspci -k会显示设备存在但没有 "Kernel driver in use"。需要找厂商要对应 4.19 的驱动源码重新编译,或者确认芯片型号,在 4.19 的drivers/net/ethernet/目录里找找有没有原生支持。

二是驱动在,但固件没加载。dmesg | grep -i firmware会显示 "Direct firmware load for xxx failed"。检查/lib/firmware下有没有对应的.bin文件,没有的话从linux-firmware包里找,或者从厂商那里拿。

三是驱动和固件都在,但网卡名字变了,配置文件对不上。CentOS 7 的网络配置是按接口名找/etc/sysconfig/network-scripts/ifcfg-xxx,名字一变,配置就不生效。前面提到的net.ifnames=0 biosdevname=0就是防这个的。

排查顺序建议这样:先ip a看有没有设备,再lspci -k看驱动绑没绑上,再dmesg看固件加载,最后查命名。

5.3 容器跑不起来 / 存储挂不上

容器起不来,先分清楚是运行时的问题还是存储的问题。

如果mount -t overlay overlay ...手动挂载失败,报 "invalid argument",说明内核的 overlayfs 配置没开,或者挂载参数不被支持。4.19 的 overlayfs 支持metacopy=on选项,但只有在 4.19 之后的版本才稳定,早期版本加上去可能出问题,先别加。

如果是容器网络不通,检查br_netfilter模块和net.bridge.bridge-nf-call-iptables这个 sysctl。4.19 里这些还是默认关闭的,需要手动开:

modprobe br_netfilter sysctl -w net.bridge.bridge-nf-call-iptables=1

还有一种情况是存储挂载失败但日志里没有任何有用信息,这种时候看dmesg里的 SCSI 层日志,往往能看到 "rejecting I/O to offline device" 之类的话,说明存储链路本身有问题,和内核版本无关。

5.4 第三方模块编译失败

DKMS 模块在新内核上编译失败,最常见的原因是没有匹配的kernel-devel。检查/lib/modules/4.19.xxx/build这个软链接指向哪里:

ls -l /lib/modules/4.19.xxx/build

如果指向一个不存在的目录,说明kernel-devel装的是别的版本,或者根本没装。源码编译的内核,这个链接是指向源码目录的,编译完之后别把源码目录删了,否则后面装驱动会失败。

编译报错"找不到某个头文件"的话,大概率是模块源码太老,用了 4.x 里被移除的 API。这种情况只能升级模块源码。常见的 API 变化包括:get_ds()被移除、create_proc_entry换成proc_create、init_timer换成timer_setup。这些在模块源码里搜一下就能定位。

5.5 常见问题速查表

现象最可能原因快速验证处理
panic 无法挂根initramfs 缺存储驱动lsinitrd /boot/initramfs-*.img | grep 驱动名重新 dracut 并--add-drivers
卡住不动无输出启动参数缺失grub 编辑项看 argsgrubby --copy-default重加条目
网卡消失驱动为树外或命名变化lspci -k、dmesg编译驱动或加net.ifnames=0
容器异常退出seccomp 白名单或 cgroup 路径dmesg | grep seccomp升级运行时版本
dmesg 读不了kptr/dmesg_restrict 收紧sysctl kernel.dmesg_restrict用 root 读取或调低限制
sysctl 报 cannot stat键在新内核被移除比对 before/after从配置文件中删除
模块加载失败未签名或 kABI 不匹配modprobe -v 模块名重编或关闭签名校验
/boot 写满分区空间不足df -h /boot清理旧内核或扩容

6. 生产环境的落地节奏与长期维护

单台机器升完不算什么,真正的考验是怎么在一批机器上稳定落地,以及后面几年怎么维护。

6.1 灰度批次与变更窗口怎么排

我习惯分三批。第一批 1 台,挑业务压力最小的那台,升完观察 48 小时。观察指标除了常规的 CPU、内存、负载,还要特别看:dmesg里有没有新的 call trace、内核日志里有没有 OOM、网卡有没有丢包计数增长、文件系统有没有报错。48 小时里只要出现一次无法解释的异常,就暂停后续批次,回去查。

第二批 10% 左右,观察一周。这一批的意义是覆盖不同的负载形态,比如第一批挑的是 Web 服务,第二批就要包含数据库、消息队列这类对 IO 和网络敏感的角色。

第三批全量。全量之前准备一份回滚脚本,内容就是grub2-set-default切回旧条目然后reboot,放在每台机器上都有的路径下,并且确认每个运维同学都知道它在哪。

变更窗口的选择上,我倾向选业务低峰的前半段,而不是正中间。原因是如果出问题,你还有时间在低峰期内恢复,不至于把故障拖到业务高峰期。

6.2 升级后必须盯的监控项

新增几个针对内核本身的监控项,这些在升级前是没必要看的,升完必须要看:

  • node_uname_info之类的指标,确认每台机器的内核版本和你预期一致
  • dmesg里的关键字计数,比如 "Call Trace"、"Out of memory"、"hardware error"
  • 软中断占用的 CPU 时间,/proc/softirqs的变化,网络密集型业务尤其要看
  • 网卡层面的rx_dropped、tx_dropped、rx_fifo_errors
  • TCP 重传率,/proc/net/snmp里的RetransSegs
  • 文件系统的xfssm之类健康检查指标,如果有的话

前面几项是通用的,TCP 重传率这一项值得单独说。BBR 上线之后重传率的行为和之前的 cubic 完全不一样,BBR 会把重传率压得很低,如果你还按老阈值告警,会发现告警再也不会触发,这本身不是坏事,但要知道阈值要重新标定。

6.3 长期维护:跟不跟主线,跟到什么程度

这里要给个明确态度:4.19 是一个长期支持版本,但它的支持期是有限的,而且第三方仓库提供的构建没有商业支持背书。如果你的机器要跑五年以上,最好在做这件事的时候就想清楚后续路线。

我的建议是分成两种情况。如果机器数量少、业务重要度高,就别折腾内核了,把它当成一次性动作,锁死一个版本,只在有明确安全问题时打补丁。如果机器数量多、有统一的自动化能力,那就在升级内核的同时规划用户态升级,把整个系统迁移到新的大版本上,一劳永逸。

还有一个折中方案:容器化的业务可以只升级宿主机内核,把用户态的老化问题用容器镜像来规避。这个方案在国内不少团队里跑得挺顺,代价是宿主机本身还是老系统,某些系统级组件的新特性用不上。

最后分享一个我自己踩过的小坑。有一次升完内核,一切正常,三个月后业务量涨上来,突然出现大量连接超时。查了两天才发现,是 3.10 时代留下的一条net.ipv4.tcp_max_syn_backlog调优参数在新内核上被另一个配置项覆盖了,实际生效值一直是默认的。这件事教会我一个习惯:升级内核之后,把所有关键 sysctl 的实际生效值(不是配置文件里的值)导出成一份清单,存进配置管理里,后面每次变更都跟这份清单对比。配置文件骗人,/proc/sys不骗人。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询