整体架构
核心组件
master节点
etcd
- 保存了整个集群的状态
- 所有对etcd的操作都要走apiserver
- 因为需要
apiserver
负责授权
- 因为需要
apiserver
- 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制
- 所有尝试访问或更改 Kubernetes 系统状态的请求都会通过
apiserver
进行
controller manager
- 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
- 通过控制循环,不断将系统状态从“当前状态”修正到“期望状态”,最终达到与申明一致
- 每种API对象都有一个对应的control负责维护对应对象
scheduler
- 负责资源的调度,按照预定的调度策略将Pod调度到相应的node上
node节点
kubelet
- 负责 Pod 生命周期管理、网络配置、Volume 管理、设备管理、资源管理等一系列跟容器基础设施
- 会定时上报node状态给
apiserver
- 会定时上报node状态给
- 负责 Pod 生命周期管理、网络配置、Volume 管理、设备管理、资源管理等一系列跟容器基础设施
kube-proxy
- 负责为Service提供cluster内部的服务发现和负载均衡
控制器
informer
从 APIServer 获取关心的API对象, 缓存在了本地,并负责更新和维护这个缓存- 其中的
Reflector
使用的是一种叫作ListAndWatch
的方法,来 “获取”并“监听” 这些 Network 对象实例的 变化- 如果一个master 管理的node非常多,通过ListAndWatch 会对master的性能有影响
- 所以迄今为止k8s最大规模为5000
- 如果一个master 管理的node非常多,通过ListAndWatch 会对master的性能有影响
- 一旦 APIServer 端有新的对象实例被创建、删除或者更新,
Reflector
都会收到“事件通知” - 该事件及它对应的 API 对象这个组合,就被称为增量(Delta),它会被放进一个
Delta FIFO Queue
- 另一方面,Informer 会定时不断地从这个Queue 里读取增量
- 每拿到一个增量,Informer 就会判断这个增量里的事件类型,然后创建或者更新本地对象的缓存
LocalStore
- 如果事件类型是 Added,那么 Informer 就会通过一个叫作·
Indexer
的库把这个增量里的 API 对象保存在本地缓存中,并为它创建索引 - 相反地,如果增量的事件类型是 Deleted,那么 Informer 就会从本地缓存中删除这个对象
- 每拿到一个增量,Informer 就会判断这个增量里的事件类型,然后创建或者更新本地对象的缓存
- 然后
informer
把要操作(更新)的API对象放入working queue
中- 工作队列是为了解耦以及匹配双方速度不一致
- 防止控制循环执行过慢把Informer 拖死
- 也可以控制并发度
- 工作队列是为了解耦以及匹配双方速度不一致
- 其中的
Control Loop
不断定时从working queue
取出对象,对比“期望状态”和“实际状态”,然后决定接下来要做的业务逻辑
调度
- 调度器对一个 Pod 调度成功的行为,实际上就是将它的
spec.nodeName
字段填上调度结果的node的名字
- 选择pod的算法
- 首先调用一组叫作
Predicate
的调度算法,挑选出所有可以运行该 Pod 的节点 - 再调用一组叫作
Priority
的调度算法,来给上一步得到的结果里的每个 Node 打分,最终的调度结果,就是得分最高的那个 Node
- 首先调用一组叫作
- 亲和度
- 当pod之间有清河或排斥关系时,可以通过
podAffinity
和podAntiaffinity
设定 - 当pod和node之间有考核或排斥关系时,可以通过
NodeSelector
和NodeAffinity
- 当pod之间有清河或排斥关系时,可以通过
- 调度失败后的抢占机制
- 当一个高优先级的 Pod 调度失败后希望能 “挤走”某个 Node 上的一些低优先级的 Pod
- 可以通过
PriorityClass
API对象定义- 优先级
value
是一个 32 bit 的整数,最大值不超过10 亿,值越大代表优先级越高- 超过10亿的值被保留给系统pod,避免其被用户抢占
- pod可以通过设置
spec.containers.priorityClassName
使用上面定义的对象,从而获得高优先级
- 优先级
容器运行时接口(CRI)
- 容器运行时接口(
CRI
)调用底层容器运行时- 例如 Docker 和 Rkt
- CRI 提供了 Kubelet 和特定的运行时之间的抽象接口,它们之间通过gRPC通信
- 屏蔽下层容器运行时的差异
- 通过使用 Kubelet 和运行时之间定义的契约关系,可以以最小的开销添加新的运行时实现
CRI shim
负责响应CRI
的指令,组装成API 请求发给具体容器运行时,例如 Docker DaemonCRI shim
实现 CRI 规定的每个接口,然后把具体的 CRI 请求 “翻译” 成对后端容器项目的请求或者操作
- CRI 接口的设计是比较宽松的,作为底层容器在实现 CRI 的具体接口时,往往拥有着很高的自由度:
- 容器的生命周期管理
- 如何将 Pod 映射成为我自己的实现
- 如何调用 CNI 插件来为 Pod 设置网络的过程
API 对象
- k8s是声明式API,而不是命令式API
- 只需要声明对象的理想状态,而不是一系列控制命令
- 允许有多个 API 写端,以 PATCH 的方式对 API 对象进行修改,而无需关心本地原始 YAML 文件的内容
- 每个API对象有三大类属性
- 元数据(metadata)
- 用于标示对象
- 包括
namspace, name, labels
等
- 规范(spec)
- 用于描述期望的理想状态
- 所有配置都是通过spec来设置的
- 状态(status)
- 当前系统的实际状态
- 元数据(metadata)
- 由于k8s对象之间是有依赖关系的,在部署时是需要按照顺序启动的
- 部署次序:Secret->ConfigMap->Volume->Deployment->Service
- k8s通过
label
组织,分类和选择这些对象- 一个对象可以包含多个label,从而实现多维度管理
- label是key和value都是string类型的map
- 其中key只能由
a-z,0-9,-,_,.
组成,且开头结尾需小写,最多长63个字符
- 其中key只能由
集群(namespace)
- 可以通过资源配额在多个namespace之间划分群集(cluster) 资源
- 默认namespace是
default
- 系统组件(kube-proxy等)位于
kube-system
命名空间下,而不是default
- 同一个namespace里的pod的很多信息都是通过环境变量写入每一个pod中的
- 例如每个service的IP和端口等
- 但是这会导致环境变量泛滥的问题