kubernetes学习记录(16)——使用operator实现kubernetes的sidecar管理

使用operator实现kubernetes的sidecar管理主要参考阿里开源的openkruise项目,github地址:https://github.com/openkruise/kruise。

openkruise目前提供了5个工作负载控制器:

  • Advanced StatefulSet
    增强的默认的版本StatefulSet,有额外的功能,如inplace-update,pasue和MaxUnavailable。
  • BroadcastJob
    在集群所有节点上运行pod完成任务(类似于job的DaemonSet模式)。
  • SidecarSet
    它基于选择器将Sidecar容器注入Pod spec中,并且还能够升级Sidecar容器。
  • UnitedDeployment
    此控制器通过使用多个工作负载来管理分布在多个故障域中的应用程序容器。
  • CloneSet
    主要用于管理无状态应用程序的工作负载。它提供了一些新的功能,例如inplace update, specified pod deletion, configurable priority/scatter update, preUpdate/postUpdate hooks。

目前我只需要参考SidecarSet管理功能。

sidecarSet源码分析

openkruise/kruise/pkg/controller/sidecarset_controller.go

  • 获取请求的sidecarSet对象
sidecarSet := &appsv1alpha1.SidecarSet{}
	err := r.Get(context.TODO(), request.NamespacedName, sidecarSet)
	if err != nil {
		if errors.IsNotFound(err) {
			// Object not found, return.  Created objects are automatically garbage collected.
			// For additional cleanup logic use finalizers.
			return reconcile.Result{}, nil
		}
		// Error reading the object - requeue the request.
		return reconcile.Result{}, err
	}
  • 根据sidecarSet的标签选择器获取pod列表
selector, err := metav1.LabelSelectorAsSelector(sidecarSet.Spec.Selector)
	if err != nil {
		return reconcile.Result{}, err
	}
	matchedPods := &corev1.PodList{}
	if err := r.List(context.TODO(), &client.ListOptions{LabelSelector: selector}, matchedPods); err != nil {
		return reconcile.Result{}, err
	}
  • 过滤筛选pod列表
var filteredPods []*corev1.Pod
	for i := range matchedPods.Items {
		pod := &matchedPods.Items[i]
		podCreateBeforeSidecarSet, err := isPodCreatedBeforeSidecarSet(sidecarSet, pod)
		if err != nil {
			return reconcile.Result{}, err
		}
		if controllerutil.IsPodActive(pod) && !isIgnoredPod(pod) && !podCreateBeforeSidecarSet {
			filteredPods = append(filteredPods, pod)
		}
	}

isPodCreatedBeforeSidecarSet通过Annotations判断pod是否在sidecarSet前创建。
IsPodActive通过Pod.Status.Phase和Pod.DeletionTimestamp判断pod的存活状态。
isIgnoredPod通过检查Pod的namespace,会过滤掉k8s默认namespace下的pod。

  • 更新sidecarSet的status
status, err := calculateStatus(sidecarSet, filteredPods)
	if err != nil {
		return reconcile.Result{}, err
	}

	err = r.updateSidecarSetStatus(sidecarSet, status)
	if err != nil {
		return reconcile.Result{}, err
	}

calculateStatus计算各个状态pod的数量,返回SidecarSet的Status。

appsv1alpha1.SidecarSetStatus{
		ObservedGeneration: sidecarSet.Generation,
		MatchedPods:        matchedPods,
		UpdatedPods:        updatedPods,
		ReadyPods:          readyPods,
}

updateSidecarSetStatus更新SidecarSet的Status。

func (r *ReconcileSidecarSet) updateSidecarSetStatus(sidecarSet *appsv1alpha1.SidecarSet, status *appsv1alpha1.SidecarSetStatus) error {
	if !inconsistentStatus(sidecarSet, status) {
		return nil
	}
	sidecarSetClone := sidecarSet.DeepCopy()
	err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
		sidecarSetClone.Status = *status
		updateErr := r.Status().Update(context.TODO(), sidecarSetClone)
		if updateErr == nil {
			return nil
		}
		key := types.NamespacedName{
			Name: sidecarSetClone.Name,
		}
		if err := r.Get(context.TODO(), key, sidecarSetClone); err != nil {
			klog.Errorf("error getting updated sidecarset %s from client", sidecarSetClone.Name)
		}
		return updateErr
	})
	return err
}

这个函数里有两点值得我借鉴:

  1. 通过inconsistentStatus函数,判断sidecarSet的status是否发生改变,改变了则更新(我之前在写operator的时候是每次直接更新status,没管是否发生变化
  2. 通过retry.RetryOnConflict函数进行status的更新,在尝试更新之前检索部署的最新版本,避免冲突,这也是k8s官方推荐的做法(如果不这么做,很可能会报the object has been modified:please apply your changes to the latest version and try again
  • 更新sidecar

在SidecarSet的status更新完成之后,就是一系列的业务逻辑判断

1. check if sidecarset paused, if so, then quit
2. check if fields other than image in sidecarset had changed, if so, then quit
3. check unavailable pod number, if > 0, then quit(maxUnavailable=1)
4. find out pods need update
5. update one pod(maxUnavailable=1)

最终对pod的更新逻辑其实非常简单,找到需要更新的pod之后,遍历pod的container,匹配到需要更新的container之后,直接更新container的image,借助k8s本身的功能,即可完成sidecar的升级。

总结

看完之后,有一种“虎头蛇尾”的感觉。原本以为k8s的sidecar升级很难,现在看来比我想象的要简单一些,处理好业务逻辑,即可完成sidecar管理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章