SOP — 新增 CentOS 7 部署能力并创建 centos7-01(临时测试机)¶
变更档案(累积式):本次新增「CentOS 7 发行版适配模块」并据此创建一台临时测试机。 入口为
main_centos7.py,实现体deployer_centos7.py+templates/centos7/ks.cfg.tmpl。 既有 Rocky 9 实现(main.py/deployer.py/templates/ks.cfg.tmpl)一行未改。
1. 背景¶
- 需要一台 CentOS 7 主机做临时测试验证;宿主已有工具只面向 Rocky 9。
- 既有工具的发行版耦合点只有两处:kickstart 模板(RHEL9 语法/包组)与 SSH 加固写法(
sshd_config.d)。其余(配置解析、门禁、三态巡检、卷/域创建、无人值守安装、等待就绪、固化域定义、气球注入、校验、回滚)与发行版无关。 - 诉求:新增模块支持 CentOS 7,且不得影响既有 Rocky 9 批次。
- CentOS 7 DVD 镜像已放至宿主
/data/kvm/iso/CentOS-7-x86_64-DVD-1511.iso(CentOS 7.1,2015 版)。
2. 目标(可验证)¶
- 新增 CentOS 7 适配层(1 个模板 + 1 个子类 + 1 个入口 + 1 份配置),既有文件零改动。
- 创建
centos7-01:2C / 4G / 30G HDD、192.168.31.30/52:54:00:31:10:01、静态 IP、仅公钥登录、autostart=false。 - 一次性无人值守安装成功,SSH 可达,且 guest 内
passwordauthentication no、SELinux Enforcing、swap 关闭。 --drift为 0 项;复跑一次全部 skipped(幂等)。- Rocky 9 批次回归:
--drift仍为 0 项。
3. 前置条件¶
| 项 | 要求 | 本次实测 |
|---|---|---|
| 执行身份 | root(写 build/ 与调 virsh qemu:///system) |
宿主 rain 免密 sudo 可用 |
| 依赖命令 | virt-install virsh qemu-img openssl |
均存在 |
| 硬件虚拟化 | /dev/kvm 存在 |
存在 |
| 存储池 | hdd-pool 已定义且 running |
running |
| ISO | target.iso 指向可读 DVD 镜像 |
CentOS-7-x86_64-DVD-1511.iso,4.1G |
| 公钥 | /home/rain/.ssh/id_ed25519.pub 可读 |
通过 |
| 地址 | IP 空闲且不与既有批次/ DHCP 池冲突;MAC 段未占用 | .30 实测空闲;MAC 段 10 未用 |
| 镜像条目录 | libosinfo 含 centos7 |
有(virt-install --osinfo list 实测) |
4. 影响范围¶
- 主机:宿主
dell-R620单机;本次只新增 1 台域centos7-01与 1 个卷hdd-pool/centos7-01.qcow2。 - 不触碰:既有 20+ 域(k8s/slurm/lustre/lsf/rocky9 等)、
br0、物理网卡、netplan、宿主内核参数。 - 业务影响:安装期(约 10 分钟)宿主磁盘 IO 与 CPU 有占用;无需停服,无窗口限制。
- 临时机定位:
autostart=false,验证结束即可用--rollback --force销毁。
5. 分步操作¶
步骤 1:新增 CentOS 7 kickstart 模板¶
复制 Rocky 9 模板改三处发行版差异:#version=DEVEL、包组 @^minimal(CentOS 7 无 minimal-environment)、keyboard us。另加两处实测必需的兼容处理(见「注意事项与坑」)。
# 模板位置(实现体按 self.templates_dir 读取,文件名沿用 ks.cfg.tmpl)
ls -l templates/centos7/ks.cfg.tmpl
# 关键行速查
grep -nE "^#version|^network|^%packages|^@\^minimal|NETDEV=|hostnamectl" templates/centos7/ks.cfg.tmpl
预期:文件存在,#version=DEVEL、@^minimal、network 行无 --device。
失败判断:模板缺失 → 门禁 [阻断] kickstart 模板,退出码 2。
注意事项与坑(本次实测踩到,缺一不可)
network行不能写--device=link:CentOS 7.1 的 anaconda 对该写法支持不完整,该行一旦被忽略则整行失效——实测现象是安装正常完成、但 guest 网卡无 IP、无 ifcfg(宿主侧virsh domifstat显示tx_packets 0,ARP 为INCOMPLETE)。改为省略--device(anaconda 取第一个网络设备)。- 网络兜底:
%post中按实际存在的网卡名补写 ifcfg(ONBOOT=yes+ 静态 IP),已含目标 IP 则跳过;顺带hostnamectl set-hostname。这一步同时覆盖「anaconda 写了 ifcfg 但网卡名与预期不符」的情况。
步骤 2:新增发行版适配子类¶
deployer_centos7.py 继承 deployer.KvmRocky9Deployer,只覆盖两点:模板目录(templates/centos7/)与 _password_auth_post_block(SSH 加固片段)。进程锁沿用父类 module_name,与 Rocky 9 批次共用,避免并发跑 virt-install。
grep -n "class KvmCentos7Deployer" -A6 deployer_centos7.py
.venv/bin/python -c "import deployer_centos7; print('import ok')"
预期:import ok。
失败判断:ImportError → 检查工程根是否在 sys.path(入口脚本须在工程根目录执行)。
注意事项与坑(CentOS 7 的 OpenSSH 6.6.1 与 Rocky 9 的 9.x 差异)
- 不能写
sshd_config.d/片段:CentOS 7 系的/etc/ssh/sshd_config默认不Include该目录,片段会被整体忽略(口令登录静默保持可用)。必须直接改写sshd_config。 PermitRootLogin取值:禁口令时写without-password;写prohibit-password(OpenSSH 7.0 起)会被 6.6.1 判为不支持并让 sshd 启动失败——实测systemctl status sshd显示code=exited, status=255循环重启,sshd -t报unsupported option "prohibit-password"。- 同名配置项「首次匹配生效」:CentOS 7 默认文件里
PasswordAuthentication yes是未注释行,直接把PasswordAuthentication no追加到末尾会被压住——实测sshd -T仍为yes。故片段先sed删除同名旧项,再追加本工程取值(幂等,重复执行结果一致)。 - 交互式认证关键字用
ChallengeResponseAuthentication,不能用 8.7 起的KbdInteractiveAuthentication(6.6.1 未知关键字同样会让 sshd 启动失败)。
步骤 3:新增入口与配置¶
# 入口(复用 main.build_parser,覆盖 prog/description/默认配置路径)
ls -l main_centos7.py
# 配置:iso / osinfo=centos7 / autostart=false / nodes / runtime
grep -nE "iso:|osinfo:|autostart:|ip:|mac:|vcpu:|memory_mib:|system_disk_gib:|credentials_file:" \
config/hosts-20260928-centos7.yaml
预期:osinfo: centos7、autostart: false、ip: 192.168.31.30、mac: 52:54:00:31:10:01、vcpu: 2、memory_mib: 4096、system_disk_gib: 30、credentials_file: config/credentials-centos7.yaml。
步骤 4:门禁体检(只读)¶
sudo .venv/bin/python main_centos7.py --precheck --config config/hosts-20260928-centos7.yaml
预期:预检查通过:全部检查项达标,退出码 0(实测 10 项全通过,含 kickstart 模板 指向 templates/centos7/ks.cfg.tmpl)。
失败判断:退出码 2 且有 [阻断] → 按逐项建议修复后重跑;有阻断时不得继续。
步骤 5:预演(零系统改动)¶
sudo .venv/bin/python main_centos7.py --dry-run --config config/hosts-20260928-centos7.yaml
预期:状态巡检:发现 2 项待变更(存储卷 + 域),每条 [dry-run] 将执行变更: …,计划写入 build/plans/plan-hosts-20260928-centos7-<时间戳>.yaml;无自启项(配置 autostart: false)。
失败判断:出现自启变更项 → 配置未生效,检查 vm_defaults.autostart。
步骤 6:真实创建¶
sudo .venv/bin/python main_centos7.py --config config/hosts-20260928-centos7.yaml
预期:状态: success | 耗时: 约 550s,总主机数: 1 | 成功: 1;日志 logs/kvm-centos7-deploy.log。
底层等价命令(实现体内部执行,供复核):
sudo virsh --connect qemu:///system vol-create-as hdd-pool centos7-01.qcow2 30G --format qcow2
sudo virt-install --connect qemu:///system --name centos7-01 --memory 4096 --vcpus 2 \
--cpu host-passthrough --machine pc --osinfo name=centos7 \
--disk path=/data/kvm/img/centos7-01.qcow2,format=qcow2,bus=virtio,cache=none,io=native,discard=unmap \
--network bridge=br0,mac=52:54:00:31:10:01,model=virtio \
--location /data/kvm/iso/CentOS-7-x86_64-DVD-1511.iso \
--extra-args "inst.ks=file:/ks.cfg inst.text console=tty0 console=ttyS0,115200n8" \
--initrd-inject build/centos7-01/ks.cfg --graphics none --console pty,target_type=serial \
--memballoon model=virtio --noautoconsole
# kickstart 以 poweroff 收尾 → 实现体检测到域转 shut off 后固化域定义(去 direct boot、注入气球策略)并启动
失败判断:末行非 success → 若为「等待安装完成」超时,多为 kickstart 语法被 anaconda 拒绝;若为「等待就绪」超时(pending=端口未监听 / unknown=不可达),按 logs/kvm-centos7-deploy.log 定位后转第 7 章回滚重来。
步骤 7:验证(见第 6 章)¶
ssh -i /home/rain/.ssh/id_ed25519 root@192.168.31.30 "sshd -T | grep -E '^passwordauthentication'"
sudo .venv/bin/python main_centos7.py --drift --config config/hosts-20260928-centos7.yaml
预期:命令行输出 passwordauthentication no;漂移 0 项、退出码 0。
6. 验证(含实测值)¶
ssh -i /home/rain/.ssh/id_ed25519 -o BatchMode=yes root@192.168.31.30 \
"hostname; hostname -I; cat /etc/redhat-release; systemctl is-active sshd chronyd network; \
sshd -T | grep -E '^(passwordauthentication|permitrootlogin|challengeresponseauthentication)'; \
getenforce; swapon --show | wc -l; nproc; free -m | sed -n 2p"
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no root@192.168.31.30 true
| 检查项 | 预期 | 本次实测 |
|---|---|---|
| 主机名 / IP | centos7-01 / 192.168.31.30 |
一致 |
| 发行版 | CentOS 7 | CentOS Linux release 7.2.1511 (Core) |
| 服务 | sshd/chronyd/network 均 active | 三个 active |
| 规格 | 2 vCPU / 4 GiB / 30G 系统盘 | nproc=2、3791 MB、/dev/vda2 29G |
| 口令登录 | 已禁用 | passwordauthentication no;认证列表 Permission denied (publickey,gssapi-keyex,gssapi-with-mic)(无 password) |
| root 登录限制 | 仅公钥 | permitrootlogin without-password |
| SELinux / swap | Enforcing / 关闭 | Enforcing、swapon 0 行 |
| 公钥 | authorized_keys 0600 |
-rw------- |
| 漂移 | 0 项、退出码 0 | 待变更 0 项,探测失败 0 项 |
| 幂等复跑 | 全跳过、零变更 | 状态: skipped,状态巡检:全部项已达标,无需变更 |
| Rocky 9 回归 | 0 项漂移 | 未发现漂移:实机与期望态一致 |
7. 回滚¶
回滚分两层,互不依赖。
- 销毁虚机与磁盘(清掉本次的运行时交付物,不可恢复):
sudo .venv/bin/python main_centos7.py --dry-run --rollback --config config/hosts-20260928-centos7.yaml # 预演:只列清单
sudo .venv/bin/python main_centos7.py --rollback --force --config config/hosts-20260928-centos7.yaml # 高危:真实销毁
预期:【高危】回滚… 横幅 → 已销毁域与磁盘:centos7-01 → 回滚完成;virsh list --all | grep centos7 无输出。
实际执行记录(2026-09-29 00:13,临时测试已完成,已按本条销毁)
- 结果:成功。日志末段为
【高危】关闭运行中的域:centos7-01→【高危】销毁域与磁盘:centos7-01→已销毁域与磁盘:centos7-01→回滚完成;复核:virsh list --all无 centos7-01(在跑域 21 → 20 台)、/etc/libvirt/qemu/centos7-01.xml已删除、hdd-pool卷列表无centos7-01.qcow2、ping 192.168.31.30100% 丢包、/data与/var/lib/libvirt/images无残留。 - 首次执行失败(踩坑留痕):命令漏加
sudo,在log_manager.setup_all()里创建TimedRotatingFileHandler时抛PermissionError(logs/目录与日志文件由历次sudo运行创建,属主 root,普通用户无写权限)。销毁逻辑一行未执行,虚机不受影响。 -
判据(可复用):报错栈停在
python3.12/logging/handlers.py的open(),且输出中没有任何【高危】行 → 说明仍处于日志初始化阶段、尚未进入回滚流程,属无害失败,补sudo重跑即可,不必担心「半销毁」。 -
撤销本次新增能力(删除新增文件即可,既有文件不受影响):
rm -f deployer_centos7.py main_centos7.py \
templates/centos7/ks.cfg.tmpl \
config/hosts-20260928-centos7.yaml config/credentials-centos7.yaml
rmdir templates/centos7
- 文档回退:
runbook-20260925/RUNBOOK.md与docs/HANDOFF.md的本次改动,用备份覆盖回去(备份位置见下)。
备份位置:centos7-20260928/backup/,逐条登记见 backup/README.md。本次仅改动了上述两份文档,代码文件全为新增、无需备份。
回滚后验证:sudo .venv/bin/python main.py --drift --config config/hosts-20260925-rocky9.yaml 仍应 0 项(确认未伤及 Rocky 9);virsh list --all 中不应残留 centos7-01。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| CentOS 7 已于 2024-06-30 EOL,无安全更新,yum 源需指向 vault | 定位为临时测试机;不承载长期业务;用完回滚销毁 |
| 镜像为 CentOS 7.1(1511),anaconda/OpenSSH 均为老版本,兼容性与常见文档不同 | 两处老版本坑(网络行、sshd 取值)已在实现体与模板中固化为兼容写法,并在本档第 5 章留痕 |
| CentOS 7 网络配置依赖 anaconda 行为,各小版本不一致 | 模板已双保险:network 行 + %post 兜底写 ifcfg |
| 永久禁用口令认证后,公钥丢失将无法登录 | 口令仍在 config/credentials-centos7.yaml(0600,gitignore),必要时改配置重跑或经串口 console 处置 |
| 临时机遗忘导致长期占用宿主资源 | autostart=false;回滚一条命令可清 |
| 新增子类复用了父类进程锁(共用锁文件),Rocky 9 与 CentOS 7 批次不能并发执行 | 属刻意设计(避免并发 virt-install 争抢);如需并行需另行评估 |
| 宿主内存偏紧(当前可用约 28G) | 本机仅 4G,且气球兜底 |
附录 A:交付物清单¶
| 文件 | 性质 | 说明 |
|---|---|---|
templates/centos7/ks.cfg.tmpl |
新增 | CentOS 7 kickstart 模板(含网络兜底、主机名兜底) |
deployer_centos7.py |
新增 | 适配子类:模板目录 + SSH 加固片段 |
main_centos7.py |
新增 | 入口(复用 main.build_parser) |
config/hosts-20260928-centos7.yaml |
新增 | CentOS 7 批次配置 |
config/credentials-centos7.yaml |
运行时生成 | 随机 root 口令,0600,gitignore |
runbook-20260925/RUNBOOK.md |
修改 | 新增第 12 章 CentOS 7 批次、速查表与配置清单各加一行 |
docs/HANDOFF.md |
修改 | 更新当前状态与待办 |
.trae/skills/kvm-guest-preflight/SKILL.md |
新增 | 项目级技能:跨发行版建机前预检 + 装后自检(含版本差异矩阵) |
.trae/skills/kvm-guest-preflight/scripts/guest-triage.sh |
新增 | 宿主侧只读取证脚本:域状态 / 网卡收发计数 / 串口回显 |
.trae/skills/kvm-guest-preflight/assets/ks-selfcheck.snippet |
新增 | 追加到 kickstart %post 的装后自检片段(已在本机 guest 实跑验证) |
centos7-20260928/SOP.md、backup/README.md |
新增 | 本档案与备份台账 |
附录 B:排障实录(本次三次重装)¶
| 轮次 | 现象 | 根因(证据) | 处置 |
|---|---|---|---|
| 1 | 安装完成、SSH 永不可达 | guest 无 IP:virsh domifstat tx_packets 0、宿主 ARP 192.168.31.30 INCOMPLETE → CentOS 7.1 未落实 network --device=link |
去掉 --device,并加 %post 兜底写 ifcfg |
| 2 | ping 通、22 端口 connection refused(探测一直 pending) |
sshd 循环重启 code=exited, status=255;串口登录执行 sshd -t → unsupported option "prohibit-password"(6.6.1 不认该值) |
取值改 without-password |
| 3 | 安装、SSH 均成功,但 sshd -T 显示 passwordauthentication yes |
sshd「首次匹配生效」,默认文件未注释的 PasswordAuthentication yes 压住了末尾追加的 no |
片段改为先 sed 删除同名旧项再追加 |
| 4 | 全新安装一次通过 | — | 保留 |
轮次 2、3 的取证手段:
virsh domifstat/virsh ttyconsole拿到串口 pty 后经tee写入登录并回读sshd -t、systemctl status sshd输出。此手法无副作用,后续排障可复用。
附录 C:决策经过¶
- 方案选择:新增模块(子类继承 + 独立模板目录 + 独立入口)而非改造既有实现体。利用父类按实例属性
self.templates_dir读模板这一点,用「换目录、模板同名」实现零代码侵入;_render_kickstart与门禁两处读取路径自然命中新目录。 - 未采用「加
target.kickstart_template配置项」的原因:用户明确要求不影响既有功能,且该方案需改deployer.py、config_schema.py两个既有文件。 - 进程锁沿用父类
module_name(kvm_rocky9_deploy)属知情取舍:日志与锁标识与 Rocky 9 批次相同,但换来「两批次不并发」的保护;产物按config_label区分,不会互相覆盖。
来源:
/home/rain/Documents/kvm/kvm_rocky9_deploy/centos7-20260928/SOP.md| 最后更新:2026-09-29 00:21(由build/sync.py同步,请勿在站点侧修改)