3 min read
「kubefed 02」从入土到浇注

理理一些边角的库、场景,也说说感受。为什么这篇是「浇注」,被核辐射过的一个特征是脱发,看完 kubefed 头发也少了一把,那是不是可以认为 kubefed ≈ 辐射源,所以对于隔绝辐射最好的解决方式之一就是水泥浇注(必要时候再盖一快 40m(没错 40m 来自某大佬建议)厚的铅板

几个出现频率比较高的 util

  • DelayingDeliverer:基于 container/heap 实现的延迟队列
  • ReconcileWorker:结合延迟队列做的 reconcile 分发
  • ResourceInformer:通过 ResourceClient(util 中在 APIResource 基础上实现的 client)和 cache.Informer 实现的泛 Informer
  • FederatedInformer:由 ResourceInformer(Target APIResource)和 ClusterInformer 组成(具备提供 APIResource 和 ClusterLifecycle 感知能力)
  • GenericInformer:GVK 实现的 informer,但没 get 到跟 ResourceInformer 的区别(除了参数)

当前对于集群状态变更感知的几个 controller

  • ServiceDNS
  • IngressDNS
  • SchedulingPreference
  • Status
  • Sync

当前处理比较简单就是当集群状态变更的时候,所有集群的相关 Object 都进一把队列。

对于一个 Deployment 在正常情况下,当一个集群从健康变为不健康,那么会又 ClusterController 发现然后更新 KubefedCluster.Status 而后触发 ClusterLifecycle.ClusterUnavailable 进而触发 SchedulingPreference 重新计算分配而后触发 re-Sync(不健康变为健康也是类似)。在某个集群不可用期间,Object 有变更的话,重新加入的时候也会被 re-Sync 覆盖掉。

但其实这块个人觉得会有几个风险点

  • 集群从不健康变为健康的时候:全部重新入队列,有些服务排的比较后面且期间恰好有更新,那么会出现多版本并存的风险
  • 集群从健康变为不健康:全部重新入队列,假如某些服务尚未开启 HPA(因为某些特殊原因)由于 SchedulingPreference 还在排队所以在瞬时可能雪崩
  • 在出现全集群 reconcile 的时候,如果这时候穿插业务更新(如果量大的话),全集群重入的 FederatedObject 可能增加重新同步延迟(因为普通 informer 的优先级高于全集群重入)
  • 集群抖动且超过了 ClusterController 的过滤范围:带来大量的全集群重入可能直接 OOM 和严重堆积(虽然可能 version 是不变的)

高可用的问题

由于 kubefed 的控制层是在 Host Cluster(有且唯一)且当前的设计决定了只能在一个确定的 Kubernetes 集群中运行,所以在没有 Host Cluster 一定是高可用的前提下,我们可以认为其实是会有可用性问题的。但如果你假设「Host Cluster 不是高可用的」,那么就会翻车了:一旦 Host Cluster 挂了,就相当于失去全局的掌控能力和一定程度上的灾备能力(基于 SchedulingPreference)。

我考虑过一下集中奇怪的方式但都以失败告终

  • Federated on Federated Type 然后故障的时候切换 Host Cluster:死循环
  • 交叉 kubefed 也就是每个集群互为 Host 又是 Member:data race、数据同步
  • 外部控制器同步在各个集群创建 Federate Type,故障的时候切换 Host Cluster:同步可能数据不一致

而且上面都会有一个 join 和 unjoin 的问题(这个问题后面说)。

社区也有人问过这茬 control plane on multiple clusters #967,得到的答复也是预期的:拒绝,如果要实现这样的目的需要引入外部的存储。在更早一点的一个关于异常恢复的 issue 中,marun 提到了通过第三方存储解决的方式(但也是需要改架构设计)

https://github.com/kubernetes-sigs/kubefed/issues/450#issuecomment-441760508

Another possibility to reduce the cost of fedv2 control plane downtime is pull propagation. In this mode, rather than the fedv2 control plane being responsible for applying changes to member clusters, the control plane would generate the desired configuration for member clusters and write it to a distributed store (e.g. s3/gcs/etc). Controllers in member clusters would be responsible for reading from the distributed store and applying configuration. This is only a partial solution to ensure that cluster configuration is maintained. Resilient traffic coordination (i.e. load balancing) would still be required.

绝境

独立 Host Cluster

找一个能保障高可用的 Kubernetes 集群,然后把 kubefed 控制层丢上去。这样做几个好处

  1. 高可用
  2. 异常的时候恢复负担小:比如集群炸了都可以直接直接起新的,然后拿 etcd 恢复等等
  3. 升级等场景也比较快、灵活:不会有影响业务的负担

综上,没啥可解。。。

Orphan annotation & Managed label

Orphan 是打在 FederatedResource 的 annotations 上 kubefed.io/orphan=true,而 Managed 是打在 TargetResource 的 Labels 上 kubefed.io/managed: true

  • 在 FR 删除的时候如果有 Orphan 则删除 FR 的 finalizer(正常释放)并删除 TargetResource 的 Managed 标签
  • CheckRemovedOrUnlabeled 中如果未删除的 TargetResource 不含有 Managed true 的标签则不会报错
  • FederatedInformer 中的 cache 只有 Managed true 的对象,基于此有以下几点
    • 原有非选择的 cluster 如果有同名 object 不会被删除
    • 如果选择到集群且有同名,则会创建更新时报错 Already exists(创建更新在最终实际代码流都是 Create)
    • 不受 FederatedResource 删除影响

关于 cluster join/unjoin 时候的保留

一时间找不到那看过 repo 里面有个讨论是关于 clusterStrategy 的里面提及了 retain 的事项,等后面找到再贴上来。

API Resource 升级的时候怎么处理

在 FTC 中是感知 GVK 的,所以跟 k8s 一样,需要手动给新的 API Resource 做 federation 然后手动做迁移。最后下掉旧的 Federated Object 和 FTC。

SchedulingPreference 那边的 ReplicaScheduler 只感知了 apps group 暂时不提供服务给 extensions 的 Deployment 和 ReplicaSet

一些已知问题

  • 可修改 Immutable 字段 Immutable Fields:说 GA 前解决
  • 删除 SchedulingPreference 的时候不会恢复原有的 FederatedObject 中的 Placement 设定

感受

Good

  • ReplicaScheduler 中的 Rebalance 做得不错,这个特性解决了一些像集群资源不足、Quota 到达上限的问题。但在有 Cluster Autoscaler 加持的场景下可能会导致集群倾斜
  • Schema 的动态 Federation 做得挺好的,解决了 Federation v1 的灵活性、拓展性问题

Bad(maybe)

  • 将整个控制层固化在单一集群内,对于升级、迁移的场景支持不太友好
  • 对于集群 unjoin 的处理不够平滑:指当前只能通过 kubefed.io/orphan=true 实现一定程度上的保留
  • reconcileOnClusterChange 可以调整为针对涉及的 Object 做 reconcile

Ugly

  • 有些地方过度的抽象了比如 ReplicaScheduler 那边抽出了 SchedulingType、Factory、Plugin、Planner 等一大串
  • Planner 的实现可读性太差

总的来说,kubefed 还是挺好的把很多 kube API 抽象能力体现得淋漓尽致,相比起它那死在岸边的前浪 federation 这货已经被挺多人接受和使用。可惜还没有 GA,活跃度也一般般,要拿去生产用还是挺担心的。如果一定还要造轮子的话,我会选择继续使用 schema 但少一点抽象、好好给变量、函数起名。