在介绍这一篇的内容之前,我觉得有必要复习一下前面一篇文章:Kubernetes 入门五:Replicas,然后联系第五篇中的内容,试想一下该如何实现后面介绍的几种部署升级方式。
本文要介绍的是 Deployment,故名思议,就是介绍如何将 Pod 运行在 Kubernetes 的一种技术,因为是部署,所以我想先介绍一下一些在日常使用中常见的部署(升级)方式:
常见的部署方式
蓝绿部署
蓝绿部署是一种比较常见的部署方式,经常会用于新旧版本不兼容的情况,具体的操作过程为:
- 先将新版本(绿版)的服务运行起来
- 将流量都切换到新版本的服务上
- 删除旧版本(蓝版)的服务
这种部署方式可能以我们现在的了解量还是比较容易实现的:
- 创建新版本的 ReplicaSet,部署它
- 将 Service 指向新的 Pods
- 删除旧版本的 ReplicaSet
滚动升级
蓝绿部署可以有效的保证业务的正确性,但是也带来了一些风险,例如稳定性,假设新部署的应用是有问题的,一切换之后就会导致业务崩溃了,造成一些损失,于是就有了一种稍微友好一些的升级方式,滚动升级:
- 先关闭旧版本的一个实例
- 开启新版本的一个实例用于替换旧版本
- 替换成功之后循环 1 和 2,直到所有的实例升级完
在这个过程中,如果中途发现异常可以即时停手,即时止损,而且 Kubernetes 也在客户端 kubectl 中支持了这个特性:kubectl rolling-update
,但是有两个弊端:
- 升级前后的 RC 的 Selector 都被改变了。
kubectl rolling-update
滚动升级不好,因为操作都是在客户端执行的。
金丝雀发布
金丝雀发布是滚动发布的一种特例,在滚动发布中,滚动的车轮是匍匐前进的,不会等待,如果中间出错了。但是,有的时候,我们并不想要全都升级,可能出于一些 POC 的原因,我们就希望只有部分实例是新的,大部分都是旧的,而这种情形,我们就称之为金丝雀发布:
- 升级少部分实例
- 查看效果(业务/技术效果),如果好,全部升级
- 如果不好,则不升级
声明式升级
前面介绍的这些方式在非 Kubernetes 上或者在 Kubernetes 上很多时候都是半手工方式执行的,而 Kubernets 作为一款 DevOPS 友好的系统,已经内置了对于部署方式的一种资源抽象,这个资源就是:Deployment,它的工作原理为:
Deployment 存在的意义为:在升级应用程序时,需要引入一个额外的 ReplicaSet,并协调新旧两个 RS,使他们再根据彼此不断修改,而不会造成干扰,而 Deployment 将这些运维过程都代码化,内置为自己的业务逻辑,从而让升级部署过程变得简单。
我这里就演示一下如何通过 Deployment 进行升级过程的操作,这里就先通过 Deployment 创建几个运行的实例:
[root@liqiang.io]# cat 00-deployment-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rest-deployment
labels:
app: chap06-demo-app
spec:
replicas: 3
selector:
matchLabels:
app: chap06-demo-app
template:
metadata:
labels:
app: chap06-demo-app
spec:
containers:
- name: chap06-demo-app
image: lukelau/rest-docker:0.0.1
ports:
- containerPort: 8080
args:
- -server.addr=0.0.0.0:8080
[root@liqiang.io]# kubectl apply -f 00-deployment-v1.yaml
[root@liqiang.io]# kubectl get pods
NAME READY STATUS RESTARTS AGE
rest-deployment-576676486f-jf49v 1/1 Running 0 10s
rest-deployment-576676486f-l5k95 1/1 Running 0 10s
rest-deployment-576676486f-p9vm6 1/1 Running 0 10s
然后创建第一个 v2 的版本的:
[root@liqiang.io]# cat 01-deployment-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rest-deployment
labels:
app: chap06-demo-app
spec:
replicas: 3
selector:
matchLabels:
app: chap06-demo-app
template:
metadata:
labels:
app: chap06-demo-app
spec:
containers:
- name: chap06-demo-app
image: lukelau/rest-docker:0.0.2
ports:
- containerPort: 8080
args:
- -server.addr=0.0.0.0:8080
[root@liqiang.io]# kubectl apply -f 01-deployment-v2.yaml --record
[root@liqiang.io]# kubectl get pods
NAME READY STATUS RESTARTS AGE
rest-deployment-56bc74bf46-4vjz2 1/1 Running 0 10s
rest-deployment-56bc74bf46-kt8kr 1/1 Running 0 13s
rest-deployment-56bc74bf46-xk5jt 1/1 Running 0 12s
rest-deployment-576676486f-nlng8 0/1 Terminating 0 32s
可以发现旧版本的 Pods 都被关掉了,然后创建出新版本的 Pods。通过 describe deployment 我们可以看到它的升级过程:
[root@liqiang.io]# kubectl describe deployment
... ...
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m18s deployment-controller Scaled up replica set rest-deployment-576676486f to 3
Normal ScalingReplicaSet 4m7s deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 1
Normal ScalingReplicaSet 4m deployment-controller Scaled down replica set rest-deployment-576676486f to 2
Normal ScalingReplicaSet 3m58s deployment-controller Scaled down replica set rest-deployment-576676486f to 1
Normal ScalingReplicaSet 2m44s deployment-controller Scaled up replica set rest-deployment-576676486f to 1
Normal ScalingReplicaSet 2m43s deployment-controller Scaled down replica set rest-deployment-56bc74bf46 to 2
Normal ScalingReplicaSet 2m23s (x2 over 4m) deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 2
Normal ScalingReplicaSet 2m21s (x2 over 3m58s) deployment-controller Scaled up replica set rest-deployment-56bc74bf46 to 3
Normal ScalingReplicaSet 2m21s (x7 over 2m43s) deployment-controller (combined from similar events): Scaled down replica set rest-deployment-576676486f to 1
Normal ScalingReplicaSet 2m18s (x2 over 3m56s) deployment-controller Scaled down replica set rest-deployment-576676486f to 0
同样得,还可以查看 Deployment 的执行历史,这些都是通过 --record
参数记录的:
[root@liqiang.io]# kubectl rollout history deployment rest-deployment
deployment.extensions/rest-deployment
REVISION CHANGE-CAUSE
3 kubectl apply --filename=00-deployment-v1.yaml --record=true
4 kubectl apply --filename=01-deployment-v2.yaml --record=true
但是,因为刚才的升级过程太快了,我们没有看清楚,所以这里我们可以尝试一下 Deployment 的控制升级速率:
[root@liqiang.io]# cat 02-deployment-speed-limit.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rest-deployment
labels:
app: chap06-demo-app
spec:
replicas: 3
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: chap06-demo-app
template:
metadata:
labels:
app: chap06-demo-app
spec:
containers:
- name: chap06-demo-app
image: lukelau/rest-docker:0.0.3
ports:
- containerPort: 8080
args:
- -server.addr=0.0.0.0:8080
[root@liqiang.io]# kubectl get pods
NAME READY STATUS RESTARTS AGE
rest-deployment-546f7b5dcf-l2c97 1/1 Running 0 19s
rest-deployment-546f7b5dcf-lqklg 0/1 ContainerCreating 0 2s
rest-deployment-56bc74bf46-kt8kr 1/1 Running 0 11m
rest-deployment-56bc74bf46-xk5jt 1/1 Running 0 11m
这下情况就变得不一样的,我们可以发现升级过程慢下来了,然后可以观察到整个升级过程,甚至于我们都可以停止升级过程:
[root@liqiang.io]# kubectl rollout pause deployment rest-deployment
deployment.extensions/rest-deployment paused
然后可以恢复升级,或者取消升级:
[root@liqiang.io]# kubectl rollout resume deployment rest-deployment
[root@liqiang.io]# kubectl rollout undo deployment rest-deployment