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。