- 云原生
- 虚拟化
- 存储
- 后端
- 高可用
【免费下载链接】harvester
Open source hyperconverged infrastructure (HCI) software
Harvester Seeder(下文简称 Seeder)是 Harvester 生态中用于裸金属服务器自动纳管与集群自动部署的独立项目:它以一系列 Kubernetes 控制器为基础,借助tinkerbell/boots(DHCP/PXE 引导)与tinkerbell/rufio(BMC 带外管理)等 CNCF 生态组件,通过AddressPool、Inventory、Cluster三类自定义资源(CRD)将"手工装 ISO、手工配 PXE"的繁琐流程收敛为"提交一份 YAML,自动拉起一个多节点 Harvester 集群"。阅读本文后,你将掌握 Seeder 的整体架构、三类 CRD 的设计与协调机制、完整的集群部署 YAML 写法,以及 Seeder 以 Addon 形式内嵌进 Harvester 后如何实现硬件信息发现、硬件事件上报与节点电源管理。
本文的技术骨架来自仓库中的设计文档 enhancements/20220615-seeder.md(Seeder HEP),并辅以 enhancements/20230327-harvester-seeder-integration.md(嵌入式模式集成设计)及 Harvester 主仓库中对应的 Addon 定义、Node API 源码与测试用例加以印证。
为什么需要 Seeder:从"手工装裸机"到"声明式交付"
在 Seeder 出现之前,交付一套 Harvester 环境的常见方式有两条:
- ISO 镜像安装:需要人工逐台服务器插入介质、选择磁盘、配置网络与 VIP,过程依赖人工值守;
- PXE 网络安装:虽然可以批量引导,但要正确编排 DHCP、PXE、ks/cloud-init 配置与安装顺序,仍然"需要大量人工干预来保证基础设施能被正确编排和供给"(原文 Motivation)。
对于需要频繁拉起多个 Harvester 版本供开发(development)与质量保障(QA)流程使用的团队而言,这种"手把手"的方式效率过低。Seeder 的目标(Goals)因此非常明确:
Seeder 将是一个独立项目,部署到 Kubernetes 集群后,负责管理多个 Harvester 集群的初始供给(initial provisioning)。
也就是说,Seeder 的职责边界是"把一批裸金属节点编排成一个可用的 Harvester 集群",而非运行 Harvester 本身;它作为外部控制器存在于 Harvester 集群之外(直至 1.2.0 引入嵌入式模式,才作为 Addon 运行在集群内部,详见后文)。
整体架构:boots、rufio 与 seeder 的职责划分
Seeder 由三个"活动部件"(moving parts)协同工作,分别覆盖网络引导、带外电源管理与编排控制三个层次:
tinkerbell/boots:条件式 DHCP/PXE 引导组件
boots是 Tinkerbell 技术栈中的条件式 DHCP/PXE 引导组件,承担以下工作:
- 处理 DHCP 请求并分配 IP 地址;
- 对外提供 iPXE 引导服务;
- 支持 Kubernetes 后端做状态管理:用户可以定义
HardwareCRD 对象,用于标识某台节点是否需要被分配 DHCP 地址、是否为其提供 PXE 引导。
正是这个 K8s 后端能力,让 Seeder 可以把"哪台节点该被引导安装"这一决策完全声明化为 CRD 对象。
tinkerbell/rufio:BMC 带外管理控制器
rufio是基板管理控制器(BMC,Baseboard Management Controller)控制器,通过bmctool工具集与绝大多数常见硬件交互,完成两类典型操作:
- 修改启动设备顺序(如临时从 PXE/网络设备引导);
- 触发服务器重启。
与用户的交互完全对象化:通过baseboardmanagement(BMC 连接管理)、bmctask(任务定义)与bmcjob(任务执行实例)三类 CRD,用户可以用 Kubernetes 对象操作裸金属节点,而无需直接登录 BMC Web 界面或调用 IPMI 命令行。
seeder:编排控制器
seeder本身引入三类 CRD 及配套控制器,将上述两个底层组件"粘合"成完整的供给流程。三类 CRD 的定位如下表:
| CRD | 资源组/版本 | 职责 | 生命周期特征 |
|---|---|---|---|
AddressPool | metal.harvesterhci.io/v1alpha1 | 承担 IPAM(IP 地址管理),按 CIDR 分配节点地址与 Harvester VIP | 用户可定义多个,支持非连续 CIDR 段 |
Inventory | metal.harvesterhci.io/v1alpha1 | 裸金属节点的抽象,包装baseboardmanagement并补充 boots 与 Harvester 安装器所需信息 | 长期存活(long-lived),与集群绑定期间被占用,集群删除后释放回池 |
Cluster | metal.harvesterhci.io/v1alpha1 | 运行在一组Inventory管理的裸金属节点上的 Harvester 集群抽象 | 可随时通过增删Inventory引用实现集群扩缩容 |
三类核心 CRD 的职责与控制器协调流程
AddressPool:解决 boots 的 IPAM 难题
原文明确指出,传统使用boots时存在一个 IPAM 挑战:需要为条件式 DHCP 分配一个 CIDR 网段,且非 boots 管理的硬件或虚拟机也必须以Hardware对象形式注册,才能触发 DHCP 应答——这意味着往往需要分配大段连续的 CIDR 块。
AddressPool的设计正是为了破解这一矛盾:
- 用户可定义多个
AddressPool对象,并在Cluster定义中引用; - Seeder 会从被引用的
AddressPool中分配地址,并将分配结果作为 CRD 的status记录跟踪; - 多个
AddressPool的存在,使得非连续 CIDR 段(non-continuous CIDR ranges)也能被正常使用,避免必须占用一整段连续地址。
Inventory:BMC 连接 + 引导信息的长期对象
Inventory是baseboardmanagementCRD 的包装,额外携带boots与 Harvester 安装器需要的补充信息。它关联一台真实裸金属节点,并在集群对象存续期间被占用;集群删除时,Inventory被释放回池中以供复用。其控制器核心任务有三条:
- 创建即联动:创建
Inventory对象时,控制器会创建对应的baseboardManagement对象,并依据rufio上报的 BMC 连通性与电源状态,持续调和(reconcile)该对象的 state; - 分配即重启:当
Inventory被分配给某个集群(并满足特定条件,如Hardware对象 generation 条件)时,控制器创建bmcjob对象,由rufio触发关联裸金属节点重启,从而触发 DHCP/PXE 引导服务器kick-start Harvester 安装; - 释放即关机:当
Inventory从集群释放时,控制器再触发另一个bmcjob,将裸金属节点断电。
Cluster:集群的声明式抽象与供给编排
Cluster是对运行在一组Inventory管理的裸金属节点之上的真实 Harvester 集群的抽象。集群控制器承担以下核心任务:
- VIP 分配:创建
Cluster时,从关联的AddressPool中分配一个 Harvester VIP; - Hardware 生成:综合
InventoryCRD 的 spec 与HarvesterCRD 的 spec,生成带有效userdata的HardwareCRD——这份userdata会由boots在 PXE 引导流程中以内核参数(kernel arguments)的形式传递,从而通过内核参数完成全部 Harvester 配置; - 状态回写:将正确的 conditions 打到
Inventory的 status 上,驱动 Inventory 控制器如前述通过bmcjob重启节点; - 扩缩容:调和
Clusterspec 中的Inventory对象列表,用户只需增删Inventory引用即可调整集群规模。
三者合起来构成了完整的供给闭环:提交 Cluster → 分配 IP/生成 userdata → 打条件触发重启 → PXE 引导并内核参数化安装 → 节点就绪加入集群。
实战:一份 YAML 拉起多节点 Harvester 集群
下面完整复现原文档 User Story 1 的示例:用户提交一份 YAML,即可供给出一个可用的多节点 Harvester 集群。
完整部署 YAML
apiVersion: metal.harvesterhci.io/v1alpha1 kind: AddressPool metadata: name: node-pool namespace: default spec: cidr: "172.16.128.11/32" gateway: "172.16.128.1" netmask: "255.255.248.0" --- apiVersion: metal.harvesterhci.io/v1alpha1 kind: AddressPool metadata: name: node2-pool namespace: default spec: cidr: "172.16.128.12/32" gateway: "172.16.128.1" netmask: "255.255.248.0" --- apiVersion: metal.harvesterhci.io/v1alpha1 kind: AddressPool metadata: name: vip-pool namespace: default spec: cidr: "172.16.128.7/32" gateway: "172.16.128.1" netmask: "255.255.248.0" --- apiVersion: v1 kind: Secret metadata: name: node namespace: default stringData: username: "ADMIN" password: "ADMIN" --- apiVersion: metal.harvesterhci.io/v1alpha1 kind: Inventory metadata: name: node namespace: default spec: primaryDisk: "/dev/sda" managementInterfaceMacAddress: "0c:c4:7a:6b:84:20" baseboardSpec: connection: host: "172.16.1.53" port: 623 insecureTLS: true authSecretRef: name: node namespace: default --- apiVersion: v1 kind: Secret metadata: name: node2 namespace: default stringData: username: "ADMIN" password: "ADMIN" --- apiVersion: metal.harvesterhci.io/v1alpha1 kind: Inventory metadata: name: node2 namespace: default spec: primaryDisk: "/dev/sda" managementInterfaceMacAddress: "0c:c4:7a:6b:80:d0" baseboardSpec: connection: host: "172.16.1.52" port: 623 insecureTLS: true authSecretRef: name: node2 namespace: default --- apiVersion: metal.harvesterhci.io/v1alpha1 kind: Cluster metadata: name: first namespace: default spec: version: "v1.0.2" imageURL: "http://172.16.135.50:8080" clusterConfig: nameservers: - 172.16.128.1 nodes: - inventoryReference: name: node namespace: default addressPoolReference: name: node-pool namespace: default - inventoryReference: name: node2 namespace: default addressPoolReference: name: node2-pool namespace: default vipConfig: addressPoolReference: name: vip-pool namespace: default各资源要点解读
- AddressPool 三件套:
node-pool与node2-pool分别给两台节点分配固定地址(/32单主机段,配合gateway/netmask使用),vip-pool则单独为集群 VIP 预留。三个独立/32池正好演示了"多个AddressPool支持非连续 CIDR"的设计意图。 - Secret 与 Inventory 一一对应:每个
Inventory通过baseboardSpec.connection.authSecretRef引用一个保存 BMC 用户名/密码的Secret;connection.port: 623是 IPMI 默认端口,insecureTLS: true表示允许以非安全 TLS 连接 BMC。 - Inventory 的关键字段:
primaryDisk: "/dev/sda"指定安装目标盘;managementInterfaceMacAddress是管理网口 MAC,用于让boots将 DHCP/PXE 应答精准匹配到该节点(生成Hardware对象时使用)。 - Cluster 的供给参数:
version指定 Harvester 版本;imageURL指向 Harvester ISO/镜像文件服务器地址;clusterConfig.nameservers配置集群 DNS;nodes列表把每台Inventory与对应AddressPool配对;vipConfig引用 VIP 地址池。
调和后的对象状态(reconciled state)
提交后,各控制器自动运转,最终可观察到如下状态(原文测试输出):
NAME CLUSTERSTATUS CLUSTERTOKEN CLUSTERADDRESS cluster.metal.harvesterhci.io/first tinkHardwareCreated iAnHlMWjrm0KF17Y 172.16.128.7 NAME INVENTORYSTATUS GENERATEDPASSWORD ALLOCATEDNODEADDRESS inventory.metal.harvesterhci.io/node inventoryNodeReady yE4d99nP2JIujlIw 172.16.128.11 inventory.metal.harvesterhci.io/node2 inventoryNodeReady lv3xkopziz8r4Mbl 172.16.128.12 NAME AGE baseboardmanagement.bmc.tinkerbell.org/idrac 25h baseboardmanagement.bmc.tinkerbell.org/node 25h baseboardmanagement.bmc.tinkerbell.org/node2 25h NAME AGE bmcjob.bmc.tinkerbell.org/node-reboot 25h bmcjob.bmc.tinkerbell.org/node2-reboot 25h NAME AGE bmctask.bmc.tinkerbell.org/node-reboot-task-0 25h bmctask.bmc.tinkerbell.org/node-reboot-task-1 25h bmctask.bmc.tinkerbell.org/node-reboot-task-2 25h bmctask.bmc.tinkerbell.org/node2-reboot-task-0 25h bmctask.bmc.tinkerbell.org/node2-reboot-task-1 25h bmctask.bmc.tinkerbell.org/node2-reboot-task-2 25h NAME ADDRESSPOOLSTATUS STARTADDRESS LASTADDRESS NETMASK addresspool.metal.harvesterhci.io/node-pool poolExhausted 172.16.128.11 172.16.128.11 255.255.248.0 addresspool.metal.harvesterhci.io/node2-pool poolExhausted 172.16.128.12 172.16.128.12 255.255.248.0 addresspool.metal.harvesterhci.io/vip-pool poolExhausted 172.16.128.7 172.16.128.7 255.255.248.0 NAME STATE hardware.tinkerbell.org/node hardware.tinkerbell.org/node2从这份状态输出可以清晰看出完整的供给链路:
Cluster状态变为tinkHardwareCreated,并获得集群 Token(iAnHlMWjrm0KF17Y)与 VIP 地址(172.16.128.7);- 两个
Inventory均进入inventoryNodeReady,各自生成了随机访问密码(如yE4d99nP2JIujlIw),并拿到分配到的节点地址(172.16.128.11、172.16.128.12); - 出现了
baseboardmanagement.bmc.tinkerbell.org/{idrac,node,node2}三个 BMC 连接对象(idrac是 BMC 自身被纳管产生的对象),以及bmcjob/bmctask系列重启任务对象,印证了 rufio 侧的带外操作已经执行; - 三个
AddressPool的ADDRESSPOOLSTATUS均为poolExhausted(单地址池已被分配耗尽),STARTADDRESS与LASTADDRESS相同; hardware.tinkerbell.org/{node,node2}两个Hardware对象已生成,供 boots 做条件式 DHCP/PXE 应答。
用户使用体验:从提交到访问的四步
原文档给出的端到端用户体验为:
- 用户提交一份 YAML spec,请求供给一个 Harvester 集群;
- 用户可通过BMC 控制台(BMC console)实时查看安装进度;
- 集群就绪后,通过Cluster Status中的Cluster VIP访问 Harvester Web UI;
- 每个节点可通过Inventory Status中记录的节点地址与生成的随机密码(
GENERATEDPASSWORD)登录访问。
API 变更与版本演进:从独立项目到嵌入式 Addon
原 HEP 明确指出:核心 Harvester 无需任何 API 变更——Seeder 是独立项目,CRD 是与其控制器交互的唯一方式。这一状态在后续发生了重要演进:自 Harvester 1.2.0 起,Seeder 引入嵌入式模式(embedded mode),可通过Harvester Addons直接部署到现有集群中,允许终端用户在 Harvester 侧直接定义 Inventory、查看硬件信息并执行电源操作。这部分的详细设计见 enhancements/20230327-harvester-seeder-integration.md。
嵌入式模式的定位与目标
嵌入式模式的三个核心目标:
- 将硬件信息(propagation of hardware information)传播进 Harvester;
- 将硬件事件传播为 Harvester 事件(events);
- 允许用户通过 Harvester 对硬件执行电源循环(power cycle)操作。
明确列为Non-goal的是:不再承担额外硬件的供给(provisioning of additional hardware)——即嵌入式模式下 Seeder 只做"纳管已有节点",不做"装机拉新集群"。
嵌入式模式下使用的 CRD
嵌入式模式只使用以下 CRD 的子集:
inventories.metal.harvesterhci.ioclusters.metal.harvesterhci.iojobs.bmc.tinkerbell.org(rufio 任务执行实例)machines.bmc.tinkerbell.org(rufio 机器对象)tasks.bmc.tinkerbell.org(rufio 任务定义)
部署形态与控制器裁剪
- Seeder 以deployment形式运行在
harvester-system命名空间; - Addon 部署时将 Seeder 的环境变量
EMBEDDED_MODE置为true; - 在该模式下,供给类控制器(provisioning controllers)不会被引导启动,仅引导以下控制器:
- local cluster controller(本地集群控制器)
- local node controller(本地节点控制器)
- rufio machine controller
- rufio job controller
- rufio task controller
- cluster event controller(集群事件控制器)
在仓库中可找到对应的 Addon 定义:addons/built-in/harvester-seeder/addon-template.yaml 以harvesterhci.io/v1beta1的Addon资源声明了harvester-seeder(命名空间harvester-system),chart 为harvester-seeder,默认enabled: false,可通过Addons.harvester_seeder.Enabled开关启用;addons/built-in/harvester-seeder/metadata.yaml 声明其交付成熟度为ga。镜像与 chart 版本由 addons/version_info 统一管理,例如当前开发版本为HARVESTER_SEEDER_CHART_VERSION="1.10.0-dev.0"、镜像rancher/harvester-seeder:main-head。Addon 的完整目录与交付模型说明见 addons/README.md。
嵌入式模式下的 local Cluster 与 Inventory 绑定
嵌入式模式启动时,Seeder 会创建一个名为local的cluster.metal对象,其集群端点指向K8s 默认 service 地址,作为后续新增Inventory对象的占位容器:
apiVersion: metal.harvesterhci.io/v1alpha1 kind: Cluster metadata: creationTimestamp: "2023-03-26T22:50:15Z" generation: 4 name: local namespace: harvester-system resourceVersion: "31519751" uid: 44ad4855-65c7-46ba-a321-04c54ab69337 spec: clusterConfig: {} nodes: - addressPoolReference: name: "" namespace: "" inventoryReference: name: harvester-659jw namespace: harvester-system version: local vipConfig: addressPoolReference: name: "" namespace: "" status: clusterAddress: 10.53.0.1 status: clusterRunningHarvester UI 允许用户为现有节点创建 Secret 与 Inventory 定义。典型的 Secret 创建请求(调用${ENDPOINT}/v1/harvester/secrets):
{ "type": "Opaque", "metadata": { "namespace": "harvester-system", "name": "harvester-659jw" }, "_type": "Opaque", "data": { "password": "TjBJTE80dSEh", "username": "RXRob3M=" } }典型的 Inventory 创建请求(调用${ENDPOINT}/v1/harvester/metal.harvesterhci.io.inventories):
{ "kind": "Inventory", "metadata": { "name": "harvester-659jw", "namespace": "harvester-system", "annotations": { "metal.harvesterhci.io/localInventory": "true", "metal.harvesterhci.io/localNodeName": "harvester-659jw" } }, "spec": { "baseboardSpec": { "connection": { "authSecretRef": { "name": "harvester-659jw", "namespace": "harvester-system" }, "host": "172.19.1.172", "insecureTLS": true, "port": 623 } }, "events": { "enabled": true, "pollingInterval": "1h" } } }这里的两个注解非常关键:
metal.harvesterhci.io/localInventory: "true"标记该 Inventory 属于嵌入式模式的本地纳管对象;metal.harvesterhci.io/localNodeName指定 Inventory 关联的 K8s 节点名——由于嵌入式模式下 Seeder 不执行真实供给,本地集群控制器正是依据该注解去查询 K8s node 对象并生成正确的 Inventory status。
Inventory 被创建后,其 status 大致如下(其中pxeBootConfig.address来自节点实际地址):
{ "ownerCluster": { "name": "local", "namespace": "harvester-system" }, "pxeBootConfig": { "address": "172.19.109.10", "gateway": "", "netmask": "" }, "status": "inventoryNodeReady", "generatedPassword": "", "hardwareID": "" }后续的数据流为:local cluster controller 将 Inventory 加入harvester-system命名空间下的local集群对象 → cluster event controller 周期性调和 Inventory,查询底层硬件细节与事件并触发对 Harvester 节点的更新 → inventory controller 监听节点的电源操作请求(通过 Inventory status 中的powerActionStatus变化)→ 电源操作完成后,powerStatus字段的LastPowerAction被更新为对应状态。
硬件信息与事件的 UI 呈现
启用 Addon 并定义 Inventory 后,用户即可在 Harvester UI 中查看底层硬件信息,包括但不限于:硬件厂商(manufacture)、硬件序列号(serial number)、硬件型号(model)以及硬件事件。
源码印证:Harvester 侧的电源操作 API 实现
嵌入式模式的"通过 Harvester UI 执行电源操作"能力,在 Harvester 主仓库中有完整的源码落地。核心实现在 pkg/api/node/formatter.go:
- 常量定义(L49-L58):
seederAddonName = "harvester-seeder"、defaultAddonNamespace = "harvester-system"、nodeReady = "inventoryNodeReady",以及电源操作白名单possiblePowerActions = []string{"shutdown", "poweron", "reboot"};动态客户端通过seederGVR(metal.harvesterhci.io/v1alpha1的inventories资源)操作 Inventory 对象; - Action 注册(L62-L108):所有节点默认暴露
powerActionPossibleaction;powerAction仅在节点维护模式排空完成(MaintenanceMode=True/Evacuating或True/Completed)后才会注册到资源上,从而保证"先进入维护模式、排空 VM 后再执行电源操作"的安全语义; powerActionPossible(L357-L407):先检查harvester-seederAddon 是否存在且spec.enabled == true(否则返回HTTP 424 Failed Dependency),再通过动态客户端按节点名获取 Inventory 对象,并校验其status.status == "inventoryNodeReady"(不满足则返回HTTP 409 Conflict),全部通过后返回HTTP 204 No Content;powerAction(L409-L468):校验操作在白名单内、且节点处于维护模式排空完成状态;若上一次powerActionRequested尚未完成(status 中powerAction.actionStatus为空),则拒绝新请求;随后将操作写入 Inventory 的spec.powerActionRequested并清理status.powerAction.lastJobName,交由 Seeder 侧控制器执行真实的 BMC 操作。
对应的单元测试位于 pkg/api/node/formatter_test.go:Test_powerActionNotPossible(L526-L549)验证 Addon 不存在时返回424;Test_powerActionPossible(L551-L571)验证 Addon 启用且 Inventory 就绪时返回204;Test_powerAction(L573-L596)验证执行shutdown后 Inventory 的spec.powerActionRequested被正确写入"shutdown";Test_invalidPowerAction与TestPowerActionRequiresMaintenancePhase则验证非法操作与非维护模式状态会被拒绝。
测试计划与升级策略
- 独立模式测试:原 HEP 的 Test plan 指出,代码库中已存在大量集成测试(integration tests),用于模拟并验证绝大部分 reconcile 功能(地址分配、Hardware 生成、BMC 任务触发等);升级策略标注为 N/A。
- 嵌入式模式测试(来自 enhancements/20230327-harvester-seeder-integration.md):
- 启用 Seeder Addon 集成;
- 为节点定义合法的 Inventory 与 Secret;
- 等待额外的节点标签(node labels)被传播到节点上;
- 等待为该节点生成额外的硬件事件;
- 通过 Harvester UI 对节点执行电源操作。
总结
Seeder 的设计核心可以概括为一句话:把"裸金属节点"变成可声明式管理的 Kubernetes 资源,让"拉起一个 Harvester 集群"从需要人工值守的运维操作变成幂等、可审计的控制器调和过程。在独立模式下,AddressPool、Inventory、Cluster三驾马车配合 boots 与 rufio 完成了从 IPAM、userdata 生成、BMC 重启到 PXE 安装的完整闭环;在嵌入式模式下,它又收敛为运行在harvester-system内的 Addon,专注硬件纳管、事件上报与安全的电源操作。无论是开发/QA 团队批量交付测试环境,还是生产集群的裸金属可观测性与远程管理,Seeder 都提供了一条值得参考的、以 CRD 为中心的自动化路径。
若需继续深入,可阅读仓库内的原始设计文档 enhancements/20220615-seeder.md 与 enhancements/20230327-harvester-seeder-integration.md,并结合 pkg/api/node/formatter.go、addons/built-in/harvester-seeder/addon-template.yaml 及 addons/version_info 追踪最新实现。
- 云原生
- 虚拟化
- 存储
- 后端
- 高可用
【免费下载链接】harvester
Open source hyperconverged infrastructure (HCI) software
相关推荐
YCBlogs生命周期管理:深入理解Android组件生命周期
YCBlogs生命周期管理:深入理解Android组件生命周期 在Android开发中,Activity和Fragment的生命周期管理是构建稳定、高效应用的基
教程技术博客文档探索未来基础设施:Tinkerbell - 灵活且强大的裸金属自动化平台
探索未来基础设施:Tinkerbell 灵活且强大的裸金属自动化平台 在当今数字化转型的时代,高效的资源管理和部署是关键。开源项目 Tinkerbell htt
OpenObserve数据保留自动化:基于策略的生命周期管理
OpenObserve数据保留自动化:基于策略的生命周期管理 你是否还在为日志存储成本飙升而焦虑?是否因手动清理过期数据而消耗大量运维精力?OpenObserv
可观测性日志分析指标监控链路追踪后端云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考