乐趣区

关于数据库:TiDB-Operator-源码阅读-三-编排组件控制循环

上篇文章中,咱们介绍了 TiDB Operator 的 Controller Manager 的设计和实现,理解了各个 Controller 如何承受和解决变更。在这篇文章中,咱们将探讨组件的 Controller 的实现。TiDBCluster Controller 负责了 TiDB 次要组件的生命周期治理,咱们将以此为例,介绍组件管制循环的编排设计。咱们将会理解到实现 TiDB 集群的生命周期治理过程中,各种管制循环事件通过了怎么的编排,这些事件中又实现了哪些资源管理操作。在浏览时,大家理解这些工作的大抵过程和定义即可,咱们将在下一篇文章中具体介绍各个组件如何套用上面的范式。

组件管制循环的调用

在上一篇文章的代码介绍中,咱们提到了 TiDBCluster controllerupdateTidbCluster 函数,位于 pkg/controller/tidbcluster/tidb_cluster_control.go,它是 TiDB 组件生命周期治理的入口,调用了一系列生命周期治理函数。略去正文,咱们能够发现 updateTidbCluster 函数顺次调用了以下函数:

  1. c.reclaimPolicyManager.Sync(tc)
  2. c.orphanPodsCleaner.Clean(tc)
  3. c.discoveryManager.Reconcile(tc)
  4. c.ticdcMemberManager.Sync(tc)
  5. c.tiflashMemberManager.Sync(tc)
  6. c.pdMemberManager.Sync(tc)
  7. c.tikvMemberManager.Sync(tc)
  8. c.pumpMemberManager.Sync(tc)
  9. c.tidbMemberManager.Sync(tc)
  10. c.metaManager.Sync(tc)
  11. c.pvcCleaner.Clean(tc)
  12. c.pvcResizer.Resize(tc)
  13. c.tidbClusterStatusManager.Sync(tc)

这些函数能够分为两类,一是 TiDB 组件的视角组织的管制循环实现,例如 PD,TiDB,TiKV,TiFlash,TiCDC,Pump,Discovery,另外一类是负责管理 TiDB 组件所应用的 Kubernetes 资源的治理以及其余组件外围的生命周期治理操作,例如 PV 的 ReclaimPolicy 的保护,OrphanPod 的清理,Kubernetes 资源的 Meta 信息保护,PVC 的清理和扩容,TiDBCluster 对象的状态治理等。

TiDB 组件的生命周期治理过程

TiDB 的次要组件管制循环的代码散布在 pkg/manager/member 目录下以 _member_manager.go 结尾的文件下,比方 pd_member_manager.go,这些文件又援用了诸如 _scaler.go_upgrader.go 的文件,这些文件蕴含了扩缩容和降级相干性能的实现。从各个组件的 _member_manager.go 相干文件,咱们能够提炼出以下通用实现:

// Sync Service
if err := m.syncServiceForTidbCluster(tc); err != nil {return err}
 
// Sync Headless Service
if err := m.syncHeadlessServiceForTidbCluster(tc); err != nil {return err}
 
// Sync StatefulSet
return syncStatefulSetForTidbCluster(tc)
 
func syncStatefulSetForTidbCluster(tc *v1alpha1.TidbCluster) error {if err := m.syncTidbClusterStatus(tc, oldSet); err != nil {klog.Errorf("failed to sync TidbCluster: [%s/%s]'s status, error: %v", ns, tcName, err)
    }
 
    if tc.Spec.Paused {klog.V(4).Infof("tidb cluster %s/%s is paused, skip syncing for statefulset", tc.GetNamespace(), tc.GetName())
        return nil
    }
 
    cm, err := m.syncConfigMap(tc, oldSet)
 
    newSet, err := getnewSetForTidbCluster(tc, cm)
 
    if err := m.scaler.Scale(tc, oldSet, newSet); err != nil {return err}
 
    if err := m.failover.Failover(tc); err != nil {return err}
 
    if err := m.upgrader.Upgrade(tc, oldSet, newSet); err != nil {return err}
 
    return UpdateStatefulSet(m.deps.StatefulSetControl, tc, newSet, oldSet)
}

这段代码次要实现了同步 Service 和 同步 Statefulset 的工作,同步 Service 次要是为组件创立或同步 Service 资源,同步 Statefulset 具体蕴含了一下工作:

  1. 同步组件的 Status;
  2. 查看 TiDBCluster 是否进行暂停了同步;
  3. 同步 ConfigMap;
  4. 依据 TidbCluster 配置生成新的 Statefulset,并且对新 Statefulset 进行滚动更新,扩缩容,Failover 相干逻辑的解决;
  5. 创立或者更新 Statefulset;

组件的管制循环是对下面几项工作循环执行,使得组件放弃最新状态。上面将介绍 TiDB Operator 中这几项工作具体须要实现哪些方面的工作。

同步 Service

个别 Service 的 Reconcile 在组件 Reconcile 开始局部,它负责创立和同步组件所应用的 Service,例如 cluster1-pdcluster1-pd-peer。在管制循环函数中,会调用 getNewServiceForTidbCluster 函数,通过 Tidbcluster CR 中记录的信息创立一个新的 Service 的模板,如果 Service 不存在,间接创立 Service,否则,通过比对新老 Service Spec 是否统一,来决定是否要更新 Service 对象。

TiDB 组件应用的 Service 中,包含了 Service 和 Headless Serivce,为组件提供了被拜访的能力。当组件不须要晓得是哪个实例正在与它通信,并且能够承受负载平衡形式的拜访,则能够应用 Service 服务,例如 TiKV,TiDB 等组件拜访 PD 时,就能够应用 Service 地址。当组件须要辨别是那个 Pod 在提供服务时,则须要用 Pod DNS 进行通信,例如 TiKV 在启动时,会将本人的 Pod DNS 作为 Advertise Address 对外裸露,其余 Pod 能够通过这个 Pod DNS 拜访到本人。

同步 Status

实现 Service 的同步后,组件接入了集群的网络,能够在集群内拜访和被拜访。管制循环会进入 syncStatefulSetForTidbCluster,开始对 Statefulset 进行 Reconcile,首先是应用 syncTidbClusterStatus 对组件的 Status 信息进行同步,后续的扩缩容、Failover、降级等操作会依赖 Status 中的信息进行决策。

同步 Status 是 TiDB Operator 比拟要害的操作,它须要同步 Kubernetes 各个组件的信息和 TiDB 的集群外部信息,例如在 Kubernetes 方面,在这一操作中会同步集群的正本数量,更新状态,镜像版本等信息,查看 Statefulset 是否处于更新状态。在 TiDB 集群信息方面,TiDB Operator 还须要将 TiDB 集群外部的信息从 PD 中同步下来。例如 PD 的成员信息,TiKV 的存储信息,TiDB 的成员信息等,TiDB 集群的健康检查的操作便是在更新 Status 这一操作内实现。

查看 TiDBCluster 是否暂停同步

更新完状态后,会通过 tc.Spec.Paused 判断集群是否处于暂停同步状态,如果暂停同步,则会跳过上面更新 Statefulset 的操作。

同步 ConfigMap

在同步完 Status 之后,syncConfigMap 函数会更新 ConfigMap,ConfigMap 个别包含了组件的配置文件和启动脚本。配置文件是通过 YAML 中 Spec 的 Config 项提取而来,目前反对 TOML 透传和 YAML 转换,并且举荐 TOML 格局。启动脚本则蕴含了获取组件所需的启动参数,而后用获取好的启动参数启动组件过程。在 TiDB Operator 中,当组件启动时须要向 TiDB Operator 获取启动参数时,TiDB Operator 侧的信息处理会放到 discovery 组件实现。如 PD 获取参数用于决定初始化还是退出某个节点,就会应用 wget 拜访 discovery 拿到本人须要的参数。这种在启动脚本中获取参数的办法,防止了更新 Statefulset 过程中引起的非预期滚动更新,对线上业务造成影响。

生成新 Statefulset

getNewPDSetForTidbCluster 函数会失去一个新的 Statefulset 的模板,它蕴含了对方才生成的 Service,ConfigMap 的援用,并且依据最新的 Status 和 Spec 生成其余项,例如 env,container,volume 等,这个新的 Statefulset 还须要送到上面 3 个流程进行滚动更新,扩缩容,Failover 方面的加工,最初将这个新生成的 Statefulset 会被送到 UpdateStatefulSet 函数解决,判断其是否须要理论更新到组件对应的 Statefulset。

新 Statefulset 的加工 (一): 滚动更新

m.upgrader.Upgrade 函数负责滚动更新相干操作,次要更新 Statefulset 中 UpgradeStrategy.TypeUpgradeStrategy.Partition,滚动更新是借助 Statefulset 的 RollingUpdate 策略实现的。组件 Reconcile 会设置 Statefulset 的降级策略为滚动更新,即组件 Statefulset 的 UpgradeStrategy.Type 设置为 RollingUpdate。在 Kubernetes 的 Statefulset 应用中,能够通过配置 UpgradeStrategy.Partition 管制滚动更新的进度,即 Statefulset 只会更新序号大于或等于 partition 的值,并且未被更新的 Pod。通过这一机制就能够实现每个 Pod 在失常对外服务后才持续滚动更新。在非降级状态或者降级的启动阶段,组件的 Reconcile 会将 Statefulset 的 UpgradeStrategy.Partition 设置为 Statefulset 中最大的 Pod 序号,避免有 Pod 更新。在开始更新后,当一个 Pod 更新,并且重启后对外提供服务,例如 TiKV 的 Store 状态变为 Up,TiDB 的 Member 状态为 healthy,满足这样的条件的 Pod 才被视为降级胜利,而后调小 Partition 的值进行下一 Pod 的更新。

新 Statefulset 的加工 (二): 扩缩容

m.scaler.Scale 函数负责扩缩容相干操作,次要是更新 Statefulset 中组件的 Replicas。Scale 遵循一一扩缩容的准则,每次扩缩容的跨度为 1。Scale 函数会将 TiDBCluster 中指定的组件 Replicas 数,如 tc.Spec.PD.Replicas,与现有比拟,先判断是扩容需要还是缩容需要,而后实现一个步长的扩缩容的操作,再进入下一次组件 Reconcile,通过屡次 Reconcile 实现所有的扩缩容需要。在扩缩容的过程中,会波及到 PD 转移 Leader,TiKV 删除 Store 等应用 PD API 的操作,组件 Reconcile 过程中会应用 PD API 实现上述操作,并且判断操作是否胜利,再逐渐进行下一次扩缩容。

新 Statefulset 的加工 (三): Failover

m.failover.Failover 函数负责容灾相干的操作,包含发现和记录劫难状态,复原劫难状态等,在部署 TiDB Operator 时配置关上 AutoFailover 后,当发现有 Failure 的组件时记录相干信息到 FailureStores 或者 FailureMembers 这样的状态存储的键值,并启动一个新的组件 Pod 用于承当这个 Pod 的工作负载。当原 Pod 复原工作后,通过批改 Statefulset 的 Replicas 数量,将用于容灾时分担工作负载的 Pod 进行缩容操作。然而在 TiKV/TiFlash 的容灾逻辑中,主动缩容容灾过程中的 Pod 不是默认操作,须要设置 spec.tikv.recoverFailover: true 才会对新启动的 Pod 缩容。

应用新 Statefulset 进行更新

在同步 Statefulset 的最初阶段,曾经实现了新 Statefulset 的生成,这时候会进入 UpdateStatefulSet 函数,这一函数中次要比对新的 Statefulset 和现有 StatefulSet 差别,如果不统一,则进行 Statefulset 的理论更新。另外,这一函数还须要查看是否有没有被治理的 Statefulset,这部分次要是旧版本应用 Helm Chart 部署的 TiDB,须要将这些 Statefulset 纳入 TiDB Operator 的治理,给他们增加依赖标记。

实现上述操作后,TiDBCluster CR 的 Status 更新到最新,相干 Service,ConfigMap 被创立,生成了新的 Statefulset,并且满足了滚动更新,扩缩容,Failover 的工作。组件的 Reconcile 周而复始,监控着组件的生命周期状态,响应生命周期状态扭转和用户输出扭转,使得集群在合乎预期的状态下失常运行。

其余生命周期保护工作

除了 TiDB 次要组件的视角之外,还有一些运维操作被编排到了上面的函数实现中,他们别离负责了以下工作:

  1. Discovery,用于 PD 启动参数的配置和 TiDB Dashboard Proxy,Discovery 的存在,能够提供一些动静信息供组件索取,防止了批改 ConfigMap 导致 Pod 滚动更新。
  2. Reclaim PolicyManager,用于同步 tc.Spec.PVReclaimPolicy 的配置,默认配置下会将 PV 的 Reclaim Policy 设置为 Retain,升高数据失落的危险。
  3. OrphanPodCleaner,用于在 PVC 创立失败的时候革除 Pod,让 Statefulset Controller 重试 Pod 和对应 PVC 的创立。
  4. PVC Cleaner 用于 PVC 相干资源清理,清理被标记能够删除的 PVC。
  5. PVC Resizer 用于 PVC 的扩容,在云上应用时能够通过批改 TidbCluster 中的 Storage 相干配置批改 PVC 的大小。
  6. Meta Manager 用于同步 StoreIDLabel,MemberIDLabel,NamespaceLabel 等信息到 Pod,PVC,PV 的 label 上。
  7. TiDBCluster Status Manager 用于同步 TidbMonitor 和 TiDB Dashboard 相干信息。

小结

这篇文章介绍了 TiDBCluster 组件的管制循环逻辑的设计,试图让大家理解,当 TiDBCluster Controller 循环触发各个组件的 Reconcile 函数时,组件 Reconcile 函数是依照怎么的流程巡检组件的相干资源,用户预期的状态,是如何通过 Reconcile 函数,变成理论运行的组件。TiDB Operator 中的管制循环都大抵合乎以上的设计逻辑,在前面的文章中,咱们会具体介绍每个组件是如何套用下面的设计逻辑,实现组件的生命周期治理。

如果有什么好的想法,欢送通过 #sig-k8s 或 pingcap/tidb-operator 参加 TiDB Operator 社区交换。

退出移动版