容器编排1:理解控制器模型
在前面的文章中,我们已经对Pod对象进行了深入地讨论。接下来,我们一起来看Kubernetes项目最核心的功能,编排。
一、从一个例子说起
在前面讨论Pod对象时,我们做出了一个这样的总结:“容器,是进程;Pod,是操作系统”。由于容器描述应用时过于简单,因此kubernetes将容器进一步抽象、封装成了Pod。在kubernetes的世界中,通常不会直接操作Pod对象,而是由控制器完成。比如,下面这个名叫nginx-deployment 的例子:
1 |
|
在这个例子中,Deployment控制器会确保携带了 app=nginx 标签的 Pod 的个数,永远等于 spec.replicas 指定的个数,即 2 个。这意味着,如果在这个集群中,携带 app=nginx 标签的 Pod 的个数大于 2 的时候,就会有旧的 Pod 被删除;反之,就会有新的 Pod 被创建。
二、控制器
在Kubernetes架构中,有一个名叫kube-controller-manager的组件。这个组件,就是一系列控制器的集合。我们可以查看一下 Kubernetes 项目的 pkg/controller 目录:
1 |
|
这个目录下面的每一个控制器,都会以独有的方式负责某种编排功能。而我们的 Deployment,正是这些控制器中的一种。
2.1、控制循环
我们需要知道的是,上面这些控制器之所以被统一放在 pkg/controller 目录下,是因为它们都遵循 Kubernetes 项目中的一个通用编排模式,即:控制循环(control loop)。比如,现在有一个待编排对象 X,它有一个对应的控制器。那么,我就可以用一段 Go 语言风格的伪代码,来描述这个控制循环:
1 |
|
在具体的实现中,实际状态往往来自于 Kubernetes 集群本身。比如,kubelet 通过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,再或者控制器主动收集的它自己感兴趣的信息,这些都是常见的实际状态的来源。而期望状态,一般来自于用户提交的 YAML 文件。比如,Deployment 对象中 Replicas 字段的值。很明显,这些信息往往都保存在 Etcd 中。
2.2、调谐循环
我们可以以上面的 Deployment 对象为例,简单描述一下控制器模型的实现:
- Deployment控制器从Etcd中获取到所有携带了“app:ningx”标签的Pod,然后统计它们的数量,这就是实际状态;
- YAML文件中Deployment对象的Replicas字段的值就是期望状态;
- Deployment控制器将两个状态做比较,然后根据比较结果,确定是创建Pod,还是删除已有的Pod。
可以看到,一个 Kubernetes 对象的主要编排逻辑,实际上是在第三步的“对比”阶段完成的。这个操作,通常被叫作调谐(Reconcile)。这个调谐的过程,则被称作“Reconcile Loop”(调谐循环)或者“Sync Loop”(同步循环)。而调谐的最终结果,往往都是对被控制对象的某种写操作。比如,增加 Pod,删除已有的 Pod,或者更新 Pod 的某个字段。
2.3、对象模板
控制器,主要负责定义被控制对象的期望状态。比如,Deployment 里的 replicas=2 这个字段。而被控制对象,则来自于一个“模板”。比如,Deployment 里的 template 字段。可以看到,Deployment 这个 template 字段里的内容,跟一个标准的 Pod 对象的 API 定义,丝毫不差。而所有被这个 Deployment 管理的 Pod 实例,其实都是根据这个 template 字段的内容创建出来的。
像 Deployment 定义的 template 字段,在 Kubernetes 项目中有一个专有的名字,叫作 PodTemplate(Pod 模板)。Kubernetes中的大多数控制器,都会使用 PodTemplate 来统一定义它所要管理的 Pod。此外,我们还会看到其他类型的对象模板,比如 Volume 的模板。
在这里,我们可以对 Deployment 以及其他类似的控制器,做一个简单总结:
如上图所示,类似于 Deployment 这样的控制器,实际上都是由上半部分的控制器定义(包括期望状态),加上下半部分的被控制对象的模板组成的。
三、小结
在这篇文章中,我们以Deployment为例,讨论了Kubernetes如何通过“控制器”,实现对各种不同对象或资源的编排:
- Kubernetes支持多种容器编排功能,比如ReplicaSet、Deployment、StatefulSet、DaemonSet、CronJob等
- 每一种编排功能都是由一个或多个控制器实现的,每个控制器通过控制循环完成各自的编排逻辑。
- 在控制循环的作用下,最后的执行结果都和Deployment类似,即要么创建、更新一些Pod,要么删除一些已经存在的Pod(或者其他的API对象、资源)。
- 当我们描述一个对象时,主要会涉及到两个核心内容:控制器定义(对象所使用的控制器)和被控制对象(即对象本身)。