在介绍这一篇的内容之前,我觉得有必要复习一下前面一篇文章:Kubernetes 入门五:Replicas,然后联系第五篇中的内容,试想一下该如何实现后面介绍的几种部署升级方式。

本文要介绍的是 Deployment,故名思议,就是介绍如何将 Pod 运行在 Kubernetes 的一种技术,因为是部署,所以我想先介绍一下一些在日常使用中常见的部署(升级)方式:

常见的部署方式

蓝绿部署

蓝绿部署是一种比较常见的部署方式,经常会用于新旧版本不兼容的情况,具体的操作过程为:

  1. 先将新版本(绿版)的服务运行起来
  2. 将流量都切换到新版本的服务上
  3. 删除旧版本(蓝版)的服务

这种部署方式可能以我们现在的了解量还是比较容易实现的:

  1. 创建新版本的 ReplicaSet,部署它
  2. 将 Service 指向新的 Pods
  3. 删除旧版本的 ReplicaSet

滚动升级

蓝绿部署可以有效的保证业务的正确性,但是也带来了一些风险,例如稳定性,假设新部署的应用是有问题的,一切换之后就会导致业务崩溃了,造成一些损失,于是就有了一种稍微友好一些的升级方式,滚动升级:

  1. 先关闭旧版本的一个实例
  2. 开启新版本的一个实例用于替换旧版本
  3. 替换成功之后循环 1 和 2,直到所有的实例升级完

在这个过程中,如果中途发现异常可以即时停手,即时止损,而且 Kubernetes 也在客户端 kubectl 中支持了这个特性:kubectl rolling-update,但是有两个弊端:

金丝雀发布

金丝雀发布是滚动发布的一种特例,在滚动发布中,滚动的车轮是匍匐前进的,不会等待,如果中间出错了。但是,有的时候,我们并不想要全都升级,可能出于一些 POC 的原因,我们就希望只有部分实例是新的,大部分都是旧的,而这种情形,我们就称之为金丝雀发布:

  1. 升级少部分实例
  2. 查看效果(业务/技术效果),如果好,全部升级
  3. 如果不好,则不升级

声明式升级

前面介绍的这些方式在非 Kubernetes 上或者在 Kubernetes 上很多时候都是半手工方式执行的,而 Kubernets 作为一款 DevOPS 友好的系统,已经内置了对于部署方式的一种资源抽象,这个资源就是:Deployment,它的工作原理为:

Deployment 存在的意义为:在升级应用程序时,需要引入一个额外的 ReplicaSet,并协调新旧两个 RS,使他们再根据彼此不断修改,而不会造成干扰,而 Deployment 将这些运维过程都代码化,内置为自己的业务逻辑,从而让升级部署过程变得简单。

我这里就演示一下如何通过 Deployment 进行升级过程的操作,这里就先通过 Deployment 创建几个运行的实例:

  1. [root@liqiang.io]# cat 00-deployment-v1.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: rest-deployment
  6. labels:
  7. app: chap06-demo-app
  8. spec:
  9. replicas: 3
  10. selector:
  11. matchLabels:
  12. app: chap06-demo-app
  13. template:
  14. metadata:
  15. labels:
  16. app: chap06-demo-app
  17. spec:
  18. containers:
  19. - name: chap06-demo-app
  20. image: lukelau/rest-docker:0.0.1
  21. ports:
  22. - containerPort: 8080
  23. args:
  24. - -server.addr=0.0.0.0:8080
  25. [root@liqiang.io]# kubectl apply -f 00-deployment-v1.yaml
  26. [root@liqiang.io]# kubectl get pods
  27. NAME READY STATUS RESTARTS AGE
  28. rest-deployment-576676486f-jf49v 1/1 Running 0 10s
  29. rest-deployment-576676486f-l5k95 1/1 Running 0 10s
  30. rest-deployment-576676486f-p9vm6 1/1 Running 0 10s

然后创建第一个 v2 的版本的:

  1. [root@liqiang.io]# cat 01-deployment-v2.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: rest-deployment
  6. labels:
  7. app: chap06-demo-app
  8. spec:
  9. replicas: 3
  10. selector:
  11. matchLabels:
  12. app: chap06-demo-app
  13. template:
  14. metadata:
  15. labels:
  16. app: chap06-demo-app
  17. spec:
  18. containers:
  19. - name: chap06-demo-app
  20. image: lukelau/rest-docker:0.0.2
  21. ports:
  22. - containerPort: 8080
  23. args:
  24. - -server.addr=0.0.0.0:8080
  25. [root@liqiang.io]# kubectl apply -f 01-deployment-v2.yaml --record
  26. [root@liqiang.io]# kubectl get pods
  27. NAME READY STATUS RESTARTS AGE
  28. rest-deployment-56bc74bf46-4vjz2 1/1 Running 0 10s
  29. rest-deployment-56bc74bf46-kt8kr 1/1 Running 0 13s
  30. rest-deployment-56bc74bf46-xk5jt 1/1 Running 0 12s
  31. rest-deployment-576676486f-nlng8 0/1 Terminating 0 32s

可以发现旧版本的 Pods 都被关掉了,然后创建出新版本的 Pods。通过 describe deployment 我们可以看到它的升级过程:

  1. [root@liqiang.io]# kubectl describe deployment
  2. ... ...
  3. Type Reason Age From Message
  4. ---- ------ ---- ---- -------
  5. Normal ScalingReplicaSet 5m18s deployment-controller Scaled up replica set rest-deployment-576676486f to 3
  6. Normal ScalingReplicaSet 4m7s deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 1
  7. Normal ScalingReplicaSet 4m deployment-controller Scaled down replica set rest-deployment-576676486f to 2
  8. Normal ScalingReplicaSet 3m58s deployment-controller Scaled down replica set rest-deployment-576676486f to 1
  9. Normal ScalingReplicaSet 2m44s deployment-controller Scaled up replica set rest-deployment-576676486f to 1
  10. Normal ScalingReplicaSet 2m43s deployment-controller Scaled down replica set rest-deployment-56bc74bf46 to 2
  11. Normal ScalingReplicaSet 2m23s (x2 over 4m) deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 2
  12. Normal ScalingReplicaSet 2m21s (x2 over 3m58s) deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 3
  13. Normal ScalingReplicaSet 2m21s (x7 over 2m43s) deployment-controller (combined from similar events): Scaled down replica set rest-deployment-576676486f to 1
  14. Normal ScalingReplicaSet 2m18s (x2 over 3m56s) deployment-controller Scaled down replica set rest-deployment-576676486f to 0

同样得,还可以查看 Deployment 的执行历史,这些都是通过 --record 参数记录的:

  1. [root@liqiang.io]# kubectl rollout history deployment rest-deployment
  2. deployment.extensions/rest-deployment
  3. REVISION CHANGE-CAUSE
  4. 3 kubectl apply --filename=00-deployment-v1.yaml --record=true
  5. 4 kubectl apply --filename=01-deployment-v2.yaml --record=true

但是,因为刚才的升级过程太快了,我们没有看清楚,所以这里我们可以尝试一下 Deployment 的控制升级速率:

  1. [root@liqiang.io]# cat 02-deployment-speed-limit.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: rest-deployment
  6. labels:
  7. app: chap06-demo-app
  8. spec:
  9. replicas: 3
  10. minReadySeconds: 10
  11. strategy:
  12. type: RollingUpdate
  13. rollingUpdate:
  14. maxSurge: 1
  15. maxUnavailable: 0
  16. selector:
  17. matchLabels:
  18. app: chap06-demo-app
  19. template:
  20. metadata:
  21. labels:
  22. app: chap06-demo-app
  23. spec:
  24. containers:
  25. - name: chap06-demo-app
  26. image: lukelau/rest-docker:0.0.3
  27. ports:
  28. - containerPort: 8080
  29. args:
  30. - -server.addr=0.0.0.0:8080
  31. [root@liqiang.io]# kubectl get pods
  32. NAME READY STATUS RESTARTS AGE
  33. rest-deployment-546f7b5dcf-l2c97 1/1 Running 0 19s
  34. rest-deployment-546f7b5dcf-lqklg 0/1 ContainerCreating 0 2s
  35. rest-deployment-56bc74bf46-kt8kr 1/1 Running 0 11m
  36. rest-deployment-56bc74bf46-xk5jt 1/1 Running 0 11m

这下情况就变得不一样的,我们可以发现升级过程慢下来了,然后可以观察到整个升级过程,甚至于我们都可以停止升级过程:

  1. [root@liqiang.io]# kubectl rollout pause deployment rest-deployment
  2. deployment.extensions/rest-deployment paused

然后可以恢复升级,或者取消升级:

  1. [root@liqiang.io]# kubectl rollout resume deployment rest-deployment
  2. [root@liqiang.io]# kubectl rollout undo deployment rest-deployment

Reference