以无侵方式实现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           98sNAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   1         1         1       98sNAME                         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           3m12sNAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   1         1         1       3m12s
replicaset.apps/nginx-564768b864   1         1         0       2sNAME                         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=nginxContainers:nginx:Image:        nginx:1.25.4Port:         80/TCPHost Port:    0/TCPEnvironment:  <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存在// 即不需要更新的rsexistingNewRS := deploymentutil.FindNewReplicaSet(d, rsList)// 存在的最大版本maxOldRevision := deploymentutil.MaxRevision(oldRSs)// 新版本 newRevision := strconv.FormatInt(maxOldRevision+1, 10)// 注意看这里, 如果存在新的rs, 会更新同步rs与deployment中关联的信息, 使其保持一致// 这里包含:// deploy.annotations -> rs.annotations// rs.revision -> deploy.revisionif existingNewRS != nil {rsCopy := existingNewRS.DeepCopy()// 同步tAnnotationannotationsUpdated := deploymentutil.SetNewReplicaSetAnnotations(ctx, d, rsCopy, newRevision, true, maxRevHistoryLengthInChars)minReadySecondsNeedsUpdate := rsCopy.Spec.MinReadySeconds != d.Spec.MinReadySecondsif annotationsUpdated || minReadySecondsNeedsUpdate {rsCopy.Spec.MinReadySeconds = d.Spec.MinReadySecondsreturn dc.client.AppsV1().ReplicaSets(rsCopy.ObjectMeta.Namespace).Update(ctx, rsCopy, metav1.UpdateOptions{})}// 同步revisionneedsUpdate := 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 errorif _, 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           5h18mNAME                               DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-54b596f5bf   0         0         0       5h18m
replicaset.apps/nginx-564768b864   1         1         1       5h15mNAME                         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=nginxContainers:nginx:Image:        nginx:1.25.4Port:         80/TCPHost Port:    0/TCPEnvironment:  <none>Mounts:       <none>Volumes:        <none>
Conditions:Type           Status   Reason----           ------   ------Available      True     MinimumReplicasAvailableProgressing    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.mzph.cn/news/835213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下GraspNet复现流程

Linux&#xff0c;Ubuntu中GraspNet复现流程 文章目录 Linux&#xff0c;Ubuntu中GraspNet复现流程1.安装cuda和cudnn2.安装pytorch3.编译graspnetAPIReference &#x1f680;非常重要的环境配置&#x1f680; ubuntu 20.04cuda 11.0.1cudnn v8.9.7python 3.8.19pytorch 1.7.0…

十大排序算法(java实现)

注&#xff1a;本篇仅用来自己学习&#xff0c;大量内容来自菜鸟教程&#xff08;地址&#xff1a;1.0 十大经典排序算法 | 菜鸟教程&#xff09; 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大…

Microsoft Edge浏览器,便携增强版 v118.0.5993.69

01 软件介绍 Microsoft Edge浏览器&#xff0c;便携增强版&#xff0c;旨在无需更新组件的情况下提供额外的功能强化。这一增强版专注于优化用户体验和系统兼容性&#xff0c;具体包含以下核心功能的提升&#xff1a; 数据保存&#xff1a;通过优化算法增强了其数据保存能力&…

1707jsp电影视频网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 校园商城派送系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用web模式&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数…

Bugku Crypto 部分题目简单题解(三)

where is flag 5 下载打开附件 Gx8EAA8SCBIfHQARCxMUHwsAHRwRHh8BEQwaFBQfGwMYCBYRHx4SBRQdGR8HAQ0QFQ 看着像base64解码 尝试后发现&#xff0c;使用在线工具无法解密 编写脚本 import base64enc Gx8EAA8SCBIfHQARCxMUHwsAHRwRHh8BEQwaFBQfGwMYCBYRHx4SBRQdGR8HAQ0QFQ tex…

【嵌入式DIY实例】-DDS信号生成器

DDS信号生成器 文章目录 DDS信号生成器1、AD9805介绍2、硬件准备与接线3、代码实现在本文中,将详细介绍如何使用AD9850来搭建一个简易的DDS(Direct Digital signal )信号生成器。 1、AD9805介绍 AD9850是一款高度集成的器件,采用先进的DDS技术,内置一个高速、高性能数模转…

powershell@管道符过滤的顺序问题@powershell管道符如何工作

文章目录 select 和 where谁先执行powershell管道符stop-service 为例查看文档中的典型参数介绍stop-process为例介绍管道符传参是怎么工作的Id参数InputObject 参数Name参数额外的试验反面例子应用:get-process 和stop-process配合 select 和 where谁先执行 在执行筛选时&…

每日一练 | 华为认证真题练习 - OSPF 协议基础

★ 题目 关于OSPF&#xff08;开放最短路径优先&#xff09;邻居状态机的描述&#xff0c;以下哪项是正确的&#xff1f; A. Attempt 状态只在 NBMA&#xff08;非广播多路访问&#xff09;网络中出现 B. Attempt 状态只在 NBMA 和 P2MP&#xff08;点对多点&#xff09;网络…

Unity构建详解(12)——自动构建

【前言】 自动构建是指整个构建流程不需要人工操作&#xff0c;只需要输入启动构建指令即可获取构建结果。实现这样的自动构建需要满足以下条件&#xff1a; 支持命令行参数启动 我们不可能每次构建时都打开Unity去手动点击构建&#xff0c;必须支持通过命令行启动Unity自动执…

java.lang.NoSuchMethodException: com.ruoyi.web.controller.test.bean.HeadTeacher

软件开发过程中使用Java反射机制时遇到了下面的问题 com.ruoyi.web.controller.test.bean.HeadTeacher4b9af9a9 com.ruoyi.web.controller.test.bean.HeadTeacher4b9af9a9java.lang.NoSuchMethodException: com.ruoyi.web.controller.test.bean.HeadTeacher.<init>(java…

【软考高项】三十八、风险管理7个过程

一、规划风险管理 1、定义、作用 定义&#xff1a;定义如何实施项目风险管理活动的过程作用&#xff1a;确保风险管理的水平、方法和可见度与项目风险程度相匹配&#xff0c;与对组织和其他干系人的重要程度相匹配 2、输入 项目管理计划 项目章程 项目文件 干系人登记册…

C语言从头学04——介绍占位符和输出格式

占位符、输出格式都是与 printf() 相关的&#xff0c;当然其它函数也有用到占位符的。这里先介绍它们在 printf() 的使用。 一、先介绍占位符&#xff0c;所谓“占位符”通俗讲就是先占个位置&#xff0c;后边再找具体值(参数)代入进行显示的一种方法。先用一个例子说明…

【一刷《剑指Offer》】面试题 17:合并两个排序的链表

力扣对应题目链接&#xff1a;21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 核心考点&#xff1a;链表合并。 一、《剑指Offer》内容 二、分析题目 这道题的解题思路有很多&#xff1a; 可以一个一个节点的归并。可以采用递归完成。 三、代码 1、易于理解的…

Java 多线程补充

线程池 Java线程池是一种能够有效管理线程资源的机制&#xff0c;它可以显著提高应用性能并降低资源消耗。 线程池的主要优点包括&#xff1a; 资源利用高效&#xff1a;通过重用已存在的线程&#xff0c;减少了频繁创建和销毁线程带来的系统开销。响应速度提升&#xff1a;…

智慧公厕,小民生里的“大智慧”!

公共厕所是城市社会生活的基础设施&#xff0c;而智慧公厕则以其独特的管理模式为城市居民提供更优质的服务。通过智能化的监测和控制系统&#xff0c;智慧公厕实现了厕位智能引导、环境监测、资源消耗监测、安全防范管理、卫生消杀设备、多媒体信息交互、自动化控制、自动化清…

ThinkPHP+MySQL查询数据的时候计算两个经纬度之间的距离

需求&#xff0c;数据表中有lng&#xff08;经度&#xff09;lat&#xff08;维度&#xff09;两个字段&#xff0c;查询数据的时候要计算记录经纬度距离目标经纬度之间的距离。 方法中还有根据生日计算年龄(YEAR(CURDATE()) - YEAR(birthday)) AS age public function get_li…

抽象类基本概念

抽象类及抽象方法 概念&#xff1a;一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这种类被定义为抽象类&#xff0c;含有抽象方法的类也被称为抽象类。 用通俗的话来说就是当一个类的某个功能&#xff08;方法&#xff09;实现不确定时&#xff0c;我们就将该…

Java防挨骂--01

在遇到字符拼接需求时&#xff0c;习惯使用StringBuilder,而不要使用String. 因为String是不可变字符序列&#xff0c;在拼接时会产生新的String对象来进行拼接 StringBuider是可变字符序列&#xff0c;在拼接时效率和对资源的占用都表现更优。 StringBuffer也是可变字符序列…

一篇详解Git版本控制工具

华子目录 版本控制集中化版本控制分布式版本控制 Git简史Git工作机制Git和代码托管中心局域网互联网 Git安装基础配置git的--local&#xff0c;--global&#xff0c;--system的区别 创建仓库方式1git init方式2git clone git网址 工作区&#xff0c;暂存区&#xff0c;本地仓库…

React19学习-初体验

升级react19版本 安装 npm install reactbeta react-dombeta如果使用ts则需要在package.json中添加。等正式版发布直接可以使用types/react了 "overrides": {"types/react": "npm:types-reactbeta","types/react-dom": "npm:ty…