Kubernetes — 控制器

Pod 这个看似复杂的 API 对象,实际上就是对容器的进一步抽象和封装而已。 说得更形象些,“容器”镜像虽然好用,可是容器这样一个“沙盒”的概念,对于描述应用来讲, 仍是太过简单了。node

这就比如,集装箱当然好用,可是若是它四面都光秃秃的,吊车还怎么把这个集 装箱吊起来并摆放好呢? 因此,Pod 对象,其实就是容器的升级版。它对容器进行了组合,添加了更多的属性和字段。这就 比如给集装箱四面安装了吊环,使得 Kubernetes 这架“吊车”,能够更轻松地操做它。 而 Kubernetes 操做这些“集装箱”的逻辑,都由控制器(Controller)完成。咱们曾经使用过 Deployment 这个最基本的控制器对 象。 如今,咱们一块儿来回顾一下这个名叫 nginx-deployment 的例子:nginx

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

  

这个 Deployment 定义的编排动做很是简单,即:确保携带了 app=nginx 标签的 Pod 的个数,永 远等于 spec.replicas 指定的个数,即 2 个。编程

这就意味着,若是在这个集群中,携带 app=nginx 标签的 Pod 的个数大于 2 的时候,就会有旧的 Pod 被删除;反之,就会有新的 Pod 被建立。 这时,你也许就会好奇:到底是 Kubernetes 项目中的哪一个组件,在执行这些操做呢?api

我在前面介绍 Kubernetes 架构的时候,曾经提到过一个叫做 kube-controller-manager 的组件。 实际上,这个组件,就是一系列控制器的集合。咱们能够查看一下 Kubernetes 项目的 pkg/controller 目录:架构

$ cd kubernetes/pkg/controller/
$ ls -d */              
deployment/             job/                    podautoscaler/          
cloud/                  disruption/             namespace/              
replicaset/             serviceaccount/         volume/
cronjob/                garbagecollector/       nodelifecycle/          replication/            statefulset/            daemon/
...

  

这个目录下面的每个控制器,都以独有的方式负责某种编排功能。而咱们的 Deployment,正是 这些控制器中的一种。 实际上,这些控制器之因此被统一放在 pkg/controller 目录下,就是由于它们都遵循 Kubernetes 项目中的一个通用编排模式,即:控制循环(control loop)。 好比,如今有一种待编排的对象 X,它有一个对应的控制器。那么,我就能够用一段 Go 语言风格 的伪代码,为你描述这个控制循环:app

for {
  实际状态 := 获取集群中对象 X 的实际状态(Actual State)
  指望状态 := 获取集群中对象 X 的指望状态(Desired State)
  if 实际状态 == 指望状态{
    什么都不作
  } else {
    执行编排动做,将实际状态调整为指望状态
  }
}

  

在具体实现中,实际状态每每来自于 Kubernetes 集群自己。 好比,kubelet 经过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,或者控 制器主动收集的它本身感兴趣的信息,这些都是常见的实际状态的来源。 而指望状态,通常来自于用户提交的 YAML 文件。框架

好比,Deployment 对象中 Replicas 字段的值。很明显,这些信息每每都保存在 Etcd 中。 接下来,以 Deployment 为例,我和你简单描述一下它对控制器模型的实现:oop

  • 1. Deployment 控制器从 Etcd 中获取到全部携带了“app: nginx”标签的 Pod,而后统计它们的 数量,这就是实际状态;
  • 2. Deployment 对象的 Replicas 字段的值就是指望状态;
  • 3. Deployment 控制器将两个状态作比较,而后根据比较结果,肯定是建立 Pod,仍是删除已有的 Pod(具体如何操做 Pod 对象,我会在下一篇文章详细介绍)。

能够看到,一个 Kubernetes 对象的主要编排逻辑,其实是在第三步的“对比”阶段完成的。 这个操做,一般被叫做调谐(Reconcile)。这个调谐的过程,则被称做“Reconcile Loop”(调谐 循环)或者“Sync Loop”(同步循环)。 因此,若是你之后在文档或者社区中碰到这些词,都不要担忧,它们其实指的都是同一个东西:控 制循环。 而调谐的最终结果,每每都是对被控制对象的某种写操做。spa

好比,增长 Pod,删除已有的 Pod,或者更新 Pod 的某个字段。这也是 Kubernetes 项目“面向 API 对象编程”的一个直观体现。 其实,像 Deployment 这种控制器的设计原理,就是咱们前面提到过的,“用一种对象管理另外一种 对象”的“艺术”。 其中,这个控制器对象自己,负责定义被管理对象的指望状态。好比,Deployment 里的 replicas=2 这个字段。设计

而被控制对象的定义,则来自于一个“模板”。好比,Deployment 里的 template 字段。 能够看到,Deployment 这个 template 字段里的内容,跟一个标准的 Pod 对象的 API 定义,丝毫 不差。而全部被这个 Deployment 管理的 Pod 实例,其实都是根据这个 template 字段的内容建立 出来的。

像 Deployment 定义的 template 字段,在 Kubernetes 项目中有一个专有的名字,叫做 PodTemplate(Pod 模板)。 这个概念很是重要,由于后面我要讲解到的大多数控制器,都会使用 PodTemplate 来统必定义它 所要管理的 Pod。更有意思的是,咱们还会看到其余类型的对象模板,好比 Volume 的模板。 至此,咱们就能够对 Deployment 以及其余相似的控制器,作一个简单总结了:

 

如上图所示,相似 Deployment 这样的一个控制器,实际上都是由上半部分的控制器定义(包括期 望状态),加上下半部分的被控制对象的模板组成的。

这就是为何,在全部 API 对象的 Metadata 里,都有一个字段叫做 ownerReference,用于保存 当前这个 API 对象的拥有者(Owner)的信息。 那么,对于咱们这个 nginx-deployment 来讲,它建立出来的 Pod 的 ownerReference 就是 nginx-deployment 吗?或者说,nginx-deployment 所直接控制的,就是 Pod 对象么? 这个问题的答案,我就留到下一篇文章时再作详细解释吧。

总结

不少不一样类型的容器编排功能,好比 StatefulSet、DaemonSet 等 等,它们无一例外地都有这样一个甚至多个控制器的存在,并遵循控制循环(control loop)的流 程,完成各自的编排逻辑。 实际上,跟 Deployment 类似,这些控制循环最后的执行结果,要么就是建立、更新一些 Pod(或 者其余的 API 对象、资源),要么就是删除一些已经存在的 Pod(或者其余的 API 对象、资源)。 但也正是在这个统一的编排框架下,不一样的控制器能够在具体执行过程当中,设计不一样的业务逻辑, 从而达到不一样的编排效果。 这个实现思路,正是 Kubernetes 项目进行容器编排的核心原理。在此后讲解 Kubernetes 编排功 能的文章中,我都会遵循这个逻辑展开,而且带你逐步领悟控制器模式在不一样的容器化做业中的实 现方式。

相关文章
相关标签/搜索