本节主要介绍 controller-runtime 框架如何将 Manager 与 Controller 进行关联以及如何启动控制器的。

上文我们介绍了 controller-runtime 中的 Controller 的实现,这个控制器的实现和我们自定义控制器的流程基本一致的,那么 controller-runtime 是如何来使用这个 Controller 的呢,本节我们就来详细介绍下。

在 controller-runtime 中使用了一个 Manager 的接口来管理 Controller,除了控制器其实还可以管理 Admission Webhook,也包括访问资源对象的 client、cache、scheme 等,如下图所示:

Manager 如何使用

首先我们先来查看下 controller-runtime 中的 Manager 是如何使用的,查看 controller-runtime 代码仓库中的示例,位于 https://github.com/kubernetes-sigs/controller-runtime/tree/master/examples/crd,示例中关于 Manager 的使用步骤为:

  1. 实例化 manager,参数 config
  2. 向 manager 添加 scheme
  3. 向 manager 添加 controller,该 controller 包含一个 reconciler 结构体,我们需要在 reconciler 结构体实现逻辑处理
  4. 向 manager 添加 webhook,同样需要实现逻辑处理
  5. 启动 manager.start()

代码如下所示:

// 根据 config 实例化 Manager
// config.GetConfigOrDie() 使用默认的配置~/.kube/config
manager.New(config.GetConfigOrDie(), manager.Options{})

// 将 api 注册到 Scheme,Scheme 提供了 GVK 到 go type 的映射。
// 如果多个 crd,需要多次调用 AddToScheme
api.AddToScheme(mgr.GetScheme())

// 注册 Controller 到 Manager
// For:监控的资源,相当于调用 Watches(&source.Kind{Type: apiType},&handler.EnqueueRequestForObject{})
// Owns:拥有的下属资源,如果 corev1.Pod{} 资源属于 api.ChaosPod{},也将会被监控,相当于调用 Watches(&source.Kind{Type: <ForType-apiType>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})
// reconciler 结构体:继承 Reconciler,需要实现该结构体和 Reconcile 方法
// mgr.GetClient()、mgr.GetScheme() 是 Client 和 Scheme,前面的 manager.New 初始化了
err = builder.ControllerManagedBy(mgr).
		For(&api.ChaosPod{}).
		Owns(&corev1.Pod{}).
		Complete(&reconciler{
			Client: mgr.GetClient(),
			scheme: mgr.GetScheme(),
		})
// 构建webhook
err = builder.WebhookManagedBy(mgr).For(&api.ChaosPod{}).Complete()
// 启动manager,实际上是启动controller
mgr.Start(ctrl.SetupSignalHandler())

Manager 是一个用于初始化共享依赖关系的接口,接口定义如下所示(只显示了核心的几个方法):

// pkg/manager/manager.go

// Manager 初始化共享的依赖关系,比如 Caches 和 Client,并将他们提供给 Runnables
type Manager interface {
	// Add 将在组件上设置所需的依赖关系,并在调用 Start 时启动组件
  // Add 将注入接口的依赖关系 - 比如 注入 inject.Client
  // 根据 Runnable 是否实现了 LeaderElectionRunnable 接口判断
  // Runnable 可以在非 LeaderElection 模式(始终运行)或 LeaderElection 模式(如果启用了 LeaderElection,则由 LeaderElection 管理)下运行
  Add(Runnable) error

	// SetFields 设置对象上的所有依赖关系,而该对象已经实现了 inject 接口
  // 比如 inject.Client
	SetFields(interface{}) error

	// Start 启动所有已注册的控制器,并一直运行,直到停止通道关闭
  // 如果启动任何控制器都出错,则返回错误。
  // 如果使用了 LeaderElection,则必须在此返回后立即退出二进制,否则需要 Leader 选举的组件可能会在 Leader 锁丢失后继续运行
	Start(<-chan struct{}) error

	......
}

Manager 可以管理 Runnable 的生命周期(添加/启动),如果您不通过 Manager 启动(需要处理各种常见的依赖关系)。

Manager 还保持共同的依赖性:client、cache、scheme 等。

此外还支持领导人选举,只需用选项指定即可,还提供了一个用于优雅关闭的信号处理程序。

Manager 实例化

然后查看下 Manager 的实例化 New 函数的实现:

// 返回一个新的 Manager,用于创建 Controllers
func New(config *rest.Config, options Options) (Manager, error) {
	if config == nil {
		return nil, fmt.Errorf("must specify Config")
	}
	// 设置 options 属性的默认值
	options = setOptionsDefaults(options)
	......
	return &controllerManager{
		......
	}, nil
}