关于 CentOS 7 至 Stream 10 (及 Rocky/Alma) 装机自动应答 ks 文件指令的重大差异

引言

在企业级服务器部署中,自动化安装是提升效率、保证一致性的关键。CentOS 作为广泛使用的 Linux 发行版,其从版本 7 到版本 10 的演进过程中,用于自动化安装的 Kickstart(ks)文件格式与指令发生了一系列破坏性变化

⚠️ 重要版本说明

传统的静态版 "CentOS Linux" 在版本 8 之后已停止维护。目前的生态系统中,"CentOS 9/10" 通常指 CentOS Stream(上游开发版),而生产环境稳定版多指 Rocky LinuxAlmaLinux。本文所述指令适用于 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 等),无需前置声明。

错误写法 (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 脚本来动态适配版本。

核心策略:

  1. 移除 install:全篇不再出现该指令。
  2. 动态检测 AppStream:在 %pre 中检查安装源是否有 AppStream 目录,以此判断是 CentOS 7 还是 8+。
  3. 动态生成 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,自动化安装的门槛在细节上变高了,主要体现在以下三点:

  1. 引导参数强制化:必须使用 inst.cmdlinetext 模式已成为历史。
  2. 指令语法精简与严格化install 指令被废弃,必须直接定义源;模块化管理要求必须先 modulepackage
  3. 逻辑后置:服务管理等操作应更多地在 %post 中通过脚本完成,而非依赖可能失效的内置指令。

在进行大规模 PXE 部署时,建议维护两套 PXE 配置文件(一套带 inst.text 对应旧机器,一套带 inst.cmdline 对应新机器),但尽量复用同一套经过 %pre 动态处理的 Kickstart 文件。通过理解这些底层逻辑的变更,您可以构建出既能兼容老旧资产,又能平滑演进到新架构的健壮自动化部署体系。