[k8s理论知识]6.k8s调度器

k8s默认调度器

k8s调度器的主要职责,就是为一个新创建出来的pod寻找一个适合的节点Node。这包括两个步骤,第一,从所有集群的节点中,根据调度算法挑选出所有可以运行该pod的节点,第二,从第一步的结果中,根据调度算法挑选一个最符合条件的pod作为最终结果。

所以在具体的调度流程中,默认调度器会首先调用一组叫作 Predicate 的调度算法,来检查每个 Node。然后,再调用一组叫作 Priority 的调度算法,来给上一步得到的结果里的每个 Node 打分。最终的调度结果,就是得分最高的那个 Node。

cache化

为了高效的完成调度过程,调度器必须要频繁的查询集群的状态信息,如节点的资源使用情况,节标签、污点等等。这些操作每次都从etcd中获取数据,会导致性能瓶颈。为了解决这个问题,Kubernetes 引入了调度器缓存(scheduler cache)。调度器缓存是一个内存中的数据结构,用于存储集群的节点信息和其他相关元数据。通过将这些信息缓存起来,调度器可以更快地访问和处理这些数据,从而提高调度算法的执行效率。

通过将集群信息尽可能的缓存化,可以提高k8s的调度效率,提高性能。通过将节点信息和其他元数据缓存到内存中,调度器可以减少对 etcd 的直接访问次数,从而降低网络延迟和 I/O 开销。

从上图可以看出,第一个控制循环informer Path的主要目的就是启动一系列的informer,用来监听etcd中的pod、node、service等与调度相关的API对象的变化,如果有调度需求,就将这个pod加入调度队列,这个调度队列是一个优先级队列。调度器还要负责对调度器缓存进行更新(update cache)。

第二个控制循环是负责Pod调度的主循环,叫做scheduling path。Scheduling Path 的主要逻辑,就是不断地从调度队列里出队一个 Pod。然后,调用 Predicates 算法进行"过滤"。这一步"过滤"得到的一组 Node,就是所有可以运行这个 Pod 的宿主机列表。当然,Predicates 算法需要的 Node 信息,都是从 Scheduler Cache 里直接拿到的,这是调度器保证算法执行效率的主要手段之一。接下来,调度器就会再调用 Priorities 算法为上述列表里的 Node 打分,分数从 0 到 10。得分最高的 Node,就会作为这次调度的结果。执行完调度算法之后,调度器就需要将 Pod 对象的 nodeName 字段的值,修改为上述 Node 的名字。这个步骤在 Kubernetes 里面被称作 Bind。

乐观绑定

当进行到上面说的修改nodeName的时候,其实pod还没有被部署到这个节点。但是这个时候scheduler就已经把这个调度信息写入到scheduler cache中了。然后再异步的向API server发起更新pod的请求,以真正的完成bind操作。这种设计被称为乐观假设。

这是因为如果选定node名称之后,发起同步的API server通信等待响应,会显著增加调度器的延迟。通过异步绑定,调度器可以在确定合适节点后立即更新调度器缓存,然后继续处理下一个 Pod 的调度,而不需要等待 API Server 的响应。这大大减少了关键路径上的延迟,提高了调度器的吞吐量。

如果调度失败的话,scheduler cache中写入的内容也会在与etcd更新的时候进行修正,并不会使集群的信息不同步。

admit操作

由于乐观绑定的操作,当一个新的 Pod 完成调度需要在某个节点上运行起来之前,该节点上的 kubelet 还会通过一个叫作 Admit 的操作来再次验证该 Pod 是否确实能够运行在该节点上。Admit 操作会重新执行一组最基本的调度算法,如"资源是否可用"、"端口是否冲突"等,作为 kubelet 端的二次确认。

无锁化

调度器会启动多个 Goroutine,以节点为粒度并发执行 Predicates 算法。这样可以并行地检查多个节点是否满足 Pod 的调度要求,从而显著提高这一阶段的执行效率。在并发编程中,锁是一种常用的同步机制,用于确保多个 Goroutine 之间对共享资源的访问是安全的。比如说多个goroutine可能会同时的向队列中加入或移除pod,或是同时操作scheduler cache。然而,锁的使用会带来一定的性能开销,特别是在高并发场景下。

为了增加predicate过程的速度,调度器采用了无锁化设计,在这些并发路径上,调度器避免设置任何全局的竞争资源,从而避免了没有锁带来的状态难以管理的问题。

默认调度器的重构

随着 Kubernetes 项目的逐步稳定,越来越多的用户开始将其应用于规模更大、业务更加复杂的私有集群中。在这些复杂场景下,用户对默认调度器的扩展和重新实现提出了更高的需求。社区对 Kubernetes 项目的主要诉求之一就是增强默认调度器的可扩展性。

下图是默认调度器的可扩展性设计的示意图。可以看到在调度器生命周期的各个关键点上,为用户暴露出了可以扩展和实现的接口。每一个绿色的箭头都是一个可以插入自定义逻辑的接口。比如,上面的 Queue 部分,就意味着你可以在这一部分提供一个自己的调度队列的实现,从而控制每个 Pod 开始被调度(出队)的时机。

在 Kubernetes 的整体架构中,kube-scheduler 的责任虽然重大,但其实它却是在社区里最少受到关注的组件之一。这里的原因也很简单,调度这个事情,在不同的公司和团队里的实际需求一定是大相径庭的,上游社区不可能提供一个大而全的方案出来。所以,将默认调度器进一步做轻做薄,并且插件化,才是 kube-scheduler 正确的演进方向。

相关推荐
关关长语7 分钟前
Vue本地部署包快速构建为Docker镜像
前端·vue.js·docker
水滴与鱼1 小时前
DOCKER制作ROS运行的镜像文件
运维·docker·容器
洒家肉山大魔王3 小时前
Kubernetes中Pod 处于 CrashLoopBackOff 状态(生产环境)
linux·容器·kubernetes·pod·pod循环重启
杨浦老苏4 小时前
安全的消息传递和协作工具Virola Messenger
docker·即时通讯·群晖·im
Lynnxiaowen5 小时前
今天我们学习kubernetes内容Ingress资源对象
学习·容器·kubernetes
jason成都5 小时前
emqx的docker部署
运维·docker·容器
mixboot6 小时前
docker 国内镜像源
docker·镜像源
谷粒.6 小时前
云原生时代的测试策略:Kubernetes环境下的测试实践
运维·网络·云原生·容器·kubernetes
java_logo7 小时前
Milvus GUI ATTU Docker 容器化部署指南
运维·数据库·docker·容器·eureka·milvus
❥ღ Komo·8 小时前
K8s Secrets:敏感数据安全存储指南
安全·容器·kubernetes