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管理。

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