深入解析kubernetes controller-runtime

Overview

controller-runtime 是 Kubernetes 社区提供可供快速搭建一套 实现了controller 功能的工具,无需自行实现Controller的功能了;在 Kubebuilder 与 Operator SDK 也是使用 controller-runtime 。本文将对 controller-runtime 的工作原理以及在不同场景下的使用方式进行简要的总结和介绍。

controller-runtime structure

controller-runtime 主要组成是需要用户创建的 Manager 和 Reconciler 以及 Controller Runtime 自己启动的 Cache 和 Controller 。

  • Manager :是用户在初始化时创建的,用于启动 Controller Runtime 组件
  • Reconciler :是用户需要提供来处理自己的业务逻辑的组件(即在通过 code-generator 生成的api-like而实现的controller中的业务处理部分)。
  • Cache :一个缓存,用来建立 Informer 到 ApiServer 的连接来监听资源并将被监听的对象推送到queue中。
  • Controller : 一方面向 Informer 注册 eventHandler ,另一方面从队列中获取数据。controller 将从队列中获取数据并执行用户自定义的 Reconciler 功能。

图:controller-runtime structure

图:controller-runtime flowchart

由图可知,Controller会向 Informer 注册一些列eventHandler;然后Cache启动Informer(informer属于cache包中),与ApiServer建立监听;当Informer检测到资源变化时,将对象加入queue,Controller 将元素取出并在用户端执行 Reconciler。

Controller引入

我们从 controller-rumtime项目的 example 进行引入看下,整个架构都是如何实现的。

可以看到 example 下的实际上实现了一个 reconciler 的结构体,实现了 Reconciler 抽象和 Client 结构体

type reconciler struct {
	client.Client
	scheme *runtime.Scheme
}

那么来看下 抽象的 Reconciler 是什么,可以看到就是抽象了 Reconcile 方法,这个是具体处理的逻辑过程

type Reconciler interface {
	Reconcile(context.Context, Request) (Result, error)
}

下面在看下谁来实现了这个 Reconciler 抽象

type Controller interface {
	reconcile.Reconciler // 协调的具体步骤,通过ns/name\
    // 通过predicates来评估来源数据,并加入queue中(放入队列的是reconcile.Requests)
	Watch(src source.Source, eventhandler handler.EventHandler, predicates ...predicate.Predicate) error
    // 启动controller,类似于自定义的Run()
	Start(ctx context.Context) error
	GetLogger() logr.Logger
}

controller structure

在 controller-runtime\pkg\internal\controller\controller.go 中实现了这个 Controller

type Controller struct {
	Name string // controller的标识
    
	MaxConcurrentReconciles int // 并发运行Reconciler的数量,默认1
	// 实现了reconcile.Reconciler的调节器, 默认DefaultReconcileFunc
	Do reconcile.Reconciler
	// makeQueue会构建一个对应的队列,就是返回一个限速队列
	MakeQueue func() workqueue.RateLimitingInterface
	// MakeQueue创造出来的,在出入队列就是操作的这个
	Queue workqueue.RateLimitingInterface

	// 用于注入其他内容
    // 已弃用
	SetFields func(i interface{}) error

	mu sync.Mutex
	// 标识开始的状态
	Started bool
	// 在启动时传递的上下文,用于停止控制器
	ctx context.Context
	// 等待缓存同步的时间 默认2分钟
	CacheSyncTimeout time.Duration

	// 维护了eventHandler predicates,在控制器启动时启动
	startWatches []watchDescription

	// 日志构建器,输出入日志
	LogConstructor func(request *reconcile.Request) logr.Logger

	// RecoverPanic为是否对reconcile引起的panic恢复
	RecoverPanic bool
}

看完了controller的structure,接下来看看controller是如何使用的

injection

Controller.Watch 实现了注入的动作,可以看到 watch() 通过参数将 对应的事件函数传入到内部

func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// 使用SetFields来完成注入操作
	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还未启动,那么将这些动作缓存到本地
	if !c.Started {
		c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
		return nil
	}

	c.LogConstructor(nil).Info("Starting EventSource", "source", src)
	return src.Start(c.ctx, evthdler, c.Queue, prct...)
}

启动操作实际上为informer注入事件函数

type Source interface {
	// start 是Controller 调用,用以向 Informer 注册 EventHandler, 将 reconcile.Requests(一个入队列的动作) 排入队列。
	Start(context.Context, handler.EventHandler, workqueue.RateLimitingInterface, ...predicate.Predicate) error
}

func (is *Informer) Start(ctx context.Context, handler handler.EventHandler, queue workqueue.RateLimitingInterface,
	prct ...predicate.Predicate) error {
	// Informer should have been specified by the user.
	if is.Informer == nil {
		return fmt.Errorf("must specify Informer.Informer")
	}

	is.Informer.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})
	return nil
}

我们知道对于 eventHandler,实际上应该是一个 onAdd , onUpdate 这种类型的函数,queue则是workqueue,那么 Predicates 是什么呢?

通过追踪可以看到定义了 Predicate 抽象,可以看出Predicate 是Watch到的事件时什么类型的,当对于每个类型的事件,对应的函数就为 tru

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值