概述

本文核心问题是:如何升级应用。
对于Pod的更新有两种策略:

  • 一是删除全部旧Pod之后再创建新Pod。好处是,同一时间只会有一个版本的应用存在;缺点是,应用有一段时间不可用。
  • 二是先创建新Pod,再删除旧Pod。可以一次性创建全部,再删除全部,也可以逐渐创建删除。好处是应用一直可用,缺点是要同时支持两个版本。

蓝绿部署

对于应用的版本v1和版本v2:

  • 在运行v1前,流量一直都在v2上
  • 部署v1,然后测试通过后,将流量切换到v2,v2就成为了新的生产环境
  • 一旦v2出现问题,可以在切回v1

金丝雀部署(也称灰度部署)

金丝雀部署一种增量发布,先是在小范围内发布,然后观察测试,如无问题逐渐发布全部。

kubectl rolling-update

因为kubectl rolling-update的方式已经过时,所以只是做一下简介。
假设现在有一个名为test-v1,Pod选择器为app=order的ReplicationController要升级为test-v2,则执行下面命令可升级:

k rolling-update test-v1 test-v2 --image=test:v2

运行此命令后:

  1. 立刻创建一个名为test-v2的ReplicationController,他的Pod模板镜像正是test:v2,并添加一个标签deployment=xxxx
  2. test-v1以及app=order选中的Pod都会被加上一个标签:deployment=yyyy。如此做法是防止Pod的管理混乱。
  3. 先将test-v2的Pod扩展为1,使用更新后的模板创建新Pod;再将test-v1缩小1,如此循环。
  4. 因为在滚动过程中Service的标签选择器一直是app=order,所以新老版本都会接收到流量。

过时的原因是:伸缩的请求时由kubectl发起的,如果因为任何原因丢失了网络连接,升级将处于中间状态。另一个原因是:期望只修改Pod定义中的镜像tag,就能时Kubernetes运行升级工作

使用Deployment声明式的升级

Deployment是一种更高阶的资源,用于部署程序并以声明的方式升级应用,而不是通过ReplicationController或ReplicaSet进行部署。

在使用Deployment时,Pod是由Deployment的ReplicaSet创建的。

准备镜像

将之前的文章(Kubernetes学习笔记(四):服务)里的拿过来做一下微小的改动,生成两个镜像。改动内容就是在输出内容加上版本号。

fmt.Fprintf(w,"this is v1, hostname: %v\n",hostname)
docker push registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v1

fmt.Fprintf(w,"this is v2, hostname: %v\n",hostname)
docker push registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v2

创建Deployment

Deployment与ReplicaSet的配置相似,都含有标签选择器、副本数量和Pod模板。此外Deployment还会包含一个部署策略。

定义Service

定义了一个NodePort类型的Service

# goweb-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: goweb
spec:
  type: NodePort
  selector:
    app: goweb
  ports:
    - port: 80
      targetPort: 8000
      nodePort: 31234

定义Deployment

# goweb-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goweb
spec:
  replicas: 3
  selector:
    matchLabels:
      app: goweb
  template:
    metadata:
      labels:
        app: goweb
    spec:
      containers:
      - name: goweb
        image: registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v1

运行查看

可以看到名为goweb-fdfcfdcc6的RS,有个label是pod-template-hash=fdfcfdcc6,顾名思义,fdfcfdcc6就是pod模板的hash值。

创建Deployment时指定 --record 记录历史版本号,非常有用

-> [root@kube0.vm] [~] k create -f goweb-svc.yaml
service/goweb created

-> [root@kube0.vm] [~] k create -f goweb-deployment.yaml --record

deployment.apps/goweb created
-> [root@kube0.vm] [~] k get all -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
pod/goweb-fdfcfdcc6-4wklw   1/1     Running   0          9s    10.244.2.37   kube2.vm   <none>           <none>
pod/goweb-fdfcfdcc6-bw8c4   1/1     Running   0          9s    10.244.2.36   kube2.vm   <none>           <none>
pod/goweb-fdfcfdcc6-xjcwf   1/1     Running   0          9s    10.244.1.33   kube1.vm   <none>           <none>

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service/goweb        NodePort    10.100.193.94   <none>        80:31234/TCP   28s   app=goweb
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        54s   <none>

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES                                            SELECTOR
deployment.apps/goweb   3/3     3            3           9s    goweb        registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v1   app=goweb

NAME                              DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES                                            SELECTOR
replicaset.apps/goweb-fdfcfdcc6   3         3         3       9s    goweb        registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v1   app=goweb,pod-template-hash=fdfcfdcc6

升级Deployment

只要修改Deployment的Pod模板定义,Kubernetes会自动的将实际状态收敛为修改后的状态。对于升级,只需要修改Pod中镜像的tag。

升级策略由deployment.spec.strategy.type定义,值是Recreate或RollingUpdate,默认RollingUpdate。

kubectl patch及minReadySeconds

使用kubectl patch定义deployment.spec.minReadySeconds来减慢滚动升级时间,以便观察升级过程

-> [root@kube0.vm] [~] k patch deployment goweb -p '{"spec":{"minReadySeconds":5}}'
deployment.apps/goweb patched

循环请求服务

在执行升级前新开窗口,运行下面的命令查看输出

-> [root@kube0.vm] [~] while true; do curl http://192.168.199.231:31234/ ; sleep 1; done
this is v1, hostname: goweb-fdfcfdcc6-pw7b4
this is v1, hostname: goweb-fdfcfdcc6-pw7b4
this is v1, hostname: goweb-fdfcfdcc6-x8n7h
this is v1, hostname: goweb-fdfcfdcc6-pw7b4
this is v1, hostname: goweb-fdfcfdcc6-x8n7h
this is v1, hostname: goweb-fdfcfdcc6-j4mz8
this is v1, hostname: goweb-fdfcfdcc6-j4mz8
# 以上是升级之前的输出、以下是开始升级后的
this is v1, hostname: goweb-fdfcfdcc6-x8n7h
this is v1, hostname: goweb-fdfcfdcc6-pw7b4
this is v1, hostname: goweb-fdfcfdcc6-pw7b4
this is v1, hostname: goweb-fdfcfdcc6-j4mz8
this is v2, hostname: goweb-65cc575865-25988
this is v2, hostname: goweb-65cc575865-25988
this is v1, hostname: goweb-fdfcfdcc6-x8n7h
this is v1, hostname: goweb-fdfcfdcc6-j4mz8
this is v2, hostname: goweb-65cc575865-bfd98
this is v2, hostname: goweb-65cc575865-bfd98
this is v2, hostname: goweb-65cc575865-25988
this is v2, hostname: goweb-65cc575865-25988
this is v2, hostname: goweb-65cc575865-25988
# 这之后就是升级完成了

kubectl set

使用 kubectl set image 更新任何包含容器资源的镜像。

-> [root@kube0.vm] [~] k set image deployment goweb goweb=registry.cn-hangzhou.aliyuncs.com/orzi/goweb:v2
deployment.apps/goweb image updated

kubectl rollout

查看升级状态信息。执行完kubectl set image,立刻执行下面的命令。手速得快,不然赶不上热乎的。

-> [root@kube0.vm] [~] k rollout status deployment goweb
Waiting for deployment "goweb" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "goweb" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "goweb" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "goweb" rollout to finish: 1 old replicas are pending termination...
deployment "goweb" successfully rolled out

修改Deployment或其他资源的方式

回滚

使用kubectl rollout undo 回滚到上一个版本

-> [root@kube0.vm] [~] k rollout undo deployment goweb
deployment.apps/goweb rolled back

使用 kubectl rollout history 查看版本记录

-> [root@kube0.vm] [~] k rollout history deployment goweb
deployment.apps/goweb
REVISION  CHANGE-CAUSE
6         kubectl create --filename=goweb-deployment.yaml --record=true
7         kubectl create --filename=goweb-deployment.yaml --record=true

回滚到特定版本

-> [root@kube0.vm] [~] k rollout undo deployment goweb --to-revision=5
error: unable to find specified revision 5 in history

-> [root@kube0.vm] [~] k rollout undo deployment goweb --to-revision=7
deployment.apps/goweb skipped rollback (current template already matches revision 7)

-> [root@kube0.vm] [~] k rollout undo deployment goweb --to-revision=6
deployment.apps/goweb rolled back

通过deployment.spec.revisionHistoryLimit指定历史版本个数,默认为2。也就是当前和上一个版本。

控制滚动升级速率

deployment.spec.strategy.rollingUpdate下有两个字段,用来控制升级速率

  • maxSurge:超出期望副本数的Pod实例的比例或个数。默认25%,转换成绝对值后四舍五入,也可以直接指定为绝对值。
  • maxUnavailable:滚动升级时,最多允许有多少实例不可用,默认25%,转换成绝对值后四舍五入,也可以直接指定为绝对值。

暂停、恢复升级

  • 使用kubectl rollout pause暂停升级
  • 使用kubectl rollout resume取消暂停

阻止出错版本的滚动升级

  • minReadySeconds:指定新创建的Pod至少要运行多久才视为可用。
  • 配置就绪探针
  • 为滚动升级配置progressDeadlineSeconds

小结

  • kubectl rolling-update 过时的原因:伸缩的请求时由kubectl发起的,如果因为任何原因丢失了网络连接,升级将处于中间状态
  • Deployment是一种更高阶的资源,用于部署程序并以声明的方式升级应用,而不是通过ReplicationController或ReplicaSet进行部署。
  • 创建Deployment时指定 --record 记录历史版本号
  • Deployment下的ReplicaSet命名是DeploymentName+Pod模板Hash,而ReplicaSet下的Pod是在此基础拼接个随机字符串。
  • 升级策略由deployment.spec.strategy.type定义,值是Recreate或RollingUpdate,默认RollingUpdate。
  • 定义deployment.spec.minReadySeconds来减慢滚动升级时间
  • maxSurge与maxUnavailable控制滚动升级速率
  • 命令:patch、set、rollout
  • kubectl rollout:status、undo、pause、resume、history。
05-27 00:48