1. 资源模型、资源管理
Pod 是最小的原子调度单位,所有跟调度和资源管理相关的属性,都是 Pod 对象属性的字段。其中最重要的是 Pod 和 CPU 配置。其中,CPU 属于可压缩资源,内存属于不可压缩资源。当可压缩资源不足时,Pod 会饥饿;当不可压缩资源不足时,Pod 就会因为 OOM 被内核杀掉。
Pod ,即容器组,由多个容器组成,其 CPU 和内存配额在 Container 上定义,其 Container 配置的累加值即为 Pod 的配额。
1、requests和limits
- requests:kube-scheduler 只会按照 requests 的值进行计算。
- limits:kubelet 则会按照 limits 的值来进行设置 Cgroups 限制.
2、Qos模型
- Guaranteed: 同时设置 requests 和 limits,并且 requests 和 limit 值相等。优势一是在资源不足 Eviction 发生时,最后被删除;并且删除的是 Pod 资源用量超过 limits 时才会被删除;优势二是该模型与 docker cpuset 的方式绑定 CPU 核,避免频繁的上下午文切换,性能会得到大幅提升。
- Burstable:不满足 Guaranteed 条件, 但至少有一个 Container 设置了 requests
- BestEffort:没有设置 requests 和 limits。
这个模型这样划分是因为Eviction(资源回收)要用到的。
3、Eviction
两种模式:
- soft: 如 `eviction-soft-grace-period=imagefs.available=2m` eviction 会在阈值达到 2 分钟后才会开始
- hard:evivtion 会立即开始。
**eviction 计算原理: 将 Cgroups (limits属性)设置的值和 cAdvisor 监控的数据相比较。
4、最佳实践
DaemonSet 的 Pod 都设置为 Guaranteed, 避免进入“回收->创建->回收->创建”的“死循环”。
5、Cpuset方式
你可以通过设置 cpuset 把容器绑定到某个 CPU 的核上,而不是像 cpushare 那样共享 CPU 的计算能力;
如何实现?
- 首先,你的 Pod 必须是 Guaranteed 的 QoS 类型;
- 然后,你只需要将 Pod 的 CPU 资源的 requests 和 limits 设置为同一个相等的整数值即可。
2. 默认调度器的优先级和抢占机制
调度器的作用就是为Pod寻找一个合适的Node。
优先级和抢占机制解决的是Pod调度失败的问题。
调度过程:待调度Pod被提交到apiServer -> 更新到etcd -> 调度器Watch etcd感知到有需要调度的pod(Informer) -> 取出待调度Pod的信息 ->Predicates: 挑选出可以运行该Pod的所有Node -> Priority:给所有Node打分 -> 将Pod绑定到得分最高的Node上 -> 将Pod信息更新回Etcd -> node的kubelet感知到etcd中有自己node需要拉起的pod -> 取出该Pod信息,做基本的二次检测(端口,资源等) -> 在node 上拉起该pod 。
Predicates阶段会有很多过滤规则:比如volume相关,node相关,pod相关;
Priorities阶段会为Node打分,Pod调度到得分最高的Node上,打分规则比如: 空余资源、实际物理剩余、镜像大小、Pod亲和性等;
Kuberentes中可以为Pod设置优先级,高优先级的Pod可以: 1、在调度队列中先出队进行调度 2、调度失败时,触发抢占,调度器为其抢占低优先级Pod的资源。
Kuberentes默认调度器有两个调度队列:
activeQ:凡事在该队列里的Pod,都是下一个调度周期需要调度的;
unschedulableQ: 存放调度失败的Pod,当里面的Pod更新后就会重新回到activeQ,进行“重新调度”。
默认调度器的抢占过程: 确定要发生抢占 -> 调度器将所有节点信息复制一份,开始模拟抢占 -> 检查副本里的每一个节点,然后从该节点上逐个删除低优先级Pod,直到满足抢占者能运行 -> 找到一个能运行抢占者Pod的node -> 记录下这个Node名字和被删除Pod的列表 -> 模拟抢占结束 -> 开始真正抢占 -> 删除被抢占者的Pod,将抢占者调度到Node上。