k8s crd operator踩坑点

1、在函数中,有两种方式声明一个crd变量

var a *appv1.deployment

b := new(appv1.deployment)

区别,new是有初始化值的。因此 b != nil。但是a == nil。

因此,new(appv1.deployment)可以作为client.get(ctx, b),进行赋值。但是如果要进行判断,用var比较好,不会进行初始化。

 

2、crd的status字段,如果要更新,需要使用client.status().update()方法,而不能直接使用client.update()

 

3、利用kubebuider时,需要在crd type定义代码上加一行注释:

// +kubebuilder:subresource:status
type xxx struct {
   metav1.TypeMeta   `json:",inline"`
   metav1.ObjectMeta `json:"metadata,omitempty"`

   Spec   xxx   `json:"spec,omitempty"`
   Status xxx `json:"status,omitempty"`
}

这样在代码中更新status,才会成功,不然会报错。

 

4、crd都有一个spec字段,叫Generation。该字段是自动生成的,是每次修改/创建crd对象,都会修改,创建时为1。后面每次修改都会+1。

只有当spec修改时,才会触发Generation+1,status的修改不会触发。

因此可以用该字段,判断是spec修改还是status修改。防止在reconclie中,spec和status变化,引起reconclie死循环。

 

5、使用kubebuider时,可以在setupmanager时,watch方法,添加fliter,只监听符合要求的对象,入队列处理。也可以依靠这里过滤掉status的变化。

 

6、client-go 的list方法,其中listoption,如果要使用namespace作为条件进行过滤,不能在listoption里添加,应如下操作:

labelsSelect := make(map[string]string)
	labelsSelect[xxx] = xxx
	wn := &client.ListOptions{
		LabelSelector: labels.SelectorFromSet(labelsSelect),
	}
	err := h.r.List(ctx, appContextList, wn, client.InNamespace(Namespace))

 

7、client-go 创建一个cr对象时,如果立即去get对象,可能会获取不到,因为create是向api server请求创建对象,创建需要时间。而get是获取本地缓存,informer从api server watch到,并缓存在本地,这个过程存在时间差。

因此好的方法,是创建cr对象后,重新入队列,等待下一次reconclie处理。

 

 

8、如果想获取/更新,不确定的对象。k8s提供了一种结构体,

unstructured.Unstructured

该结构体可以表示所有对象。在get/update时,只需要指定该结构体的GVK即可。

	obj := new(unstructured.Unstructured)

	w, err := oamutil.RawExtension2Unstructured(&component.Spec.Workload)
	if err != nil {
		r.Log.Info("Unstructured  component workload failed. componentName", fmt.Sprintf("error is "), err, "componentName", component.Name)
		return nil, err
	}
	obj.SetKind(w.GetKind())
	obj.SetAPIVersion(w.GetAPIVersion())
	key := ctypes.NamespacedName{Name: component.Name + "-" + canaryColor, Namespace: workloadNs}
	err = client.Get(ctx, key, obj)

 

对于获取该结构体的字段,可以使用绝对路径。比如想获取replicas

unstructured.NestedInt64(obj.Object, "spec", "replicas")

 

9、在更新某个对象时,很有可能会报错object is already modify, please apply the latest one。这个意思是,在更新该对象时,该对象被其他更新了

因为client.update(ctx, &app)时,app已经是有个值的,他的generation和revision是有值的,但是被其他修改后,这个值会变化。导致更新时,发现revision对不上。

解决方法:1、更新错误,再入队列,重新reconclie即可。2、或者采用patch操作。

 

10、sharedinformer是复用reflector来监听对象,当同时监听deployment,多个informer都会收到该deployment curd的事件。为了让各个infomer不互相干扰,不要采用sharedinformer。采用普通的informer

podListWatcher := cache.NewListWatchFromClient(client.AppsV1().RESTClient(), "deployments", v12.NamespaceAll, fields.Everything())
indexer, informer := cache.NewIndexerInformer(listerWatcher, &appsv1.Deployment{}, 0, cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				deploy := obj.(*appsv1.Deployment)
				deployobj := DeployObj {
					deploy: deploy,
					cluster: cluster,
					types: "add",
				}

				controller.handleObject(deployobj)
			},
			UpdateFunc: func(old, new interface{}) {
				newDepl := new.(*appsv1.Deployment)
				oldDepl := old.(*appsv1.Deployment)

				if newDepl.ResourceVersion != oldDepl.ResourceVersion && newDepl.Generation != oldDepl.Generation {
					// Periodic resync will send update events for all known Deployments.
					// Two different versions of the same Deployment will always have different RVs.
					deployobj := DeployObj {
						deploy: newDepl,
						cluster: cluster,
						types: "update",
					}

					klog.Info("update handleObject, ",deployobj)
					controller.handleObject(deployobj)
				}

			},
			DeleteFunc: func(obj interface{}) {
				deployobj := DeployObj {
					deploy: obj.(*appsv1.Deployment),
					cluster: cluster,
					types: "delete",
				}

				controller.handleObject(deployobj)
			},
		}, cache.Indexers{})
go informer.Run(stopCh)

 

 

11、一些字段的拷贝,如果是字段结构体里包含slice类型的数据,那么这些字段的拷贝,一定要用deepcopy,而不是简单的值传递。

例子:

status字段的拷贝:

status{

a int

b int

[]condition

}

a.status = b.status

这样拷贝,由于[]condtion是slice,传递过去的是地址,因此a.status的condition和b的condition的地址其实是一样的,那么修改a的condition时,b的condition也会被修改。

因此可以采用deepcopy方法,a.status=b.deepcopy().status。

相关文章:https://halfrost.com/go_slice/#toc-8

 

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值