Kubernetes零宕机发布应用

传统的单机应用发布方式会导致服务中断,用户请求会收到受影响,如果并发量较大用户体验就比较差,出了问题回退也慢。使用kubernetes零宕机发布,发布过程中服务和请求不会中断,平稳的更新版本,用户五感知,出现问题快速回。

1. Pod探针类型

探针是K8s对Pod内运行的应用的一种保护和预检监控机制,能够保障Pod和Pod内应用的正常运行。

1.1 StartupProbe(启动探针)

K8s 1.16版本后新增的探测方式,用于判断容器内应用程序是否已经启动。如果配置了StartupProbe,会先禁止其他探测,直到其成功为止。成功后将不再进行探测(只会探测一次),成功后交由其它探针进行探测。StartupProbe主要保护启动慢的应用,比如一些初始化时间较长的应用。

1.2 LivenessProbe(存活性探针)

存活性探针,用于探测容器是否运行,检测应用实例当前是否处于正常运行状态,如果不是,K8s会重启容器。

1.3 ReadinessProbe(就绪探针)

就绪性探针,用于探测容器内的程序是否健康,检测应用实例当前是否可以接收请求,如果不能,K8s不会转发流量。

1.4 钩子函数

Kubernetes在主容器的启动后和停止前提供了两个钩子函数:

  • postStart:容器创建后执行,如果失败会重启容器。
  • preStop:容器终止前执行,执行完成后容器将成功终止,在其完成前会阻塞删除容器的操作。

2. Pod探针的检测方式

Exec:在容器内执行一个命令,如果返回值为0,则认为容器健康。 TCPSocket:通过TCP连接检查容器内的端口是否通畅,如果通畅则认为容器健康。 HTTPGet:通过应用程序暴露的API地址检查程序是否正常,如果状态码为200~400之间,则认为容器健康。

2.1 探针检查参数配置

  • initialDelaySeconds: 60 # 初始化时间
  • timeoutSeconds: 2 # 超时时间
  • periodSeconds: 5 # 检测间隔
  • successThreshold: 1 # 检查成功1次表示就绪
  • failureThreshold: 2 # 检测失败2次表示未就绪

示例:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: carterwang/k8s-gin:1.0.0
    ports:
    - containerPort: 8080
    livenessProbe:
      exec:
        command:
          - cat
          - /tmp/healthy.txt
      initialDelaySeconds: 5
      periodSeconds: 2
      successThreshold: 1
    readinessProbe:
      httpGet:
        path: /ping
        port: 8080
      initialDelaySeconds: 10
      timeoutSeconds: 2
    lifecycle:
      postStart:
        exec:
          command: [ "/bin/sh", "-c", "echo start... > /tmp/healthy.txt" ]
      preStop:
        exec:
          command: [ "/bin/sh", "-c", "echo postStop... > /tmp/stop.txt" ]

进入容器查看:

kubectl exec -it my-pod sh

3. 滚动更新

滚动更新指的是在更新多副本的Deployment版本时,逐步创建新版本的Pod,逐步停止旧版本的Pod,以便应用一直处于可用状态。这个过程中,Service能够监视Pod的状态,将流量始终转发到可用的Pod上。

3.1 滚动更新

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pc-deployment
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
  replicas: 5
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.2

maxSurge:升级过程中存在的pod数可超过replicas的个数。(值高时,保持高可用性) maxUnavailable:升级过程中可用的pod数不能小于maxUnavailable值。(值高时,侧重快速更新)

推荐配置:maxSurge: 1 和 maxUnavailable: 0 通过命令行修改镜像版本:

kubectl set image deployment pc-deployment nginx=nginx:1.17.3

使用 watch 查看滚动更新的状态:

watch kubectl get pods -l app=nginx-pod

3.2 回滚版本

kubectl rollout:版本升级相关功能,支持以下选项:

status:显示当前升级状态

  kubectl rollout status deploy pc-deployment

history:显示升级历史记录

  kubectl rollout history deploy pc-deployment

undo:回滚到上一级版本(可以使用--to-revision回滚到指定版本)

  kubectl rollout undo deployment pc-deployment --to-revision=1

3.3 更新暂停与恢复

pause:暂停版本升级过程 resume:继续已暂停的版本升级过程 restart:重启版本升级过程

3.4 扩缩容

即控制Pod的数量,直接修改副本数即可。 方式一:

kubectl scale deploy pc-deployment --replicas=5

方式二:修改yaml文件中副本数,然后通过 kubectl apply -f 应用。

4. 金丝雀发布(灰度发布)

扩展知识:矿井中的金丝雀 17世纪,英国矿井工人发现金丝雀对瓦斯气体十分敏感。空气中哪怕有极微量的瓦斯气体,金丝雀也会停止唱歌;当瓦斯含量超过一定限度时,人类依旧毫无察觉,而金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为瓦斯检测工具,以便在危险情况下紧急撤离。 金丝雀发布步骤

  • 启动新版本应用:不直接将流量切过来,测试人员对新版本进行线上测试。
  • 导入少量流量:收集运行时数据,进行A/B测试。
  • 逐步导入更多流量:确认新版本运行良好后,逐步增加流量,直到切换到新版本。
  • 回滚:如果发现问题,立即将流量切回老版本。

老版本Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  strategy:
      type: RollingUpdate
      rollingUpdate:
        maxSurge: 1
        maxUnavailable: 0
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: nginx-port
      protocol: TCP
      port: 80
      nodePort: 32600
      targetPort: 80
  type: NodePort

方式一:

kubectl set image deployments nginx-deployment nginx=nginx:1.8.0 && kubectl rollout pause deployments nginx-deployment

继续全部更新:

kubectl rollout resume deployments nginx-deployment

方式二: 调整镜像版本,新建一个Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-canary
  labels:
    app: nginx
    track: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      track: canary
  template:
    metadata:
      labels:
        app: nginx
        track: canary
    spec:
      containers:
      - name: nginx
        image: nginx:1.8.0

Service的LabelSelector 是 app: nginx,由nginx-deployment和nginx-deployment-canary创建的Pod都带有标签app: nginx,所以Service的流量将会在两个release之间分配。 在新旧版本之间,流量分配的比例为两个版本副本数的比例,此处为1:3。

4.1 基于权重的流量切分

nginx.ingress.kubernetes.io/canary-weight

基于服务权重的流量切分,适用于蓝绿部署,权重范围0-100按百分比将请求路由到Canary Ingress中指定的服务。权重为0意味着该金丝雀规则不会向Canary入口的服务发送任何请求。权重为100意味着所有请求都将被发送到Canary入口。

for i in {1..20}; do curl demo.nginx.test; done;

4.2 基于header的流量切分

nginx.ingress.kubernetes.io/canary-by-header:

基于Request Header的流量切分,适用于灰度发布以及A/B测试。当Request Header设置为"key:value"(自定义)时,请求将会被一直发送到Canary版本;对于任何其他Header值,将忽略Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。 curl demo.nginx.test -H "region: zh"

4.3 基于Cookie的流量切分

nginx.ingress.kubernetes.io/canary-by-cookie

基于Cookie的流量切分,适用于灰度发布与A/B测试。用于通知Ingress将请求路由到Canary Ingress中指定的服务的cookie。当cookie值设置为always时,它将被路由到Canary入口; 当cookie值设置为never时,请求不会被发送到Canary入口;对于任何其他值,将忽略cookie并将请求与其他金丝雀规则进行优先级的比较。

curl demo.nginx.test --cookie "user_from_bj=always"

金丝雀规则优先顺序 canary-by-header canary-by-cookie canary-weight

示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-v1
  labels:
    app: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: carterwang/k8s-gin:1.0.0
          ports:
            - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-v2
  labels:
    app: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: carterwang/k8s-gin:2.0.0
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: nginx-port
      protocol: TCP
      port: 80
      nodePort: 80
      targetPort: 8080
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-v2
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: nginx-port
      protocol: TCP
      port: 80
      nodePort: 81
      targetPort: 8080
  type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress-v1
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: demo.nginx.test
      http:
        paths:
          - path: /
            backend:
              serviceName: nginx-service
              servicePort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress-v2
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "50"
    #nginx.ingress.kubernetes.io/canary-by-header: "region"
    #nginx.ingress.kubernetes.io/canary-by-header-value: "zh"
    #nginx.ingress.kubernetes.io/canary-by-cookie: "canary-cookie"
spec:
  rules:
    - host: demo.nginx.test
      http:
        paths:
          - path: /
            backend:
              serviceName: nginx-service-v2
              servicePort: 80

5. 蓝绿发布

蓝绿发布指的是一次只有一个版本处于活动状态。在创建和测试绿色部署时,流量被路由到蓝色部署。完成测试后,我们将流量路由到新版本,遇到问题时全部回滚。 正常时:

遇到异常时:

缺点: 成本高昂,因为它需要双倍的资源。

打 赏