以无侵方式实现Deployment原地升级

如何以无侵方式实现Deployment原地升级?

本文将展示如何以无侵、原生的方式实现Deployment原地升级。

在文章末尾会提供shell脚本供大家参考。

本文的原地升级仅指镜像更新

本篇kubernetes版本为v1.27.3。

原地升级的概念以及OpenKruise的实现方式可以参考文章:从源码解析Kruise原地升级原理

kubernetes项目地址: https://github.com/kubernetes/kubernetes

controller命令main入口: cmd/kube-controller-manager/controller-manager.go

controller相关代码目录: pkg/controller

需要解决的问题

我们知道, Deployment是以管理多个RS的方式来控制升级的。 当我们修改image之后, 会同时存在两个镜像分别为"old image"和"new image"的RS,当"new image"的RS状态正常后, 另外一个RS会被回收。 在这期间,pod也同时完成新建的操作。

~|⇒ kubectl get deployment,rs,pod
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           98s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   1         1         1       98s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-54b596f5bf-cw9n8   1/1     Running   0          98s
~|⇒ kubectl edit deployments.apps nginx # 修改image
deployment.apps/nginx edited
~|⇒ kubectl get deployment,rs,pod # 出现两个rs, 两个pod
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           3m12s

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   1         1         1       3m12s
replicaset.apps/nginx-564768b864   1         1         0       2s

NAME                         READY   STATUS              RESTARTS   AGE
pod/nginx-54b596f5bf-cw9n8   1/1     Running             0          3m12s
pod/nginx-564768b864-vzqfp   0/1     ContainerCreating   0          2s
~|⇒ kubectl describe deployments.apps nginx
Name:                   nginx
Namespace:              default
CreationTimestamp:      Mon, 04 Mar 2024 11:44:49 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 2
Selector:               name=nginx
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  name=nginx
  Containers:
   nginx:
    Image:        nginx:1.25.4
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable 
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  nginx-54b596f5bf (0/0 replicas created) # 标记出新旧的rs
NewReplicaSet:   nginx-564768b864 (1/1 replicas created)

如果想要实现原地升级, 需要解决以下问题:

  • 修改Deploymentimage字段后, 阻止资源的的重建
  • 不重建pod的前提下,更新pod中的容器
  • 保证DeploymentReplicaSet以及Pod的相关信息一致,状态正常

解决方案

更新容器

先说更新容器的问题。

在 从源码解析Kruise原地升级原理 这篇文章中有提, 修改Pod中容器的镜像,Pod是不会重建的,本身是具有原地升级的能力。

相关信息一致

这个也好解决, 把更新内容同时更新到DeploymentReplicaSet以及Pod中即可。

阻止资源的的重建

阻止资源的的重建才是这个问题的关键。

我们可以通过修改代码的运行逻辑,或者一些hack(如用webhook)的手段来做这件事情,但这不够优雅或者入侵了k8s的原生逻辑。

有一个命令可以满足我们的需求 – rollout pause

csi-driver-nfs|master ⇒ kubectl rollout pause --help
Mark the provided resource as paused.

 Paused resources will not be reconciled by a controller. Use "kubectl rollout resume" to resume a paused resource.
Currently only deployments support being paused.

这个命令可以暂停Deployment,被暂停的资源不会被Controller控制,这正好满足我们的需求。

pause功能(源码)分析

Deployment控制器的源码解析,可以看文章 《Deployment控制器源码解析》
源码位置 pkg/controller/deployment

Deployment处理最终会由DeploymentController.syncDeployment方法处理, 方法中会对Pause状态判断并处理

func (dc *DeploymentController) syncDeployment(ctx context.Context, key string) error {
    //...
    if d.Spec.Paused {
		return dc.sync(ctx, d, rsList)
	}
    //...
}
func (dc *DeploymentController) sync(ctx context.Context, d *apps.Deployment, rsList []*apps.ReplicaSet) error {
    // 负责更新rs, 我们只看这里
	newRS, oldRSs, err := dc.getAllReplicaSetsAndSyncRevision(ctx, d, rsList, false)
    // ...
}
// 最终会由这个函数处理
func (dc *DeploymentController) getNewReplicaSet(ctx context.Context, d *apps.Deployment, rsList, oldRSs []*apps.ReplicaSet, createIfNotExisted bool) (*apps.ReplicaSet, error) {
	logger := klog.FromContext(ctx)
    // 通过对比deployment中的pod template hash和rs中的pod template hash来判断是否有新的rs存在
    // 即不需要更新的rs
	existingNewRS := deploymentutil.FindNewReplicaSet(d, rsList)
	// 存在的最大版本
	maxOldRevision := deploymentutil.MaxRevision(oldRSs)
	// 新版本 
	newRevision := strconv.FormatInt(maxOldRevision+1, 10)
    // 注意看这里, 如果存在新的rs, 会更新同步rs与deployment中关联的信息, 使其保持一致
    // 这里包含:
    // deploy.annotations -> rs.annotations
    // rs.revision -> deploy.revision
	if existingNewRS != nil {
		rsCopy := existingNewRS.DeepCopy()
        // 同步tAnnotation
		annotationsUpdated := deploymentutil.SetNewReplicaSetAnnotations(ctx, d, rsCopy, newRevision, true, maxRevHistoryLengthInChars)
		minReadySecondsNeedsUpdate := rsCopy.Spec.MinReadySeconds != d.Spec.MinReadySeconds
		if annotationsUpdated || minReadySecondsNeedsUpdate {
			rsCopy.Spec.MinReadySeconds = d.Spec.MinReadySeconds
			return dc.client.AppsV1().ReplicaSets(rsCopy.ObjectMeta.Namespace).Update(ctx, rsCopy, metav1.UpdateOptions{})
		}
		// 同步revision
		needsUpdate := deploymentutil.SetDeploymentRevision(d, rsCopy.Annotations[deploymentutil.RevisionAnnotation])
        // 更新进度
		cond := deploymentutil.GetDeploymentCondition(d.Status, apps.DeploymentProgressing)
		if deploymentutil.HasProgressDeadline(d) && cond == nil {
			msg := fmt.Sprintf("Found new replica set %q", rsCopy.Name)
			condition := deploymentutil.NewDeploymentCondition(apps.DeploymentProgressing, v1.ConditionTrue, deploymentutil.FoundNewRSReason, msg)
			deploymentutil.SetDeploymentCondition(&d.Status, *condition)
			needsUpdate = true
		}

		if needsUpdate {
			var err error
			if _, err = dc.client.AppsV1().Deployments(d.Namespace).UpdateStatus(ctx, d, metav1.UpdateOptions{}); err != nil {
				return nil, err
			}
		}
		return rsCopy, nil
	}
    // sync调用时 createIfNotExisted = false
    // 所以到这里就结束了, 下面的函数省略....
	if !createIfNotExisted {
		return nil, nil
	}

	// ...
}

从上述代码我们可以确定我们的操作顺序及方法:

  1. kubectl rollout pause deployment xxx 暂停Deployment
  2. 修改pod中的image字段
  3. 修改rs中的image字段
  4. 修改Deployment中的image字段
  5. kubectl rollout resume deployment xxx 恢复Deployment

从pod开始修改,ownerReference资源的更新动作触发时, 检查"pod template"会始终与被控资源保持一致, 以此跳过资源的重建。

实践

  1. 获取当前资源信息
csi-driver-nfs|master ⇒ kubectl get deployment,rs,pod
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           5h18m

NAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   0         0         0       5h18m
replicaset.apps/nginx-564768b864   1         1         1       5h15m

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-564768b864-vzqfp   1/1     Running   0          5h15m
  1. 暂停deployemnt
csi-driver-nfs|master ⇒ kubectl rollout pause deployment nginx
csi-driver-nfs|master ⇒ kubectl describe deployments.apps nginx
Name:                   nginx
Namespace:              default
CreationTimestamp:      Mon, 04 Mar 2024 11:44:49 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 2
Selector:               name=nginx
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  name=nginx
  Containers:
   nginx:
    Image:        nginx:1.25.4
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status   Reason
  ----           ------   ------
  Available      True     MinimumReplicasAvailable
  Progressing    Unknown  DeploymentPaused # 标记出deployment被暂停
OldReplicaSets:  nginx-54b596f5bf (0/0 replicas created)
NewReplicaSet:   nginx-564768b864 (1/1 replicas created)
Events:          <none>
  1. 修改pod中的image字段, nginx:1.25.4 --> nginx:1.25

修改完成后pod没有被重建, restrts+1 , revision+1

csi-driver-nfs|master ⇒ kubectl get deployment nginx -o jsonpath="{.spec.template.spec.containers[0]}"
{"image":"nginx:1.25.4","imagePullPolicy":"Always","name":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}
csi-driver-nfs|master ⇒ kubectl edit pod nginx-564768b864-vzqfp
pod/nginx-564768b864-vzqfp edited
csi-driver-nfs|master ⇒ kubectl get pod
NAME                     READY   STATUS    RESTARTS     AGE
nginx-564768b864-vzqfp   1/1     Running   1 (6s ago)   5h20m 
csi-driver-nfs|master ⇒ kubectl get pod nginx-564768b864-vzqfp -o jsonpath='{.spec.containers[0]}'
{"image":"nginx:1.25","imagePullPolicy":"Always","name":"nginx","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"kube-api-access-qwfr5","readOnly":true}]}%
  1. 修改rs中的image字段, pod无变化
csi-driver-nfs|master ⇒ kubectl edit rs nginx-564768b864
replicaset.apps/nginx-564768b864 edited
csi-driver-nfs|master ⇒ kubectl get rs
NAME               DESIRED   CURRENT   READY   AGE
nginx-54b596f5bf   0         0         0       5h28m # 这个旧版本是之前修改非本次实验内容留存的, 不用管
nginx-564768b864   1         1         1       5h25m # 注意看我们后续的操作会不会使这个rs被回收
csi-driver-nfs|master ⇒ kubectl get pod
NAME                     READY   STATUS    RESTARTS        AGE
nginx-564768b864-vzqfp   1/1     Running   1 (4m56s ago)   5h25m
  1. 修改Deployment中的image字段, pod无变化
csi-driver-nfs|master ⇒ kubectl edit deployments.apps nginx
deployment.apps/nginx edited
csi-driver-nfs|master ⇒ kubectl get rs
NAME               DESIRED   CURRENT   READY   AGE
nginx-54b596f5bf   0         0         0       5h30m
nginx-564768b864   1         1         1       5h27m
csi-driver-nfs|master ⇒ kubectl get pod
NAME                     READY   STATUS    RESTARTS       AGE
nginx-564768b864-vzqfp   1/1     Running   1 (7m4s ago)   5h27m
  1. 记录当前资源信息
  • Deployment状态为 DeploymentPaused,
    • OldReplicaSets: nginx-54b596f5bf (0/0 replicas created)
    • NewReplicaSet: nginx-564768b864 (1/1 replicas created)
  • Deployment revision版本: deployment.kubernetes.io/revision: “2”
  • Deployment resource版本: resourceVersion: “159028”
  • RS revision版本:deployment.kubernetes.io/revision: “2”
  • RS resource版本: resourceVersion: “158921”
  1. 恢复Deployment
csi-driver-nfs|master ⇒ kubectl rollout resume deployment nginx
  1. 查看资源信息
  • Deployment状态为 NewReplicaSetAvailable , rs状态与上文一致
  • Deployment revision版本 与上文一致
  • Deployment resource版本 变更 (因为状态变化)
  • RS信息均无变换
  1. 确认原地升级完成

原地升级脚本

脚本代码访问https://github.com/Forget-C/demo/tree/main/inplaceupdate/scripts

使用方法

脚本接收4个参数:

  • Deployment名称
  • Deployment的namespace
  • Deployment的container名称
  • Deployment的container的镜像

4个参数缺一不可, 且顺序不能错。

scripts|main⚡ ⇒ bash inplaceupdate.sh help                            
Usage: inplaceupdate.sh <name> <namespace> <container> <image>

脚本执行后,会修改pod、rs、deployment的镜像, 但不会删除pod, pod的属性也不会变更。

检查原地升级是否成功的方法为查看

  • pod的镜像是否变更
  • pod restart次数+1

执行

scripts|main⚡ ⇒ bash  inplaceupdate.sh nginx default nginx nginx:1.25  
deployment.apps/nginx paused
Pod nginx-54b596f5bf-qwgkl updated
Replicaset nginx-54b596f5bf updated
Deployment nginx updated
deployment.apps/nginx resumed
Deployment nginx change to nginx:1.25 completed successfully
Waiting for pods to be ready...
Pod nginx-54b596f5bf-qwgkl is ready
All pods are ready

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/611414.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算图与自动微分

计算图与自动微分 一、自动梯度计算1.1 数值微分&#xff08;Numerical Differentiation&#xff09;1.2 符号微分&#xff08;Symbolic Differentiation&#xff09;1.3 自动微分&#xff08;Automatic Differentiation&#xff0c;AD&#xff09;1.3.1 计算图1.3.2 正向传播1…

java技术总结

1.java基本数据类型? byte 1,short 2 ,int 4,long 8 ,float 4,double 8,boolean 1,char 2 2.java为什么要有包装类型? 前 6 个类派生于公共的超类 Number,而 Character 和 Boolean 是 Object 的直接子类。 被 final 修饰, Java 内置的包装类是无法被继承的。 包装…

FreeRTOS标准库例程代码

1.设备STM32F103C8T6 2.工程模板 单片机: 部分单片机的程序例程 - Gitee.comhttps://gitee.com/lovefoolnotme/singlechip/tree/master/STM32_FREERTOS/1.%E5%B7%A5%E7%A8%8B%E6%A8%A1%E6%9D%BF 3.代码 1-FreeRTOS移植模板 #include "system.h" #include "…

腾讯云coding代码托管平台配置问题公钥拉取失败提示 Permission denied(publickey)

前言 最近在学校有个课设多人开发一个游戏&#xff0c;要团队协作&#xff0c;选用了腾讯云的coding作为代码管理仓库&#xff0c;但在配置的时候遇到了一些问题&#xff0c;相比于github&#xff0c;发现腾讯的coding更难用&#xff0c;&#xff0c;&#xff0c;这里记录一下…

【进程替换】进程程序替换函数execl | 单进程程序替换原理

目录 直接看现象&#xff08;单进程&#xff09; 单进程程序替换原理 替换函数 直接看现象&#xff08;单进程&#xff09; 我们先使用execl来直接看现象man 3 execlexecute a file执行一个程序int execl(const char *path, const char *arg, ...);execl函数的返回值&#x…

使用GitLab自带的CI/CD功能在K8S集群里部署项目(四)

前置内容&#xff1a; 通过Docker Compose部署GitLab和GitLab Runner&#xff08;一&#xff09; 使用GitLab自带的CI/CD功能在本地部署项目&#xff08;二&#xff09; 使用GitLab自带的CI/CD功能在远程服务器部署项目&#xff08;三&#xff09; 一、K8S集群信息 节点名称…

Unity TileMap入门

概述 相信很多同学学习制作游戏都是从2D游戏开始制作的吧&#xff0c;瓦片地图相信大家都有接触&#xff0c;那接下来让我们学习一下这部分的内容吧&#xff01; Tilemap AnimationFrameRate:设置每帧动画的播放速率。Color:瓦片地图的颜色TileAnchor:锚点&#xff0c;&#x…

笔试强训week4

day1 Q1 难度⭐⭐ 小易的升级之路_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 小易经常沉迷于网络游戏.有一次,他在玩一个打怪升级的游戏,他的角色的初始能力值为 a.在接下来的一段时间内,他将会依次遇见n个怪物,每个怪物的防御力为b1,b2,b3...bn. 如果遇到的怪物防…

马斯克:脑机接口迎来首例植入者,芯片接线发生故障。

马斯克旗下的脑机接口公司Neuralink近日传出关于首例植入者诺兰阿博脑机接口芯片故障的消息。根据Neuralink发布的文章&#xff0c;诺兰阿博的脑机设备发生了故障&#xff0c;多根植入他大脑的接线已经脱落&#xff0c;导致获取数据量减少。目前该公司正在研究导致接线脱落的原…

Java进阶08 集合(续)Stream流

Java进阶08 集合&#xff08;续&#xff09;&Stream流 一、HashSet集合类&#xff08;续&#xff09; 1、JDK7(-)HashSet原理解析 1.1 底层结构 数组链表 1.2 执行过程 ①创建一个默认长度为16的数组&#xff0c;数组名为table ②根据元素的哈希值跟数组的长度求余计…

灰狼优化算法(Grey Wolf Optimizer)

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 算法引言 灰狼算法&#xff08;Grey Wolf Optimizer, GWO&#xff09;是一种受自然界灰狼行为启发的优化算法。它模拟了灰狼的社会层次和狩猎策…

JS实现初始化、动态点击切换激活样式

食用须知&#xff0c;不懂得把代码交给AI解释一下&#xff0c;明白流程就会用了&#xff0c;本文只有js与html&#xff0c;样式代码一概没有&#xff1a; 效果展示 1、点击显示的盒子代码 <div data-v-e1dd37c4"" class"news-container main-width-contain…

JAVA获取application.yml配置文件的属性值

application.yml配置参数 方式一&#xff1a;使用Value方式(常用) 语法 Value("${配置文件中的key:默认值}") Value("${配置文件中的key}")方法1&#xff1a;使用的类文件中定义变量&#xff0c;直接使用变量 import org.springframework.beans.factory.an…

通义千问2.5中文能力地表最强

随着人工智能技术的不断进步&#xff0c;智能问答系统已成为人们日常生活中不可或缺的一部分。阿里巴巴集团作为全球领先的科技公司&#xff0c;一直致力于AI领域的研发和创新。最近&#xff0c;阿里巴巴发布了其最新的智能问答系统——通义千问2.5。 通义千问2.5在AI问答领域…

抖音新店怎么对接达人?对接达人秘籍流程分享,让你学会找达人

大家好&#xff0c;我是电商花花。 新手怎么对接达人带货&#xff1f;这是我们新手商家 要考虑的问题。 很多新手抱怨自己新店铺不出单&#xff0c;没有销量&#xff0c;对接达人又怕达人看不上&#xff0c;没有达人愿意帮我带货&#xff0c;在面临这样的情况下不知道该怎么办…

基于自我对弈框架的偏好优化算法SPPO

传统的从人类反馈中进行强化学习&#xff08;RLHF&#xff09;的方法仰赖如Bradley-Terry模型等参数模型,但这样的模型难以充分捕捉人类偏好中的非递移性和非理性。最新的研究进展显示,直接使用偏好机率可以更准确地反映人类偏好,从而实现更灵活、更精确的语言模型对齐。本文提…

会话劫持攻击就在我们身边,我们要如何防范

会话劫持攻击&#xff08;Session Hijacking&#xff09;是一种网络攻击方式&#xff0c;攻击者通过某种手段获取到用户的会话标识&#xff08;Session ID&#xff09;&#xff0c;然后使用这个会话标识冒充合法用户进行恶意操作。这种攻击方式允许攻击者以合法用户的身份访问受…

Go语言系统学习笔记(一):基础篇

1. 写在前面 公司的新业务开发需要用到go语言&#xff0c;虽然之前没接触过这门语言&#xff0c;但在大模型的帮助下&#xff0c;边看项目边写代码也能进行go的项目开发&#xff0c;不过&#xff0c;写了一段时间代码之后&#xff0c;总感觉对go语言本身&#xff0c;我的知识体…

Python尝试安装 pyaudio 时遇到的错误信息表示安装过程失败,原因是找不到 Python.h 头文件

环境&#xff1a; Python 3.8.10 WSL2 问题描述&#xff1a; 尝试安装 pyaudio 时遇到的错误信息表示安装过程失败&#xff0c;原因是找不到 Python.h 头文件 error: subprocess-exited-with-error Building wheel for pyaudio (pyproject.toml) did not run successfully…

【数据结构】手把手带你玩转线性表

前言&#xff1a; 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累。我…