跳转至

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. 目标(可验证)

  1. 新增 CentOS 7 适配层(1 个模板 + 1 个子类 + 1 个入口 + 1 份配置),既有文件零改动。
  2. 创建 centos7-01:2C / 4G / 30G HDD、192.168.31.30 / 52:54:00:31:10:01、静态 IP、仅公钥登录、autostart=false。
  3. 一次性无人值守安装成功,SSH 可达,且 guest 内 passwordauthentication no、SELinux Enforcing、swap 关闭。
  4. --drift 为 0 项;复跑一次全部 skipped(幂等)。
  5. 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。

注意事项与坑(本次实测踩到,缺一不可)

  1. network 行不能写 --device=link:CentOS 7.1 的 anaconda 对该写法支持不完整,该行一旦被忽略则整行失效——实测现象是安装正常完成、但 guest 网卡无 IP、无 ifcfg(宿主侧 virsh domifstat 显示 tx_packets 0,ARP 为 INCOMPLETE)。改为省略 --device(anaconda 取第一个网络设备)。
  2. 网络兜底:%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 差异)

  1. 不能写 sshd_config.d/ 片段:CentOS 7 系的 /etc/ssh/sshd_config 默认不 Include 该目录,片段会被整体忽略(口令登录静默保持可用)。必须直接改写 sshd_config。
  2. 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"。
  3. 同名配置项「首次匹配生效」:CentOS 7 默认文件里 PasswordAuthentication yes 是未注释行,直接把 PasswordAuthentication no 追加到末尾会被压住——实测 sshd -T 仍为 yes。故片段先 sed 删除同名旧项,再追加本工程取值(幂等,重复执行结果一致)。
  4. 交互式认证关键字用 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. 回滚

回滚分两层,互不依赖。

  1. 销毁虚机与磁盘(清掉本次的运行时交付物,不可恢复):
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.30 100% 丢包、/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
  1. 文档回退: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 同步,请勿在站点侧修改)