主题
01 · 入门篇:Harvester 架构全景与核心概念
目标:读完这一章,你能在白板上画出 Harvester 的分层架构,说清每个组件的职责, 并解释「一台虚拟机在 Harvester 里到底是什么」。 版本基准:Harvester v1.8.2(内置 RKE2
v1.35.7+rke2r1、KubeVirt1.7.4、Longhorn1.11.2)。
1. 一句话认识 Harvester
Harvester 是一个开源的超融合基础设施(HCI)平台,它把「虚拟机」变成 Kubernetes 的一等公民: VM 不再是独立虚拟化平台里的对象,而是一个带 qemu-kvm 进程的 Pod, 和容器共享同一套调度器、同一套存储、同一套网络、同一套 RBAC、同一套 GitOps 流水线。
它由四层能力叠加而成:
容器编排 Kubernetes(RKE2) —— 调度、声明式 API、自愈、RBAC
+
虚拟化 KubeVirt + CDI —— 把 VM 跑成 Pod,把镜像灌成 PVC
+
分布式存储 Longhorn —— 块存储多副本、快照、备份、RWX
+
产品化 Harvester 控制面 + UI —— Dashboard、Webhook、20+ 控制器、Steve 聚合 API
=
超融合 Harvester —— 一台机器既是计算节点,也是存储节点,也是管理节点1.1 为什么现在需要它
| 驱动力 | 说明 |
|---|---|
| VM 与容器长期共存 | 数据库、有状态中间件、Windows 应用、需要内核模块的业务,短期内不会容器化;但平台团队只想维护一套 K8s |
| VMware 授权模式变化 | 2023 年后订阅制与捆绑销售推高成本,大量企业评估开源 HCI 替代(Harvester / OpenStack / Proxmox) |
| 边缘与分支机构 | 3 台服务器起步、无专职虚拟化团队、需要「装完就能用」的一体机形态 |
| GitOps 统一交付 | VM 定义变成 YAML,就能进 Git、走 ArgoCD/Fleet、被 CI 校验,这是传统虚拟化平台做不到的 |
| AI/GPU 场景 | GPU 直通给 VM 与给 Pod 用同一套设备插件与调度语义 |
1.2 它不是什么
- ❌ 不是公有云 IaaS 的等价物:没有多区域、没有弹性公网 IP 市场、没有按秒计费的商业结算体系;
- ❌ 不是「K8s 的皮肤」:它自带不可变操作系统与装机器(ISO 形态),是一整套交付物;
- ❌ 不替代 KubeVirt,而是封装并加固 KubeVirt:所有 VM 语义仍是 KubeVirt CRD,Harvester 在其上加校验、默认值、UI 与运维闭环。
2. 七层架构全景(实测拓扑)
┌──────────────────────────────────────────────────────────────────────────┐
│ L7 入口层 Dashboard(UI) / kubectl / Terraform / Rancher Virtualization Mgmt │
│ ISO 形态:VIP + kube-vip(https://<VIP>/) │
│ 容器化形态:Service NodePort + SNI 主机名校验 │
├──────────────────────────────────────────────────────────────────────────┤
│ L6 产品层 harvester / harvester-webhook(Deployment,各 3 副本) │
│ Steve 聚合 API(APIService: harvesterhci.io …) │
│ 20+ 控制器:vm-image / vlan / lb / node-manager / promote-node … │
│ vm-import-controller(v2v)×2 │
├──────────────────────────────────────────────────────────────────────────┤
│ L5 虚拟化层 virt-operator → virt-api(校验/默认化 webhook) │
│ virt-controller(调和 VM→VMI) virt-handler(DaemonSet,每节点) │
│ virt-launcher Pod(每 VMI 一个,内含 qemu-kvm + libvirtd) │
│ CDI:cdi-operator / cdi-deployment / cdi-upload / importer Pod │
├──────────────────────────────────────────────────────────────────────────┤
│ L4 存储层 Longhorn:longhorn-manager(DS) / longhorn-csi / instance-manager │
│ share-manager(RWX 用,依赖 NFS) / backupstore(NFS/S3) │
│ CSI 快照:rke2-snapshot-controller 或 csi-snapshotter 子 chart │
├──────────────────────────────────────────────────────────────────────────┤
│ L3 网络层 CNI(ISO:Canal/Flannel+Calico;容器化:随既有集群,如 Calico VXLAN)│
│ harvester-network-controller(VlanConfig / Network-Attachment-Def)│
│ whereabouts(VLAN 网络 IPAM) harvester-load-balancer + kube-vip │
├──────────────────────────────────────────────────────────────────────────┤
│ L2 编排层 RKE2(kube-apiserver / etcd / kubelet / containerd) │
│ CoreDNS(forward . /etc/resolv.conf) │
├──────────────────────────────────────────────────────────────────────────┤
│ L1 操作系统 Harvester OS(Elemental 不可变系统,A/B 分区 + oem 挂载) │
│ 或:既有集群节点的任意 Linux(容器化形态) │
│ 硬件:VT-x/AMD-V(嵌套虚拟化需透传 vmx)、NVMe、virtio/直通网卡 │
└──────────────────────────────────────────────────────────────────────────┘2.1 组件职责速查
| 组件 | 归属 | 职责 | 出问题时的典型症状 |
|---|---|---|---|
harvester | Harvester | 主 API/控制器集合,Steve 聚合层 | UI 打不开、API 5xx;启动期依赖 VolumeSnapshotClass 会重启 |
harvester-webhook | Harvester | 校验与默认化(如要求 masquerade 接口、running/runStrategy 互斥) | admission webhook "validator.harvesterhci.io" denied |
virt-api | KubeVirt | VM/VMI 的校验与默认化 webhook,feature gate 只在进程启动时加载 | snapshot feature gate not enabled |
virt-controller | KubeVirt | 调和 VM → VMI → Pod | VM 长期 Starting |
virt-handler | KubeVirt | 节点上的 VM 生命周期代理(DaemonSet) | 无硬件虚拟化支持、VMI 起不来 |
virt-launcher | KubeVirt | 每个 VMI 一个 Pod,内含 qemu-kvm | VM「就是」这个 Pod |
CDI importer | KubeVirt/CDI | 下载/转换镜像写入 PVC | 镜像导入进度回退、卡在 99.62% |
longhorn-manager | Longhorn | 卷/副本/引擎的编排 | 卷 degraded、VolumeAttachment sync 失败 |
instance-manager | Longhorn | 每节点的数据面进程(engine/replica) | 卷无法 attach |
share-manager | Longhorn | RWX 卷的 NFS 服务端 | 没有它 RWX 数据面不可用 → 热迁移阻塞 |
kube-vip | 网络 | 承载管理 VIP / Service LB | VIP 无响应、Dashboard 打不开 |
whereabouts | 网络 | VLAN 网络的 IPAM | VM 拿不到二层网段 IP |
harvester-promote-node-controller | Harvester | 自动把 join 节点提升为 control-plane+etcd | 节点短暂 Ready,SchedulingDisabled(正常) |
vm-import-controller | Harvester | v2v(从 VMware 等导入) | 导入任务不推进 |
3. 资源模型:一台 VM 到底是什么
3.1 对象层级(自上而下)
VirtualMachine (kubevirt.io/v1) ← 期望状态(用户写的 YAML)
├── dataVolumeTemplates[] ← 磁盘模板
│ └── DataVolume (cdi.kubevirt.io/v1beta1)
│ └── PVC → PV → volumes.longhorn.io ← ★ 四层状态要分别确认
│ └── engine / replica(Longhorn 数据面)
├── volumes[].cloudInitNoCloud ← cloud-init(Secret 或 userData)
└── template.spec ← VMI 模板
└── VirtualMachineInstance (VMI) ← 运行实例(每次启动生成,停机即回收)
└── virt-launcher-<vm>-xxxxx Pod(内含 qemu-kvm)
└── domain(libvirt 定义的虚机)关键认知:kubectl get pod -l kubevirt.io/domain=<vm> 看到的那个 Pod, 就是这台虚拟机。VM 的 CPU/内存申请就是 Pod 的 requests/limits,因此它参与 K8s 调度、被 ResourceQuota 限制、被 PriorityClass 抢占、被节点污点排斥—— 这是「VM 成为 K8s 一等公民」的确切含义。
3.2 核心 CRD 与 API group
| API group | 关键对象 | 用途 | 备注 |
|---|---|---|---|
kubevirt.io/v1 | VirtualMachine(vm)、VirtualMachineInstance(vmi)、VirtualMachineInstanceMigration | VM 定义/实例/热迁移 | KubeVirt 原生 |
snapshot.kubevirt.io/v1beta1 | VirtualMachineSnapshot、VirtualMachineSnapshotContent、VirtualMachineRestore | VM 快照与恢复 | ★ Harvester v1.8 无自有快照 CRD,一律用这套 |
cdi.kubevirt.io/v1beta1 | DataVolume(dv) | 镜像导入/克隆/空盘 | 状态含 phase + progress% |
harvesterhci.io/v1beta1 | VirtualMachineImage、VirtualMachineBackup、VirtualMachineRestore、VolumeRemoteRestore、Setting、SupportBundle | Harvester 产品化对象 | 镜像库、设置项、支持包 |
network.harvesterhci.io/v1beta1 | VlanConfig、NetworkAttachmentDefinition | VLAN/二层网络 | 配合 whereabouts IPAM |
longhorn.io/v1beta2 | Volume、Node、Engine、Replica、Snapshot、BackupTarget、Setting | 分布式块存储 | 数据面真相在这里 |
snapshot.storage.k8s.io/v1 | VolumeSnapshotClass、VolumeSnapshot、VolumeSnapshotContent | CSI 快照 | ⚠️ 短名 vsc 有歧义 |
node.harvesterhci.io/v1beta1 | NodeConfig、ProvisionRequest | 节点级配置/自动装机 | ISO 形态用 |
实测枚举方法(确认聚合层与 CRD 都正常):
bashcurl -sk --resolve <SNI_HOST>:<NODEPORT>:<NODE_IP> \ https://<SNI_HOST>:<NODEPORT>/v1/harvester | tr ',' '\n' | grep -i snapshot # snapshot.kubevirt.io.virtualmachinesnapshots / …snapshotcontents / …restores # harvesterhci.io.virtualmachinerestores / volumeremoterestores # groupsnapshot.storage.k8s.io.volumegroupsnapshot{classes,contents,snapshots}
3.3 kubectl 短名陷阱(实测踩过)
bash
# ✗ 短名 vsc 在本集群被解析为 volumesnapshotcontents(与 classes 短名冲突)
kubectl get vsc -A
# ✓ 排查快照类务必用全名
kubectl get volumesnapshotclasses.snapshot.storage.k8s.io
kubectl get virtualmachinesnapshots.snapshot.kubevirt.io -A
kubectl get volumes.longhorn.io -n longhorn-system4. VM 生命周期状态机
4.1 VirtualMachine 的 printableStatus
┌──────────────► DataVolumeError(导入失败,看 DV conditions)
│
(创建)──► Provisioning ──► Starting ──► Running ──► Stopping ──► Stopped
(DV 创建/导入中) (VMI 调度中) │ ▲ │
▲ │ │ │
│ ▼ │ ▼
└──────────────────── Scheduling ◄──────┘ (runStrategy=Always 再启动)
(卷无法 attach / 资源不足)| 状态 | 含义 | 第一反应查什么 |
|---|---|---|
Provisioning | DataVolume 创建/导入中 | kubectl get dv 的 phase 与 progress;importer Pod 日志 |
DataVolumeError | 导入失败 | kubectl get dv -o yaml 的 conditions/message |
Starting | VMI 调度中 | kubectl describe vm 事件;VMI conditions |
Running | 正常 | — |
长期 Scheduling | 卷 attach 失败或资源不足 | Longhorn 卷 state/robustness/cloneStatus、节点容量、nodeSelector |
Stopped/Halted | 已停机 | runStrategy=Always 启动 |
Migrating | 热迁移中 | VirtualMachineInstanceMigration 状态 |
Unknown/ErrorUnschedulable | 调度被拒 | 污点/容忍、节点 cordon 状态 |
4.2 VMI 与 DataVolume 的对应状态
VMI: Scheduling → Scheduled → Running
DV : ImportScheduled → ImportInProgress(2.5%→99.6%) → Succeeded 100%
PVC: Pending → Bound⚠️
Succeeded/Bound/readyToUse=true都只是控制面信号。 实测中出现过 DVSucceeded、PVCBound、CSIreadyToUse=true、VirtualMachineRestore complete=true、 甚至DataVolumesReady=True全部正常,而 Longhorn 卷robustness=degraded、cloneStatus卡initiated、副本mode=<none>→ VM 起不来。 详见 06 存储实战 与 12 实战案例集 场景三。
4.3 RunStrategy 五值
| RunStrategy | 含义 |
|---|---|
Always | 保持运行(VMI 异常退出自动重建) |
Halted | 停止(回收 VMI;不存在 Stopped 这个值) |
RerunOnFailure | 失败时重跑,正常关机后不重启 |
Manual | 由 start/stop 子资源显式控制 |
Once | 只运行一次,退出后不再拉起 |
5. 三种部署形态(+ 两种「非 Harvester」参照系)
| 形态 | 节点 OS | K8s 来源 | 存储 | 入口 | 装机配置 | 适用 |
|---|---|---|---|---|---|---|
| Harvester ISO 一体机 | Harvester OS(Elemental 不可变系统) | 装机自带 RKE2 | 自带 Longhorn(data_disk 自动纳管) | VIP + kube-vip | config.yaml(scheme_version: 1,HTTP/PXE 托管) | 全新裸金属虚拟化集群、交付型项目 |
| Harvester 容器化 | 既有集群节点的任意 Linux | 既有 RKE2/K8s | 子 chart Longhorn(或复用既有 CSI) | Service NodePort + SNI(或 Ingress/LB) | Helm values.yaml + promote | 已有 K8s,叠加虚拟化能力 |
| 裸 KubeVirt | 任意 | 任意 | 任意 CSI(需自己配 RWX 才能热迁移) | 自己解决 | kubectl apply -f kubevirt-cr.yaml | 只要 VM 能力、不要产品化外壳 |
| 参照:公有云 IaaS | 云托管 | 无(VM 与 K8s 分离,或用托管 K8s) | 云盘(EBS/ESSD/PD) | 云 API/控制台 | 云 SDK/Terraform | 弹性、免运维、数据可出域 |
| 参照:vSphere/OpenStack | 宿主 OS | 无 | vSAN/Ceph | 自有 API | 自有编排 | 传统企业虚拟化存量 |
5.1 两种 Harvester 形态的关键差异(别把结论互相套用)
| 维度 | ISO 一体机 | 容器化(Helm on 既有 RKE2) |
|---|---|---|
| 节点 OS 可控性 | 不可变系统,改动走 oem 挂载/ Elemental 机制 | 完全可控(就是普通 Linux) |
| 与既有集群共存 | 不存在(它自己就是集群) | 必须处理冲突:snapshot-controller 双装、双默认 StorageClass、ingress/Rancher 缺失噪声 |
| 首登密码 | 装机配置无法指定 → 引导密码 xxx + `mustChangePassword=xxx | 由 chart/Rancher 逻辑决定 |
| 节点角色 | join 节点被 harvester-promote-node-controller 自动提升为 control-plane+etcd | 取决于既有集群拓扑 |
| 典型坑 | nm-online 亚秒竞态、镜像预加载 >30min 卡死、CoreDNS 随机上游 | gate Snapshot 单数、helm upgrade 覆盖 CR、Longhorn RWX 数据面 |
| 排障层数 | 宿主 → 引导 → 安装器 → 集群 → 应用(5 层) | 集群 → 应用(2 层) |
5.2 Harvester OS(Elemental)到底特殊在哪
| 特性 | 说明 | 运维含义 |
|---|---|---|
| 不可变根文件系统 | 系统分区只读,A/B 双状态分区(COS_STATE/COS_RECOVERY) | 升级=换分区,失败可回滚;但别指望改 /usr 下的东西能持久 |
/oem 与 /var/lib/rancher 可写 | 配置与数据落在持久分区 | 装机配置里的 os.* 最终写到这里 |
| 内置二进制不在 PATH | kubectl/crictl/ctr 位于 /var/lib/rancher/rke2/bin/ | ★ 必须用绝对路径:sudo /var/lib/rancher/rke2/bin/kubectl --kubeconfig /etc/rancher/rke2/rke2.yaml |
registries.yaml 由控制器渲染 | 声明源是 containerd-registry 设置 | 手写该文件会被回收(实测 167B → 0B) |
| 安装期是 live 内存 overlay | root=live:http://…squashfs | 安装期对 live 环境的任何修改重启即失效(自愈脚本要重新注入) |
6. 与 Kubernetes 的关系(为什么这是「云原生虚拟化」)
| K8s 机制 | 在 Harvester 里如何作用于 VM |
|---|---|
| 调度器 | VM 的落点由 virt-launcher Pod 的调度决定;可用 nodeSelector、亲和性、污点/容忍控制 |
| 资源模型 | domain.resources.requests/limits = Pod requests/limits;超分靠 requests < 实际用量 |
| 命名空间 + RBAC | 租户隔离用 namespace;权限用 K8s RBAC(Harvester 再包一层「项目」概念) |
| ResourceQuota / LimitRange | 直接限制某租户能开多少 VM、多少 CPU/内存 |
| 声明式 + 控制器 | 改 YAML 即改 VM;kubectl apply 可进 Git,走 ArgoCD/Fleet |
| 可观测性 | VM 指标就是 Pod 指标(cAdvisor)+ KubeVirt 的 kubevirt_* 指标,Prometheus 直接抓 |
| 准入控制 | KubeVirt 的 virt-api + Harvester 的 harvester-webhook 两级校验——大量「明明 YAML 没错却建不起来」都发生在这里 |
| CRD 生态 | 快照走 KubeVirt 原生 snapshot.kubevirt.io;卷走 CSI + Longhorn;网络走 NetworkAttachmentDefinition(Multus) |
这条关系链解释了 Harvester 的两个最大红利(GitOps、统一可观测)与两个最大约束 (一切受 K8s 准入与调度语义约束、存储必须走 CSI——而 CSI 的 accessModes 直接决定了热迁移能不能做,见 08)。
7. 术语表
| 术语 | 全称/含义 |
|---|---|
| HCI | Hyper-Converged Infrastructure,超融合:计算+存储+网络+管理同节点 |
| KubeVirt | CNCF 项目,用 CRD + Pod 把 KVM 虚拟机跑进 K8s |
| CDI | Containerized Data Importer,KubeVirt 的镜像导入/克隆/上传组件 |
| VMI | VirtualMachineInstance,VM 的运行实例(停机即消失) |
| virt-launcher | 承载单个 VMI 的 Pod,内含 qemu-kvm |
| Longhorn | CNCF 分布式块存储,Harvester 默认存储后端 |
| RWO/RWX | ReadWriteOnce / ReadWriteMany,PVC 的访问模式;★ 热迁移要求全部 RWX |
| DataVolume(DV) | CDI 对象,声明「从哪来、放哪、多大」 |
| VolumeSnapshotClass | CSI 快照类;Longhorn 的 parameters.type 取 snap(卷内快照)或 bak(备份) |
| backup target | Longhorn 备份目标(NFS/S3);不配则 type: bak 失败 |
| masquerade | KubeVirt 默认网络绑定模式:VM 出网走 Pod 网络 SNAT |
| bridge | KubeVirt 二层桥接模式;配合 Multus + VLAN 用 |
| Multus / NAD | 多网卡 CNI 元插件 / NetworkAttachmentDefinition |
| whereabouts | 集群级 IPAM,为二层网络分配 IP |
| kube-vip | 提供 VIP 与 Service LoadBalancer 实现 |
| Elemental | SUSE 的不可变 OS 框架,Harvester OS 的底座 |
| promote | 容器化形态下,把既有集群真实 CIDR/DNS 告知 Harvester 的配置段 |
| Steve | Rancher 的 API 聚合/代理层,Harvester 的 /v1 API 由它提供 |
| v2v | Virt-to-Virt,从 VMware 等平台迁移 VM(vm-import-controller) |
| cloud-init | Guest OS 首启初始化(用户/密钥/网络/包) |
| qemu-guest-agent | Guest 内代理,让宿主能做优雅关机、冻结文件系统做一致性快照 |
| quorum | etcd 多数派(3 成员需 ≥2 存活) |
| VIP | 虚拟 IP,ISO 形态的 Dashboard/API 入口 |
| SNI | TLS Server Name Indication;容器化形态入口会校验 Host/SNI |
8. 下一步
| 目标 | 去 |
|---|---|
| 30 分钟跑出第一台 VM | 02 快速上手 |
| 直接看部署 | 03 ISO 一体机 / 04 容器化 |
| 想知道「该选哪个」 | 10 五方对比 |