前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >kubernetes学习记录(16)——使用operator实现kubernetes的sidecar管理

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

作者头像
胡了了
发布2020-05-27 09:38:18
发布2020-05-27 09:38:18
1.4K00
代码可运行
举报
运行总次数:0
代码可运行

使用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对象
代码语言:javascript
代码运行次数:0
运行
复制
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列表
代码语言:javascript
代码运行次数:0
运行
复制
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列表
代码语言:javascript
代码运行次数:0
运行
复制
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
代码语言:javascript
代码运行次数:0
运行
复制
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。

代码语言:javascript
代码运行次数:0
运行
复制
appsv1alpha1.SidecarSetStatus{
		ObservedGeneration: sidecarSet.Generation,
		MatchedPods:        matchedPods,
		UpdatedPods:        updatedPods,
		ReadyPods:          readyPods,
}

updateSidecarSetStatus更新SidecarSet的Status。

代码语言:javascript
代码运行次数:0
运行
复制
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更新完成之后,就是一系列的业务逻辑判断

代码语言:javascript
代码运行次数:0
运行
复制
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管理。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/05/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • sidecarSet源码分析
    • openkruise/kruise/pkg/controller/sidecarset_controller.go
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档