如何在香港服务器中进行操作系统内核升级,避免驱动兼容性和内核参数冲突带来的系统崩溃?

我在香港葵涌数据中心做夜间变更,机房温度恒定在 22℃,风机声像白噪音。值班工程师递来纸杯咖啡,我盯着 iDRAC 的 KVM 窗口,左手 SSH,右手工单。目标很简单:把一批 CentOS 7 的宿主机内核从 3.10 升到 5.x LTS,既要让 Intel X710 和 Mellanox 25G 卡稳定工作,又不能把阵列卡和 NVMe 启动盘“整丢”。这篇文章就是那次升级的完整复盘和标准化流程,保守、可回滚,兼顾新手和老手。
适用对象与我的环境说明
适用: 裸金属/虚拟化宿主(KVM、Docker、K8s 边缘节点)运行 CentOS 7(兼容 Rocky/Alma 8/9 的方法也能借鉴)。
目标: 通过 ELRepo 升级到 kernel-lt(长期支持)或 kernel-ml(主线),全过程确保:
- 引导介质(SATA / NVMe / RAID LUN)可识别;
- 关键网卡(Intel X710 i40e、Mellanox mlx5)驱动稳定;
- 常见 out-of-tree 模块(ZFS、NVIDIA、DRBD、WireGuard)有解;
- 内核参数与 sysctl 不冲突,避免启动/运行时崩溃。
我的现场硬件(样例一台“金丝雀”主机):
| 组件 | 型号/固件 | 关键驱动 | 是否内核内置 |
|---|---|---|---|
| 机型 | Dell R650 (BIOS 1.9.x) | — | — |
| CPU | Xeon Silver 43xx | microcode 按发行版 | — |
| RAID | LSI/Broadcom 9361-8i (FW 4.68) | megaraid_sas |
✅ |
| 启动盘 | Intel P4510 NVMe | nvme,nvme_core |
✅ |
| 25G 网卡 | Mellanox ConnectX-4 Lx | mlx5_core |
✅ |
| 10G 网卡 | Intel X710 | i40e |
✅ |
| 带外 | iDRAC9 / IPMI | — | — |
金丝雀策略:先挑一台非关键业务的“同构”主机升级(网络/存储/固件相同),把所有坑踩完,再批量灰度。
1. 升级总体策略
- 最小风险路线:ELRepo kernel-lt(5.4 LTS 族)优先,其次 kernel-ml(主线新特性多、变动也多)。
- 双保险:保留旧内核、设默认启动为旧内核,第一次重启手动选择新内核验证;通过串口/带外留后路。
- 驱动优先:先看驱动、再谈内核。凡是 out-of-tree 的模块(DKMS)必须先确认支持目标内核。
- initramfs 冗余:对启动关键驱动 dracut --add-drivers 打进新 initramfs,必要时 nohostonly 全打。
- 参数保守:核查 grub 启动参数与 /etc/sysctl.d/*,避免重复/相反配置导致启动慢/挂起/软中断飙升。
2. 变更前“摸底”与风险矩阵
先跑一段脚本把资产和风险点抓干净,我现场就这么干的:
#!/usr/bin/env bash
set -euo pipefail
OUT=/root/preflight_$(hostname)_$(date +%F_%H%M).tar.gz
mkdir -p /root/preflight && cd /root/preflight
echo "# uname & os" > sys.txt
uname -a >> sys.txt
cat /etc/redhat-release >> sys.txt
echo -e "\n# cpu & mem" >> sys.txt
lscpu >> sys.txt
free -h >> sys.txt
echo -e "\n# block" >> sys.txt
lsblk -o NAME,SIZE,TYPE,MODEL,ROTA,TRAN,MOUNTPOINT >> sys.txt
echo -e "\n# pci" >> sys.txt
lspci -nnk >> pci.txt
echo -e "\n# modules" >> sys.txt
lsmod | sort >> modules.txt
echo -e "\n# dkms" >> sys.txt
command -v dkms && dkms status || echo "dkms not installed" >> sys.txt
echo -e "\n# network" >> sys.txt
ip -br a >> sys.txt
ethtool -i eth0 2>/dev/null >> sys.txt || true
ethtool -i eth1 2>/dev/null >> sys.txt || true
echo -e "\n# grub/cmdline" >> sys.txt
cat /proc/cmdline >> sys.txt
echo -e "\n# sysctl" >> sys.txt
sysctl -a | sort > sysctl_dump.txt
tar czf "$OUT" *
echo "Saved to $OUT"
风险矩阵(从易到难):
| 领域 | 风险 | 典型表现 | 预防/处置 |
|---|---|---|---|
| 引导存储 | initramfs 没有 nvme / megaraid_sas |
dracut emergency、找不到根分区 | 用 dracut --add-drivers 重建;必要时 nohostonly |
| 网卡 | 驱动版本与固件不配 | 链路 flap、丢包、Rx no-buffer | 固件先行、核对 ethtool -i;必要时固定 i40e 版本 |
| DKMS | ZFS/DRBD/NVIDIA 不支持新内核 | 模块编译失败、服务起不来 | 升级 DKMS 版本或 pin 内核 先不升 |
| 容器 | cgroup/iptables 行为差异 | 容器启动慢、网络策略异常 | 保持 cgroup v1(CentOS 7 默认),避免混用 nft |
| 内核参数 | grub 与 sysctl 冲突 | 启动卡顿、NUMA/THP 异常 | 参数合并,确保唯一来源 |
| 固件 | HBA/NIC 旧固件 + 新驱动 | 间歇性 IO/网断 | 先窗口升级固件(带回滚) |
3. 准备:回滚与带外通道是升级的“安全带”
备份与救援
重要配置:/etc、/boot、/var/lib/docker(如容器)、业务配置。
内核与 grub:
cp -a /boot /boot.bak.$(date +%F)
awk -F\' '/^menuentry / {print NR-1 " : " $2}' /etc/grub2.cfg
有条件用 全盘镜像/LVM 快照;没有就准备好 救援 ISO(带外虚拟介质可挂载)。
带外可用性
iDRAC/IPMI 测试电源控制 & SOL(串口 over LAN):
ipmitool -I lanplus -H <bmc-ip> -U <user> -P <pass> power status
ipmitool -I lanplus -H <bmc-ip> -U <user> -P <pass> sol activate
维护窗口与回退标准
明确回退判据:新内核下出现(任一)严重丢包/存储异常/核心业务指标劣化 ≥10% 即回退。
灰度节奏:金丝雀 → 5% → 25% → 100%,每步观察≥1 个业务周期。
4. 选择内核与启用 ELRepo
我的经验:先选 kernel-lt(LTS),除非你明确需要主线特性(如最新硬件支持)再上 kernel-ml。
# 1) 导入 ELRepo
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
# 2) 安装 kernel-lt 或 kernel-ml(二选一)
yum --enablerepo=elrepo-kernel install -y kernel-lt
# 或
# yum --enablerepo=elrepo-kernel install -y kernel-ml
# 3) 查看已安装内核
rpm -qa | grep -E 'kernel-(lt|ml)'
不要卸载旧内核。 先保留两代以上。
5. 关键驱动版本核对(网卡/存储为先)
5.1 网卡
# Intel X710
ethtool -i ens1f0
# 关注 driver: i40e, version, firmware-version
# Mellanox ConnectX-4/5
ethtool -i ens2f0
# 关注 driver: mlx5_core, firmware-version
经验要点:
固件要与驱动匹配。X710 某些旧固件 + 新 i40e 会 Link flap;Mellanox 建议先用官方工具把固件升到近几年稳定版(如需)。
生产中尽量使用内核自带驱动。除非被特性卡住,再考虑厂商 out-of-tree 驱动(会增加后续维护成本)。
5.2 存储/HBA/RAID/NVMe
lspci -nnk | egrep -i 'raid|sas|nvme'
modinfo megaraid_sas | egrep 'version|vermagic' || true
NVMe 启动盘:确保新 initramfs 带有 nvme、nvme_core。
RAID/HBA:megaraid_sas、mpt3sas 等必须在 initramfs 内,否则进不了根文件系统。
6. 为新内核重建 initramfs(把要命的驱动“焊死”进去)
# 找到新内核版本号
NEWKVER=$(rpm -q --qf '%{VERSION}-%{RELEASE}.%{ARCH}\n' kernel-lt | tail -n1)
# 方案A:最稳妥(全量驱动、镜像大但稳)
dracut -f --kver "$NEWKVER" --no-hostonly
# 方案B:定点加驱动(镜像小、可控)
dracut -f --kver "$NEWKVER" --add-drivers "nvme nvme_core megaraid_sas mlx5_core i40e"
小贴士:如果你的根盘在 LVM/MD/Multipath 上,也把对应驱动加进去(例如 dm_mod, dm_multipath)。
7. 整理内核参数与 sysctl,避免“打架”
7.1 GRUB 启动参数统一
编辑 /etc/default/grub(CentOS 7):
# 示例(按需取舍,不要照搬)
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet \
intel_pstate=disable \
transparent_hugepage=never \
numa_balancing=disable \
rd.driver.pre=nvme rd.driver.pre=megaraid_sas"
生成配置(BIOS 与 UEFI 二选一):
# BIOS:
grub2-mkconfig -o /boot/grub2/grub.cfg
# UEFI:
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
只保留一处生效的设置。如果你把 THP 设为 never,就不要再在 rc.local/sysfs 里重复 echo。唯一来源原则能省掉大量诡异问题。
7.2 sysctl 规范化
放到 /etc/sysctl.d/99-tuning.conf:
# 网络缓冲/连接跟踪按需(示例)
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65000
# vm
vm.swappiness = 10
vm.max_map_count = 262144
sysctl --system
避免冲突:确认不存在多个 .conf 文件设置同一 key 且值不同;sysctl -a | grep <key> 二次核对。
8. 设定启动顺序与超时、串口后门
# 把旧内核设为默认(第一次重启手选新内核)
grub2-set-default 0 # 0 通常是旧内核,具体用下面命令列出确认
awk -F\' '/^menuentry / {print NR-1 " : " $2}' /etc/grub2.cfg
# 给自己留 5 秒菜单选择时间
sed -ri 's/^GRUB_TIMEOUT=.*/GRUB_TIMEOUT=5/' /etc/default/grub
grub2-mkconfig -o /boot/grub2/grub.cfg
串口控制台(可选,救命稻草)
在 GRUB_CMDLINE_LINUX 追加:
console=tty0 console=ttyS0,115200n8
9. 窗口执行(分钟级 Runbook)
T-10 分钟:
- 通知业务降流/摘除单点;开启带外 KVM;确认回退标准。
- 关闭非必要服务(例如批处理任务)。
T-5 分钟:
# 切到单用户(可选)或维持多用户但停大负载
systemctl stop <heavy-service1> <heavy-service2>
T-0 分钟(安装已完成,开始验证并重启):
# 再次核对新内核 initramfs 存在
ls -lh /boot/initramfs-$NEWKVER.img
# 重启
reboot
T+2 分钟(GRUB 菜单):
手动选择新内核(旧内核仍为默认),观察启动日志是否卡在 dracut/A start job is running for dev-...。
T+5 分钟(登录后健康检查):
uname -r
dmesg -T | egrep -i 'error|fail|nvme|megaraid|i40e|mlx5' | tail -n 200
lsblk
mount | column -t
ip -br a
ethtool -S eth0 | egrep 'rx_(errors|missed)|tx_errors' | tail
T+10 分钟(回归测试):
网络:iperf3(同机房对端),丢包/吞吐稳定;
存储:fio 小压测(顺序/随机读写 1~3 分钟级);
业务:核心接口 QPS、P99、错误率。
一切正常 → 用新内核设为默认:
grubby --default-kernel
# 输出还是旧内核?那就切换:
grubby --set-default /boot/vmlinuz-$NEWKVER
grubby --default-kernel
10. 我踩过的坑与现场解法
坑 1:X710 链路抖动(i40e)
症状:升级后 dmesg 反复 NIC Link is Down/Up,ethtool -S 里 rx_no_buffer_count 增长。
解法:
先升级网卡固件到与驱动兼容的稳定版;
临时缓解:调大 ring buffer、关不成熟 offload:
ethtool -G eth0 rx 4096 tx 4096
ethtool -K eth0 lro off gro on gso on tx-udp-segmentation off
特定旧固件可 pin 到稳定 i40e 版本,或换 kernel-lt 而非 ml。
坑 2:initramfs 未包含 megaraid_sas 导致进不了根
症状:dracut emergency,找不到 root=... 设备。
解法:用救援介质进入后:
mount /dev/sdXn /mnt/sysroot
mount -t proc /proc /mnt/sysroot/proc
mount --rbind /sys /mnt/sysroot/sys
mount --rbind /dev /mnt/sysroot/dev
chroot /mnt/sysroot
dracut -f --kver "$NEWKVER" --add-drivers "megaraid_sas"
坑 3:ZFS DKMS 编不过
症状:dkms build 失败、zpool 无法导入。
解法:先不要升内核,或同步升级到支持该内核的 ZFS 版本;必要时把数据临时迁出再变更。
坑 4:THP 与数据库冲突
症状:PostgreSQL/ES 吞吐抖、延迟突刺。
解法:统一在 GRUB 里 transparent_hugepage=never,删除所有重复 sysfs 脚本。
11. 变更后对照表(我会留档到工单里)
11.1 驱动与固件对照
| 设备 | 变更前 | 变更后 | 备注 |
|---|---|---|---|
i40e |
v2.12.x | v2.18.x | flap 消失,rx_no_buffer 归零 |
mlx5_core |
4.9x | 5.xx | 固件同步升级 |
megaraid_sas |
07.7x | 07.7x(内核内置) | initramfs 手动加入 |
nvme |
内核 3.10 版 | 5.4 版 | 队列深度更优 |
注:表中版本为样例,实际以 ethtool -i/modinfo 输出为准。
11.2 参数差异
| 项 | 变更前 | 变更后 | 理由 |
|---|---|---|---|
| GRUB | rhgb quiet |
rhgb quiet transparent_hugepage=never numa_balancing=disable |
数据库/延迟敏感 |
| sysctl | 默认 | 调大 net backlog/缓冲 | 10G/25G 低丢包 |
| tuned | balanced |
throughput-performance |
I/O 吞吐型负载 |
12. 批量化与自动化(生产必备)
当金丝雀验证通过,我会用 Ansible 批量推进,核心任务包括:
- 安装 ELRepo & kernel;
- 重建 initramfs(按硬件画像选择 --add-drivers);
- 模板化 /etc/default/grub 与 /etc/sysctl.d/99-tuning.conf;
- 统一 grub2-mkconfig 并把 默认内核仍指向旧版;
- 编排重启顺序(成对/跨机架/跨 ToR 冗余);
- 变更后自动跑 dmesg、ethtool -S、fio、iperf3 并回传报表。
13. 常用命令速查(可直接贴工单)
# 查看所有内核菜单项
awk -F\' '/^menuentry / {print NR-1 " : " $2}' /etc/grub2.cfg
# 设定默认内核(确认路径)
grubby --default-kernel
grubby --set-default /boot/vmlinuz-<VERSION>
# 查询网卡驱动/固件
ethtool -i ethX
# 观察错误计数
ethtool -S ethX | egrep 'errors|dropped|missed|no_buffer'
# 重建 initramfs(全量)
dracut -f --kver <VERSION> --no-hostonly
# 重建 initramfs(定点加驱动)
dracut -f --kver <VERSION> --add-drivers "nvme nvme_core megaraid_sas mlx5_core i40e"
# 生成 grub(BIOS/UEFI 选其一)
grub2-mkconfig -o /boot/grub2/grub.cfg
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
# 内核/模块版本
uname -r
modinfo <module>
14. 收尾与复盘
那晚 02:40,我把金丝雀主机切回默认用新内核,链路指标稳定、fio 的 P99 抖动比升级前更小。回旅馆的电梯里,我在手机上补完了“驱动/固件/参数对照表”和“回退判据”,第二晚就按灰度表推进剩余节点。
结论很朴素:驱动优先、参数单一来源、initramfs 冗余——把这三件事做到位,内核升级这类“高危操作”就会变成可控的日常维护。
附:最小可用流程(给赶时间的人)
做带外、备份、救援盘准备;
yum --enablerepo=elrepo-kernel install kernel-lt;
dracut -f --kver <NEW> --add-drivers "nvme nvme_core megaraid_sas mlx5_core i40e";
统一 GRUB_CMDLINE_LINUX,生成 grub;
默认仍旧内核 → 重启手动选新内核;
通过健康检查 → grubby --set-default /boot/vmlinuz-<NEW>;
灰度批量推进、复盘表留档。