本文讲解Kubernetes
中第一个控制器模式的完整实现:Deployment
。它实现了Kubernetes
项目中非常重要的功能:Pod的水平扩展/收缩。
例如,如果你更新了Deployment
的Pod
模板,那么Deployment
就需要遵循一种叫作滚动更新rolling update的方式来升级现有容器。而这个能力的实现依赖Kubernetes
项目中一个非常重要的概念(API对象):ReplicaSet
。
ReplicaSet
一个ReplicaSet
对象是由副本数目的定义和一个Pod
模板组成的。
1 | apiVersion: v1 |
因此,对于一个Deployment
所管理的Pod
,它的ownerReference
就是ReplicaSet
。
接下来分析nginx-deployment.yaml
:
1 | apiVersion: apps/v1 |
一个定义了replicas=3
的Deployment
,与它的ReplicaSet
以及Pod
之间实际上是一种“层层控制”的关系
ReplicaSet
负责通过控制器模式保证系统中Pod
个数永远等于指定个数。这也是Deployment
只允许容器的restartPolicy=Always
的主要原因:只有在容器保证自己始终处于Running
状态的前提下,ReplicaSet
调整Pod
的个数才有意义。
在此基础上,Deployment
通过控制器模式来操作ReplicaSet
的个数和属性,进而实现水平扩展/收缩和滚动更新这两个编排动作:
水平扩展/收缩
实现:Deployment Controller
只需要修改它所控制的ReplicaSet
的Pod
副本个数就可以了。比如,把这个值从3改成4,那么Deployment
所对应的ReplicaSet
就会根据修改后的值自动创建一个新的Pod
。
操作指令:kubectl scale
,比如:
滚动更新
以nginx-deployment
为例,首先,创建这个nginx-deployment
,并检查一下nginx-deployment
创建后的状态信息:
1 | # -- record参数作用 |
返回结果包含4个状态字段,含义分别是:
- 1,
DESIRED
:用户期望的Pod
副本个数(spec.replicas
的值)。 - 2,
CURRENT
:当前处于Running
状态的Pod
的个数 - 3,
UP-TO-DATE
:当前处于最新版本的Pod
的个数。所谓最新版本,指的是Pod
的Spec
部分与Deployment
里的Pod
模板里定义的完全一致。 - 4,
AVALLABLE
:当前已经可用的Pod
的个数,即既是Running
状态,又是最新版本,并且已经处于Ready
(健康检查显示正常)状态的Pod
的个数。——只有这个字段描述是用户所期望的最终状态
此时,可以查看这个Deployment
所控制的ReplicaSet
:
在用户提交了一个Deployment
对象后,Deployment Controller
会立即创建一个Pod
副本数为2的ReplicaSet
。这个ReplicaSet
的名字由Deployment
的名字和一个随机字符串共同组成。
ReplicaSet
的DESIRED
、CURRENT
和READY
字段的含义,和Deployment
中是一致的。所以,Deployment
只是在ReplicaSet
的基础上添加了UP-TO-DATE
这个跟版本有关的状态字段。
此时,如果修改了Deployment
的Pod
模板,“滚动更新”就会被触发。
1 | # 修改 nginx镜像的版本 |
可以看到,当修改了Deployment
里的Pod
定义之后,Deployment Controller
会使用这个修改后的Pod
模板创建一个新的ReplicaSet
,这个新的ReplicaSet
的初始Pod
副本数为0。
在Age=118s
的位置,Deployment Controller
开始将这个新的ReplicaSet
所控制的Pod
副本数从0变2,即水平拓展出两个副本。紧接着,在Age=119s
所控制的旧的Pod
副本数减少一个…如此交替进行——像这样将一个集群中正在运行的多个Pod版本交替地逐一升级的过程,就是滚动更新。
在滚动更新完成之后,可以查看一下新旧两个ReplicaSet
的最终状态:
其中,旧的ReplicaSet
已经被水平收缩成了0个副本。
这种滚动更新的好处:升级服务的过程中,对原来业务的使用不受影响。
Deployment-ReplicaSet-Pod的关系
综上所述:扩展Deployment
、ReplicaSet
、Pod
的关系图:
Deployment
的控制器实际上控制的是ReplicaSet
的数目,以及每个ReplicaSet
的属性。而一个应用的版本对应的正是一个ReplicaSet
,这个版本应用的Pod
数量则由ReplicaSet
通过它自己的控制器(ReplicaSet Controller
)来保证。通过这样的多个ReplicaSet
对象,Kubernetes
项目就实现了对多个应用版本的描述。
Deployment对应用进行版本控制的原理
1 | # 直接修改nginx-development所使用的镜像 |
检查一下ReplicaSet
状态:
新版本的ReplicaSet
的水平拓展已经停止。而且,此时它已经创建一个Pod
,但是没有进入READY
状态,因为拉取不到有效镜像。
如何回滚到旧版本呢?
执行kubectl rollout undo
:
可以看到,前面执行的创建和更新操作分别对应了版本1和版本3,而那次失败的更新操作对应的是版本4。
也可以通过kubectl rollout histroty
指令,查看每个版本对应的Deployment
的API对象的细节:
1 | kubectl rollout histroty deployment/nginx-deployment --revision=2 |
问题:每次更新操作都会生成一个新的ReplicaSet
对象,是否浪费资源?
是的。因此可以用kubectl rollout pause
指令,能让我们对Deployment
的多次更新操作最后只生成一个ReplicaSet
。
总结
Deployment
实际上是一个两层控制器:它通过ReplicaSet
的个数来描述应用的版本,通过ReplicaSet
的属性来保证Pod
的副本数量。Deployment
控制ReplicaSet
(版本),ReplicaSet
控制Pod
(副本数)。