TiDB操作符源代码读取(II):操作符模式
作者:谊文陈(TiDB营办商提交人)
Transcreator:黄了;编辑器:汤姆政府高级官员
在我的最后一篇文章,我介绍了TiDB Operator的架构和它的功能。但是TiDB操作符代码是如何运行的?TiDB Operator如何管理TiDB集群中每个组件的生命周期?
在这篇文章中,我将介绍Kubernetes的操作符模式以及它是如何在TiDB操作符中实现的。更具体地说,我们将通过TiDB运营商的主要控制循环,从其入口点到生命周期管理的触发器.
从控制器到操作员
因为TiDB操作符从kube-controller-manager,理解设计kube-controller-manager
帮助您更好地理解TiDB Operator的内部逻辑。
在Kubernetes,各控制器管理资源的生命周期,如Namespace、Node、Deployment和StatefulSet。控制器监视集群资源的当前状态,将其与所需状态进行比较,并将集群移动到所需状态。Kubernetes的内置控制器在内部运行kube-controller-manager
并被它管理。
为了允许用户自定义资源管理,Kubernetes提出了操作模式.用户可以创建自己的自定义资源(CRs)通过定义CustomResourceDefinitions(crd)对象,并使用自定义控制器监视相应crd的状态,完成相关的管理任务。Operator模式允许用户扩展Kubernetes行为,而无需修改其代码。
TiDB控制器管理器如何工作
见TiDB操作符源代码读取(I):概述, TiDB Operator有一个核心组件,tidb-controller-manager
,它运行一组自定义控制器来管理TiDB的crd。
入口点
从cmd / controller-manager / main.go
,tidb-controller-manager
首先加载kubeconfig以访问kube-apiserver。然后使用一系列NewController
函数,用于加载每个控制器的init函数。
控制器:=[]控制器{tidbcluster.NewController(deps),dmcluster.NewController(deps),备份.NewController(deps),恢复.NewController(deps),backupschedule.NewController(deps),tidbinitializer.NewController(deps),tidbmonitor.NewController(deps),}
在执行init函数时,tidb-controller-manager
初始化一组告密者,告密者与kube-apiserver交互以获取CRs和相关资源的变化。采取TidbCluster
举个例子,在NewController
函数,tidb-controller-manager
初始化Informer对象:
tidbClusterInformer.告密者().AddEventHandler(缓存.ResourceEventHandlerFuncs{AddFunc:c.enqueueTidbCluster,UpdateFunc:函数(老,坏蛋接口{}){c.enqueueTidbCluster(坏蛋)},DeleteFunc:c.enqueueTidbCluster,})statefulsetInformer.告密者().AddEventHandler(缓存.ResourceEventHandlerFuncs{AddFunc:c.addStatefulSet,UpdateFunc:函数(老,坏蛋接口{}){c.updateStatefulSet(老,坏蛋)},DeleteFunc:c.deleteStatefulSet,})
事件的过程添加
,更新
,删除
事件都登记在告密者名下。这些eventhandler处理事件并向队列添加相关的CR键。
控制器的内部逻辑
初始化后,tidb-controller-manager
启动InformerFactory并等待缓存同步完成:
informerFactories:=[]InformerFactory{deps.InformerFactory,deps.KubeInformerFactory,deps.LabelFilterKubeInformerFactory,}为_,f:=范围informerFactories{f.开始(ctx.完成())为v,同步:=范围f.WaitForCacheSync(等待.NeverStop){如果!同步{klog.Fatalf(" %v的错误同步通知器",v)}}}
接下来,tidb-controller-manager
调用每个控制器的Run函数,并在循环中执行控制器的内部逻辑:
//为所有控制器启动syncLoop。为_,控制器:=范围控制器{c:=控制器去等待.永远(函数(){c.运行(cliCfg.工人,ctx.完成())},cliCfg.WaitDuration)}
再次,以TidbCluster
以控制器为例。Run函数启动工作队列:
//运行控制器tidbcluster。函数(c*控制器)运行(工人int,stopCh<-陈结构体{}){推迟utilruntime.HandleCrash()推迟c.队列.关闭()klog.信息(“开始tidbcluster控制器”)推迟klog.信息("关闭tidbcluster控制器")为我:=0;我<工人;我++{去等待.直到(c.工人,时间.第二个,stopCh)}<-stopCh}
工人叫processNextWorkItem
函数,将项从队列中取出,并调用同步
函数同步CR:
// worker运行一个调用processNextWorkItem的worker goroutine,直到控制器队列关闭。函数(c*控制器)工人(){为c.processNextWorkItem(){}}// processNextWorkItem将项目从队列中取出,处理它们,并标记它们已完成。它强制syncHandler永远不存在//使用相同的键并发调用。函数(c*控制器)processNextWorkItem()保龄球{关键,辞职:=c.队列.得到()如果辞职{返回假}推迟c.队列.完成(关键)如果犯错:=c.同步(关键.(字符串));犯错! =零{如果perror.找到(犯错,控制器.IsRequeueError)! =零{klog.Infof("TidbCluster: %v, still need sync: %v, requeing ",关键.(字符串),犯错)}其他的{utilruntime.HandleError(fmt.Errorf("TidbCluster: %v, sync failed %v, requeing ",关键.(字符串),犯错))}c.队列.AddRateLimited(关键)}其他的{c.队列.忘记(关键)}返回真正的}
基于密钥,同步
函数获取相应的CR对象(例如TidbCluster
对象)并同步它:
// sync同步给定的tidbcluster。函数(c*控制器)同步(关键字符串)错误{开始时间:=时间.现在()推迟函数(){klog.V(4).Infof("已完成同步TidbCluster %q (%v)",关键,时间.自(开始时间))}()ns,的名字,犯错:=缓存.SplitMetaNamespaceKey(关键)如果犯错! =零{返回犯错}tc,犯错:=c.deps.TiDBClusterLister.TidbClusters(ns).得到(的名字)如果错误.IsNotFound(犯错){klog.Infof("TidbCluster已被删除%v",关键)返回零}如果犯错! =零{返回犯错}返回c.syncTidbCluster(tc.DeepCopy())}函数(c*控制器)syncTidbCluster(tc*v1alpha1.TidbCluster)错误{返回c.控制.UpdateTidbCluster(tc)}
的syncTidbCluster
函数调用的updateTidbCluster
函数,它进一步调用一系列组件同步
功能,完成对整个TiDB集群的管理。
在包裹/控制器/ tidbcluster / tidb_cluster_control.go
,可以检查实现情况updateTidbCluster
函数,其中有注释描述了每个函数执行的生命周期管理同步
函数。通过这些注释,您可以了解协调每个组件所需的操作。例如,在放置驱动程序(PD)中:
//使pd集群的当前状态匹配期望的状态:// -创建或更新pd服务//创建或更新pd headless服务// -如果不存在,则创建pd statefulset//同步pd集群状态从pd到TidbCluster对象// -升级pd集群// -在pd集群中扩展/// -切换pd集群如果犯错:=c.pdMemberManager.同步(tc);犯错! =零{返回犯错}
现在就讲到这里。
总结
在这篇文章中,我们介绍了TiDB Operator的入口点cmd / controller-manager / main.go
介绍了控制器的实现,并说明了控制器的内部逻辑.现在,您已经熟悉了控制回路是如何触发的。唯一的问题是如何完善控制回路,并在其中注入TiDB的特殊操作逻辑。这样,您就可以根据需要部署TiDB并在Kubernetes中运行它。
对于那些想要开发一个资源管理系统的人,我们推荐两个脚手架项目:Kubebuilder和运营商框架.这些项目生成的代码模板基于controller-runtime,允许您关注CRD对象的协调循环。
在下一篇文章中,我将讨论如何细化控制循环和实现组件协调循环.如果你有任何问题,请通过我们的松弛通道或在pingcap / tidb-operator.