TiDB操作符源代码读取(III):组件控制循环
作者:谊文陈(TiDB运营商委员,PingCAP软件工程师)
Transcreator:黄了;编辑器:汤姆政府高级官员
本系列之前的文章:
在我最后一篇文章,我介绍了我们如何设计和实现tidb-controller-manager
以及控制器的内部逻辑,以及每个控制器如何接收和处理变化。这一次,我将描述如何实现组件控制器。
的TidbCluster
控制器管理TiDB中主要组件的生命周期。我要TidbCluster
并以实例介绍了元件控制回路的设计。您将了解在TiDB集群生命周期管理期间如何编排控制循环事件,以及这些事件如何管理资源。
本文只是对这些过程和定义的一般介绍。下一篇文章将介绍每个组件的具体应用程序。现在,让我们开始吧。
调用组件控制循环
在关于控制器的内部逻辑,我提到过updateTidbCluster
的函数TidbCluster
控制器,位于包裹/控制器/ tidbcluster / tidb_cluster_control.go
.作为TiDB组件生命周期管理的入口,该函数调用一系列生命周期管理函数:
c.reclaimPolicyManager.Sync (tc)
c.orphanPodsCleaner.Clean (tc)
c.discoveryManager.Reconcile (tc)
c.ticdcMemberManager.Sync (tc)
c.tiflashMemberManager.Sync (tc)
c.pdMemberManager.Sync (tc)
c.tikvMemberManager.Sync (tc)
c.pumpMemberManager.Sync (tc)
c.tidbMemberManager.Sync (tc)
c.metaManager.Sync (tc)
c.pvcCleaner.Clean (tc)
c.pvcResizer.Resize (tc)
c.tidbClusterStatusManager.Sync (tc)
这些功能分为两类:
- TiDB组件控制回路的实现如PD、TiDB、TiKV、TiFlash、TiCDC、Pump、Discovery等。
- TiDB组件使用的Kubernetes资源的管理和其他组件的生命周期管理,例如维护PV的回收策略、清理孤立的Pods、维护Kubernetes资源的元信息、清理和扩展pvc,以及管理状态
TidbCluster
对象。
TiDB组件的生命周期管理过程
TiDB的主要组件的控制循环代码位于该目录中包裹/经理/成员
文件的结尾是_member_manager.go
,如pd_member_manager.go
.这些文件引用其他实现缩放和升级特性的文件,例如_scaler.go
和_upgrader.go
.
从_member_manager.go
文件的组件,我们可以识别通用实现:
/ /同步服务如果犯错:=米.syncServiceForTidbCluster(tc);犯错! =零{返回犯错}//同步Headless服务如果犯错:=米.syncHeadlessServiceForTidbCluster(tc);犯错! =零{返回犯错}/ /同步StatefulSet返回syncStatefulSetForTidbCluster(tc)函数syncStatefulSetForTidbCluster(tc*v1alpha1.TidbCluster)错误{如果犯错:=米.syncTidbClusterStatus(tc,oldSet);犯错! =零{klog.Errorf("failed to sync TidbCluster: [%s/%s]'s status, error: %v",ns,tcName,犯错)}如果tc.规范.停顿了一下{klog.V(4).Infof("tidb cluster %s/%s is paused, skip syncing for statefulset",tc.GetNamespace(),tc.GetName())返回零}厘米,犯错:=米.syncConfigMap(tc,oldSet)newSet,犯错:=getnewSetForTidbCluster(tc,厘米)如果犯错:=米.定标器.规模(tc,oldSet,newSet);犯错! =零{返回犯错}如果犯错:=米.故障转移.故障转移(tc);犯错! =零{返回犯错}如果犯错:=米.升级程序.升级(tc,oldSet,newSet);犯错! =零{返回犯错}返回UpdateStatefulSet(米.deps.StatefulSetControl,tc,newSet,oldSet)}
上面的代码执行两个主要任务:
- 同步服务:创建或同步TiDB组件的服务资源。
- 同步StatefulSet:
- 同步组件状态。
- 检查是否
TidbCluster
停止同步。 - 同步ConfigMap。
- 根据中的配置生成新的StatefulSet
TidbCluster
并对新的StatefulSet执行相关操作,如滚动更新、向外扩展、向内扩展和故障转移。 - 创建或更新StatefulSet。
组件控制循环重复执行上述任务,以确保组件保持最新状态。
下面几节介绍在控制回路中完成的具体工作。
同步服务
当组件协调启动时,服务协调也会启动。这个过程创建并同步组件使用的服务,如cluster1-pd
和cluster1-pd-peer
.
控制循环函数调用getNewServiceForTidbCluster
函数中记录的信息创建新的Service模板TidbCluster
自定义资源(CR)。如果该服务不存在,控制循环函数将创建一个服务;如果服务存在,它将比较旧的服务规范和新服务规范,并确定是否更新服务对象。
服务和Headless服务都允许组件被其他人访问。
- 如果组件不需要知道它正在与哪个实例通信,以及组件是否支持负载平衡,例如当TiKV和TiDB访问Placement Driver (PD)时,组件将使用Service。
- 如果组件需要知道哪个Pod提供服务,它使用Headless service进行通信豆荚DNS.例如,当TiKV启动时,它将其Pod DNS暴露为广告地址,以便其他Pod可以通过Pod DNS访问TiKV。
同步StatefulSet
服务同步完成后,组件将连接到集群网络,这样它们就可以访问集群,并且可以从集群内部访问它们。控制回路进入syncStatefulSetForTidbCluster
函数并开始协调StatefulSet。
协调StatefulSet的第一步是通过运行syncTidbClusterStatus
函数。然后根据状态信息进行升级、向内扩展、向外扩展、故障转移等操作。
同步组件状态
TiDB Operator的关键操作之一是同步组件状态。状态信息包括:
- Kubernetes的组件信息,包括集群中副本的数量、更新状态和映像版本。TiDB操作符还检查StatefulSet是否在更新过程中。
- TiDB的内部集群信息。TiDB Operator从PD同步信息,包括关于PD成员、TiKV存储和TiDB成员的信息。TiDB操作符还对TiDB执行运行状况检查。
检查TiDB集群是否暂停同步
同步完成后,TiDB Operator通过检查判断集群是否停止同步tc.Spec.Paused
.如果同步暂停,TiDB Operator将跳过以下所有更新StatefulSet的操作。
同步ConfigMap
状态同步完成后,syncConfigMap
函数更新ConfigMap,其中包含组件的配置文件和启动脚本。
配置文件是从配置
项规范
YAML文件的。TiDB Operator支持直接使用TOML配置(推荐)或从YAML转换配置。
启动脚本包含组件所需的启动参数,并使用这些参数启动组件进程。
当组件需要从TiDB Operator获取启动参数时,在Discovery组件中执行信息处理。例如,当PD需要使用参数来确定它是应该初始化一个节点还是加入一个节点时,它使用wget访问Discovery并获取参数。通过从启动脚本获取参数,TiDB Operator可以避免在更新StatefulSet时发生意外的滚动更新。这可能会影响在线服务。
生成一个新的StatefulSet
的getNewPDSetForTidbCluster
函数获取一个新的StatefulSet模板,该模板包含新生成的Service和ConfigMap的引用。该函数使用最新的状态和规范来生成其他项,例如env
,容器
,体积
.
然后,这个新的StatefulSet将经历三个过程:滚动更新、扩展和故障转移。最后,UpdateStatefulSet
函数比较现有的StatefulSet和新的StatefulSet,并确定是否更新现有的StatefulSet。
滚动更新
的m.upgraded.Upgrade
函数执行与滚动更新相关的操作,主要是更新UpgradeStrategy。类型
和UpgradeStrategy。分区
StatefulSet。
使用StatefulSet中的滚动更新策略执行滚动更新操作。当组件被协调时,它将StatefulSet的更新策略设置为滚动更新。在Kubernetes中,您可以通过配置来控制滚动更新进度UpgradeStrategy。分区
.StatefulSet只更新之前没有更新且ID大于或等于的PodsUpgradeStrategy。分区
.TiDB Operator使用这种机制来确保每个Pod只有在能够正常地向外部应用程序提供服务之后才进行滚动更新。
当集群未被更新或仅处于更新的开始阶段时,组件协调集UpgradeStrategy。分区
为StatefulSet中最大的Pod ID。这将阻止任何Pod被更新。升级开始后,当Pod升级并重新启动后提供服务时,视为升级成功。然后,TiDB操作符自减UpgradeStrategy。分区
值并更新下一个Pod。
扩张和扩张
m.scaler.Scale
缩放组件进出。它的主要任务是更新副本
StatefulSet中的组件。
的m.scaler.Scale
函数一步一步地对组件进行缩放。它比较了副本
中的组件TidbCluster
CR(例如tc.Spec.PD.Replicas
)和水流副本
.在此基础上,它决定是向外扩展还是在组件中扩展,并在一个副本上执行扩展操作。然后进入下一轮组件协调。经过多轮和解,m.scaler.Scale
完成所有缩放要求。
在扩展过程中,PD需要转移Leader, TiKV需要删除stores。这些操作使用PD api。在和解过程中,m.scaler.Scale
功能使用PD接口执行操作,查看操作是否成功。如果是,则转移到下一个缩放操作。
故障转移
的m.failover.Failover
功能用于执行灾难恢复的相关操作,包括发现失败、记录失败状态和从失败中恢复。
如果你使AutoFailover
在部署TiDB Operator时,TiDB Operator会监控组件的故障状态。当发现一个故障时,TiDB Operator将故障信息记录到FailureStores
或FailureMembers
.接下来,它启动一个新的组件Pod来接管失败的Pod的工作负载。失败的Pod恢复后,TiDB Operator在新的Pod中修改StatefulSet中的副本数量为可伸缩的。
当TiDB Operator为TiKV和TiFlash执行故障转移时,默认情况下不会扩展新创建的Pod。您需要配置spec.tikv.recoverFailover:真
启用自动扩展。
更新现有的StatefulSet
在最后一个阶段,创建新的StatefulSet。控制循环现在进入UpdateStatefulSet
函数,该函数比较新的StatefulSet和现有的StatefulSet。如果两个StatefulSet不一致,则该函数更新现有的StatefulSet。
该函数还检查是否有未被TiDB操作符管理的statefulset。由于TiDB Operator的早期版本使用Helm Chart来部署TiDB, TiDB Operator需要向这些旧的statefulset添加依赖标记,并将它们包含在生命周期管理中。
通过以上操作,可以查看TidbCluster
CR升级到最新版本。创建相应的Service和ConfigMap。生成新的StatefulSet,它执行滚动更新、扩展和故障转移。组件协调继续进行,监视组件的生命周期并响应生命周期状态的更改和用户指定的更改。整个集群运行正常。
其他生命周期管理任务
除了对TiDB中的主要组件进行协调外,其他的生命周期管理操作由以下功能来完成:
- Discovery配置PD启动参数和TiDB Dashboard Proxy。它为组件提供了要使用的动态信息,避免了修改ConfigMap而导致Pod滚动更新。
- 回收策略管理器同步的配置
tc.Spec.PVReclaimPolicy
.默认情况下,PV回收策略为保留
降低数据丢失的风险。 - 孤儿Pod Cleaner在PVC创建失败时清除Pod。然后StatefulSet Controller再次尝试创建Pods和相应的pvc。
- PVC Cleaner可删除标记为可删除的PVC。
- PVC调整器缩放PVC。在云上运行TiDB Operator时,可以通过修改中的存储配置来更改pvc的大小
TidbCluster
. - 元数据管理器同步
StoreIDLabel
,MemberIDLabel
,NamespaceLabel
到pod, pvc和pv的标签。 - TiDBCluster Status Manager同步相关的信息
TidbMonitor
和TiDB仪表板。
总结
到目前为止,您已经学习了TiDB组件控制回路的设计,包括:
- 协调函数如何遵循它们相应的程序来检查组件资源
- 协调函数如何将用户期望的状态转换为实际的组件状态
TiDB Operator中几乎所有的控制回路都符合本文描述的设计逻辑。在以后的文章中,我将进一步解释如何将这种逻辑应用于每个组件来管理组件的生命周期。
如果您对TiDB运营商有任何问题或想法,请随时访问加入我们的Slack频道或加入我们的讨论pingcap / tidb-operator!