前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubebuilder 学习笔记之 Watching Resources

Kubebuilder 学习笔记之 Watching Resources

原创
作者头像
blazehu
修改2022-08-12 18:14:32
7610
修改2022-08-12 18:14:32
举报
文章被收录于专栏:小千世界

我们在开发过程中,可能需要开发一个类似Deployment的资源逻辑,管理依赖资源是控制器的基础,如果不能观察它们的状态变化就不可能管理它们。这就意味着,我们需要 reconciler 能监控多个资源的变化。

NOTE: Deployment 必须知道其管理的 ReplicaSet 何时更改,ReplicaSet 必须知道其管理的 Pod 何时被删除,或者从健康变为不健康等。

控制器运行时库为管理和监视资源提供了多种方式。这包括从简单而明显的用例(例如查看由控制器创建和管理的资源)到更独特和更高级的用例。

  • 控制器创建和管理的资源 (Watching Operator Managed Resources)
  • 外部管理的资源 (Watching Externally Managed Resources)

背景

Tcaplus 资源为例,Tcaplus 资源通过 ConfigMap(proto 文件)来创建表格。当 ConfigMap 发生变化时自动更新表格,下面例子不实际调用腾讯云API,只要验证接收到事件请求即可。

NOTE: TcaplusDB 是腾讯出品的分布式NoSQL数据库。官方API文档:https://cloud.tencent.com/document/product/596/39648

控制器创建和管理的资源

资源定义 (Defined Tcaplus Resources)

api/v1/tcaplus_types.go

代码语言:go
复制
type TcaplusSpec struct {
   Checksum          string             `json:"checksum,omitempty"`
   ConfigMapTemplate ConfigMapTemplate  `json:"configMapTemplate,omitempty"`
}

type ConfigMapTemplate struct {
   Name string            `json:"name,omitempty"`
   Data map[string]string `json:"data,omitempty"`
}
控制器逻辑 (Manage the Owned Resource)

controllers/tcaplus_controller.go

tcaplus CR 创建时根据 ConfigMapTemplate 创建附属的 ConfigMap 资源并设置属主关系

  • Reconcile 方法:根据模版创建 ConfigMap 并设置属主关系
  • SetupWithManager 方法:For 方法之后调用 Owns 方法
代码语言:go
复制
func (r *TcaplusReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
   logger := log.FromContext(ctx)

   logger.Info("reconciling")
   tcaplus := &examplev1.Tcaplus{}
   if err := r.Get(ctx, req.NamespacedName, tcaplus); err != nil {
      return ctrl.Result{}, client.IgnoreNotFound(err)
   }

   configMap := &corev1.ConfigMap{}
   configMap.Name = tcaplus.Spec.ConfigMapTemplate.Name
   configMap.Namespace = tcaplus.Namespace
   configMap.Data = tcaplus.Spec.ConfigMapTemplate.Data
    
   if err := controllerutil.SetControllerReference(tcaplus, configMap, r.Scheme); err != nil {
      logger.Error(err, "get configmap failed", "configmap", configMap.Name)
      return ctrl.Result{}, err
   }

   foundConfigMap := &corev1.ConfigMap{}
   err := r.Get(ctx, types.NamespacedName{Name: configMap.Name, Namespace: tcaplus.Namespace}, foundConfigMap)
   if err != nil && errors.IsNotFound(err) {
      logger.V(1).Info("creating configmap", "configmap", configMap.Name)
      err = r.Create(ctx, configMap)
   }
   return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *TcaplusReconciler) SetupWithManager(mgr ctrl.Manager) error {
   return ctrl.NewControllerManagedBy(mgr).
      For(&examplev1.Tcaplus{}).
      Owns(&corev1.ConfigMap{}).
      Complete(r)
}

NOTE:同一控制器创建的资源才可以设置属主关系,不然会提示:already owned by another controller。

测试

config/samples/example_v1_tcaplus.yaml

代码语言:yaml
复制
apiVersion: example.blazehu.com/v1
kind: Tcaplus
metadata:
  name: tcaplus-sample
spec:
  checksum: "123"
  configMapTemplate:
    name: "tcaplus-configmap-example"
    data:
      demo.proto: |
        syntax = "proto3";
        package example;
        message Example {
          uint32 a = 1;
          uint32 b = 2;
          uint32 c = 3;
        }

使用上述配置文件创建 tcaplus 资源。创建结果:

代码语言:shell
复制
BLAZEHU-MB2:samples $ k get tcaplus
NAME             AGE
tcaplus-sample   19m
BLAZEHU-MB2:samples $ k get configmap
NAME                        DATA   AGE
tcaplus-configmap-example   1      19m

可以查看 tcaplus-configmap-example 的属主关系:

代码语言:yaml
复制
apiVersion: v1
data:
  demo.proto: |
    syntax = "proto3";
    package example;
    message Example {
      uint32 a = 1;
      uint32 b = 2;
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2022-07-07T09:02:43Z"
  name: tcaplus-configmap-example
  namespace: default
  ownerReferences:
  - apiVersion: example.blazehu.com/v1
    blockOwnerDeletion: true
    controller: true
    kind: Tcaplus
    name: tcaplus-sample
    uid: 7c50f2e1-0e37-4aa0-bf49-c2d410d6153e
  resourceVersion: "6837330713"
  selfLink: /api/v1/namespaces/default/configmaps/tcaplus-configmap-example
  uid: 6c29f90b-0e51-4d9f-a6a8-cfb6906ed1b0

手动修改 tcaplus-sampletcaplus-configmap-example 后查看控制器日志发现能正常观察 CRConfigMap 的变化了。

外部管理的资源

资源定义 (Defined Tcaplus Resources)

api/v1/tcaplus_types.go

代码语言:go
复制
type TcaplusSpec struct {
   Checksum     string             `json:"checksum,omitempty"`
   ConfigMapRef ConfigMapReference `json:"configMapRef,omitempty"`
}

type ConfigMapReference struct {
   Name string `json:"name,omitempty"`
}
控制器逻辑 (Manage the Owned Resource)

controllers/tcaplus_controller.go

For 方法之后调用 Watches 方法就可以监听对应资源的事件,但是会监听集群里所有相关资源的事件,所以这里我们自定义事件处理方法来过滤出我们关注的资源的事件。

  • 通过 EnqueueRequestsFromMapFunc 创建一个事件处理方法,该方法通过 FieldSelectorConfigMap 的事件中过滤出跟 tcaplus CR 相关联的事件。
  • 使用 FieldSelector 时我们需要建立对应的索引,使用 mgr.GetFieldIndexer().IndexField() 创建。
代码语言:go
复制
const (
   ConfigMapField = ".spec.configMapRef.name"
)

func (r *TcaplusReconciler) findObjectsForConfigMap(configMap client.Object) []reconcile.Request {
   attachedTcaplusList := &examplev1.TcaplusList{}
   listOps := &client.ListOptions{
      FieldSelector: fields.OneTermEqualSelector(ConfigMapField, configMap.GetName()),
      Namespace:     configMap.GetNamespace(),
   }
   err := r.List(context.TODO(), attachedTcaplusList, listOps)
   if err != nil {
      return []reconcile.Request{}
   }

   requests := make([]reconcile.Request, len(attachedTcaplusList.Items))
   for i, item := range attachedTcaplusList.Items {
      requests[i] = reconcile.Request{
         NamespacedName: types.NamespacedName{
            Name:      item.GetName(),
            Namespace: item.GetNamespace(),
         },
      }
   }
   return requests
}

// SetupWithManager sets up the controller with the Manager.
func (r *TcaplusReconciler) SetupWithManager(mgr ctrl.Manager) error {
   if err := mgr.GetFieldIndexer().IndexField(context.Background(), &examplev1.Tcaplus{}, ConfigMapField, func(rawObj client.Object) []string {
      tcaplus := rawObj.(*examplev1.Tcaplus)
      if tcaplus.Spec.ConfigMapRef.Name == "" {
         return nil
      }
      return []string{tcaplus.Spec.ConfigMapRef.Name}
   }); err != nil {
      return err
   }

   return ctrl.NewControllerManagedBy(mgr).
      For(&examplev1.Tcaplus{}).
      Watches(
         &source.Kind{Type: &corev1.ConfigMap{}},
         handler.EnqueueRequestsFromMapFunc(r.findObjectsForConfigMap),
         builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
      ).
      Complete(r)
}

上面 ConfigMap 监听使用默认的 Predicates 过滤器 ResourceVersionChangedPredicate,查看源码定义就是说当 resource version 变化时事件会入队。

代码语言:go
复制
// ResourceVersionChangedPredicate implements a default update predicate function on resource version change.
type ResourceVersionChangedPredicate struct {
   Funcs
}

NOTE: 我们也可以自己定一个变更过滤器 Predicate。也可以通过 WithEventFilter 来针对监听的所有资源过滤。

测试

config/samples/example_v1_tcaplus.yaml

代码语言:yaml
复制
apiVersion: v1
kind: ConfigMap
metadata:
  name: tcaplus-configmap-example
data:
  demo.proto: |
    syntax = "proto3";
    package example;
    message Example {
      uint32 a = 1;
      uint32 b = 2;
      uint32 c = 3;
    }

---
apiVersion: example.blazehu.com/v1
kind: Tcaplus
metadata:
  name: tcaplus-sample
spec:
  checksum: "123"
  configMapRef:
    name: "tcaplus-configmap-example"

使用上述配置创建完毕后,手动修改 tcaplus-sampletcaplus-configmap-example 查看控制器日志发现同样能正常观察 CRConfigMap 的变化。

NOTE: 查看 tcaplus-configmap-example 可以看到没有和 tcaplus 的属主关系。

总结

  • EventHandler 可以在 watch 特定资源时设置该资源的事件监听规则。
  • WithEventFilter 配置变更过滤器,可以针对 watch 的所有资源,统一地设置事件监听规则。
  • Owns 源码分析可以发现 Owns 相当于调用 Watches(&amp;source.Kind{Type: <ForType-forInput>}, &amp;handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})

参考文档

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 控制器创建和管理的资源
    • 资源定义 (Defined Tcaplus Resources)
      • 控制器逻辑 (Manage the Owned Resource)
        • 测试
        • 外部管理的资源
          • 资源定义 (Defined Tcaplus Resources)
            • 控制器逻辑 (Manage the Owned Resource)
              • 测试
              • 总结
              • 参考文档
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档