首先,通过一个编排对象,比如Pod、Job、CronJob等,来描述你试图管理的应用
然后,再为编排对象定义一些服务对象,比如Service、Secret、Horizontal Pod Autoscaler(自动水平扩展器)等,服务对象会负责具体的平台级功能
编排对象和服务对象是Kubernetes项目中的API对象(API Object),即面向API对象编程。
一个API对象在Etcd里的完整资源路径是由 Group(API组)、Version(API版本)和 Resource(API资源类型)三个部分组成的。整个Kubernetes里的所有API对象可以用如下的树形结构表示出来:
控制器模型(Controller Pattern)是Kubernetes进行容器编排的核心原理。
上篇文章有一张Kubernetes架构图,里面有一个组件叫 kube-controller-manager
,这个组件就是一系列控制器的集合。每一个控制器,都以独有的方式负责某种编排功能。例如,Deployment和Job就是Kubernetes内置控制器的典型例子。
这些控制器都遵循一个通用编排模式,即控制循环(Control Loop),说白了就是一个死循环,其伪代码如下:
for {
实际状态 := 获取集群中对象 X 的实际状态(Actual State)
期望状态 := 获取集群中对象 X 的期望状态(Desired State)
if 实际状态 == 期望状态{
什么都不做
} else {
执行编排动作,将实际状态调整为期望状态
}
}
简单来说,一个控制器对象是由 控制器定义(包括期望状态) 加 被控制对象的模板 组成的,如下图所示:
kube-apiserver
在处理命令式请求时,一次只能处理一个写请求,否则会有产生冲突的可能;而对于声明式请求,一次能处理多个写操作,并且具备Merge能力。
# 命令式
kubectl replace -f nginx.yaml
# 声明式
kubectl apply -f nginx.yaml
上面两条命令本质是不一样的,你可以简单地理解为,kubectl replace
的执行过程是使用新的YAML文件中的API对象替换原有的API对象,而 kubectl apply
则是执行了一个对原有API对象的 PATCH
操作。
可能大家看到这里对声明式API的重要性还是无感,那么请看下面这个更有说明力的栗子:
这个栗子来源于微服务治理框架Istio,而Envoy项目则是一个高性能C++网络代理。简单来说,Istio项目会为每一个被治理的Pod动态添加一个Envoy容器(以 sidecar
方式运行),Envoy容器通过配置Pod里的iptables规则,把整个Pod的进出流量接管下来,从而实现微服务治理。
要实现动态无感添加容器的功能,需要两方面支持:
git merge
这样的操作,也就是说,需要用到声明式API最主要的功能 PATCH APIKubernetes默认调度器的主要职责,就是为一个新创建出来的Pod寻找一个最合适的Node。
如上图所示,调度器的核心就是两个独立的控制循环:
第1个控制循环称为 Informer Path
:在这个控制循环中启动了一系列的Informer监听Etcd中Pod、Node、Service等与调度相关的API对象的变化;这里需要重点关注的是 调度队列 和 Scheduler Cache
第2个控制循环称为 Scheduling Path
:这个控制循环是控制Pod调度的主循环,循环里不断地从调度队列pop出来一个Pod,然后调用 Predicates
进行过滤,得到一组Node,接着调用 Priorities
来打分,选出得分最高的Node
关于默认调度器有3个设计思想需要重点关注:
Cache化,比方说,Predicates算法需要的Node信息是从 Scheduler Cache 里直接拿的
乐观绑定,也称为 Assume
,Kubernetes的默认调度器在Bind阶段,只会更新 Scheduler Cache 里的Pod和Node的信息,正因为如此,当Pod被调度到某个Node上时,该Node上的kubelet还会再做一次 Admit
的校验
无锁化,从图中可以看到,Kubernetes调度器只在操作 调度队列 和 Scheduler Cache 时才需要加锁,而这两部分操作都不在 Scheduling Path 的执行路径上
调度一个Pod分为两个阶段,即 调度周期 和 绑定周期。在Kubernetes调度框架中定义了一些扩展点,如下图所示:
骚年,看到这里对Kubernetes有点感觉了吗,更多精彩内容下期分享!