KVM 宿主创建 Rocky 9 k8s 节点集群 — 变更 SOP¶
工程位置:
kvm_rocky9_deploy/(Python 幂等部署工程,基于共享库py_public_modules) 本文档为 C 类生产变更的交付物;第 5 章为文档呈现,一切以实现体为准。
1. 背景¶
需要在 Dell PowerEdge R620(KVM 宿主)上创建 6 台 k8s 节点(3 master + 3 node),
用于个人学习与测试环境。宿主已运行 OpenWrt 旁路由虚机,并已存在 hdd-pool(HDD)
与 ssd-pool(SSD)两个 libvirt 存储池。
关键约束(本次变更的核心决策依据):宿主 CPU 为 Intel Xeon E5-2650 v0
(Sandy Bridge,2012),仅支持到 x86-64-v2 微架构级别(有 AVX、无 AVX2/BMI2/FMA)。
而 RHEL/Rocky 10 起要求 x86-64-v3,安装时 glibc 会直接终止:
Fatal glibc error: CPU does not support x86-64-v3。
因此节点操作系统必须使用 Rocky 9(要求 x86-64-v2),该架构基线差异不可绕过。
2. 目标¶
变更完成后应达到以下可验证状态:
| 目标项 | 期望值 |
|---|---|
| 节点数量与角色 | 6 台:k8s-master-01/02/03(master)、k8s-node-01/02/03(node) |
| 操作系统 | Rocky Linux 9.8 |
| 规格 | master 4 vCPU / 8 GiB;node 8 vCPU / 16 GiB |
| 系统盘 | master 50 GiB、node 100 GiB(qcow2,落在 hdd-pool) |
| 数据盘 | 仅 master:1 块 10 GiB raw,落在 ssd-pool,挂载至 /var/lib/etcd(配置项 nodes[].data_disks,suffix: etcd) |
| 网络 | 桥接 br0,静态 IP 192.168.31.10–15,网关与 DNS 均为 192.168.31.2 |
| SSH | 仅公钥登录(PasswordAuthentication no),注入宿主 id_ed25519.pub(配置项 ssh.pubkey_file 与 ssh.disable_password_auth) |
| swap | 关闭(配置项 vm_defaults.disable_swap: true) |
| 开机自启 | 6 台均 autostart=yes |
| 内存气球策略 | 6 台均启用 deflate-on-oom(配置项 vm_defaults.deflate_on_oom: true) |
| 宿主调优 | vm.swappiness=10;KSM 持久化单元已启用 |
| 幂等 | 连续第二次执行零变更、全跳过 |
3. 前置条件¶
权限与身份
- 以 root 执行(需写
/etc并调用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(卷标 Rocky-9-8-x86_64-dvd) |
blkid <iso> |
| 存储池 | hdd-pool(/data/kvm/img)、ssd-pool(/kvm/img)均为 running |
virsh pool-list |
| AppArmor | 已放行 /kvm/** 与 /data/** |
cat /etc/apparmor.d/local/abstractions/libvirt-qemu |
| 嵌套虚拟化 | kvm_intel 的 nested=Y |
cat /sys/module/kvm_intel/parameters/nested |
执行前检查项
- 目标 IP
192.168.31.10–15未被占用(ping逐台确认) - 小米路由器 DHCP 池不覆盖上述地址段,否则会出现静态 IP 与 DHCP 租约冲突
- 宿主剩余内存充足:6 台名义内存合计 72 GiB,宿主物理 117 GiB
4. 影响范围¶
变更主机:KVM 宿主 dell-R620(唯一主机,本次操作全部在本机执行)。
宿主侧改动(2 处,均为新增文件)
| 路径 | 动作 |
|---|---|
/etc/sysctl.d/99-kvm-host.conf |
新增(vm.swappiness=10),已存在则先带时间戳备份 |
/etc/systemd/system/ksm.service |
新增(KSM 持久化;此前 KSM 在跑但重启即失效) |
虚机侧新增:6 台虚机的磁盘卷、域定义、静态网络配置、SSH 加固。
是否停服:不停服。不重启宿主、不动 br0/netplan/物理网卡、不影响既有 OpenWrt 虚机。
窗口期:无人值守安装约 6–10 分钟/台,并发度 2,6 台合计约 20–35 分钟。 期间宿主 HDD 存在持续写入(安装产物),CPU 与 IO 负载上升,属预期现象。
在跑业务:变更前宿主仅有 OpenWrt 虚机在运行,不受影响。
5. 分步操作¶
对应实现文件:
deployer.py(生命周期编排)、main.py(入口)、templates/ks.cfg.tmpl(kickstart 模板)、config/deploy.yaml(配置)。 以下为与实现等价的操作序列,供阅读与手工复现;幂等、备份与错误处理由实现体保证。注:工程此后已改为「配置驱动创建主机」——主机名、IP、MAC、CPU/内存、磁盘、 登录方式全部来自配置,与用途绑定的行为(关闭 swap、内存气球、宿主调优等) 均为可选项。本文档已按该格式对齐,事实结论未变。
5.1 预演(不改动任何状态)¶
sudo .venv/bin/python main.py --dry-run --config config/deploy.yaml # 仅输出待变更清单
5.2 宿主层调优¶
# 写入内核参数文件(KVM 宿主降低虚机内存被换出概率)
cat > /etc/sysctl.d/99-kvm-host.conf <<'EOF'
vm.swappiness = 10
EOF
sysctl -p /etc/sysctl.d/99-kvm-host.conf # 使其立即生效
# KSM 当前已在运行但无持久化单元,重启即失效,故补一个 oneshot 单元
cat > /etc/systemd/system/ksm.service <<'EOF'
[Unit]
Description=Enable Kernel Samepage Merging (KSM)
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'echo 1 > /sys/kernel/mm/ksm/run'
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now ksm.service # 启用并启动
5.3 创建存储卷(稀疏分配,不预占宿主空间)¶
virsh --connect qemu:///system vol-create-as hdd-pool k8s-master-01.qcow2 50G --format qcow2
virsh --connect qemu:///system vol-create-as ssd-pool k8s-master-01-etcd.raw 10G --format raw
# 系统盘由 nodes[].system_disk_gib 生成;数据盘由 nodes[].data_disks 逐块生成,
# 卷名规则为 <主机名>-<suffix>.<格式>,故 suffix: etcd 即得到 k8s-master-01-etcd.raw
# 其余 5 台同构:master 各 50G 系统盘 + 1 块 10G etcd 盘;node 各 100G 系统盘、无数据盘
5.4 无人值守安装(每台一次)¶
# ks.cfg 由模板按配置渲染,四段内容各自独立生成:SSH 公钥注入、SSH 认证方式、
# 数据盘格式化与挂载、关闭 swap。本批次 master 的数据盘为 suffix: etcd、
# mount: /var/lib/etcd,故生成对应的 mkfs 与 fstab 挂载片段;node 无数据盘则该段为空
virt-install --connect qemu:///system \
--name k8s-master-01 \
--memory 8192 --vcpus 4 --cpu host-passthrough --machine pc \
--osinfo name=rocky9 \
--disk path=/data/kvm/img/k8s-master-01.qcow2,format=qcow2,bus=virtio,cache=none,io=native,discard=unmap \
--disk path=/kvm/img/k8s-master-01-etcd.raw,format=raw,bus=virtio,cache=none,io=native \
--network bridge=br0,mac=52:54:00:31:0a:01,model=virtio \
--location /data/kvm/iso/Rocky-9-latest-x86_64-dvd.iso \
--initrd-inject build/k8s-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 --noautoconsole
# 注意:不要加 --boot hd,它会覆盖 virt-install 的 direct kernel boot
kickstart 以 poweroff 收尾(而非 reboot):此时域定义仍带 direct kernel boot 参数,
直接重启会再次进入安装器形成循环。
5.5 安装完成后固化域定义(等待域转为 shut off 后执行)¶
virsh --connect qemu:///system dumpxml k8s-master-01 --inactive > build/k8s-master-01/domain.xml
# 移除 <kernel>/<initrd>/<cmdline>,改写为 <boot dev='hd'/>,使系统从硬盘引导
virsh --connect qemu:///system define build/k8s-master-01/domain.xml
virsh --connect qemu:///system start k8s-master-01
5.6 启用内存气球策略(deflate-on-oom,可选项)¶
# 仅在 vm_defaults.deflate_on_oom=true(默认值)时执行;置 false 则完全跳过本节
# 说明:libvirt 10.0 的能力检测误判 QEMU 不支持该属性(其二进制内含
# "deflate-on-oom is not supported by this QEMU binary"),直接写在 <memballoon> 内会被静默丢弃。
# 实测 QEMU 8.2 支持,故经 qemu:commandline 传 -global 绕过。用 -global 而非
# -set device.balloon0:libvirt 以 JSON 形式下发 -device,-set 会因找不到 id 而启动失败。
# 高危:以下操作会关闭并重启该虚机,请在业务空闲窗口执行
virsh --connect qemu:///system destroy k8s-master-01
virsh --connect qemu:///system define build/k8s-master-01/domain.xml # 已含 qemu:commandline
virsh --connect qemu:///system start k8s-master-01
5.7 设置开机自启¶
virsh --connect qemu:///system autostart k8s-master-01
6. 验证¶
# 1) 域状态:6 台均为 running
virsh --connect qemu:///system list --all
# 2) 引导方式已固化:不应出现 <kernel>/<initrd>
virsh --connect qemu:///system dumpxml k8s-master-01 --inactive | grep -cE "<kernel>|<initrd>" # 期望 0
# 3) 内存气球策略:应输出 -global 与 virtio-balloon-pci.deflate-on-oom=on
virsh --connect qemu:///system dumpxml k8s-master-01 | grep -A4 qemu:commandline
# 4) 节点内部(对 6 台逐一执行)
ssh -o BatchMode=yes root@192.168.31.10 '
hostname; cat /etc/redhat-release; free -h | tail -n1; nproc;
df -hT /var/lib/etcd;
sshd -T | grep -i ^passwordauthentication;
wc -l < /root/.ssh/authorized_keys'
# 期望:主机名正确、Rocky Linux 9.8、swap 为 0B、/var/lib/etcd 为独立 /dev/vdb、
# passwordauthentication no、授权密钥 1 行
# 5) 宿主调优
sysctl -n vm.swappiness # 期望 10
systemctl is-enabled ksm.service # 期望 enabled
# 6) 幂等复跑:第二次应零变更
sudo .venv/bin/python main.py --config config/deploy.yaml
# 期望输出:状态巡检:全部项已达标,无需变更 / 状态: skipped
7. 回滚¶
备份方式:实现体在改写既有文件前自动做带时间戳备份(<文件名>.bak.<YYYYmmdd-HHMMSS>);
本次宿主侧两处均为新增文件,原先不存在,故无备份产生。
回滚步骤
# 高危:销毁本项目创建的全部虚机与其磁盘数据(不可恢复)
sudo .venv/bin/python main.py --rollback --config config/deploy.yaml
# 等价的手工操作(逐台):
virsh --connect qemu:///system destroy k8s-master-01 # 高危:强制关闭
virsh --connect qemu:///system undefine k8s-master-01 --remove-all-storage # 高危:连磁盘一并删除
# 撤销宿主层改动
systemctl disable --now ksm.service && rm -f /etc/systemd/system/ksm.service && systemctl daemon-reload
rm -f /etc/sysctl.d/99-kvm-host.conf # 高危:删除内核参数文件
sysctl -w vm.swappiness=60 # 恢复到变更前取值
回滚后验证:virsh list --all 仅剩 openwrt;sysctl -n vm.swappiness 回到 60;
systemctl status ksm 不再存在该单元;/data/kvm/img 与 /kvm/img 下相关卷已清除。
8. 风险说明¶
| 风险点 | 影响 | 缓解措施 |
|---|---|---|
| 操作系统架构基线 | Rocky 10 要求 x86-64-v3,本宿主仅 v2,装不上 | 锁定 Rocky 9;变更前用 grep -qw avx2 /proc/cpuinfo 确认基线 |
--boot hd 覆盖 direct boot |
域无 <kernel>,虚机从空盘引导后立即关机,且无日志 |
不传 --boot;安装后由实现体移除 direct boot 参数 |
| kickstart 以 reboot 收尾会循环安装 | 域定义仍带 direct boot,重启再次进安装器 | kickstart 改用 poweroff,由实现体固化定义后再启动 |
| libvirt 静默丢弃 deflate-on-oom | virsh define 返回成功但配置未生效 |
经 qemu:commandline 传 -global;验证时直接查 qemu 进程参数 |
ks 文件位于 /home 下被 AppArmor 拒绝 |
域启动失败:Permission denied |
需挂载给虚机的 ISO 一律放在已放行的 /data、/kvm 下 |
| 静态 IP 与路由器 DHCP 池冲突 | 间歇性断网,且难归因 | 变更前确认 DHCP 池范围,或在路由器上排除 192.168.31.10–15 |
| 配置取值进入 guest 内脚本 | 错误的挂载点或主机名会破坏 kickstart,甚至造成脚本注入 | 实现体对主机名、IPv4、MAC、卷名后缀、挂载点做白名单校验,并在批次内查重;不通过则前置校验直接失败 |
| 宿主内存压力 | 6 台常驻占名义 72 GiB,后续还要装 slurm/lsf/lustre | 已启用 deflate-on-oom 兜底;后续系统按需开关,弹性层设 autostart=no |
| etcd 盘为 raw 非稀疏 | 3 块实占 30 GiB SSD(libvirt 创建时不稀疏) | 属预期开销;如需节省可改建稀疏文件后重新格式化 |
| 并发安装争抢 HDD IO | 安装变慢 | 并发度限制为 2;如需更快可提高,但会加剧争抢 |
| root 口令 | 明文落盘 | 脚本随机生成 24 位口令,存于 config/credentials.yaml(已入 .gitignore),供控制台兜底登录 |
来源:
/home/rain/Documents/kvm/kvm_rocky9_deploy/docs/SOP.md| 最后更新:2026-09-18 04:11(由build/sync.py同步,请勿在站点侧修改)