k8s容器编排技术实践——K8s如何实现滚动更新

k8s容器编排技术实践------k8s对象service应用详解https://blog.csdn.net/xiaochenXIHUA/article/details/161593299k8s容器编排技术实践------K8s对象deployment应用详解https://coffeemilk.blog.csdn.net/article/details/161427275

一、k8s的滚动更新简介

当某个服务需要升级时,传统的做法是,先将要更新的服务下线,业务停止后再更新版本和配置,然后重新启动服务。如果业务集群规模较大时,这个工作就变成了一个挑战,而且全部停止了服务,再逐步升级的方式会导致服务较长时间不可用。针对这个问题,k8s提供了滚动更新(rolling-update)的方式来解决这些问题。

滚动更新是针对pod来操作的,它通过一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新。滚动更新的最大的好处是零停机,整个更新过程始终有副本在运行,从而保证了服务的连续性

1.1、滚动更新是什么

滚动更新是 K8s Deployment 默认的升级策略 ,核心逻辑:分批、逐步创建新版本 Pod,等新 Pod 就绪后再删除旧 Pod,新旧实例短时并存、循序渐进替换,全程不一次性关停所有业务

配置:spec.strategy.type: RollingUpdate,Deployment 默认开启。

两个核心控制参数:

  1. maxSurge:升级期间最多额外新增的 Pod 数量 / 比例(默认 25%),控制多扩多少新实例;

  2. maxUnavailable:升级期间最大不可用旧 Pod 比例 / 数量(默认 25%),控制同时下线多少旧实例。

    举例:副本 4 个、默认参数:每次最多新增 1 个新 Pod,最多下线 1 个旧 Pod,循环迭代完成全量更新。

1.2、滚动更新有啥用

滚动更新有啥用 说明
实现应用不停机升级,保障业务连续性 Service 通过标签负载均衡自动把流量调度到就绪的新 Pod,升级全程服务不中断,满足 7×24 高可用 SLA。
平滑迭代版本 镜像升级、BUG 修复、环境变量 / 配置修改不用关停整站。
快速故障回滚 新版本异常,执行kubectl rollout undo deploy xxx反向滚动,分批切回旧版本。
节约资源 只临时扩容少量 Pod,不用提前准备一整套备用环境。

1.3、滚动更新的适用场景

✅滚动更新的适用场景 说明
绝大多数无状态微服务 / WEB 业务(最主流) Java 后端、Nginx 网关、前端服务、API 接口、微服务集群(SpringCloud/Dubbo);
不能中断的在线业务 电商、SAAS、支付、政企系统、线上生产环境常规小版本迭代;
资源有限,无法承担双倍机器资源 蓝绿部署需要两套全量环境
小补丁、配置微调、小版本迭代上线
❌滚动更新不适用场景 说明
有状态应用(MySQL、Redis 集群、MongoDB) 分批启停容易造成主从同步异常、数据不一致;
新旧版本 API 完全不兼容 更新过程新旧实例并存,部分请求到老版本、部分到新版本,接口报错;
需要瞬时全量切流验证新版本(需要蓝绿部署) 小流量灰度验证(需要金丝雀发布)

1.4、滚动更新的优缺点

✅滚动更新的优点 说明
零停机发布 生产环境业务无中断,线上用户无感知;
资源开销小 仅临时多出少量 Pod(maxSurge),相比蓝绿部署节省一倍服务器成本;
原生 K8s 支持,开箱即用 无需额外部署灰度 / 发布组件;
更新节奏可控 通过调整maxSurge/maxUnavailable实现慢速分批发布,出现异常及时停止;
回滚简单 一键回滚,反向滚动恢复旧版本。
❌滚动更新的缺点 说明
发布耗时长 实例数量多时,逐个分批替换,全量升级周期很长;
升级过程新旧版本共存 若前后版本接口、数据协议不兼容,会出现线上混合报错;
无法瞬间全量切换新版本 不能一键 100% 切新版本流量,想要瞬时全量切换需要蓝绿;
有状态服务兼容性差 数据库、缓存集群慎用,容易引发集群故障;
故障定位麻烦 线上同时跑两个版本代码,问题难以区分是新 / 旧版本 BUG。

1.5、滚动更新、蓝绿部署、金丝雀发布对比

发布方式 核心特点 资源 风险
滚动更新 分批逐步替换,慢慢切流 低,仅少量临时扩容 ⭐⭐ 中等
蓝绿部署 两套完整环境,全量部署完一键切 Service 高,双倍资源 ⭐⭐ 低
金丝雀发布 少量实例先升级验证,没问题再全量滚动 介于两者之间 ⭐ 极低

金丝雀发布 = 最安全的生产发布方案( 它的核心价值是:用最小的流量代价,验证最大的发布风险,是互联网公司核心业务的标配发布策略)。

简单记:滚动更新求快,蓝绿部署求稳,金丝雀发布求安全

对比维度 滚动更新 (RollingUpdate) 蓝绿部署 (Blue-Green) 金丝雀发布 (Canary)
核心原理 逐步创建新 Pod,逐步删除旧 Pod 同时运行新旧两套完整环境,切换流量 先上线少量新版本,小流量验证后全量
流量切换 自动均匀分流,渐进式替换 瞬间 100% 全量切到新版本 自定义比例(1%/5%/10%)精准控流
资源消耗 极低(仅临时多几个 Pod) 极高(双倍服务器资源) (仅多 1~2 个金丝雀 Pod)
发布风险 ⭐⭐ 中等 ⭐ 低 ⭐ 极低(最安全)
发布速度 较慢(分批耗时) 极快(一键切流) 较慢(需要验证等待)
回滚方式 反向分批替换 一键切回旧环境 直接删除金丝雀 / 切回流量
回滚速度 较慢 秒级 秒级
K8s 原生支持 原生自带(Deployment 默认) ❌ 手动 / 简单配置实现 ❌ 原生仅简易版,需组件控流
核心优势 零停机、省资源、开箱即用 无版本共存、切换 / 回滚极速 风险最小,不影响大部分用户
核心劣势 新旧版本短时共存 浪费双倍资源 流程复杂,需要监控配合

发布新版本 比作饭店换菜单

  1. 滚动更新:厨师一边做新菜,一边撤旧菜,分批替换,不耽误客人吃饭,最省成本;
  2. 蓝绿部署 :直接开第二个一模一样的厨房,新菜全部做好后,瞬间把老菜单换成新菜单;
  3. 金丝雀发布 :先给1 桌客人上新菜试吃,没问题再给所有客人换,出问题只影响这 1 桌。

二、k8s中滚动更新实现

部署一个httpd应用,指定三个副本,初始镜像为 httpd:2.4.60,然后将其更新到 httpd:2.4.67,详细操作步骤如下:

bash 复制代码
#实现滚动更新示例
#一、创建初始的低版本httpd资源
#1.1-创建httpd2.4.60-deployment.yml资源文件
cat>httpd2.4.60-deployment.yml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
spec:
  replicas: 3
  selector:
   matchLabels:
     app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:2.4.60
        ports:
        - containerPort: 80
EOF



#1.2-创建httpd2.4.60-deployment.yml资源
kubectl apply -f httpd2.4.60-deployment.yml

#1.3-查看当前所有pod的详细状态
kubectl get pod -o wide

#1.4-查看当前的所有pod副本状态详情(可查看到镜像、标签、pod列表等内容)
kubectl get replicaset -o wide

#1.5-查看当前所有的Deployment(应用顶层控制器)的状态详情(可查看到容器名、镜像版本、标签选择器)
kubectl get deploy -o wide




#二、创建高版本的httpd资源
#2.1-创建httpd2.4.67-deployment.yml资源文件
cat>httpd2.4.67-deployment.yml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment #注意:这个原数据的名称一定不能改变,否则就是一个新的deployment了!!!
spec:
  replicas: 3
  selector:
   matchLabels:
     app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:2.4.67
        ports:
        - containerPort: 80
EOF

#2.2-创建httpd2.4.67-deployment.yml资源
kubectl apply -f httpd2.4.67-deployment.yml

#2.3-查看当前所有pod的详细状态
kubectl get pod -o wide

#2.4-查看当前的所有pod副本状态详情(可查看到镜像、标签、pod列表等内容)
kubectl get replicaset -o wide

#2.5-查看当前所有的Deployment(应用顶层控制器)的状态详情(可查看到容器名、镜像版本、标签选择器)
kubectl get deploy -o wide

#2.6-查看指定deployment的状态详情(可查看到滚动更新的详情)
kubectl describe deploy httpd-deployment

执行kubectl get replicaset -o wide命令与kubectl describe deploy httpd-deployment后可以发现:

新创建的ReplicaSet镜像为httpd:2.4.67,并且管理了三个新的 Pod。而老的ReplicaSet里面已经没有任何Pod。结论是老的ReplicaSet的三个httpd:2.4.60 Pod 已经被新的ReplicaSet的三个httpd:2.4.67 Pod逐渐替换了。

三、k8s中版本回滚实现

在执行kubectl apply命令更新应用时,K8s都会记录下当前的配置,保存为一个revision(版本),通过这个版本记录,就可以回滚到某个特定的revision。默认配置下,K8s只会保留最近的几个版本,不过可以在Deployment配置文件中通过revisionHistoryLimit属性增加revision的数量。

bash 复制代码
#k8s中版本回滚实现
#1、创建三个deployment资源文件httpd2.4.60-rolldeployment.yml、httpd2.4.65-rolldeployment.yml、httpd2.4.67-rolldeployment.yml;
cat>httpd2.4.60-rolldeployment.yml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-roll-deployment
spec:
  revisionHistoryLimit: 10
  replicas: 3
  selector:
   matchLabels:
     app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:2.4.60
        ports:
        - containerPort: 80
EOF



cat>httpd2.4.65-rolldeployment.yml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-roll-deployment
spec:
  revisionHistoryLimit: 10
  replicas: 3
  selector:
   matchLabels:
     app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:2.4.65
        ports:
        - containerPort: 80
EOF



cat>httpd2.4.67-rolldeployment.yml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-roll-deployment
spec:
  revisionHistoryLimit: 10
  replicas: 3
  selector:
   matchLabels:
     app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:2.4.67
        ports:
        - containerPort: 80
EOF



#2、开始创建httpd2.4.60-rolldeployment.yml资源
kubectl apply -f httpd2.4.60-rolldeployment.yml --record
kubectl get deploy -o wide
kubectl get pod
kubectl describe deploy httpd-roll-deployment

#3-开始滚动更新httpd2.4.65-rolldeployment.yml资源,然后查看对应的deploy与pod状态详情
kubectl apply -f httpd2.4.65-rolldeployment.yml --record
kubectl get deploy -o wide
kubectl get pod
kubectl describe deploy httpd-roll-deployment
kubectl describe pod httpd-roll-deployment-79c57c7bcf-jvxhn
kubectl get replicaset -o wide


#4-开始滚动更新httpd2.4.67-rolldeployment.yml资源,然后查看对应的deploy与pod状态详情
kubectl apply -f httpd2.4.67-rolldeployment.yml --record
kubectl get deploy -o wide
kubectl get pod
kubectl describe deploy httpd-roll-deployment
kubectl describe pod httpd-roll-deployment-67476867f7-qh4c5
kubectl get replicaset -o wide


#5-查看revison历史记录、与当前的deploy版本
kubectl rollout history deployment httpd-roll-deployment
kubectl get deploy -o wide

#6-回滚到指定版本(如回滚到revision 2【即httpd2.4.65】)
kubectl rollout undo deployment httpd-roll-deployment --to-revision=2

#7-再次查看revison历史记录、与当前的deploy版本
kubectl rollout history deployment httpd-roll-deployment
kubectl get deploy -o wide

回滚执行完成后,revison历史记录也会发生相应变化,从如上从输出可以看出,回滚后,revison 2变成了revison 4。因此,有回滚需求的话,一定要在执行kubectl apply时加上 --record参数(这样在revison历史记录中可以清晰的了解到序号对应的版本内容,方便区分)。