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 形式运行,其核心循环可以概括为“注册—订阅—落地—回报”四步:
- 注册:agent 启动后读取本地配置(如
cluster-registrationSecret 或启动参数中的--cluster-namespace、--api-server-url),在中心创建或更新对应的 Cluster 对象,并周期性上报心跳。 - 订阅:agent 以本集群的 Cluster 名称为过滤条件,watch 中心侧与自己相关的 BundleDeployment 与 Bundle 内容。
- 落地:把 Bundle 中的资源按命名空间、标签选择器应用到本地,并记录每个资源的 apply 结果。
- 回报:把本地资源的就绪状态、错误信息写回 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 控制面与必要的系统组件,避免业务 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 层面的扩展。
把上述关系串起来看:中心控制面负责“决定什么内容应该出现在哪些集群”,成员集群的 fleet-agent 负责“把内容变成本地真实资源并回报结果”,推送与拉取决定“连接由谁发起”,而 GitRepo、Cluster 标签、CRD/RBAC 三个扩展点决定“如何把差异化需求表达出来”。掌握这四层关系,后续排查下发失败、状态不一致、资源被还原等问题时,就能快速定位到具体环节。