关于 CentOS 7 至 Stream 10 (及 Rocky/Alma) 装机自动应答 ks 文件指令的重大差异
引言
在企业级服务器部署中,自动化安装是提升效率、保证一致性的关键。CentOS 作为广泛使用的 Linux 发行版,其从版本 7 到版本 10 的演进过程中,用于自动化安装的 Kickstart(ks)文件格式与指令发生了一系列破坏性变化。
⚠️ 重要版本说明
传统的静态版 "CentOS Linux" 在版本 8 之后已停止维护。目前的生态系统中,"CentOS 9/10" 通常指 CentOS Stream(上游开发版),而生产环境稳定版多指 Rocky Linux 或 AlmaLinux。本文所述指令适用于 RHEL 8/9/10 及其所有下游衍生版。
对于依赖 PXE(Preboot Execution Environment)网络启动进行大规模部署的系统管理员而言,理解这些差异至关重要。特别是从 CentOS 8 开始引入的“纯命令行模式”强制要求,以及 CentOS 9/10 中对旧版安装指令的废弃,如果沿用 CentOS 7 的脚本,安装将直接失败。本文将深入解析这些核心差异。
核心变革:启动模式与安装指令
这是本次更新中最关键的变动,直接决定了自动化安装能否启动。
1. 启动模式:从 text 到强制 cmdline
在 CentOS 7 时代,为了实现自动化,我们通常在内核参数中添加 text 来跳过图形界面,或者依靠 ks 文件自动触发文本模式。
- CentOS 7: 支持
text模式。内核参数可以是inst.text或不指定(Anaconda 会自动检测并尝试图形化,若检测到 ks 文件则降级)。 - CentOS 8 / 9 / 10:
text模式已被彻底移除。- 如果你在内核参数中写
inst.text,安装程序会报错或直接忽略。 - 必须使用
inst.cmdline。 - 原因:RHEL 8+ 的 Anaconda 安装程序架构重构,完全非交互式安装(Unattended Install)只能在
cmdline模式下运行。如果在图形模式或文本模式下运行且遇到任何未定义的选项(即使有 ks 文件),安装程序也会暂停并等待人工输入,导致自动化失败。
- 如果你在内核参数中写
修正后的 PXE 引导配置示例:
# CentOS 7 (兼容写法,但推荐也加 cmdline)
append initrd=initrd.img inst.ks=http://server/ks.cfg inst.text
# CentOS 8 / 9 / 10 (必须这样写!)
append initrd=initrd.img inst.ks=http://server/ks.cfg inst.cmdline
2. install 指令的废弃 (CentOS 9/10 特有)
在早期的 Kickstart 语法中,习惯先写一行 install 来声明进入安装模式,然后再指定源。
- CentOS 7 / 8: 支持
install指令(虽然在 8 中已不推荐,但仍能工作)。 - CentOS 9 / 10:
install指令已被完全移除。- 如果在 ks 文件第一行写
install,Anaconda 会直接报错退出,提示未知指令。 - 正确做法:直接指定安装源指令(如
url,cdrom,nfs等),无需前置声明。
- 如果在 ks 文件第一行写
错误写法 (CentOS 9/10 会失败):
install
url --url="http://mirror.centos.org/..."
正确写法 (通用且符合新版规范):
# 直接定义源,去掉 install
url --url="http://mirror.centos.org/..."
软件包管理与仓库配置的深层差异
除了上述启动层面的巨变,底层的包管理机制也发生了根本性转变。
CentOS 7:Yum 与传统仓库
- 包管理器:
yum。 - 仓库结构:单一的大型仓库(Base, Updates, Extras)。
- 语法:直接使用
repo定义 URL,在%packages中列出包名即可。
CentOS 8/9/10:DNF 与模块化 (AppStream)
- 包管理器:
dnf。 - 仓库结构:分裂为 BaseOS (核心系统) 和 AppStream (应用流)。
- 模块化 (Modules):这是最大的痛点。软件包(如 PostgreSQL, NodeJS, Python)被组织成模块,每个模块有多个版本流(Stream)。
- 必须显式启用模块流:在安装包之前,必须使用
module --name=<name> --stream=<version>指令。如果不启用,DNF 可能不知道安装哪个版本,或者安装默认版本而非你需要的版本。 - 语法陷阱:不能在
%packages中使用@module:name这种旧式写法,必须用独立的module指令。
- 必须显式启用模块流:在安装包之前,必须使用
CentOS 8/9/10 标准片段:
# 1. 定义分离的仓库
repo --name="BaseOS" --baseurl=...
repo --name="AppStream" --baseurl=...
# 2. 【关键】启用模块流 (必须在 %packages 之前)
module --name=postgresql --stream=15
module --name=nodejs --stream=18
# 3. 选择包
%packages
@^minimal-environment
postgresql-server # 这将安装上面指定的 v15 版本
%end
分区与服务管理的演变
分区策略的变化
- CentOS 7:
autopart默认会创建/,/home,swap等完整布局。 - CentOS 8/9/10:
autopart的默认行为改变,不再自动创建/home,所有空间默认给根目录。如果需要/home,必须显式添加--home参数或手动分区。 - 文件系统:XFS 成为绝对默认,Btrfs 支持在 RHEL/CentOS 主线中被进一步边缘化。
服务管理:告别 services 指令
虽然 CentOS 7 已经使用 systemd,但 Kickstart 中的 services --enabled=xxx 指令在旧版中尚可工作。
- 现状:在 CentOS 8/9/10 中,
services指令经常失效或被忽略,尤其是在复杂的模块依赖场景下。 - 最佳实践:完全弃用
services指令。所有服务的启用、禁用、掩蔽操作,统一写入%post脚本中,使用systemctl命令执行。这不仅能确保生效,还能记录日志。
%post
# 推荐做法
systemctl enable sshd
systemctl disable firewalld
systemctl mask NetworkManager-wait-online.service
%end
实战:编写一个适配 CentOS 7-10 的高兼容 ks 文件
为了应对上述差异,特别是 install 指令的废弃和 cmdline 的强制要求,我们需要编写更智能的脚本。虽然内核参数 (inst.cmdline) 需要在 PXE 配置中区分,但 ks 文件内部可以通过 %pre 脚本来动态适配版本。
核心策略:
- 移除
install:全篇不再出现该指令。 - 动态检测 AppStream:在
%pre中检查安装源是否有AppStream目录,以此判断是 CentOS 7 还是 8+。 - 动态生成 Module 指令:如果是 8+,动态写入
module指令;如果是 7,则跳过。
高兼容性 ks 模板片段:
# 基础设置 (通用)
lang en_US.UTF-8
keyboard us
timezone Asia/Shanghai --utc
rootpw --iscrypted $6$...
# 注意:这里没有 'install' 指令!直接定义源
# 使用 $releasever 变量,Anaconda 会自动替换为版本号
url --url="http://mirror.example.com/centos/$releasever/"
# %pre 脚本:动态适配版本差异
%pre --interpreter=/bin/bash
#!/bin/bash
REPO_PATH="/run/install/repo"
# 某些环境下可能需要挂载,此处省略挂载逻辑,假设已挂载
# 检测是否为 CentOS 8/9/10 (存在 AppStream 目录)
if [ -d "$REPO_PATH/AppStream" ]; then
echo "Detected CentOS 8/9/10+"
# 生成针对 8/9/10 的配置
cat > /tmp/ks_modular.cfg << EOF
repo --name="BaseOS" --baseurl=file://$REPO_PATH/BaseOS
repo --name="AppStream" --baseurl=file://$REPO_PATH/AppStream
# 启用必要的模块流
module --name=postgresql --stream=15
module --name=nodejs --stream=18
EOF
else
echo "Detected CentOS 7"
# 生成针对 CentOS 7 的配置
cat > /tmp/ks_modular.cfg << EOF
repo --name="Base" --baseurl=file://$REPO_PATH
EOF
fi
%end
# 引入动态生成的配置
%include /tmp/ks_modular.cfg
# 分区 (兼容写法)
clearpart --all --initlabel
# 8/9/10 需要显式指定 --home 如果需要独立/home,或者手动分区
autopart --type=lvm
# 软件包
%packages
@^minimal-environment
@core
vim-enhanced
wget
# 模块包
postgresql-server
nodejs
%end
# 后置脚本:统一管理服务
%post
systemctl enable sshd
systemctl start sshd
# 根据版本执行特定逻辑(可选)
if [ -d "/run/install/repo/AppStream" ]; then
# CentOS 8+ 特定优化
dnf clean all
else
# CentOS 7 特定优化
yum clean all
fi
%end
总结与运维建议
从 CentOS 7 到 CentOS Stream 10,自动化安装的门槛在细节上变高了,主要体现在以下三点:
- 引导参数强制化:必须使用
inst.cmdline,text模式已成为历史。 - 指令语法精简与严格化:
install指令被废弃,必须直接定义源;模块化管理要求必须先module后package。 - 逻辑后置:服务管理等操作应更多地在
%post中通过脚本完成,而非依赖可能失效的内置指令。
在进行大规模 PXE 部署时,建议维护两套 PXE 配置文件(一套带 inst.text 对应旧机器,一套带 inst.cmdline 对应新机器),但尽量复用同一套经过 %pre 动态处理的 Kickstart 文件。通过理解这些底层逻辑的变更,您可以构建出既能兼容老旧资产,又能平滑演进到新架构的健壮自动化部署体系。
参与讨论