本文讲解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(副本数)。