• kubernetes(k8s)滚动发布,不宕机实战


    前言

    • 之前一直想怎么能让自己的服务在发布的时候也能提供服务,做到无缝衔接。所幸k8s就提供滚动发布的功能。下面我们先了解下什么是滚动发布。

    滚动发布

    • 滚动发布就是在升级过程中,先启动一台新版本的服务,等新的那台服务稳定后,就把旧的一台服务干掉,后面的老版本服务都是这样替换的过程。如下图所示。
      在这里插入图片描述

    在这里插入图片描述

    • 虚线部分表示服务已经被替换掉了。
    • 理论来说可以实现更新时使用户无感知,做到无缝衔接。

    开始前的准备

    镜像

    这个镜像其实是我学kubernetes ConfigMap的时候准备的,ConfigMap怎么使用可参考拙作kubernetes(k8s) ConfigMap实战

    • 看过kubernetes(k8s) ConfigMap实战就知道这2个版本没什么差别,只是7.7.1909版本的打印增加了customValue
    • 我要做的就是把7.7.1908升级到7.7.1909

    k8s配置文件

    • k8s的配置文件(spring-boot-kubernetes-deployment.yaml)如下代码。

    a1030907690/centos_java:7.7.1908 ;a1030907690/centos_java:7.7.1909我一早就下载好了。

     # 把jar包打到centos里的办法
    # 从本地文件创建ConfigMap kubectl create cm spring-boot-kubernetes-conf --from-file=application.yml
    # 修改 kubectl edit cm spring-boot-kubernetes-conf  ,也可以先删除 kubectl delete cm spring-boot-kubernetes-conf 再从本地文件创建
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-boot-kubernetes-deployment
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: spring-boot-kubernetes-deployment
      template:
        metadata:
          labels:
            app: spring-boot-kubernetes-deployment
        spec:
          containers:
            - name: spring-boot-kubernetes
              image: a1030907690/centos_java:7.7.1908
              command: [ "java","-jar","spring-boot-kubernetes-0.0.1-SNAPSHOT.jar" ]
              imagePullPolicy: Never # 只使用本地镜像,防止ErrImagePull异常
              ports:
                - containerPort: 8080
              env: # 解决Java程序时区问题
                - name: TZ
                  value: Asia/Shanghai
              volumeMounts:
                - name: config
                  mountPath: /config  # 应用配置文件路径
          volumes:
            - name: config
              configMap:
                name: spring-boot-kubernetes-conf  # 这个名字与创建时对应
                items:
                  - key: application.yml
                    path: application.yml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    注意:这个配置并不完美,先埋个伏笔。

    验证工具

    • 我使用JMeter一直调用接口,然后执行滚动发布,看汇总结果就可以了。当然也可以使用其他工具(手写一个也行呀)。
      在这里插入图片描述

    实战

    • 执行命令,创建deployment
    kubectl apply -f spring-boot-kubernetes-deployment.yaml 
    
    • 1

    中途我会使用kubectl set image deployment/spring-boot-kubernetes-deployment spring-boot-kubernetes=a1030907690/centos_java:7.7.1909 --record来滚动更新。

    在这里插入图片描述

    在这里插入图片描述

    • 可以看到有异常情况发生,有的请求不通。
    • 本想实现更新时不宕机,无缝衔接,我这波被打脸了。

    问题

    在这里插入图片描述

    • 经过大半天的苦战,各处搜索,多方实战。终于让我发现了问题。

      • k8s就绪检查:在新的pod创建后,这是程序正在初始化,请求不应该转发到新的pod,并且旧pod的也不应该在新pod真正能接收请求前被删除。配置k8s的就绪探测器可以解决。
      • 删除pod前的优雅停机:旧的pod内部可能还有些请求未处理,有可能是个耗时操作,我们需要实现优雅停机,简单的利用睡眠,在删除pod前阻塞一点时间,给程序多一些时间处理未完成的请求。
    • 要解决前面的问题,我新建了yaml文件配置(spring-boot-kubernetes-deployment-rolling-publishing.yaml),配置如下。

    # 参考 https://www.jianshu.com/p/c63c9efaeac3
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-boot-kubernetes-deployment
    spec:
      replicas: 2
      strategy:
        rollingUpdate:
          maxSurge: 1 # 最大峰值用来指定可以创建的超出期望 Pod 个数的 Pod 数量。此值可以是绝对数(例如,5)或所需 Pods 的百分比(例如,10%)
          maxUnavailable: 0 #最大不可用  是一个可选字段,用来指定 更新过程中不可用的 Pod 的个数上限。该值可以是绝对数字(例如,5)也可以是所需 Pods 的百分比(例如,10%)
      selector:
        matchLabels:
          app: spring-boot-kubernetes-deployment
      template:
        metadata:
          labels:
            app: spring-boot-kubernetes-deployment
        spec:
          terminationGracePeriodSeconds: 300 #如果需要的优雅终止时间比较长 (preStop + 业务进程停止可能超过 30s),可根据实际情况自定义 terminationGracePeriodSeconds,避免过早的被 SIGKILL杀死,与下面preStop有关联,300属于总时间
          containers:
            - name: spring-boot-kubernetes
              image: a1030907690/centos_java:7.7.1908
              command: [ "java","-jar","spring-boot-kubernetes-0.0.1-SNAPSHOT.jar" ]
              imagePullPolicy: Never # 只使用本地镜像,防止ErrImagePull异常
              ports:
                - containerPort: 8080
              readinessProbe: #就绪探针
                httpGet:
                  path: /
                  port: 8080
                initialDelaySeconds: 50 #容器启动后要等待多少秒后才启动存活和就绪探测器, 默认是 0 秒,最小值是 0
                periodSeconds: 5  # 指定了 kubelet 应该每 5 秒执行一次存活探测。
                successThreshold: 1 #探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
                failureThreshold: 2 #当探测失败时,Kubernetes 的重试次数。 对存活探测而言,放弃就意味着重新启动容器。 对就绪探测而言,放弃意味着 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1
              env: # 解决Java程序时区问题
                - name: TZ
                  value: Asia/Shanghai
              volumeMounts:
                - name: config
                  mountPath: /config  # 应用配置文件路径
              lifecycle:
                preStop:
                  exec:
                    command: ["/bin/sh","-c","echo this pod is stopping. > /stop.log && sleep 90s"]
          volumes:
            - name: config
              configMap:
                name: spring-boot-kubernetes-conf  # 这个名字与创建时对应
                items:
                  - key: application.yml
                    path: application.yml
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    terminationGracePeriodSeconds 终止宽限期秒属于是一个总的时间控制,默认值是30s,如果不修改这个值,后面preStop钩子里sleep 90s是无效的。

    再次实战

    • 再创建deployment
    kubectl apply -f spring-boot-kubernetes-deployment-rolling-publishing.yaml 
    
    • 1
    • 滚动更新命令还是一样的。
    kubectl set image  deployment/spring-boot-kubernetes-deployment  spring-boot-kubernetes=a1030907690/centos_java:7.7.1909 --record
    
    • 1
    • 依旧来看Jmeter的结果,如下所示。

    在这里插入图片描述
    在这里插入图片描述

    • 这次滚动发布就没有问题了。实现了不宕机,无缝衔接。
      在这里插入图片描述
    • 删除pod的宽限期差不多是300秒,和terminationGracePeriodSeconds配置的差不多。
      在这里插入图片描述

    web管理后台我用的是kubesphere

    • 滚动更新其他命令(spring-boot-kubernetes-deployment是我的deployment)。

      • 查看滚动更新状态
      kubectl rollout status deployment/spring-boot-kubernetes-deployment
      
      • 1
      • 历史记录
      kubectl rollout history  deployment/spring-boot-kubernetes-deployment
      
      
      • 1
      • 2
      • 查看某个历史详情
      kubectl rollout history  deployment/spring-boot-kubernetes-deployment --revision=2
      
      • 1
      • 回滚(回到上次)
      kubectl rollout undo  deployment/spring-boot-kubernetes-deployment
      
      • 1
      • 回滚(回到指定版本)
      kubectl rollout undo deployment/spring-boot-kubernetes-deployment --to-revision=2
      
      • 1

    小结

    • k8s真的很强大,滚动发布很好用,只需要一些配置就可以了。理论上任何编程语言的程序都可以使用。为企业发布程序解决了大难题。
    • 不过滚动发布是不像灰度(金丝雀)发布那样可以细粒度控制流量,只有发布好的状态流量就会进来。所以要求生产发布的程序保证经过严密测试,如果到处bug,造成脏数据就尴尬了。

    参考

  • 相关阅读:
    设计模式: 模板方法模式
    分布式一致性协议 之 Paxos协议
    【Qt】桌面应用开发教程——布局|按钮组|容器|常用控件|消息事件机制
    Leetcode 951. 翻转等价二叉树
    css PC端弹窗时禁止底部页面滚动
    实时的语音降噪神经网络算法
    MongoDB升级步骤
    Multisim14 逻辑分析仪的使用教程(打开&关闭+详细具体)
    java计算机毕业设计教学质量评价系统源码+数据库+lw文档+系统
    2022亚太数学杯数学建模竞赛A题(思路分析......)
  • 原文地址:https://blog.csdn.net/baidu_19473529/article/details/125361620