☰
RHEL 9.7生产环境部署与性能调优实战
2026/10/9 3:04:32 网站建设 项目流程

想在一台新服务器上把RHEL 9.7 装好,还要让它稳定扛住线上负载,这绝不只是“装完系统”这么简单。我最近刚给三台新机器完成了 RHEL 9.7 部署和优化,从订阅注册、分区规划一路做到文件系统、CPU 中断、容器和监控联调,前后花了两个周末。整个过程下来,我最深的感触是:RHEL 9.7 的安装其实没什么门槛,真正拉开差距的是安装阶段和初始化阶段的细节取舍,以及后面那套调优动作是不是成体系。这篇文章适合准备部署 RHEL 9.7 的运维、后端同学,也适合那些已经装好了 RHEL 9.7,但总感觉系统跑得别别扭扭、想找一个系统化优化思路的人。我不打算写一本手把手安装说明书,而是把这些天踩过的坑、留下来的配置和命令行一并整理出来,你对照着自己环境筛一遍就行。

1. 部署前要敲定的事:版本、硬件、订阅一个都不能漏

1.1 为什么这次选 RHEL 9.7

先把版本关系说清楚。RHEL 9.7 不是一次推翻式的版本升级,它依然基于 kernel 5.14 主线,本质上属于 9.x 系列的持续更新。相比 9.4、9.5、9.6,9.7 的价值主要集中在新硬件支持的补齐、部分语言运行时和工具链版本前移,更重要的是累积了大量安全补丁和稳定性修复。做生产系统选型的时候,我通常不会盲目追最新的大版本,因为生态和第三方软件的兼容性需要重新验证;反而是选一个像 9.7 这样已经打磨过几轮的小版本,既能拿到新硬件驱动,又不至于和周边系统脱节。

如果你现在还在跑 RHEL 8,或者正从 CentOS 7 迁移过来,那么成本最高的不是装机,而是业务系统对 systemd、网络管理方式(nmcli 替代 network)、防火墙 firewalld 和 SELinux 策略的适配。RHEL 9.x 默认就是这套新机制,团队越早统一越省事。我的建议是,新购机器直接用 9.7,存量机器如果没有特殊兼容性约束,也值得尽快纳入 9.x 轨道。

这一版对内存大、CPU 核数多的物理机以及高密度虚拟化场景支持很稳。我自己遇到的一个典型场景是:同一套 Java 服务,之前 CentOS 7 上每台只敢跑两个实例,切到 RHEL 9.7 之后可以直接跑到四个实例。除了系统层调整更规范之外,内核调度和内存管理表现确实更平顺。

1.2 硬件评估:别等装完才来后悔

部署前我会先花三十分钟把硬件过一遍,避免装完再返工。关键看五样东西:CPU 型号和核数、内存容量与通道数、磁盘数量和类型、是否接了 RAID/HBA 卡、网卡型号与最大队列数。很多人忽略网卡队列数,后面做中断优化的时候才发现网卡根本不支持多队列,那就被动了。

分区规划是整个部署里最影响长期使用的一步。我的基本做法是,只要有条件就用 LVM,不要把所有东西堆在一个分区里:

  • /boot独立分区,500MB 到 1GB,不要放进 LVM,引导管理器识别最稳妥;
  • /var单独分出来,日志、容器数据、包缓存都会慢慢往里塞;
  • 跑数据库的话,数据目录单独划一个 LV,后续扩容和备份都方便;
  • 交换分区按业务诉求定,别太大也别完全没有。

UEFI 和 Legacy 的问题很容易踩。现在新服务器 BIOS 基本都是 UEFI,但有些维护团队习惯用 Legacy 引导。RHEL 9.7 对 UEFI 支持很好,但如果你计划走 PXE 加 Kickstart 批量装机,必须提前确认是 BIOS 还是 UEFI 启动,因为对应的安装程序引导文件和分区表类型不一样。PXE 菜单配错会一直卡在 GRUB 找不到盘。个人建议现代服务器统一走 UEFI 加 GPT,少一套兼容性包袱。

1.3 订阅、软件源:没搞定这些,优化无从谈起

RHEL 9.7 安装完成之后默认会弹注册提示,它要拿到 BaseOS 和 AppStream 的更新,必须有有效订阅。内网环境建议先在一台机器上完成注册,再把本地 repo 同步好。注册和启用仓库的命令核心就是 register、attach、enable repo 这三步,下面这个组合是我在 RHEL 9.7 上验证过的:

subscription-manager register --username=your_user --auto-attach subscription-manager attach --auto subscription-manager repos --enable=rhel-9-for-x86_64-baseos-rpms --enable=rhel-9-for-x86_64-appstream-rpms

如果没有正式商业订阅,Red Hat 的开发者订阅或评估订阅也能覆盖大多数学习验证场景。企业内网更常见的做法是把 repo 同步到本地,锁定版本和包清单。这里有个小经验:不要一股脑把 EPEL、docker-ce、elrepo 全部 enable,仓库越杂,dnf update时依赖冲突的概率越大。我一般只在真正需要某个第三方包的时候临时启用对应仓库,装完立刻 disable。

2. 安装与首次启动:这一步慢一点,后面省一整个星期

2.1 装服务器就选最小化安装

这个选择直接决定系统的干净程度。RHEL 9.7 安装源里提供多个软件集合,我的生产环境一律选 Minimal Install,图形界面一个都不装。不要小看这个决定,带 GUI 的服务器不仅多消耗几百 MB 内存,还会多出一堆 dbus、桌面相关服务,既增加受攻击面又干扰性能分析。等你在 top 里看到一堆桌面进程的时候,排查问题的耐心会非常有限。

如果手动安装,我的流程一般是:设置时区,键盘布局选标准,安装源指向 HTTP 仓库或本地镜像,软件选择选 Minimal,安装目标点开手动分区或自动分区后再调整,网络与主机名这一步直接配好静态 IP。这样重启后机器马上能用,不用再折腾一遍网卡。最关键的是在安装目标里把分区方案确认一遍,RHEL 9 安装器默认会预留 LVM 卷组,不要直接一路 Next 把整个盘全自动分了,后期想扩容单独目录会非常痛苦。

Kdump 也要想清楚。默认crashkernel=auto会预留一部分内存,192GB 物理内存的机器可能被预留掉几百 MB。物理机内存紧张、或者你觉得崩溃转储用处不大时,装完可以调小或关闭。不过我建议至少在关键数据库机器上保留,真碰上内核崩溃,没有 kdump 日志排查起来像没头苍蝇。

2.2 批量部署就走 Kickstart

如果只有一两台机器,手动装没问题。数量一多,我强烈建议用 Kickstart。RHEL 9.7 安装程序支持在启动参数里指定inst.ks=...,可以指向 HTTP、NFS 或者本地文件。下面这个片段是缩略但完整的参考,你按自己环境改就能用:

# kickstart.cfg 简化示例 install url --url="http://192.168.1.100/repo/rhel9.7/" lang en_US.UTF-8 keyboard us timezone Asia/Shanghai --utc rootpw --iscrypted $6$xxxxxxxxxxxx user --name=ops --groups=wheel --iscrypted $6$xxxxxxxxxxxx zerombr clearpart --all --initlabel part /boot --fstype=xfs --size=1024 part pv.01 --size=1 --grow volgroup vg0 pv.01 logvol / --fstype=xfs --name=root --vgname=vg0 --size=10240 logvol /var --fstype=xfs --name=var --vgname=vg0 --size=20480 logvol swap --name=swap --vgname=vg0 --size=8192 network --bootproto=static --device=ens192 --ip=192.168.1.30 --netmask=255.255.255.0 --gateway=192.168.1.1 --nameserver=192.168.1.1 --hostname=rhel9-01 firewall --enabled --port=22/tcp selinux --enforcing services --enabled=sshd,chronyd reboot %packages @minimal %end

这里有个特别容易被忽略的坑:Kickstart 里 root 密码哈希如果粘贴的是旧系统里的加密值,装完用旧密码根本登不上,因为哈希算法和盐的格式可能变了。我一般先在本机用openssl passwd -6生成新的哈希,或者干脆装完第一台之后,用生成的/root/anaconda-ks.cfg里的真实哈希作为模板,保证批量机器一致。

2.3 开机后第一件事:网络、时间、日志一起捋顺

系统重启之后我会做一套固定动作:配置完整网络、同步时间、检查硬件错误。命令本身不复杂,但执行顺序别乱:

nmcli con mod ens192 ipv4.addresses 192.168.1.30/24 ipv4.gateway 192.168.1.1 ipv4.dns 192.168.1.1 ipv4.method manual nmcli con up ens192 hostnamectl set-hostname rhel9-01 timedatectl set-timezone Asia/Shanghai systemctl enable --now chronyd chronyc sources -v

时间同步这个事很多人觉得无所谓,但后面部署 Zabbix、数据库、证书验证全部依赖时间准确。RHEL 9.7 默认用的是 chronyd,不要再按 ntpd 那套老接口配置时区。NTP 确认没问题之后,再看 dmesg 有没有异常输出:

dmesg -T -l err,emerg lspci | grep -i -E 'ethernet|raid|vga'

安装完的第一件事不是直接做调优,而是dnf update到当前仓库最新状态。内网离线环境要确认 repo 配置正确,联网环境则要注意第三方仓库是否可能带歪依赖。这一步稳定了,后面的优化才是在一个干净基础上展开。

3. 全局调优的第一层:tuned、sysctl 与 CPU 中断处理

3.1 为什么我不建议抛开 tuned 裸改内核参数

RHEL 9.7 自带的 tuned 不只是一个调优模板工具,它还会周期性把内核参数、CPU governor、磁盘调度器刷成自己管理的状态。你如果直接改/etc/sysctl.conf,当时生效,但 tuned 服务重新加载或者重启之后,很可能会被重置。这个坑我踩过不止一次:熬夜改完参数,第二天早上发现系统又回到默认值。

所以正确的顺序是:先用tuned-adm list看当前有什么 profile,再用tuned-adm active确认生效的是哪一个。需要微调的时候,在/etc/tuned/下新建自定义 profile,或者直接修改现有 profile。

我的生产机器一般这样选:普通虚拟机、云主机选virtual-guest;物理机跑通用业务压榨吞吐选throughput-performance;低延迟交互服务或数据库选latency-performance。但这只是起点,我还会再加一层自定义配置。下面是我常用的一个自定义 profile:

# /etc/tuned/custom-prod/tuned.conf [main] summary=Custom production profile based on throughput-performance include=throughput-performance [cpu] governor=performance energy_perf_bias=performance [sysctl] vm.swappiness=10 vm.vfs_cache_pressure=50 vm.max_map_count=262144 net.core.somaxconn=65535 net.ipv4.tcp_max_syn_backlog=65535

写完之后执行tuned-adm profile custom-prod,用tuned-adm active和sysctl vm.swappiness验证。这套方式的优点在于所有优化配置集中在一个 profile 里,换机器、做基线都能直接复制,不会像散落的 sysctl.conf 一样难以收敛。

3.2 sysctl 里最值得调的几项

说完了方法论,给一套我基本会落到生产环境的参数和适用场景。这些值不是“越大越好”,要根据业务形态去选:

参数建议值适用场景
vm.swappiness10内存相对充足、偏向缓存保留
vm.vfs_cache_pressure50文件/目录密集型访问
vm.max_map_count262144Java、Elasticsearch、内存数据库
fs.file-max6553560高连接数、消息队列、代理
net.core.somaxconn65535反向代理、高并发入口
net.ipv4.tcp_max_syn_backlog65535高并发 SYN 流量

逐个说下为什么动它们。vm.swappiness控制内核在内存紧张时倾向于把匿名页换到 swap 还是回收 page cache,默认值对内存不太够的机器偏高,我一般调到 10 左右。但要小心,本身内存就很小的机器别盲目调低,否则 OOM 压力变大。vm.vfs_cache_pressure默认 100,会让内核较快回收 inode 和 dentry 缓存,文件密集场景调到 50 能明显减少频繁 stat 的系统调用延迟。vm.max_map_count不用多解释,Java 和 ES 经常因为默认 65530 爆max map count exceeded,建议直接按 262144 设。fs.file-max默认多数机器够用,但跑大量连接、消息队列时最好调大,同时配合ulimit -n一起看。

这些参数我会归档到一个文件里管理,比如/etc/sysctl.d/99-production.conf,但如果 tuned profile 的[sysctl]段落里已经写了,就不要两边同时配置,避免源不一致互相打架。

3.3 CPU 中断绑定与 NUMA:最容易出隐形收益的部分

RHEL 9.7 的优化里,CPU 层面回报最快的是中断亲和性和 NUMA 感知。

先看网卡是不是多队列。千兆口可能只有一个队列,万兆网卡一般支持 4 到 8 个队列,硬件队列多了才能把中断负载分散到不同 CPU。查看和设置的方法:

ethtool -l eth0 ethtool -L eth0 combined 8

多队列设置好之后,还是会有多个中断落在同一个 CPU 或者同一个 NUMA 节点上。用cat /proc/interrupts找到网卡各队列的中断号,比如 83 到 90,然后逐个写 smp_affinity,让它们分散到不同核:

echo 1 > /proc/irq/83/smp_affinity echo 2 > /proc/irq/84/smp_affinity

如果硬件本身只有单队列,比如虚拟机里的 virtio-net,RPS 就很有用。给接收队列设置掩码,让软中断分散到多个 CPU:

echo 3 > /sys/class/net/eth0/queues/rx-0/rps_cpus

在 RHEL 9.7 里 irqbalance 服务默认是开启的,它会自动平衡中断。但自动平衡在高度 NUMA 的场景下未必符合预期,所以我通常把它停掉,或者用IRQBALANCE_BANNED_CPUS屏蔽关键核,再手动绑定网卡中断。NUMA 方面,先跑numactl --hardware看节点拓扑。数据库、Java 这种大内存应用最怕内存被分散到两个 node 上,跨节点访问延迟会明显拉高。我一般给这类进程用numactl --cpunodebind=0 --membind=0指定节点,并在启动脚本里固化下来。虚拟机上 numad 通常没什么收益,物理机上如果业务进程多,可以试着开 numad 让它自动调整内存策略。

4. 存储、Swap 和文件系统:别让底层“慢”拖垮上层的优化

4.1 XFS 的格式化参数,RAID 场景必须手动对齐

RHEL 9 默认文件系统是 XFS,我没有异议。但很多人格式化 XFS 时直接mkfs.xfs -f /dev/vg1/root,什么都不带,这在普通单盘上没问题,在 RAID 上就浪费了条带优势。

XFS 的分配组长、条带单位要和底层 RAID 的 stripe 对齐。比如 RAID 10,底层 stripe size 是 256KB,有 4 个数据盘,那么格式化时应该:

mkfs.xfs -f -d su=256k,sw=4 /dev/vg1/data

这样文件系统的分配单元会和 RAID 条带对齐,顺序写不会出现跨越条带边界的小块 IO,能明显减少写放大。已经格式化过的分区也不用推倒重来,但后面新建 LV 时记得带参数。还有个常用检查:blockdev --getiomin --getioopt /dev/sda,ioopt 的值能反映 RAID 卡暴露的 strip 对齐建议。如果为 0,说明设备本身没有提示,就需要按 RAID 配置手动选择。

4.2 I/O 调度器、readahead 和队列深度

RHEL 9.7 里不同设备类型的调度器选择不同。传统机械盘和企业级 SAS 建议mq-deadline,NVMe 固态盘用none最合理。查看和切换的方法:

cat /sys/block/sda/queue/scheduler echo mq-deadline > /sys/block/sda/queue/scheduler

对云服务器里那种 virtio 块设备,调度器基本没什么可调的,但 readahead 可以调整。默认 readahead 在虚拟机上可能只有 128KB,大文件顺序读会吃亏。我一般在对存储吞吐敏感的机器上把它调到 4096:

blockdev --setra 4096 /dev/mapper/vg_data

这个值不要盲目调大,随机读写应用调高了反而增加内存浪费和寻道负担。队列深度nr_requests我很少动,默认已经不错,除非你非常清楚底层设备特性。设备对齐也可以顺手检查:parted /dev/sda align-check optimal 1,返回 aligned 就没有问题。

4.3 Swap、内存回写和 THP:延迟抖动往往来自这里

内存层面 RHEL 9 有个常被忽略的点:透明大页 THP 默认是开启的。THP 对某些大内存计算是加速器,但对数据库和 Java 低延迟服务来说,它带来的 CPU 开销和页分配延迟反而会造成周期性抖动。我通常在生产数据库机器上把它关掉,或者改成 madvise,让应用自己决定是否使用大页。启动参数里加上:

transparent_hugepage=never

系统运行中也可以临时写/sys/kernel/mm/transparent_hugepage/enabled。但注意 tuned 可能回来把它改回去,所以还是放进内核启动参数或 tuned profile 里最稳妥。

Swap 策略我一般和内存回写参数一起看。vm.swappiness=10左右,倾向于保留 page cache 而不是换出进程内存;同时调整脏页回写水位:

vm.dirty_background_ratio=5 vm.dirty_ratio=10

这两个参数在大量顺序写入场景很关键,否则一旦脏页积压超过硬上限,系统会阻塞 IO 到让你怀疑机器死了。要注意的是,内存特别大的机器需要再评估,因为 ratio 是按内存百分比算的,192GB 内存的机器,5% 也接近 10GB 脏页,实际还要结合盘片写入能力来判断。

5. 业务服务部署时的联动优化:Docker、Zabbix 不是装上就行

5.1 容器运行时和 cgroup v2:资源限额必须一开始就设好

RHEL 9.7 默认走 cgroup v2,systemd 管理所有用户会话容器。Podman 是系统自带的容器管理工具,和 systemd 集成最自然;如果团队习惯用 Docker,可以装 docker-ce,但不要忘记它的数据目录默认在/var/lib/docker,如果根分区不够大,镜像和容器日志很快会把盘填满。

我的建议是把容器数据目录放到独立分区或独立 LV 上,并在部署前用docker info确认 storage driver 是 overlay2。对 cgroup v2 来说,启动容器时就要带上资源限制:

docker run --memory=4g --cpus=2 --pids-limit=512 --name app-test your-image

在沙箱里先压测验证限额,再上生产。我见过不止一次因为容器没有 pids 限制,某个应用线程失控直接把宿主机 load 打满。另外 RHEL 9.7 上通过 systemd 管理容器时,记得在 service 里加Delegate=yes,否则容器内部的 cgroup 层级可能建不出来,导致部分资源限制不生效。

5.2 Zabbix、数据库实例和系统参数之间的坑

部署 Zabbix 是监控体系里的常规操作,但它本身只是个中等负载应用,真正的坑反而在环境配置:Zabbix 对客户端和服务端时间一致性极其敏感,如果 chronyd 没配好,历史数据会持续出现 no data 或数据错乱。所以我每次都会在部署 Zabbix 之前先把 chrony 同步手动验证一遍。

另一个坑是数据库。Zabbix 后端通常是 MySQL、MariaDB 或者 PostgreSQL,建库前就要调整连接数和 buffer pool。这里和系统优化直接挂钩:MySQL 的max_connections、innodb_buffer_pool_size如果调得很大,系统层的open_files_limit和vm.max_map_count也得跟上。经常遇到的情况是 SQL 明明已经优化完了,线上还是慢,结果iostat一看磁盘服务时间很高,mpstat一看某个核软中断 100%。这种时候,前面说的 tuned、中断绑定、文件系统对齐就全都派上用场了。

我会给一套快速定位瓶颈的命令组合:

iostat -x 2 mpstat -P ALL 2 sar -n DEV 2

看%util、单核空闲率、网卡 PPS 和丢包,基本能把 CPU、磁盘、网卡的瓶颈方向定下来,再回到对应层去调。

5.3 日志、审计和调优基线的留存

优化了半天,要是没有基线数据,后面没法证明有效果,也难定位回归。我自己的习惯是,做完一个阶段的调优后,就把这些命令输出存成快照:

tuned-adm active > baseline-tuned.txt sysctl -a > baseline-sysctl.txt ethtool -S eth0 > baseline-netstats.txt iostat -dx 1 5 > baseline-iostat.txt

RHEL 9 的日志默认全走 journald,时间久了容易越占越大。在/etc/systemd/journald.conf里限制体积,比如SystemMaxUse=500M,然后重启 journald,避免/var被撑爆。审计方面,SELinux enforcing 模式下如果某个服务起不来,第一反应应该是查ausearch -m avc -ts recent,看是不是被 SELinux 拦掉了,而不是直接setenforce 0。这个习惯能帮你避开大量无意义的“优化”,也让整个系统的安全基线好看很多。

6. 安全加固和后续维护:优化成果要能扛住时间

6.1 SELinux、firewalld 和 SSH:守住安全底线

RHEL 9.7 默认 SELinux 是 enforcing,这是很多人觉得麻烦的来源。但我强烈不建议直接关闭。最典型的场景是:Nginx 监听非标准端口、容器需要访问宿主机端口、数据库远程连接失败,这些大概率是 SELinux 策略问题。正确做法是针对服务放行,而不是一刀切禁用:

ausearch -m avc -ts recent setsebool -P nginx 1 semanage port -a -t http_port_t -p tcp 8080

firewalld 默认启用,如果前端还有硬件防火墙,也要明确机器上的规则是保留还是关闭。我一般保留 firewalld,按需开端口。SSH 这一步不能省:禁用 root 密码登录、开启密钥认证、限制 AllowUsers,最好再换掉默认端口。注意把公钥全部配好之后再关密码登录,避免把自己锁在外面。

6.2 dnf 更新策略:不要有安全更新就闭眼冲

RHEL 9.7 的软件包更新入口是 dnf,AppStream 模块经常带版本切换。生产环境我建议确定固定更新节奏,每周在变更窗口内做一次dnf update,有条件就搭内网 mirror,统一从 mirror 拉取。还要特别注意清理旧内核:

dnf remove --oldinstallonly

这个命令能清掉积攒多年的旧内核。/boot是独立分区且空间有限,不清理迟早满到无法安装新内核。第三方仓库尽量保持 disable 状态,不要全开,这也是我在第一章强调过的事情。

6.3 我自己的巡检和调优复盘方法

像 RHEL 这种长时间运行的系统,最好的维护其实是定期看。我每天只要有空就会在机器上跑一组最小命令:

uptime free -h df -hT dmesg -T -l err,emerg iostat -x 1 3 ss -lnt

这组命令能快速暴露负载异常、磁盘 IO 问题、端口异常和硬件报错。调优复盘也很重要:记录改动时间、改动内容、量化指标,比如用 fio、sysbench、ab 做前后对比,下次出问题可以直接回滚。比如压测磁盘前,我会先后台记录iostat -x,看延迟和服务时间变化;压测网络时用sar -n DEV抓丢包率,而不是只看带宽。这些才是优化真正被验证的方式。

最后说句实在话,系统优化这事不是改完一堆参数就结束了。我这些年最大的体会是:调优前一定要有基线,调优后一定要有验证,否则你根本不知道是优化有效,还是只是运气好。RHEL 9.7 的 tuned 机制加上一套自己的巡检流程,至少能让系统大方向保持稳定,后面再踩坑也有据可查。

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

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

立即咨询