主题
Harvester v1.8.2 三节点 HCI 集群(ISO 无人值守装机)文档集
形态:Harvester OS 一体机——用 KVM/libvirt 起 3 台 UEFI 虚拟机,通过 netboot(vmlinuz + initrd + rootfs squashfs)+ HTTP 托管
config.yaml完成 v1.8.2 无人值守安装(等效官方 PXE/iPXE 自动安装流程),节点自带 RKE2 + Longhorn + KubeVirt。与同目录级的
../harvester-container/(Helm 装进既有 RKE2) 是两种不同形态,排错结论不可互相套用,对照见 §5。本目录所有结论、命令、耗时均来自真实环境实测,日期 2026-09-05 ~ 09-06。
1. 环境信息
| 项 | 值 |
|---|---|
| Harvester | v1.8.2(v1.8 起 PXE 安装强制 UEFI,不再支持 Legacy BIOS) |
| 承载平台 | KVM / libvirt 8.0,3 台 UEFI 虚拟机(OVMF OVMF_CODE_4M.fd + 每节点独立 NVRAM) |
| 单节点规格 | 8 vCPU(host-passthrough + migratable='off',透传 vmx 支持嵌套虚拟化)/ 32 GiB / pc-q35-6.2 |
| 系统盘 | 250 GiB qcow2 → guest /dev/vda(实体文件在 NVMe,disks/ 下为符号链接,原因见 02 案例 7) |
| 数据盘 | 400 GiB qcow2 → guest /dev/vdb(机械盘 /mnt/xfs,Longhorn 默认盘) |
| 网卡 | virtio,PCI 地址显式固定 → guest 接口名确定为 enp1s0 |
| 节点内核 | 6.12.0-160000.36-default |
| Kubernetes | RKE2 v1.35.7+rke2r1(Harvester 内置) |
| libvirt 网络 | harvester / bridge virbr10,NAT + DHCP,192.168.150.0/24(宿主/网关 .1) |
| DHCP 动态池 | 192.168.150.100 - 199;三节点用 MAC→IP→主机名 静态保留 |
| 管理 VIP | 192.168.150.200(vip_mode: static,池外空闲地址,kube-vip 承载) |
| HTTP 引导服务 | http://192.168.150.1:8080/(python3 -m http.server) |
| Dashboard | https://192.168.150.200/(自签证书) |
| 节点 DNS | 仅 192.168.150.1(宿主 dnsmasq);★ 不并列公网 DNS,否则 CoreDNS policy=random 会间歇把内网域名解析到公网(02 案例 24) |
| Rancher 接入 | 已导入为集群 harvester-hci(id c-vd78l,provider=harvester,state=active,nodeCount=3),详见 05 |
| 节点 | MAC | IP(DHCP 保留) | 安装模式 | 最终角色 |
|---|---|---|---|---|
| harvester-01 | 52:54:00:15:01:01 | 192.168.150.11 | create | control-plane,etcd |
| harvester-02 | 52:54:00:15:01:02 | 192.168.150.12 | join | control-plane,etcd(自动提升) |
| harvester-03 | 52:54:00:15:01:03 | 192.168.150.13 | join | control-plane,etcd(自动提升) |
PCI 地址被显式固定(网卡 0x01、os 盘 0x02、data 盘 0x03),因此 guest 内
vda/vdb/enp1s0命名完全确定,安装配置里可以安全地写死设备名——这是本方案能全自动的前提。
2. 文档导航
| 文档 | 内容 |
|---|---|
01-实施手册-ISO无人值守装机.md | 架构与引导原理、目录结构、19 个脚本职责与执行顺序、装机配置逐字段说明、完整实施流程、耗时基线、验证方法 |
02-故障排查与修复记录.md | 23 个实测案例:现象 → 定位过程 → 根因 → 修复 → 验证,含排障方法论 |
03-凭据与首登认证.md | Dashboard 登录凭据、"密码正确却 401" 根因链、首登改密全流程、API 调用要点、凭据文件纠错 |
04-运维手册-巡检重置与网络打通.md | 日常巡检命令、集群重置/重建、节点自动提升机制、HA 与 quorum、故障速查表 |
05-接入Rancher与内外网分离解析.md | 被 Rancher 导入并纳入 Virtualization Management:三件事缺一不可、A~F 六步修复、导入 API 步骤、验证命令、UI 数据面代理路径 |
deploy/ | ★ 一键部署文档 + 全部资料:run-all.sh 一键入口、19 个已实测脚本、3 份真实装机配置、libvirt 网络与域定义、验收清单、参数定制指南 |
deploy/public-access/ | ★ 公网访问方案:https://ai-ear.cn:9443/ 的 frp 隧道 + Traefik TLS 终止实现、3 个可回滚脚本、10 层端到端验证清单 |
3. 30 秒速览(TL;DR)
最省事的一条路(推荐)——前置检查 + 按序调用已实测脚本:
bash
bash /mnt/xfs/harvester/scripts/run-all.sh --preflight # 8 项前置检查(不改状态)
setsid bash /mnt/xfs/harvester/scripts/run-all.sh </dev/null >/tmp/run-all.log 2>&1 &
tail -f /tmp/run-all.log # 全程约 42 分钟(不含介质下载)等价的原始编排方式(run-all.sh 内部就是按序调用这些):
bash
cd /mnt/xfs/harvester
# 一次性准备(介质 9.5GB + 磁盘 + DHCP 保留 + HTTP 服务 + 装机配置)
bash scripts/00-download.sh && bash scripts/10-prepare.sh
bash scripts/20-http-server.sh && bash scripts/50-gen-configs.sh
# ★ 无人值守:node01 装机 → 自动接力 node02/node03 → 改密 → 验证
setsid bash scripts/60-deploy.sh node01 </dev/null >/dev/null 2>&1 &
setsid bash scripts/65-deploy-all.sh </dev/null >/dev/null 2>&1 &
tail -f logs/deploy-all.log # 全程约 42 分钟(每节点 17~22 分钟)每个编排器只能启动一次:重复实例会同时对同一台 VM 执行
virsh destroy/define/start互相打断。怀疑被杀时先pgrep -af '60-deplo[y]|65-deplo[y]'确认再重启。
4. 能力矩阵(实测)
| 能力 | 状态 | 关键条件 / 说明 |
|---|---|---|
| 无人值守装机(netboot + HTTP config) | ✅ | 3/3 节点全自动完成;含"安装器卡死自动重启重试"自愈 |
| 三节点 HA(etcd quorum) | ✅ | 节点由 harvester-promote-node-controller 自动提升为 control-plane,etcd;实测 etcd 成员 3/3 |
| 管理 VIP | ✅ | vip_mode: static + kube-vip(3 节点各 1 个 Pod);curl -sk https://VIP/ping → pong |
| Dashboard UI 登录 | ✅ | \1xxx\2(★ 与 SSH 密码故意不同值),由 75-set-admin-password.sh 自动完成改密;实测 mustChangePassword=xxx 03、02` 案例 28) |
| Longhorn 默认盘 | ✅ | 3 块 default-disk × 400 GiB,schedulable=true,无告警;默认 SC harvester-longhorn |
| SSH 免密 + kubectl | ✅ | 装机配置注入宿主公钥;kubectl 必须用绝对路径(02 案例 11) |
| 串口全程录制 | ✅ | 30-console-capture.sh + pty + script,失败可复盘(旧日志轮转为 .prev) |
| 节点就绪判定 | ✅(已修) | 原 grep Ready 会把 NotReady 当就绪;改为精确节点名 + ^Ready(,|$)(02 案例 19) |
| 接入 Rancher | ✅ | 已导入为 harvester-hci(provider=harvester、state=active、nodeCount=3),纳入 Virtualization Management;靠 99 号脚本 A~F 六步 + cluster-registration-url 设置(详见 05) |
| 内网 DNS 解析稳定性 | ✅(已修) | 节点 DNS 收敛为单一宿主 dnsmasq;此前 CoreDNS policy=random 导致约 50% 概率解析到公网(02 案例 24) |
| 私仓镜像分发 | ✅(已修) | 通过 containerd-registry 设置渲染各节点 registries.yaml(266B);手写该文件会被控制器回收(02 案例 26) |
| 端到端 VM(smoke test) | ⚠️ 未完成 | KubeVirt 准入拒绝:doc is missing path: /spec/template/spec/domain/cpu/maxSockets(02 案例 23) |
虚机镜像导入(import/) | ⚠️ 进行中 | RHEL 9.6 镜像(c1/c2/c3,各 ~6GB)经内网 HTTP + CDI 导入;node03 死锁曾使 c2/c3 导入卡死 21h,硬重启后已恢复 Running(02 案例 29) |
离线镜像分发(docker.io mirror) | ✅ 已验证 | containerd-registry 设置加 docker.io → 内网 Harbor mirror;控制器逐节点重写 registries.yaml 并滚动重启 rke2-server;实测 fleet-agent 拉取 138ms 成功(02 案例 30) |
健康基线(2026-09-08 03:10 UTC 复测 · harvester-03 硬死锁恢复后):3 节点 Ready control-plane,etcd | etcd 成员 3 | kube-vip 3/3 | harvester/harvester-webhook/rancher 均 3/3 | Longhorn 3 盘 schedulable、原 faulted 卷已回 healthy(degraded 卷副本自动重建中)| VIP /ping+/dashboard/ 200 | 异常 Pod 0(fleet-agent 已于 03:53 用 docker.io → 私仓 mirror 修好,02 案例 30)。 上一版基线为 2026-09-06 09:19(异常 Pod 0);期间发生 node03 guest 硬死锁(2026-09-07 05:02 → 09-08 03:02,挂 22 小时,硬重启恢复),全过程见 02 案例 29 与 04 §4.4, 证据留档 /mnt/xfs/harvester/logs/postmortem-node03/。
5. 两种 Harvester 形态对照(别把结论互相套用)
| 维度 | 本目录:ISO 一体机 | ../harvester-container/:Helm 装进既有 RKE2 |
|---|---|---|
| 节点 OS | Harvester OS( Elemental 不可变系统 ) | 既有 RKE2 节点的任意 OS |
| K8s 来源 | 装机自带 RKE2 v1.35.7+rke2r1 | 既有集群 RKE2 v1.34.4-rc11+rke2r1 |
| 访问入口 | VIP + kube-vip(https://192.168.150.200/) | Service NodePort 32735 + SNI |
| 装机配置 | config.yaml(scheme_version: 1,HTTP 托管) | Helm values.yaml + promote 模式 |
| 首登密码 | 配置无法指定 → admin/admin + mustChangePassword(03) | 由 Rancher/Harvester chart 逻辑决定 |
| 节点角色 | 自动提升为 control-plane+etcd(HA quorum 3/3) | 取决于既有集群 |
| 典型坑 | nm-online 竞态、镜像预加载 >30min 卡死 | feature gate Snapshot(单数)、RWX 数据面 |
6. 访问与凭据
Dashboard 地址 : https://192.168.150.200/ (自签证书,浏览器需放行)
公网访问地址 : https://ai-ear.cn:9443/ (frp 隧道 + 公网 Traefik 正式证书,浏览器**无告警**)
实现与排障见 deploy/public-access/README.md
用户名 : xxx
密码 : xxx (dashboard 专用,由 scripts/75-set-admin-password.sh 设定)
★ 与下面的 SSH 密码**故意不同值**(见 02 案例 28)
SSH : rancher@192.168.150.11 / .12 / .13
SSH 密钥 : /home/xxx/.ssh/id_rsa (装机配置已注入,免密)
SSH 密码 : xxx (即装机配置的 os.password,≠ dashboard 密码)
kubectl(节点内,必须绝对路径):
sudo /var/lib/rancher/rke2/bin/kubectl --kubeconfig /etc/rancher/rke2/rke2.yaml get nodes -o wide
rke2 token : xxx (configs/token.txt,**不是** Dashboard API token)
VNC : virsh vncdisplay harvester-01
串口日志 : tail -f logs/harvester-01-console.log实测认证状态(2026-09-06 10:15 复核):admin/xxx 登录返回 201, /v3/users?me=true 返回 200 且 mustChangePassword=xxx 引导密码 xxx与原 dashboard 密码xxx` 均已 401 失效。
公网入口实测(2026-09-07 07:33 复核):deploy/public-access/30-verify-public.sh 14/14 全通过—— https://ai-ear.cn:9443/ping → 200 且证书链 Verify return code: 0 (ok)(CN=ai-ear.cn,有效期至 2026-10-15,浏览器无告警); GET / → 302 Location: /dashboard/(相对路径,响应头不泄露内网 VIP/隧道端口); SPA 骨架 200 + index.*.js 1.5MB 资源 200(不白屏);POST login → 201 拿到 token; GET /v3/users?me=true → 200;WebSocket /v1/subscribe → 101 Switching Protocols(UI 实时刷新与 VM 控制台依赖)。
⚠️ 以上是实验环境凭据。凭据文件的三个标签曾被写错(把 RKE2 加入令牌标成 API token、 把 SSH 密码标成 dashboard 密码),纠错过程与正确语义见
03-凭据与首登认证.md。
7. 遗留事项(Follow-up)
| 优先级 | 事项 | 参考 |
|---|---|---|
| 高 | 节点内核无自愈,且已实测复发:kernel.softlockup_panic=0 + hung_task_panic=0 + hung_task_timeout_secs=0 → soft lockup 只告警不 panic,节点可无限期挂着(node03 实挂 22h)。恢复后 45 分钟内 node03 又打出同一套签名(03:45–03:55 EXT4 … Detected aborted journal + limit=0,comm virt-cdi-import)。建议开 softlockup_panic=1(已有 kernel.panic=10 → 10s 自动重启);代价是重载期"误杀"重启,待决策 | 02 案例 29(含"复发信号"小节)、04 §4.4 第 5 步 |
cattle-fleet-local-system/fleet-agent 长期 ErrImagePull:已于 2026-09-08 03:52 UTC 用方案 A修复——给 Harvester containerd-registry 设置追加 docker.io → http://192.168.122.156:30000 mirror,harvester-node-manager 逐节点重写 registries.yaml 并滚动重启 rke2-server;03:53:08 Successfully pulled … in 138ms,Pod 1/1 Running,异常 Pod 归零 | 02 案例 30 | |
| 中 | 单副本卷 + 大写入导入是高危组合:harvester-longhorn-r1 的唯一副本与导入 I/O 同节点,超时即 faulted → 设备被拆 → guest 连锁死锁。导入类负载建议改用 3 副本 SC | 02 案例 29 |
| 高 | node03 I/O 过载(导入并发过高):8 vCPU 上同时跑 2 个 CDI 导入(各写 127GiB / 3 副本 scratch,经 tgtd/iSCSI 网络写三份)+ Longhorn 副本重建 + 控制面 → load 35、42% wa、jbd2/sda-8/jbd2/vdb-8 长期 D 状态 → 副本心跳超时、引擎断连拆设备 → ImportFailed 重来(25h 内 16 次,正反馈)。处置:导入类负载串行化、别都压同一节点;只读监控 logs/postmortem-node03/import-watch.log(每 120s) | 02 案例 29"复发信号" |
| 中 | VM rhel96 处于 Paused(kubectl get vm -n default → Paused,VMI READY=False);rhel96-c2/c3-disk-0 PVC 仍 Pending,需等对应 CDI 导入完成后才会绑定 | 本次巡检 |
| 中 | VM test-vm-01 的 virtualmachine 对象已不存在(2026-09-08 03:13 UTC 前后优雅停止并删除 VMI,非本次恢复操作所为);数据盘 PVC test-vm-01-disk-0 仍 Bound 完好,对应卷 detached/unknown 属正常。需要时 bash scripts/100-create-test-vm.sh apply 重建(§6 凭据不变) | 本次巡检 |
| 低 | CDI scratch 卷反复换代(pvc-45e7d22e/pvc-65a31c5c → pvc-64291252/pvc-e2d5b613 → c3 再换 pvc-2e80f9a4),当前 2 卷 degraded(pvc-2e80f9a4-r-1ea6c780 在 node02 重建中);node03 Longhorn 盘本身健康(Ready/Schedulable,可用 768G/844G,df 用 10%)。属导入临时卷,导入完成后应随之清理,若仍残留再查。另:pvc-b4800aff(=test-vm-01-disk-0)、pvc-cacb6efb(=test-vm-disk0) 的 robustness=unknown 与 longhorn-manager 每 30s Failed to get purge status for engine … 是 detached 卷、引擎未运行的正常噪声,不是孤儿卷(PVC 均 Bound) | 本次巡检 |
| 高 | smoke VM 创建被 KubeVirt 准入拒绝(maxSockets 补丁路径缺失),且残留一个空的 Bound PVC test-vm-disk0 | 02 案例 23 |
| 中 | deploy/run-all.sh 只完成了 preflight 与参数分支验证(含机械盘/NVMe 双向验证),未做"从零到集群"的端到端重跑——它只是按序调用已实测脚本,不含新逻辑 | deploy §2、§2.2 |
| 中 | smoke VM 与介质下载均走公网 URL;生产环境应先内网镜像化 | 02 案例 23、deploy §9 |
| 低 | logs/deploy-all.log 末尾仍是旧版凭据块(08:44 那次运行由旧 50-gen-configs.sh 生成),下次部署会自动刷新;以 configs/credentials.txt 为准 | 03 §6.2 |
| 低 | /mnt/xfs/harvester 曾被整体回滚到旧快照,编辑前先核对 mtime/体积 | 02 案例 18 |