controller用于将给定对象的实际状态和期望状态相匹配,每个controller专注于一个kind(kubebuiler中由于For的Forinput只能放一个obj,实际在底层controller中可以watch多个)。具体的办法是通过使用workqueue的方式缓存informer传递过来的对象,后续提取workqueue中的对象,传递给Reconciler进行处理。
builder
在kubebuilder中controller.go主要用于返回一个builder对象,即controller,结构体定义如下。
// Builder builds a Controller.
type Builder struct {
forInput ForInput
forInput []OwnsInput
watchesInput []WatchesInput
mgr manager.Manager
globalPredicates []predicate.Predicate
config *rest.Config
ctrl controller.Controller
ctrlOptions controller.Options
log logr.Logger
name string
}
forInput forInput owsInput是dowatch和docontroller时候的对象,globalPredicates是predicate的范围,mgr是manager,ctrl是controller对象,ctrloption,log,name
For watches owns
kubebuilder中Forinput的结构体,在dowatch和docontroller被使用。
type ForInput struct {
object runtime.Object
predicates []predicat⁄e.Predicate
objectProjection objectProjection
}
forinput在For方法中被使用,For需要提供runtime的object来使用,方法主要用于填充forinput。
func (blder *Builder) For(object runtime.Object, opts ...ForOption) *Builder {
input := ForInput{object: object}
for _, opt := range opts {
opt.ApplyToFor(&input)
}
blder.forInput = input
return blder
}
watches和owns是差不多的,都是要实现watch,现在仅使用for。
withconfig withEventFilter withOptions Named
提供config(不再使用).
wef提供过滤的event,event包括update delete create ge事件,可以通过过滤时间决定reconcile逻辑,填充predicate。
wo提供option。,option包括MaxConcurrentReconciles(是可以运行的最大并发 Reconciles 数量。默认为 1),reconcile, RateLimiter(用于限制请求排队的频率)。
Named提供name。
func (blder *Builder) WithConfig(config *rest.Config) *Builder {
blder.config = config
return blder
}
func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
blder.globalPredicates = append(blder.globalPredicates, p)
return blder
}
func (blder *Builder) WithOptions(options controller.Options) *Builder {
blder.ctrlOptions = options
return blder
}
complete 实现build方法. 完成应用程序的构建 ControllerManagedBy.
func (blder *Builder) Complete(r reconcile.Reconciler) error {
_, err := blder.Build(r)
return err
}
Build
主要完成 docontroller和dowatch。且确保提供mgr和reconciler,以及config(没有提供时,从mgr拿–mgr.GetConfig())
docontroller
- 拿ctrlOption(没有提供reconciler时赋值r)
- 通过forinput的obj和mgr的scheme拿gvk
- log
- 做new controller,需要mgr/option/controllername(name或用kind命名)
func (blder *Builder) doController(r reconcile.Reconciler) error {
ctrlOptions := blder.ctrlOptions
if ctrlOptions.Reconciler == nil {
ctrlOptions.Reconciler = r
}
// Retrieve the GVK from the object we're reconciling
// to prepopulate logger information, and to optionally generate a default name.
gvk, err := getGvk(blder.forInput.object, blder.mgr.GetScheme())
if err != nil {
return err
}
// Setup the logger.
if ctrlOptions.Log == nil {
ctrlOptions.Log = blder.mgr.GetLogger()
}
ctrlOptions.Log = ctrlOptions.Log.WithValues("reconcilerGroup", gvk.Group, "reconcilerKind", gvk.Kind)
// Build the controller and return.
blder.ctrl, err = newController(blder.getControllerName(gvk), blder.mgr, ctrlOptions)
return err
}
docontroller后拿到有ctrl的builder。
dowatch
仅放了for的部分,因为controller只放了forinput。
- project放置对象,如果没有设置foroption,则直接放给的runtimeobj
- src利用obj拿到
- 建立一个handler.EnqueueRequestForObject{}(queue)
- allPredicates利用event过滤后拿到。
- 做watch(Watch(src, hdler, allPredicates…))
func (blder *Builder) doWatch() error {
// Reconcile type
typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
if err != nil {
return err
}
src := &source.Kind{Type: typeForSrc}
hdler := &handler.EnqueueRequestForObject{}
allPredicates := append(blder.globalPredicates, blder.forInput.predicates...)
if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
return err
}
使用流程
- new reconciler(build和docontroller时使用)
- new mgr(通过cfg和managerOpt拿到,用于)
- NewControllerManageredBy(mgr)
- For(watch筛选)
- with…(predicate)
- Complete(Builder返回)
func (r *CrsReconciler) SetupWithManager(mgr ctrl.Manager) (err error) {
p := predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
...
},
GenericFunc: func(e event.GenericEvent) bool {
...
},
...
}
cr := &unstructured.Unstructured{} // runtimeobject
err = ctrl.NewControllerManagedBy(mgr).
// Watches(&source.Kind{Type: cr}, &handler.EnqueueRequestForObject{}).
For(&metav1.Pod{}). //
WithEventFilter(&p).
Named("testName").
Complete(r)
return
}
实际流程:
- new controller(DC)
- add mgr(DC)
- watch (DW)
底层ctrl的实现逻辑
- New mgr/reconciler
- New ctrl(提供mgr/options<–reconciler) <-- 实现了填充以及add to mgr
- 实现Watch (提供obj hdler predicate)和start(chan)
mgr的意义:
- 拿scheme(GVK)/cfg/Log
- add ctrl to mgr
reconciler的意义:
- 注册到ctrl op的r --> reconcile实现抓取状态更新后的逻辑
ctrl struct
// Controller implements controller.Controller
type Controller struct {
// Name is used to uniquely identify a Controller in tracing, logging and monitoring. Name is required.
Name string
// MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
MaxConcurrentReconciles int
// Reconciler is a function that can be called at any time with the Name / Namespace of an object and
// ensures that the state of the system matches the state specified in the object.
// Defaults to the DefaultReconcileFunc.
Do reconcile.Reconciler
// MakeQueue constructs the queue for this controller once the controller is ready to start.
// This exists because the standard Kubernetes workqueues start themselves immediately, which
// leads to goroutine leaks if something calls controller.New repeatedly.
MakeQueue func() workqueue.RateLimitingInterface
// Queue is an listeningQueue that listens for events from Informers and adds object keys to
// the Queue for processing
Queue workqueue.RateLimitingInterface
// SetFields is used to inject dependencies into other objects such as Sources, EventHandlers and Predicates
SetFields func(i interface{}) error
// mu is used to synchronize Controller setup
mu sync.Mutex
// JitterPeriod allows tests to reduce the JitterPeriod so they complete faster
JitterPeriod time.Duration
// Started is true if the Controller has been Started
Started bool
// TODO(community): Consider initializing a logger with the Controller Name as the tag
// startWatches maintains a list of sources, handlers, and predicates to start when the controller is started.
startWatches []watchDescription
// Log is used to log messages to users during reconciliation, or for example when a watch is started.
Log logr.Logger
}
ctrl.watch
// Watch implements controller.Controller
func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
c.mu.Lock()
defer c.mu.Unlock()
// Inject Cache into arguments
if err := c.SetFields(src); err != nil {
return err
}
if err := c.SetFields(evthdler); err != nil {
return err
}
for _, pr := range prct {
if err := c.SetFields(pr); err != nil {
return err
}
}
// Controller hasn't started yet, store the watches locally and return.
//
// These watches are going to be held on the controller struct until the manager or user calls Start(...).
if !c.Started {
c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
return nil
}
c.Log.Info("Starting EventSource", "source", src)
return src.Start(evthdler, c.Queue, prct...)
}
- c.setFields(src/evthdler/pr)
- watch many before start(appen startwatches)
kubebuilder operator的运行逻辑
watch:
调用了src.Start(src的类型为 source.Kind),将evthdler(&handler.EnqueueRequestForObject{})、c.Qeueue关联起来(c.Qeueue为Reconciler提供参数)。在Kind.Start 中会根据ks.Type选择合适的informer,并添加事件管理器internal.EventHandler:
在Manager初始化时(如未指定)默认会创建一个Cache,该Cache中保存了gvk到cache.SharedIndexInformer 的映射关系,ks.cache.GetInformer 中会提取对象的gvk信息,并根据gvk获取informer。
在Manager.Start的时候会启动Cache中的informer。
informer.AddEventHandler
ctrl.start
先等待informer同步完成,再启动worker(MaxConcurrentReconciles个,循环)处理资源对象。reconcileehandler处理业务,c.Do.Reconcile(req)暴露给开发者。实现re完成逻辑,result=c.Do.Reconcile(req)。
result。result.RequeueAfter > 0,queue先忘记,result.RequeueAfter时间后再加入。result.Requeue,直接加入。正常返回则忘记。