Pod是k8s中最小的API对象,是对容器的进一步封装,使得k8s能够更容易地进行操作。而k8s操作Pod都是由控制器来完成的。Deployment就是最基本的控制器对象。

给定一个配置文件:

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的数量。也就是Pod数量多了就删除,少了就创建。

这个动作由k8s中Master节点的组件kube-controller-manager来完成。在k8s项目的kubernetes/pkg/controller/目录下可以看到所有的控制器:

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

这些控制器都以独有的方式来负责某种编排功能。并且都遵循k8s的通用编排模式,即控制循环(control loop)

控制循环用伪代码描述如下:

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

就是不断尝试将实际状态变为期望状态。实际状态来自于k8s集群本身,而期望状态一般来自于用户提交的YAML文件,保存在Etcd中。

示例如下:

  • Deployment控制器从Etcd中获取携带了”app:nginx“标签的Pod,然后统计它们的数量,这就是实际状态。
  • Deployment对象的Replicas字段的值就是期望状态。
  • Deployment控制器将两个状态进行比较,根据结果来决定是删除Pod还是创建Pod。

主要编排实际是在第三步的对比阶段完成的,这个操作通常叫做调谐(Reconcile),这个调谐的过程,被称作调谐循环(Reconcile Loop) 或者同步循环(Sync Loop)

控制器的设计原理,就是用一种对象管理另一种对象。而被控制对象的定义则来自于一个模板,如Deployment的template字段,这部分与一个Pod的标准定义是一致的,这个字段叫做PodTemplate(Pod模板)

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

在所有 API 对象的 Metadata 里,都有一个字段叫作 ownerReference,用于保存当前这个 API 对象的拥有者(Owner)的信息。对于一个 Deployment 所管理的 Pod,它的 ownerReference 就是:ReplicaSet


tags: 容器编排 控制器