【Kubernetes】Operator开发之kubebuilder实战(二)

1 解决(一)中的资源删除问题

在Operator开发之kubebuilder实战(一)的最后提到了Demo和Pod删除的问题:

  • 由于DemoController只监听了Demo资源的变化,因此,删除Pod时,DemoController不会创建Pod,同时,在删除Demo时,也不会删除对应的Pod,因为此时已经没有Demo了,而且k8s也不知道该Demo管理了哪些Pod

首先,DemoController需要监听Pod资源的变化,kubebuilder提供了两种监听额外资源的变化的机制,一种是简单粗暴的Watchs

func (r *DemoReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&batchv1.Demo{}).
		Watches(&corev1.Pod{}, &handler.EnqueueRequestForObject{}).
		Complete(r)
}

这种方式就可以直接ListAndWatch Pod资源,这种方式的问题是,可以接收到所有命名空间的Pod的变化,但是,在我们的场景中,希望只监听Demo负责创建的Pod。

另一种就是Owns

func (r *DemoReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&batchv1.Demo{}).
		Owns(&corev1.Pod{}).
		Complete(r)
}

这种方式就只会监听Demo负责创建的Pod,不过使用这种方式还需要在创建Pod时增加ownerReferences属性,这种方式就跟ReplicaSet类似:当ReplicaSet在创建Pod时,会将自身的信息写入到Pod的ownerReferences字段,当Pod变化时,会根据Pod找到关联的ReplicaSet,然后向ReplicaSet推送Pod变更。这种方式可以让DemoController接收必要的消息,而不需要实现过滤逻辑。

创建Pod时在metadata部分加入ownerReferences:

        pod := &corev1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Labels:      make(map[string]string),
				Annotations: make(map[string]string),
				Name:        name,
				Namespace:   req.Namespace,
				OwnerReferences: []metav1.OwnerReference{
					{
						APIVersion:         demo.APIVersion,
						Kind:               demo.Kind,
						Name:               demo.Name,
						UID:                demo.UID,
						Controller:         pointer.Bool(true),  // 为true表示该资源是控制器
					},
				},
			},
			Spec: *demo.Spec.Template.Spec.DeepCopy(),
		}

通过这两个改动的结合就可以达到效果:

  • 通过监控Pod的变化,能够在Pod被删除时去判断当前的Pod数量和需要的Pod数量,就可以保证在Pod删除时进行Pod的重建
  • 通过给Pod设置ownerReferences属性,能够将Pod与Demo关联起来,一方面,在Pod变更时能够找到关联的Demo并给Demo推送变更,另一方面,在删除Demo时,可以级联删除Pod

需要注意的是:

  • SetupWithManager里面监听的是Pod,但是调谐函数收到的是Pod关联的Demo的变更,而不像有些文章写的在这里还需要判断收到的变更是Demo还是Pod
  • 删除Demo时,会级联删除Pod

关于级联删除:当删除一个资源时,该如何删除这个资源管理的资源。当使用kubectl delete删除资源时,有个级联删除的选项--cascade,该选项有三个值:

  • background:后台级联删除,是默认的删除策略,k8s立即删除当前资源,然后在后台清理当前资源创建的其他资源(也就是ownerReferences是当前资源的资源)
  • foreground:前台级联删除,kubectl会,k8s会给Demo加上deletionTimestamp属性和finalizers属性,其中finalizers设置为foregroundDeletion,给Pod设置deletionTimestamp,然后去删除Pod,当清理操作完成时,再移除finalizers属性并删除Pod
  • orphan:不进行级联删除,直接删除当前资源,也就是不管当前资源创建的其他资源,跟我们没有用ownerReferences效果一样

这就解释了为什么给Pod加上ownerReferences属性就可以在删除Demo时自动删除Pod。

资源删除过程中还需要提到两个属性:BlockOwnerDeletionFinalizers

  • BlockOwnerDeletion:当在ownerReferences中设置了该选项,说明当删除当前资源时需要确保父资源也在删除
  • Finalizers:当资源设置了Finalizers时,k8s收到删除请求时,不会直接删除资源,而是会添加资源的deletionTimestamp属性,然后返回Accepted,之后FinalizersController会监听该资源,当该资源的条件满足时,就会从Finalizers中删除,资源就会被删除

2 开发Webhook

2.1 什么是Webhook

Webhook是一种可以在请求过程中将请求发送给另一个web服务进行处理的机制,例如,GitLab中的Webhook,当向GitLabe仓库提交代码时,GitLab会产生一些事件,可以让GitLab将这些事件发送给我们的Web程序处理,从而可以实现流程的自动化。与GitLabe中的Webhook类似,k8s中的Webhook也是可以在apiserver处理请求时,将请求发送给其他的web服务处理,区别是,k8s中的Webhook不仅可以收到请求,还能够拦截和拒绝请求。

k8s apiserver在收到请求后,需要对请求进行准入控制,也就是判断请求是否合法,这里面有两种特殊的准入控制可以让开发人员增加自己的准入规则:

  • MutatingAdmissionWebhook:变更准入,可以对资源进行修改,例如,当用户没有设置某个字段时,可以给该字段设置默认值
  • ValidatingAdmissionWebhook:验证准入,可以对资源的字段进行验证,例如,验证某个字段是否在允许的范围内
2.2 基于kubebuilder开发Webhook

在这里我们创建Webhook,且带有变更准入和验证准入的功能:

  • 变更准入:当demo.spec未提供replicas字段时,则将demo.spec.replics设置为1
  • 验证准入:demo.spec.replicas的范围只能在[1,10]之间,如果设置的范围不在这个范围,则拒绝请求

创建Webhook:

// 创建默认和验证的webhook
kubebuilder create webhook --group batch --version v1 --kind Demo --defaulting --programmatic-validation

上述命令分别在api/v1和config增加了以下文件:

  • api/v1:demo_webhook.go、webhook_suit_test.go
  • config/:default/manager_webhook_patch.yaml、default/webhookcainjection_patch.yaml、webhook/service.yaml、

代码逻辑位于demo_webhook.go,其中Default()里面可以对未提供值的字段设置默认值,ValidateCreate()是对创建资源时进行验证,ValidateUpdate()是对更新资源时进行验证,ValidateDelete()是对删除资源时进行验证。

func (r *Demo) Default() {
	demolog.Info("default", "name", r.Name)

       // 设置replicas的默认值
	if r.Spec.Replicas == nil {
		r.Spec.Replicas = new(int64)
		*r.Spec.Replicas = 1
	}
}

func (r *Demo) ValidateCreate() (admission.Warnings, error) {
	demolog.Info("validate create", "name", r.Name)

       // 创建资源时对replicas的范围进行判断
	if *r.Spec.Replicas < 1 || *r.Spec.Replicas > 10 {
		return admission.Warnings{"replicas must greater than 1 and less than 11"}, errors.New("replicas must greater than 1 and less than 11")
	}

	return nil, nil
}

3 总结

这里解决了当前资源和子资源的删除问题,并说明了级联删除,并简单讲解了Webhook的开发。

你可能感兴趣的:(kubernetes,kubernetes,docker,云原生)