不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

Kubernetes编排原理——作业副本与水平拓展/收缩

本文讲解Kubernetes中第一个控制器模式的完整实现:Deployment。它实现了Kubernetes项目中非常重要的功能:Pod的水平扩展/收缩

例如,如果你更新了DeploymentPod模板,那么Deployment就需要遵循一种叫作滚动更新rolling update的方式来升级现有容器。而这个能力的实现依赖Kubernetes项目中一个非常重要的概念(API对象):ReplicaSet

ReplicaSet

一个ReplicaSet对象是由副本数目的定义和一个Pod模板组成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: ReplicaSet
metadata:
name: nginx-set
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9

因此,对于一个Deployment所管理的Pod,它的ownerReference就是ReplicaSet

接下来分析nginx-deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: Deployment # API对象的类型
metadata:
name: nginx-deployment
spec:
selector:
matchLabels: # 过滤规则的定义
app: nginx
replicas: 3 # 定义Pod副本个数为3
template:
metadata: labels: # 元数据
app: nginx # labels,一组键值对的标签
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

一个定义了replicas=3Deployment,与它的ReplicaSet以及Pod之间实际上是一种“层层控制”的关系

ReplicaSet负责通过控制器模式保证系统中Pod个数永远等于指定个数。这也是Deployment只允许容器的restartPolicy=Always的主要原因:只有在容器保证自己始终处于Running状态的前提下,ReplicaSet调整Pod的个数才有意义。

在此基础上,Deployment通过控制器模式来操作ReplicaSet的个数和属性,进而实现水平扩展/收缩和滚动更新这两个编排动作:

水平扩展/收缩

实现:Deployment Controller只需要修改它所控制的ReplicaSetPod副本个数就可以了。比如,把这个值从3改成4,那么Deployment所对应的ReplicaSet就会根据修改后的值自动创建一个新的Pod

操作指令:kubectl scale,比如:

滚动更新

nginx-deployment为例,首先,创建这个nginx-deployment,并检查一下nginx-deployment创建后的状态信息:

1
2
3
4
5
6
# -- record参数作用
# 记录下你每次操作所执行的命令,以便之后查看
kubectl create -f nginx-deployment.yaml --record

# 查看 deployment
kubectl get deployments

返回结果包含4个状态字段,含义分别是:

  • 1,DESIRED:用户期望的Pod副本个数(spec.replicas的值)。
  • 2,CURRENT:当前处于Running状态的Pod的个数
  • 3,UP-TO-DATE:当前处于最新版本的Pod的个数。所谓最新版本,指的是PodSpec部分与Deployment里的Pod模板里定义的完全一致。
  • 4,AVALLABLE:当前已经可用的Pod的个数,即既是Running状态,又是最新版本,并且已经处于Ready(健康检查显示正常)状态的Pod的个数。——只有这个字段描述是用户所期望的最终状态

此时,可以查看这个Deployment所控制的ReplicaSet

在用户提交了一个Deployment对象后,Deployment Controller会立即创建一个Pod副本数为2的ReplicaSet。这个ReplicaSet的名字由Deployment的名字和一个随机字符串共同组成。

ReplicaSetDESIREDCURRENTREADY字段的含义,和Deployment中是一致的。所以,Deployment只是在ReplicaSet的基础上添加了UP-TO-DATE这个跟版本有关的状态字段。

此时,如果修改了DeploymentPod模板,“滚动更新”就会被触发。

1
2
3
4
5
# 修改 nginx镜像的版本
kubectl edit deployment/nginx-deployment

# 查看deployment的events
kubectl describe deployment nginx-deployment

可以看到,当修改了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的关系

综上所述:扩展DeploymentReplicaSetPod的关系图:

Deployment的控制器实际上控制的是ReplicaSet的数目,以及每个ReplicaSet的属性。而一个应用的版本对应的正是一个ReplicaSet,这个版本应用的Pod数量则由ReplicaSet通过它自己的控制器(ReplicaSet Controller)来保证。通过这样的多个ReplicaSet对象,Kubernetes项目就实现了对多个应用版本的描述。

Deployment对应用进行版本控制的原理

1
2
# 直接修改nginx-development所使用的镜像
kubectl set image deployment/nginx-deployment nginx=nginx1.91

检查一下ReplicaSet状态:

新版本的ReplicaSet的水平拓展已经停止。而且,此时它已经创建一个Pod,但是没有进入READY状态,因为拉取不到有效镜像。

如何回滚到旧版本呢?
执行kubectl rollout undo

可以看到,前面执行的创建和更新操作分别对应了版本1和版本3,而那次失败的更新操作对应的是版本4。

也可以通过kubectl rollout histroty指令,查看每个版本对应的Deployment的API对象的细节:

1
2
3
4
kubectl rollout histroty deployment/nginx-deployment --revision=2

# 回滚到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=2

问题:每次更新操作都会生成一个新的ReplicaSet对象,是否浪费资源?

是的。因此可以用kubectl rollout pause指令,能让我们对Deployment的多次更新操作最后只生成一个ReplicaSet

总结

  • Deployment实际上是一个两层控制器:它通过ReplicaSet的个数来描述应用的版本,通过ReplicaSet的属性来保证Pod的副本数量。
  • Deployment控制ReplicaSet(版本),ReplicaSet控制Pod(副本数)。
------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!