KVM 宿主按配置创建 5 台 Rocky 9 主机 — 变更 SOP¶
工程位置:
kvm_rocky9_deploy/本次配置:config/hosts-20260918.yaml(实现体:deployer.py、main.py、templates/ks.cfg.tmpl) 本文档为 C 类生产变更交付物;第 5 章为文档呈现,一切以实现体为准。
1. 背景¶
本次需要在一台既有 KVM 宿主(Dell PowerEdge R620)上创建 5 台 Rocky 9 主机,规格与 地址由使用方直接指定。宿主上已有另一批次创建的 6 台 k8s 主机与 1 台 OpenWrt 旁路由。
工程原先只服务于 k8s 一批主机(节点角色、etcd 数据盘、关闭 swap 等都写死在实现体与 模板里)。本次一并完成两件事:
- 实现体通用化:把与用途绑定的行为改为配置项,工程定位收敛为「按配置在 KVM 上 创建主机」——主机名、IP、MAC、CPU/内存、磁盘(数量、容量、格式、存储池、挂载点)、 登录方式(公钥或口令)全部由配置文件给出,不配即不生效。
- 新增本批次配置并创建 5 台主机。
通用化后新增的可选项(均有默认值,不配置即维持原行为):
| 配置项 | 作用 | 默认 |
|---|---|---|
vm_defaults.disable_swap |
安装时是否关闭 swap | true |
vm_defaults.deflate_on_oom |
是否启用内存气球 deflate-on-oom | true |
ssh.pubkey_file |
注入的公钥;省略则不注入公钥 | 无 |
ssh.disable_password_auth |
是否禁用口令登录 | true |
ssh.root_password |
指定 root 口令;省略则随机生成并落盘 | 随机 |
nodes[].data_disks[] |
数据盘列表,每块可配容量/格式/存储池/挂载点/卷名后缀 | 无 |
host_tuning.* |
宿主内核参数与 KSM;整段省略则完全不碰宿主 | 不涉及 |
2. 目标¶
变更完成后应达到以下可验证状态:
| 目标项 | 期望值 |
|---|---|
| 主机数量 | 5 台:slurm-master-01、slurm-master-02、slurm-work-01、slurm-work-02、slurm-work-03 |
| 操作系统 | Rocky Linux 9.x |
| 规格 | master-01 4 vCPU / 4 GiB;master-02 2 vCPU / 2 GiB;work-01~03 各 8 vCPU / 16 GiB |
| 系统盘 | master 各 20 GiB、work 各 100 GiB(qcow2,落在 hdd-pool,稀疏分配) |
| 数据盘 | 无 |
| 网络 | 桥接 br0,静态 IP 192.168.31.16–20,网关与 DNS 均为 192.168.31.2 |
| 登录 | 仅公钥登录(PasswordAuthentication no),注入宿主 rain 的 id_ed25519.pub |
| swap | 关闭 |
| 开机自启 | 5 台均 autostart=yes |
| 内存气球策略 | 5 台均启用 deflate-on-oom |
| 既有环境 | 宿主内核参数与 KSM 服务零改动;既有 6 台 k8s 主机与 openwrt 零改动 |
| 幂等 | 连续第二次执行零变更、全跳过 |
3. 前置条件¶
权限与身份
- 以 root 执行(需写
build/与config/下的产物,并调用virsh qemu:///system);宿主rain用户已配置免密 sudo。
依赖组件(均已存在)
| 组件 | 要求 | 校验命令 |
|---|---|---|
| libvirt / QEMU | libvirt 10.0、QEMU 8.2 | virsh version |
| virt-install | 4.1.0(--osinfo 必填) |
virt-install --version |
| libosinfo | 含 rocky9 条目 |
osinfo-query os \| grep rocky9 |
| ISO | /data/kvm/iso/Rocky-9-latest-x86_64-dvd.iso |
ls -l /data/kvm/iso/ |
| 存储池 | hdd-pool 处于 running |
virsh pool-list |
| SSH 公钥 | /home/rain/.ssh/id_ed25519.pub |
ls -l /home/rain/.ssh/id_ed25519.pub |
执行前检查项(本次已核查)
| 检查项 | 结论 |
|---|---|
目标 IP .16–.20 是否占用 |
逐台 ping 确认空闲 |
MAC 段 52:54:00:31:0b:01~05 是否冲突 |
比对全部既有域,无冲突 |
小米路由器 DHCP 池是否覆盖 .16–20 |
待人工确认,若覆盖需在路由器上排除该段 |
| 宿主内存余量 | 117 GiB total;本批次名义占用 54 GiB |
| 宿主在跑作业 | 宿主上无调度类作业;既有主机保持运行 |
4. 影响范围¶
变更主机:KVM 宿主 dell-R620(唯一主机,全部操作在本机执行)。
工程侧改动(实现体通用化)
| 文件 | 动作 |
|---|---|
deployer.py |
改写:节点角色/etcd 盘/关 swap 等改为配置驱动与可选,新增取值白名单校验 |
main.py |
文案通用化,无逻辑变更 |
templates/ks.cfg.tmpl |
占位符拆分为公钥/认证/数据盘/swap 四段,由配置决定是否生成 |
config/deploy.yaml、config/deploy.yaml.example |
改写为通用格式(原 k8s 批次语义等价:卷名仍为 <主机名>-etcd.raw,挂载点仍为 /var/lib/etcd) |
宿主侧改动:无。本批次配置未包含 host_tuning,实现体不读写
/etc/sysctl.d/99-kvm-host.conf 与 /etc/systemd/system/ksm.service。
虚机侧新增
| 对象 | 动作 |
|---|---|
磁盘卷 hdd-pool/slurm-*.qcow2 |
新增 5 个(20G×2、100G×3,qcow2 稀疏) |
| 域定义 | 新增 5 个,并设置开机自启 |
| 主机内配置(kickstart 注入) | 静态网络、主机名与 hosts、root 口令哈希、SSH 公钥、禁用口令认证、关闭 swap |
是否停服:不停服。不重启宿主、不动 br0/netplan/物理网卡、不修改既有 6 台
k8s 主机与 openwrt 的任何定义或磁盘。
窗口期:无人值守安装按既有同规格实测(约 6–10 分钟/台)推算,并发度 2、共 5 台, 约 20–30 分钟。期间宿主 HDD 持续写入、CPU 与 IO 负载上升,属预期现象。
在跑业务:宿主上 openwrt 与 3 台 k8s 主机处于运行态(k8s-master-02/03 为 shut off),
本次变更不触碰它们。
5. 分步操作¶
对应实现文件:
deployer.py(生命周期编排)、main.py(入口)、templates/ks.cfg.tmpl(kickstart 模板);配置:config/hosts-20260918.yaml。 以下为与实现等价的操作序列,供阅读与手工复现;幂等、备份与错误处理由实现体保证。 命令均在工程根目录kvm_rocky9_deploy/下执行。
5.1 预演(不改动任何状态)¶
sudo .venv/bin/python main.py --dry-run --config config/hosts-20260918.yaml # 仅输出待变更清单
期望输出:配置解析完成:5 个节点、数据盘池=(未配置)、宿主调优=不涉及、
前置校验通过、10 项待变更(5 个系统盘 + 5 个域)。已实测通过。
5.2 宿主层:本批次不做任何改动¶
# 本配置未包含 host_tuning,实现体跳过宿主层;不生成、不修改、不删除
# /etc/sysctl.d/99-kvm-host.conf 与 /etc/systemd/system/ksm.service。
# 如需为其他批次改动宿主参数,在对应配置中显式声明 host_tuning 段。
5.3 创建系统卷(稀疏分配,不预占宿主空间)¶
virsh --connect qemu:///system vol-create-as hdd-pool slurm-master-01.qcow2 20G --format qcow2
virsh --connect qemu:///system vol-create-as hdd-pool slurm-master-02.qcow2 20G --format qcow2
virsh --connect qemu:///system vol-create-as hdd-pool slurm-work-01.qcow2 100G --format qcow2
virsh --connect qemu:///system vol-create-as hdd-pool slurm-work-02.qcow2 100G --format qcow2
virsh --connect qemu:///system vol-create-as hdd-pool slurm-work-03.qcow2 100G --format qcow2
# 本批次无数据盘(nodes[].data_disks 未配置),故不存在第二块盘
5.4 渲染 kickstart 并无人值守安装(每台一次,并发度 2)¶
# 每台渲染一份 build/<主机名>/ks.cfg,含:静态网络、主机名与 hosts 块、root 口令哈希、
# SSH 公钥注入、禁用口令认证、关闭 swap;本批次未配置数据盘,数据盘片段为空。
virt-install --connect qemu:///system \
--name slurm-master-01 \
--memory 4096 --vcpus 4 --cpu host-passthrough --machine pc \
--osinfo name=rocky9 \
--disk path=/data/kvm/img/slurm-master-01.qcow2,format=qcow2,bus=virtio,cache=none,io=native,discard=unmap \
--network bridge=br0,mac=52:54:00:31:0b:01,model=virtio \
--location /data/kvm/iso/Rocky-9-latest-x86_64-dvd.iso \
--initrd-inject build/slurm-master-01/ks.cfg \
--extra-args "inst.ks=file:/ks.cfg inst.text console=tty0 console=ttyS0,115200n8" \
--graphics none --console pty,target_type=serial \
--memballoon model=virtio --autostart --noautoconsole
# 注意:不要加 --boot hd,它会覆盖 virt-install 的 direct kernel boot
# 其余 4 台同构,仅替换 --name / --memory / --vcpus / --disk / --mac 取值
kickstart 以 poweroff 收尾(而非 reboot):此时域定义仍带 direct kernel boot 参数,
直接重启会再次进入安装器形成循环。
5.5 等待安装结束并固化域定义¶
# 判据:域转为 shut off(实现体按 15s 间隔轮询,上限 2400s)
virsh --connect qemu:///system domstate slurm-master-01 # 期望 shut off
virsh --connect qemu:///system dumpxml slurm-master-01 --inactive > build/slurm-master-01/domain.xml
# 移除 <kernel>/<initrd>/<cmdline>,改写为 <boot dev='hd'/>,使系统从硬盘引导
virsh --connect qemu:///system define build/slurm-master-01/domain.xml
virsh --connect qemu:///system start slurm-master-01
5.6 启用内存气球策略(deflate-on-oom)¶
# 说明:libvirt 10.0 的能力检测误判 QEMU 不支持该属性,直接写在 <memballoon> 内会被静默丢弃;
# 实测 QEMU 8.2 支持,故经 qemu:commandline 传 -global 绕过。
# 高危:以下操作会关闭并重启该主机,请在业务空闲窗口执行(新建主机此时无业务,风险可控)
virsh --connect qemu:///system destroy slurm-master-01
virsh --connect qemu:///system define build/slurm-master-01/domain.xml # 已含 qemu:commandline
virsh --connect qemu:///system start slurm-master-01
5.7 设置开机自启¶
virsh --connect qemu:///system autostart slurm-master-01
6. 验证¶
# 1) 域状态:5 台均为 running,且既有 6 台 k8s 与 openwrt 状态不变
virsh --connect qemu:///system list --all
# 2) 引导方式已固化:不应出现 <kernel>/<initrd>
virsh --connect qemu:///system dumpxml slurm-master-01 --inactive | grep -cE "<kernel>|<initrd>" # 期望 0
# 3) 内存气球策略:应输出 -global 与 virtio-balloon-pci.deflate-on-oom=on
virsh --connect qemu:///system dumpxml slurm-master-01 | grep -A4 qemu:commandline
# 4) 主机内部(对 5 台逐一执行,IP 取 .16–.20)
ssh -o BatchMode=yes root@192.168.31.16 '
hostname; cat /etc/redhat-release; nproc; free -h | tail -n1;
sshd -T | grep -i ^passwordauthentication;
wc -l < /root/.ssh/authorized_keys;
grep -c "[[:space:]]swap[[:space:]]" /etc/fstab'
# 期望:主机名与配置一致、Rocky Linux 9.x、nproc 与规格一致、swap 合计 0B、
# passwordauthentication no、授权密钥 1 行、fstab 中 swap 行已注释
# 5) 磁盘容量
virsh --connect qemu:///system domblklist slurm-work-01 # 系统盘指向 100G 卷
qemu-img info /data/kvm/img/slurm-work-01.qcow2 | grep virtual
# 6) 既有环境未受影响的抽查
virsh --connect qemu:///system dumpxml k8s-master-01 | grep -c "52:54:00:31:0a:01" # 期望 1
sysctl -n vm.swappiness # 期望 10(本批次未改动)
systemctl is-enabled ksm.service # 期望 enabled(本批次未改动)
# 7) 幂等复跑:第二次应零变更
sudo .venv/bin/python main.py --config config/hosts-20260918.yaml
# 期望输出:状态巡检:全部项已达标,无需变更 / 状态: skipped
7. 回滚¶
备份方式:实现体在改写既有文件前自动做带时间戳备份(<文件名>.bak.<YYYYmmdd-HHMMSS>)。
本批次宿主侧无改动,主机侧全部为新增对象。
回滚步骤
# 高危:销毁本配置中的全部 5 台主机与其磁盘数据(不可恢复)
sudo .venv/bin/python main.py --rollback --config config/hosts-20260918.yaml
由于本配置未包含 host_tuning,回滚只删主机与磁盘,不会触碰宿主内核参数与 KSM 服务,
也就不会影响 k8s 批次所依赖的宿主调优。等价的手工操作:
# 逐台执行(仅作用于本批次主机)
virsh --connect qemu:///system destroy slurm-master-01 # 高危:强制关闭
virsh --connect qemu:///system undefine slurm-master-01 --remove-all-storage # 高危:连磁盘一并删除
# slurm-master-02 / slurm-work-01 / slurm-work-02 / slurm-work-03 同理
# 清理本次凭据文件(含明文口令)
rm -f config/credentials-slurm.yaml # 高危:删除凭据文件
回滚后验证
virsh list --all中不再出现 5 台主机,且 k8s 6 台与 openwrt 状态与变更前一致ls /data/kvm/img/slurm-*.qcow2无输出sysctl -n vm.swappiness仍为 10、systemctl is-enabled ksm.service仍为 enabled
工程侧改造的回退:改造前的 deployer.py、main.py、templates/ks.cfg.tmpl、
config/deploy.yaml* 已留存快照于 _backup_before_refactor_20260918/,需要时可直接覆盖还原
(还原后本批次的通用格式配置需一并回退)。确认新版本稳定后该目录可删除。
8. 风险说明¶
| 风险点 | 影响 | 缓解措施 |
|---|---|---|
| 宿主内存压力 | 本批次名义 54 GiB;若 k8s-master-02/03 同时开机(+8 GiB),可用余量仅约 9 GiB |
已启用 deflate-on-oom;如不需常驻,可对部分主机执行 virsh autostart --disable,或将配置中 vm_defaults.autostart 改为 false 后重跑 |
| 静态 IP 与路由器 DHCP 池冲突 | 间歇性断网,且难归因 | 执行前确认 DHCP 池范围,或在路由器上排除 192.168.31.16–20(本项尚未人工确认) |
--boot hd 覆盖 direct boot |
域无 <kernel>,主机从空盘引导后立即关机,且无日志 |
不传 --boot;安装后由实现体移除 direct boot 参数 |
| kickstart 以 reboot 收尾会循环安装 | 域定义仍带 direct boot,重启再次进安装器 | 模板固定 poweroff 收尾,由实现体固化定义后再启动 |
| libvirt 静默丢弃 deflate-on-oom | virsh define 返回成功但配置未生效 |
经 qemu:commandline 传 -global;验证时直接查域 XML |
| 并发安装争抢 HDD IO | 安装变慢 | 并发度限制为 2;如需更快可提高,但会加剧争抢 |
| 关闭 swap 的影响 | 内存超用时无换页兜底,进程可能被 OOM 终止 | 由 vm_defaults.disable_swap 控制;若使用方需要 swap,改为 false 重建即可 |
| 配置取值进入 guest 内脚本 | 错误的挂载点/主机名可能破坏 kickstart 或造成脚本注入 | 实现体对主机名、IP、MAC、卷名后缀、挂载点做白名单校验,并在批次内查重,不通过则前置校验直接失败 |
| root 口令 | 明文落盘 | 未在配置中指定时由实现体随机生成 24 位口令存于 config/credentials-slurm.yaml(已 gitignore),供控制台兜底登录 |
| 历史文档漂移 | docs/SOP.md 为 k8s 批次的旧文档,其第 5 章仍按改造前的字段描述(如 etcd 盘) |
该文档第 5 章为「文档呈现」,以实现体为准;如需对外交付应同步更新 |
来源:
/home/rain/Documents/kvm/kvm_rocky9_deploy/docs/SOP-hosts-20260918.md| 最后更新:2026-09-18 03:19(由build/sync.py同步,请勿在站点侧修改)