跳转至

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 同步,请勿在站点侧修改)