上一篇 下一篇 分享链接 返回 返回顶部

香港服务器运行CentOS 7的业务系统如何迁移到Rocky Linux,避免因CentOS停更导致的安全风险?

发布人:Minchunlin 发布时间:2025-08-16 11:13 阅读量:978


我在香港机房做迁移时,面对的不是抽象的“系统升级”,而是一组真实跑在 CentOS 7 上的生产业务节点:双路 E5 的 Dell R630、带 NVMe 的白牌超融合集群,还有承载核心数据库的 HP DL360。所有节点都在承压跑流量,凌晨时段 CPU 负载依旧维持在 30% 以上。CentOS 7 已经 EOL,3.10 内核与老旧的 glibc 成为潜在的安全隐患,而应用层又依赖 Docker、MariaDB、Redis、Nginx 等关键组件。迁移目标很明确:在最小化停机的前提下,把这些节点平滑切到 Rocky Linux,确保内核、glibc、OpenSSL 等核心库升级到可长期维护的版本。

场景与目标

  • 背景:CentOS 7 已停更(EOL),3.10 内核与旧版 glibc 带来的安全风险逐周上升。业务包括:Nginx + PHP-FPM、Node.js、Go 服务、Redis、MariaDB、RabbitMQ、以及若干容器化工作负载(Docker CE,非 K8s)。
  • 机房:香港 MEGA-i + Equinix HK2 混合;对大陆与东南亚有高比例访问,晚高峰明显。

目标:

  • 将 CentOS 7 业务迁至 Rocky Linux 8(或 9);
  • 停机时间 ≤ 5 分钟/节点(大盘流量通过灰度/蓝绿摊平);
  • 迁移后通过 OpenSCAP/Lynis 与 CIS baseline 的最小要求;
  • 部署链路与回滚路径可重复、可审计。

迁移路线选择

我实际落地了两条路线,并在不同业务上混用:

就地升级(in-place):

使用 ELevate(leapp) 从 EL7 → EL8,目标直接 Rocky 8。适合状态可控、依赖清爽的节点(无第三方内核、无存储内核模块、数据库从库/只读副本等)。

重装替换(blue/green):

新装 Rocky 8/9,将流量切过去,再逐步下线旧节点。适合核心数据库主库、复杂内核模块(如旧版 Mellanox OFED)、历史遗留太多的机器。

经验:能重装就重装。就地升级只在你非常清楚这台机器上发生过什么时使用。数据库主库我一律重装替换,压力测完再切主。

我手上的硬件与网络

机型 CPU 内存 系统盘 数据盘 RAID/HBA NIC 备注
Dell R630 2×E5-2680 v4 128 GB SATA SSD 480G×2(RAID1) NVMe U.2 1.92T×4 LSI 9361-8i(CacheCade) 2×10Gb SFP+(ixgbe) 业务中台
HP DL360 Gen10 1×Gold 6130 128 GB SATA SSD 480G×2(RAID1) SAS 10K 1.8T×6(RAID10) SmartArray P408i 2×10Gb(bnx2x) 消息队列/日志
自研白牌(超融合集群) EPYC 7302P 256 GB M.2 NVMe 1T×1 NVMe U.2 3.84T×6 直通 HBA 9300-8i 2×25Gb(mlx5) 存储/对象网关

坑点提示:

leapp 会检查内核模块与驱动兼容性。ixgbe/mlx5 驱动版本在 EL8/EL9 下变化明显;有 DKMS/ELRepo 的,先移除再升级。

SmartArray 的控制器在内核切换后偶有写缓存策略被重置,升级后我都会复核 WriteBack/WriteThrough。

迁移前的“家底摸清”

我先跑了一个 Ansible playbook,把需要的事实都收上来(版本、内核模块、挂载、repo、服务端口、数据库版本、容器镜像等),结果输出到一张表,业务方一眼能懂。

检查项 命令/脚本 期望 处理动作
OS 版本 cat /etc/centos-release 7.9 不满足则先 yum update 到 7.9
3rd-party 内核 `rpm -qa grep elrepo`
Docker 版本 docker version ≥ 20.10 低于要求先离线备份镜像与卷
MariaDB mysql -V ≥ 10.3(EL8 兼容) 5.5/10.1 需 逻辑导出 + 重建
网卡驱动 ethtool -i EL8/9 支持 DKMS/自编译先卸载
防火墙 iptables-services 故障前置 迁移到 firewalld/nft,或保留兼容层
SELinux Permissive/Enforcing 最终 Enforcing 迁移后按策略修正规则
挂载 lsblk -f LVM/UEFI 足额 /boot ≥ 1G(给 leapp/内核)

我曾因 /boot 只有 200MB,leapp 直接拒绝升级。现场用 lsblk 看了下分区,最后是缩 root 扩 boot(XFS/LVM),有风险,建议提前在变更日之前做。

备份与回滚

虚拟化节点:拍快照 + 磁盘层 snapshot(Ceph RBD / VMware snapshot)。

物理机:

配置 LVM 快照(适合关键配置的小范围回滚);

业务数据一律做 逻辑备份:

MySQL/MariaDB:mysqldump --single-transaction --master-data=2;

Redis:触发 BGSAVE 并复制 dump.rdb 与 appendonly.aof;

Nginx/应用:/etc、/opt、/var/www 打包。

冷备镜像:partclone/clonezilla 到 NAS(香港本地 10Gb 链路)。

回滚策略:

就地升级失败 → 直接用 IPMI 虚拟介质引导 救援 ISO,还原镜像。

蓝绿失败 → 立即切回旧节点(保留 1:1 拓扑 24–48h)。

路线 A:就地升级(ELevate → Rocky 8)

我在“计算节点(无数据库主库)+ 中间件从节点”上采用此法,99% 准备充分时成功率很高。

0)冻结与清理

# 切换到 CentOS 7.9 的 vault 源,避免升级过程依赖漂移
sudo sed -ri 's/mirrorlist/#mirrorlist/g; s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Base.repo
sudo yum clean all && sudo yum makecache

# 升到 7.9 最新
sudo yum -y update

# 移除第三方内核/驱动(非常关键)
sudo yum -y remove 'kernel-ml*' 'kmod-*' 'elrepo-release*'

# 关停非必要服务,固定应用发布
sudo systemctl stop crond || true
sudo systemctl stop docker   # 容器后面重装

1)安装 ELevate(leapp)工具并预检查

# ELevate/Leapp 组件(面向 Rocky 目标)
sudo yum -y install leapp-upgrade leapp-data-rocky

# 预检查
sudo leapp preupgrade
# 看 /var/log/leapp/leapp-report.txt 与 answerfile

常见阻断(Inhibitors)我遇到的

  • 旧版 python2-numpy、initscripts、ntp:直接卸载或替换为 chrony。
  • boot 分区空间不足:扩大到 ≥ 1G。
  • 第三方 repo(remi/epel)未禁用:把 /etc/yum.repos.d/*.repo 里非官方的先 enabled=0。
  • SELinux 策略包冲突:先设为 permissive,升级后再恢复。

2)执行升级

# 正式升级到 Rocky 8
sudo leapp upgrade

# 重启后进入 leapp initramfs,自动完成包级迁移
sudo reboot

升级完成后登录检查:

cat /etc/os-release
uname -r
rpm -qa | wc -l

3)重装 Docker / 运行时与服务恢复

EL8/EL9 的 cgroup 与 iptables 默认栈变化较大(EL9 为 cgroup v2,EL8 仍 v1)。我在 EL8 保持 v1 以兼容历史镜像:

# Docker CE 安装(EL8)
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf -y install docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker

# 需要 cgroup v1 的,/etc/default/grub 添加:
# GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=0"
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

# 恢复镜像与卷(若前面有打包)
docker load -i /backup/docker-images.tar

网络与防火墙:

# firewalld 统一管理(保持历史 iptables 规则也可)
sudo dnf -y install firewalld
sudo systemctl enable --now firewalld

# 常用端口放行
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

SELinux:

# 迁移期 Permissive,验证通过后 Enforcing
sudo setenforce 0
# /etc/selinux/config -> SELINUX=permissive

数据库/中间件版本位移(示意):

组件 CentOS 7 Rocky 8 建议
MariaDB 5.5/10.1 10.5/10.11(AppStream)
Redis 3.x/4.x 6.x/7.x
Nginx 1.16/1.20(第三方) 1.20+(AppStream或官方)
OpenSSL 1.0.2k 1.1.1/3.0(EL9 才 3.0)

MariaDB 升级要点:5.5/10.1 → 10.5 最稳妥做逻辑导出导入,字符集、排序规则统一到 utf8mb4_0900_ai_ci(若为 MySQL 8),避免线上写入导致 redo 兼容性事故。

路线 B:重装替换(蓝绿/灰度)

我在数据库主库、流量焦点节点用此法。重装 Rocky 9 对未来寿命更长,但 Docker/K8s 与 cgroup v2 的兼容要提前评估。

1)准备新节点(或同节点 PXE 重装)

Kickstart 片段(UEFI,XFS on LVM):

#version=RHEL8
text
lang en_US.UTF-8
keyboard us
timezone Asia/Hong_Kong --ntpservers=ntp.aliyun.com,ntp.tencent.com
network --bootproto=dhcp --device=eno1 --hostname=app-rk8-01.local
rootpw --iscrypted $6$randomhash
zerombr
clearpart --all --initlabel
part /boot/efi --fstype=efi --size=600
part /boot --fstype=xfs --size=1024
part pv.01 --size=1 --grow
volgroup rklvg pv.01
logvol /  --vgname=rklvg --name=root --size=40960 --fstype=xfs
logvol /var --vgname=rklvg --name=var --size=20480 --fstype=xfs
logvol swap --vgname=rklvg --name=swap --size=8192
%packages
@^minimal-environment
vim-enhanced
chrony
tuned
%end
%post
systemctl enable chronyd
tuned-adm profile throughput-performance
%end

2)数据迁移与灰度

静态内容:rsync -a --delete,Nginx 配置注意 stream/http 模块差异。

数据库:

  • 建立并行从库/只读副本;
  • 压测新库(TPC-C/TPC-H 之类的业务型压测);
  • 秒级切换:主从角色转换或 Proxy 层(如 HAProxy/ProxySQL)指针切换。

流量灰度:

  • LVS/NGINX/SLB 按 5%→20%→50%→100%,每步观察 10–30 分钟;
  • 监控指标对比(P95/P99、错误率、GC 时间、系统负载、磁盘延迟)。

我用的“变更剧本”(节选)

一键前置检查(Shell)

#!/usr/bin/env bash
set -euo pipefail
log(){ echo "[$(date +%F' '%T)] $*"; }

log "OS: $(cat /etc/redhat-release || true)"
log "Kernel: $(uname -r)"
log "3rd kernel: $(rpm -qa | egrep 'kernel-ml|elrepo' || echo none)"
log "Docker: $(command -v docker >/dev/null && docker --version || echo none)"
log "DB: $(mysql -V 2>/dev/null || echo none)"
log "/boot: $(df -h /boot | tail -1)"
log "Repos:"
egrep -R "^\[|^enabled=" /etc/yum.repos.d/*.repo | sed 's/^/  /'

Ansible:批量滚动升级(就地)

- hosts: web_kv
  serial: 1
  any_errors_fatal: true
  tasks:
    - name: Freeze repos to vault
      become: yes
      lineinfile:
        path: /etc/yum.repos.d/CentOS-Base.repo
        regexp: '{{ item.regexp }}'
        line: '{{ item.line }}'
      loop:
        - {regexp: '^mirrorlist', line: '#mirrorlist'}
        - {regexp: '^#baseurl=http://mirror.centos.org', line: 'baseurl=http://vault.centos.org'}
    - name: Remove elrepo kernels
      become: yes
      yum:
        name:
          - "kernel-ml*"
          - "kmod-*"
          - "elrepo-release*"
        state: absent
    - name: Preupgrade
      become: yes
      command: leapp preupgrade
    - name: Upgrade
      become: yes
      command: leapp upgrade
    - name: Reboot to finish
      become: yes
      reboot:
        reboot_timeout: 3600

迁移后的系统加固与优化

内核/系统调优(通用 Web/DB)

# /etc/sysctl.d/99-tune.conf
net.core.somaxconn = 1024
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65000
vm.swappiness = 10
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
fs.file-max = 2097152

sysctl --system
ulimit -n 1048576

NVMe/XFS:

# 按周 TRIM(若直连 NVMe)
sudo systemctl enable fstrim.timer --now
# 挂载选项 noatime,nodiratime

tuned:

sudo tuned-adm profile throughput-performance

chrony(替代 ntp):

sudo dnf -y install chrony
sudo systemctl enable --now chronyd
chronyc sources -v

安全基线

SELinux:逐项放行策略后切回 Enforcing;

firewalld:只开放白名单端口,源地址段收紧;

OpenSCAP:

dnf -y install scap-security-guide openscap-scanner
oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_standard \
  --results-arf /root/scan-arf.xml --report /root/scan-report.html \
  /usr/share/xml/scap/ssg/content/ssg-rocky8-ds.xml

Lynis:

dnf -y install lynis
lynis audit system --quiet

我遇到的典型坑 & 现场处理

坑点 表现 原因 处理
leapp 卡在 preupgrade 报 initscripts 无法继续 EL7 旧包与 EL8 冲突 卸载/替换,清理后重跑
/boot 空间不足 升级中止 旧分区太小 线下扩容到 ≥1G 或重装
Docker 容器跑不起来 cgroup 不兼容 EL8/EL9 cgroup 行为变化 强制 v1(EL8),重装 docker-ce
Mellanox 驱动丢失 网卡不亮/报错 DKMS 自编译模块不兼容 先移除 DKMS,使用内核自带 mlx5
MariaDB 升级后字符集异常 某些表中文乱 升级跨越大版本 统一字符集并逻辑导出/导入
iptables 规则失效 端口未放行 转向 nftables firewalld 统一,或安装兼容层
RAID 写策略改变 IO 抖动 控制器策略恢复默认 手工改回 WriteBack + BBU

验证清单(上线前/后)

# 版本与内核
cat /etc/os-release
uname -r

# 网络
ping -c3 内部服务
curl -I https://外部健康检查域名
ss -lntp | egrep '80|443|3306|6379'

# 存储与 IO
lsblk -f
fio --name=randread --rw=randread --bs=4k --iodepth=32 --numjobs=4 --size=1G --runtime=60 --group_reporting

# 日志
journalctl -p 3 -xb   # 错误级日志
dmesg | tail -n 50

# 应用健康
systemctl status nginx
systemctl status mariadb
docker ps

我最终的时间线与人手安排(模板)

阶段 动作 负责人 时间窗
资产盘点/预检查 Ansible 扫描、堵住 inhibitors 我/系统工程师 D-7 ~ D-5
备份/回滚演练 逻辑备份 + 镜像还原演练 DBA/存储 D-4 ~ D-3
试点 10% 节点就地升级/重装 SRE 值班 D-2
全量灰度 5%→20%→50%→100% SRE/应用 D 日 00:00–04:00
验证与收尾 基线扫描/性能对比/文档归档 全员 D+1

FAQ:Rocky 8 还是 Rocky 9?

Rocky 8:兼容历史包最多,容器与 cgroup v1 迁移成本低;生命周期到 2029 左右。

Rocky 9:更长寿(到 2032+),OpenSSL 3.0、glibc 2.34;但容器与某些 SDK 要提前适配(cgroup v2、OpenSSL 3.0 不向后 ABI 兼容)。

我的做法:核心系统上先到 8,稳定后再 8→9(leapp 二次升级),避免一次跨越过大。

结语:为什么这事要“现在就做”

CentOS 7 停更后,系统不是“慢慢不安全”,而是逐渐暴露在零日与供应链风险之下:OpenSSL/内核/C 标准库 一旦出 CVE,EL7 不再修,你只能自己 backport。把不可控因素变成可控,就是我们做迁移的意义。

如果你跟我一样,需要在香港机房夜里做这件事:

  • 提前把“能重装就重装”的节点划出来;
  • 就地升级的,确保 预检查 0 个 inhibitor 再动手;
  • 备份与回滚,不是写在变更单里的口号,而是在变更前真的做过一次;
  • 最后,把脚本、Checklist、压测与事故处理复盘写进团队知识库——下一次你会更快、更稳。
目录结构
全文