K8S:应用pod滚动更新、回滚pod版本

一、背景描述

k8s版本:v1.16.6
系统版本:entOS Linux release 8.2.2004 (Core)
文档内容:测试K8S环境中如何实现pod滚动更新(更新时业务不中断),以及更新后回滚到上一版本或指定版本。

二、业务pod滚动更新

①、非滚动更新yaml

对于多实例服务,滚动更新采用对各个实例逐批次进行单独更新而非同一时刻对所有实例进行全部更新,来达到不中断服务的更新升级方式。

对于Kubernetes集群来说,一个service可能有多个pod,滚动升级(Rolling update)就是指每次更新部分Pod,而不是在同一时刻将该Service下面的所有Pod shutdown,然后去更新(例如replace --force方案),逐个更新可以避免将业务中断!

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: test-java-tomcat
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: test-java-tomcat
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: test-java-tomcat
        image: harbor.example.com/test/cm_self_profit_api:latest
        ports:
        - containerPort: 8090
          name: web
        volumeMounts:
        - name: app-logs
          mountPath: /usr/local/tomcat8/logs
      - image: harbor.example.com/ops/filebeat-7.10.2:1.0
        imagePullPolicy: Always
        name: filebeat
        volumeMounts:
        - name: app-logs
          mountPath: /logs
        - name: filebeat-config
          mountPath: /etc/filebeat
      volumes:
        - name: app-logs
          emptyDir: {}
        - name: filebeat-config
          configMap:
            name: test-java-tomcat-filebeat-config
---
apiVersion: v1
kind: Service
metadata:
  name: test-java-tomcat
spec:
  type: NodePort
  ports:
  - port: 8090
    targetPort: 8090
    nodePort: 30019
  selector:
    app: test-java-tomcat
②、滚动更新yaml

在yaml中增加以下几个参数

spec:
  revisionHistoryLimit: 10
  minReadySeconds: 5
  strategy:
  # indicate which strategy we want for rolling update
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
③、字段解释

revisionHistoryLimit
默认配置下,Kubernetes 只会保留最近的几个 revision,可以在 Deployment 配置文件中通过 revisionHistoryLimit 属性增加 revision 数量。

revisionHistoryLimit(历史版本记录):Deployment revision history存储在它控制的ReplicaSets中。默认保存记录5个    
.spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦删除的旧的RepelicaSet,Deployment就无法再回退到那个revison了。  
如果将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。
PS:为了保存版本升级的历史,需要再创建Deployment对象时,在命令中使用"–record"选项一般配合revisionHistoryLimit使用的strategy (更新策略)
minReadySeconds

Kubernetes在等待设置的时间后才进行升级
如果没有设置该值,Kubernetes会假设该容器启动起来后就提供服务了,所以这个可以设置的保守一些,比如我们的服务启动从3-20秒不等,我可以设置成30秒,这样防止pod启动了但是服务还没准备好导致系统不可用。
maxSurge

升级过程中最多可以比原先设置多出的POD数量
例如:maxSurage=1,replicas=5,则表示Kubernetes会先启动1一个新的Pod后才删掉一个旧的POD,整个升级过程中最多会有5+1个POD。
maxUnavaible

升级过程中最多有多少个POD处于无法提供服务的状态,当maxSurge不为0时,该值也不能为0,最好和maxSurge保持一致。
例如:maxUnavaible=1,则表示Kubernetes整个升级过程中最多会有1个POD处于无法服务的状态,可以保证有足够的pod(或者认为是足够的性能)提供服务。

完整yaml如下:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: test-java-tomcat
spec:
  replicas: 2
  minReadySeconds: 20
  strategy:
  # indicate which strategy we want for rolling update
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: test-java-tomcat
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: test-java-tomcat
        image: harbor.example.com/test/cm_self_profit_api:latest
        ports:
        - containerPort: 8090
          name: web
        volumeMounts:
        - name: app-logs
          mountPath: /usr/local/tomcat8/logs
      - image: harbor.example.com/ops/filebeat-7.10.2:1.0
        imagePullPolicy: Always
        name: filebeat
        volumeMounts:
        - name: app-logs
          mountPath: /logs
        - name: filebeat-config
          mountPath: /etc/filebeat
      volumes:
        - name: app-logs
          emptyDir: {}
        - name: filebeat-config
          configMap:
            name: test-java-tomcat-filebeat-config
---
apiVersion: v1
kind: Service
metadata:
  name: test-java-tomcat
spec:
  type: NodePort
  ports:
  - port: 8090
    targetPort: 8090
    nodePort: 30019
  selector:
    app: test-java-tomcat
④、滚动更新验证
kubectl apply -f test-java-tomcat.yaml --record

升级过程描述
项目JOB更新–K8S根据JOB在原有业务两个POD的情况下,再增加一个新版本POD,当新版本POD准备好时销毁原有两个实例中的其中一个,然后再次增加一个新版本POD,在第二个新版pod准备好后再销毁原两个实例中剩下的一个实例,即滚动更新完成。

扩展:
所有通过kubectl xxxx --record都会被kubernetes记录到etcd进行持久化,这无疑会占用etcd资源,最重要的是,时间久了,当你kubectl get rs时,会有成百上千的垃圾RS返回给你,这其实也没有太大的作用,一般保留几个版本就可以了。

我们可以通过设置Deployment的.spec.revisionHistoryLimit参数来限制最大保留的revision 数量,比如10个版本。

另外其实rollout history中记录的revision都和ReplicaSets一一对应。如果手动delete某个ReplicaSet,对应的rollout history就会被删除,也就是还说你无法回滚到这个revison了。

⑤、 Deployment版本回滚
# 使用kubectl rollout history命令查看deployment部署记录
kubectl rollout history deployment/test-java-tomcat

# 查看特定版本的详细信息,加上revision=<N>参数
kubectl rollout history deployment/test-java-tomcat --revision=3

# 回滚到ngin-deploymment上一个部署版本
kubectl rollout undo deployment/test-java-tomcat

# 使用--to-revision回滚到指定部署版本
kubectl rollout undo deployment/test-java-tomcat --to-revision=2