一、简介
Deployment实现了Kubernetes项目中非常重要的功能:
(1)、水平扩展
(2)、水平收缩
比如更新了Deployment的Pod模板,比如修改了镜像版本,那么Deployment就会遵循滚动更新(rolling update)的方式来升级现有的容器 。这个操作依赖Kubernetes中一个非常重要的API对象:ReplicaSet(详情见3.2)。不过Deployment又在ReplicaSet之上又做了新的封装,其新特性有如下几点:
(1)、Deployment具有ReplicaSet的全部功能;
(2)、可以查看Deployment升级详细状态和事件;
(3)、当升级出现问题时,可以使用回滚操作回滚到之前的任一版本;
(4)、新增版本记录,每一次操作Deployment都会记录下来,这也是版本回滚的基础;
(5)、对每一次升级,都能进行暂停和启动;
从上面可以知道,Deployment已经具体ReplicaSet的全部功能,并且还有许多新的功能,所以推荐使用Deployment来管理Pod。
从上图可以看到Deployment、ReplicaSet、Pod它们以层层控制关系,Deployment可以拥有多个ReplicaSet,一个ReplicaSet可以拥有多个Pod。一个Deployment拥有多个ReplicaSet主要是为了支持回滚操作,每当操作Deployment的时候,就会生成一个新的ReplicaSet,然后逐步更新新的Pod,而老的ReplicaSet会逐步减少Pod直到新的ReplicaSet全部接管。这时候并不会删除老的ReplicaSet,系统会将其保存下来,以备回滚使用。
ReplicaSet还负责通过”控制器模式”,保证系统的Pod数永远等于期望数,这也是Deployment只允许restartPolicy=Always的原因:只有在容器能保证自己始终处于running状态,通过ReplicaSet调整Pod的数量才有意义。
而在此基础上,Deployment同样通过”控制器模式”,来操作ReplicaSet的个数和属性,进而实现水平扩展/收缩和滚动更新这两个动作。其中水平扩展和收缩非常容易实现,Deployment Controller只需要修改它的ReplicaSet的Pod副本数就可以了。
二、水平扩展/收缩
我们看下面一个Deployment例子:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
然后我们创建这个Deployment。
补充字段说明:
(1)、READY:用户期望的Pod个数;
(2)、UP-TO-DATE:当前处于最新版本的Pod个数;
(3)、AVAILABLE:当前已经可用的Pod数,也就是处于running状态并且是最新的版本;
从上我们看到Deployment和Pod都正常启动并且数量和期望的一致,现在我们使用kubectl scale来做水平扩展收缩。
(1)、扩展
(2)、收缩
当然,除了使用命令的方式来做水平扩展/收缩以外,还可以直接编辑其配置文件,使用kubectl edit命令,如下:
然后保存退出,就可以看到如下扩展已经OK了,收缩是一样的操作:
除了上面两种方法,还可以直接编辑我们原始的YAML文件,然后使用kubectl apply -f nginx-deployment.yaml来做扩展和收缩,甚至可以用kubectl patch来打补丁,其内容用JSON语法。
三、滚动更新/回滚
3.1、滚动更新
上面介绍了水平扩展和收缩,下面来介绍一下滚动更新和回滚操作。
我们还是以上面的YAML文件为例,首先,我们创建这个YAML文件,这一次我们在创建的命令上加一个—record,它的作用是记录每次我们操作所执行的命令,方便后面操作。
[root@master deployment]# kubectl apply -f nginx-deployment.yaml --record
deployment.apps/nginx-deployment configured
然后我们来查看集群状态:
[root@master deployment]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 6h6m
我们可以通过kubectl rollout status 来查看Deployment对象的状态变化,如下:
[root@master deployment]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@master deployment]#
我们可以查看Deployment的ReplicaSet:
[root@master deployment]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 3 3 3 54m
然后我们修改Pod的模板做滚动更新,修改Pod模板的方式也有很多,可以直接kubectl edit 修改配置文件,也可以修改源文件,然后使用kubectl apply -f 来使配置生效,我们这里采用kubectl edit来直接用来配置文件,这种修改保存退出就会立即生效,如下:
我们把镜像版本更新为1.8然后保存退出。然后使用kubectl rollout status来查看Deployment的变化情况:
[root@master ~]# kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
然后我们可以看到会启动一个新的ReplicaSet,来使用这个新的Pod模板来创建新的副本,然后会从老的ReplicaSet删除老的副本,如此进行直到版本更新完成。像这样将一个集群中正在运行的多个Pod版本,交替进行升级的过程叫做滚动更新。
我们可以通过kubectl get rs查看新旧两个ReplicaSet的状态:
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 0 0 0 67m
nginx-deployment-6f655f5d99 3 3 3 7h2m
这种滚动更新的好处是:如果在更新过程中,新版本Pod有问题,那么滚动更新就会停止,这时候运维和开发就可以介入查看其原因,由于应用本身还有两个旧版本的Pod在线,所以并不会对服务造成太大的影响;当然,这时候应在Pod中加上health check检查应用的健康状态,而不是简单的依赖容器的running状态。为了进一步保证服务的延续性,Deployment Controller还会确保在任何时间窗口内,只有指定比例的Pod处于离线状态,同时它也会确保在任何时间窗口内,只有指定比例的Pod被创建,这个比例默认是DESIRED的25%。
当然可以通过修改Deployment对象的一个字段RollingUpdateStrategy来自定义,比如:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
...
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
说明:
(1)、maxSurge:定义除了DESIRED数量之外,在一次滚动更新过程中,Deployment还可以创建多少Pod;
(2)、maxUnavailable:定义在一次滚动更新过程中,Deployment最多可以删除多少Pod;
另外,这两个配置还可以通过设置百分值来表示。
如此,我们可以得到如下关系图:
Deployment实际控制的是ReplicaSet的数目以及每个ReplicaSet的属性。而一个应用版本,对应的就是一个ReplicaSet,而这个版本应有的Pod数量,是通过ReplicaSet自己的控制器来管理。
3.2、回滚
从上面我们明白了应用版本和ReplicaSet的对应关系,下面就来介绍一下它是如何回滚的。
现在我们来更改Pod模板中的镜像版本信息,上面介绍了直接修改配置文件的方法来修改,这次用kubectl set image命令来修改。
如下,我们修改一个不存在的nginx版本,故意制造故障。
[root@master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.999
deployment.extensions/nginx-deployment image updated
然后我们来查看ReplicaSet的状态:
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 0 0 0 103m
nginx-deployment-6f655f5d99 3 3 3 7h37m
nginx-deployment-79c5b65fdb 1 1 0 41s
我们可以看到新创建了一个ReplicaSet,其中READY状态为0,这是因为我们这个镜像并不存在,所以就无法更新。
我们可以通过一下方法进行回滚:
(1)、直接回滚到上一个版本,我们执行kubectl rollout undo命令,如下:
[root@master ~]# kubectl rollout undo deployment/nginx-deployment
deployment.extensions/nginx-deployment rolled back
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 0 0 0 106m
nginx-deployment-6f655f5d99 3 3 3 7h41m
nginx-deployment-79c5b65fdb 0 0 0 3m59s
这时候通过查看ReplicaSet的状态,可以看到刚新创建的ReplicaSet的Pod数收缩为0了。
(2)、我们通过查看历史版本,恢复到任意版本,我们通过kubectl rollout history命令,如下:
[root@master ~]# kubectl rollout history deployment/nginx-deployment
deployment.extensions/nginx-deployment
REVISION CHANGE-CAUSE
2 kubectl apply --filename=nginx-deployment.yaml --record=true
4 kubectl apply --filename=nginx-deployment.yaml --record=true
5 kubectl apply --filename=nginx-deployment.yaml --record=true
然后选择我们可以查看对应版本的详细信息:
kubectl rollout history deployment/nginx-deployment --revision=2
确定是我们需要的版本后就可以执行如下命令进行回滚操作:
[root@master ~]# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment rolled back
这时我们可以看到ReplicaSet已经回滚到上一个版本了:
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 3 3 3 115m
nginx-deployment-6f655f5d99 0 0 0 7h50m
nginx-deployment-79c5b65fdb 0 0 0 13m
显然,从上面整个过程,我们知道只要我们对这个Deployment做一次更新操作,就会生成一个ReplicaSet,如果更新很频繁,这显然是有点浪费资源了,Kubernetes为了处理这类需求提供了一个指令kubectl rollout pause,它会让我们对Deployment的多次操作只生成一个ReplicaSe。具体用法如下:
[root@master ~]# kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused
然后可以用kubectl set image 或者kubectl edit随意修改这个Deployment的内容,等到我们修改完成后,再执行kubectl rollout resume命令来做恢复操作,比如我们修改image镜像和添加一个新的容器,然后保存退出:
然后执行恢复命令:
[root@master ~]# kubectl rollout resume deployment/nginx-deployment
deployment.extensions/nginx-deployment resumed
我们上面最了两次修改,然后我们查看ReplicaSet:
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5754944d6c 0 0 0 132m
nginx-deployment-5b9b565595 1 1 0 11s
nginx-deployment-6f655f5d99 2 2 2 8h
然后我们发现只生成了一个ReplicaSet,由于我本地没有这个镜像,所以拉取镜像过程中READY状态为0。
这种办法会随着应用版本的不断增加,也会创建很多的ReplicaSet版本,所以Deployment对象还定义了一个字段revisionHistoryLimit,就是定义Kubernetes为Deployment保留的历史版本个数。
我们可以通过kubectl edit 进去查看默认保留的个数:
四、总结
从全文可知,Deployment实际是一个两层控制器:
(1)、它通过ReplicaSet的个数来描述应用版本个数;
(2)、它通过ReplicaSet的属性来保证Pod的副本数;
而且Deployment的灵活控制,很方便水平扩展/收缩还有滚动更新以及回滚操作。