3. Fleet控制平面架构

Fleet 的控制平面并不是把多个集群“合并成一个巨大的 Kubernetes”,而是采用一种中心编排、成员自治的架构:中心侧保存期望状态(Bundle、GitRepo、Cluster 注册信息等),成员集群侧通过代理组件把期望状态落地为本地资源,并把自身状态回报给中心。理解这套架构的关键,是分清“谁保存期望状态”“谁执行落地”“状态如何回流”以及“扩展点在哪里插入”。

一、中心控制面与成员集群的职责边界

Fleet 的中心控制面通常运行在一个被指定为 local 的管理集群中,核心组件包括 fleet-controller(负责 Bundle 编排、Cluster 生命周期、GitRepo 同步等)和 fleet-agent 的“中心侧入口”。中心侧并不直接向成员集群的 kube-apiserver 写入业务资源,它只维护两类关键对象:

  • Cluster 注册对象:记录成员集群的 API Server 地址、CA、访问凭据引用、标签(如 env=prod、region=cn-north),以及该集群是否可调度。
  • Bundle / BundleDeployment:Bundle 是“期望状态的打包结果”,BundleDeployment 则把某个 Bundle 与某个 Cluster 绑定,表示“这个集群应该运行这份内容”。

成员集群侧只运行一个 fleet-agent。它不参与全局编排决策,只做三件事:向中心注册自己、拉取(或接收)属于本集群的 BundleDeployment、把其中的资源应用到本地。这样设计的直接好处是:成员集群即使与中心断连,本地已下发的资源仍按 Kubernetes 自身机制继续运行,不会因为中心不可用而整体停摆。

二、推送与拉取模型:两种下发路径

Fleet 同时支持“推送”和“拉取”两种语义,但生产上更常用、也更推荐的是拉取模型。两者的差别不在于“有没有 agent”,而在于连接由谁发起。

推送模型:中心侧的 fleet-controller 直接使用 Cluster 对象中保存的 kubeconfig 凭据,连接成员集群的 kube-apiserver,把 Bundle 内容写入成员集群。它的优点是链路短、下发即时;缺点是中心必须持有所有成员集群的长期凭据,且中心到成员集群的网络必须双向可达。在跨云、跨网络域、成员集群位于 NAT 之后的场景中,这往往不可行。

拉取模型:成员集群的 fleet-agent 主动连接中心集群的 kube-apiserver,watch 属于本集群的 BundleDeployment,把内容取回本地后再 apply。中心不需要主动连成员集群,成员集群可以位于私网、NAT 之后,只需能出站访问中心 API Server。代价是状态回流存在一定延迟,且中心侧需要为每个成员集群维护一个受限的 ServiceAccount 或凭据。

判断依据:如果成员集群网络位置不可控、数量多、需要跨云纳管,优先拉取模型;如果成员集群与中心同处一个可信内网、要求秒级下发,可考虑推送模型。两者可以按集群混用,但同一集群不建议同时启用两条下发路径,否则容易出现资源归属冲突。

三、代理组件 fleet-agent 的工作循环

fleet-agent 在成员集群中通常以 Deployment 形式运行,其核心循环可以概括为“注册—订阅—落地—回报”四步:

  1. 注册:agent 启动后读取本地配置(如 cluster-registration Secret 或启动参数中的 --cluster-namespace、--api-server-url),在中心创建或更新对应的 Cluster 对象,并周期性上报心跳。
  2. 订阅:agent 以本集群的 Cluster 名称为过滤条件,watch 中心侧与自己相关的 BundleDeployment 与 Bundle 内容。
  3. 落地:把 Bundle 中的资源按命名空间、标签选择器应用到本地,并记录每个资源的 apply 结果。
  4. 回报:把本地资源的就绪状态、错误信息写回 BundleDeployment 的 status,供中心侧聚合展示。

这里有一个容易踩坑的点:agent 落地时使用的是服务端 apply(Server-Side Apply)语义,字段管理器通常固定为 fleet-agent。这意味着如果运维人员手工修改了某个由 Fleet 管理的资源字段,下一次 agent 同步时该字段会被改回期望值。排查“配置被莫名还原”类问题时,应先确认该资源是否带有 objectset.rio.cattle.io/owner 之类的 Fleet 归属标签。

四、API 聚合与扩展点

Fleet 的扩展能力主要落在三个位置,理解它们就能判断“新需求应该改哪里”:

  • GitRepo 与 Bundle 生成:通过 fleet.yaml 中的 targetCustomizations、helm、kustomize 等字段,把同一份 Git 内容按集群标签差异化渲染。这是最常用的扩展点,不需要改代码。
  • Cluster 标签与选择器:BundleDeployment 的生成依赖 Cluster 标签匹配。新增一类集群(如边缘节点、GPU 节点)时,先打标签,再在 targetCustomizations 中增加匹配规则,即可把内容定向下发。
  • API 聚合层:Fleet 通过 CRD 扩展 Kubernetes API,把 Cluster、Bundle、BundleDeployment、GitRepo 等对象注册进中心集群的 API Server。运维人员可以用 kubectl get clusters.fleet.cattle.io -A 这类命令直接查询,也可以用 RBAC 对它们做细粒度授权。

下面这张图把中心控制面、成员集群、推送/拉取两条路径以及三个扩展点放在同一张关系图中,便于对照理解。

中心控制面(local 管理集群) fleet-controller Bundle 编排 / GitRepo 同步 中心 kube-apiserver CRD 聚合层 Fleet CRD 对象 Cluster · Bundle · BundleDeployment · GitRepo Cluster 标签:env / region / tier 扩展点 ① fleet.yaml targetCustomizations ② Cluster 标签选择器 ③ CRD + RBAC 成员集群 A(拉取模型) fleet-agent watch + apply 本地 kube-apiserver 业务工作负载 出站访问中心 API,无需中心入站连接 成员集群 B(推送模型) fleet-agent 接收中心写入 本地 kube-apiserver 业务工作负载 中心持有凭据,需双向网络可达 拉取 BundleDeployment 回报 status / 心跳 推送 Bundle 内容 状态流转与判断依据 期望状态:GitRepo → Bundle → BundleDeployment(中心侧生成,按 Cluster 标签匹配) 实际状态:fleet-agent apply 后写回 BundleDeployment.status,中心聚合展示 Ready / NotReady / ErrApplied 判断:若 status 长期 NotReady,先查 agent 日志与本地资源事件;若资源被手工改动后还原,检查 SSA 字段管理器归属

五、生产实践中的几个判断点

第一,中心侧不要承载业务负载。local 集群只跑 Fleet 控制面与必要的系统组件,避免业务 Pod 与 controller 争抢资源导致编排延迟。第二,成员集群的 agent 资源要留足。agent 需要 watch 中心侧对象并在本地 apply,建议为其设置合理的 requests/limits,并监控其重启次数。第三,凭据最小化。拉取模型下,中心侧为每个成员集群创建的 ServiceAccount 应只允许访问本集群对应的 BundleDeployment 与 Bundle,避免一个集群的凭据泄露后影响其他集群。

第四,区分“下发成功”与“运行成功”。BundleDeployment 的 status 反映的是 agent 是否成功 apply,并不等于业务 Pod 已经 Ready。生产排障时应把 Fleet 状态与本地 Deployment/StatefulSet 的可用副本数结合看,才能判断是下发链路问题还是工作负载自身问题。第五,扩展点优先用配置而非改代码。绝大多数差异化下发需求都可以通过 fleet.yaml 的 targetCustomizations 与 Cluster 标签组合解决,只有在需要新增资源类型或自定义编排逻辑时,才考虑 CRD 与 controller 层面的扩展。

注意:推送模型下中心侧持有成员集群的长期凭据,一旦中心被攻破,影响面会扩散到所有成员集群。若必须使用推送模型,建议对凭据做定期轮换,并通过 NetworkPolicy 限制中心到成员集群 API Server 的访问来源。

把上述关系串起来看:中心控制面负责“决定什么内容应该出现在哪些集群”,成员集群的 fleet-agent 负责“把内容变成本地真实资源并回报结果”,推送与拉取决定“连接由谁发起”,而 GitRepo、Cluster 标签、CRD/RBAC 三个扩展点决定“如何把差异化需求表达出来”。掌握这四层关系,后续排查下发失败、状态不一致、资源被还原等问题时,就能快速定位到具体环节。