2015年,谷歌把内部用了10年的容器编排系统开源,取名Kubernetes。8年过去,全球78%的企业在生产环境跑K8s,但多数人只用过kubectl apply,没碰过调度器的核心逻辑。

这就像天天开车的人,不知道发动机ECU怎么决策点火时机。K8s调度器(Scheduler)就是那个ECU——它决定你的容器该落在哪台机器,直接影响成本、延迟和稳定性。

调度器的决策链:从Pod到Node的"相亲流程"

调度器的决策链:从Pod到Node的"相亲流程"

K8s调度不是拍脑袋。一个Pod(最小部署单元)进来,调度器先过两道筛子:预选(Predicates)和优选(Priorities)。

预选是硬性门槛。节点资源够不够?端口冲突吗?亲和性规则满足吗?一票否决,不满足直接出局。这个阶段通常过滤掉80%的候选节点。

优选是软性打分。剩下的节点按算法排序:资源均衡度、数据本地性、Pod分散度。分数最高的胜出,Pod绑定(Bind)到该节点。

整个过程必须在100毫秒内完成。集群规模越大,这个约束越像走钢丝。

谷歌工程师Brendan Burns在2016年的设计文档里写过:「调度器的目标是找到"足够好"的节点,而非最优解。」这句话被很多人误读为"K8s调度很糙",实际是分布式系统的务实——最优解计算复杂度太高,毫秒级响应比理论完美更重要。

调度框架2.0:把黑箱变成乐高

调度框架2.0:把黑箱变成乐高

K8s 1.18(2020年3月)重构了调度框架,引入扩展点(Extension Points)。之前想自定义调度逻辑,得fork整个调度器代码,现在像插U盘一样插插件。

框架定义了11个扩展点,覆盖调度全生命周期:排序(Queue Sort)、预选(Filter)、打分(Score)、绑定前(PreBind)、绑定后(PostBind)等。每个点都可以注入自定义逻辑。

阿里云容器团队在2021年开源了Coscheduling插件,解决AI训练任务的"All-or-Nothing"调度问题——4个GPU必须同时到位,否则全部等待。这个场景原生K8s处理不了,会先把2个Pod调度走,剩下2个卡住,任务死锁。

Coscheduling通过PostFilter扩展点实现" Gang Scheduling "(成组调度),把Pod按标签分组,整组资源满足才放行。插件代码不到2000行,却让K8s能跑大规模分布式训练。

火山引擎(字节跳动)的Gödel调度器更激进,完全绕过默认调度器,自研多租户抢占、混部超卖、在离线混部等能力。2023年字节披露,内部集群资源利用率从15%提升到45%,调度器重构是核心变量。

调度器的暗面:那些生产环境踩过的坑

调度器的暗面:那些生产环境踩过的坑

调度器不是银弹。社区Issue #72479记录了经典案例:某用户集群3000节点,调度器CPU占用飙升到800%,Pod创建延迟从50ms涨到30秒。

根因是优选阶段的"节点亲和性"算法——每次打分遍历所有节点,复杂度O(n²)。集群规模翻倍,计算量翻四倍。修复方案是引入缓存和增量计算,把复杂度压到O(n)。

另一个坑是绑定阶段的竞态条件。调度器选出节点A,通知API Server,但在这几毫秒内节点A资源被别的Pod占满。K8s 1.22增加了"预留机制"(Reservation),先占坑再通知,降低冲突概率。

更隐蔽的是打分权重的设计。默认算法里,"资源均衡"权重1,"数据本地性"权重10。这意味着调度器优先把Pod往有数据的节点堆,可能导致热点。某视频公司调反了权重,把数据本地性降到1,资源均衡提到10,延迟涨了3%,但集群利用率提升了12%。

没有万能配置,只有 trade-off。

从Borg到K8s:谷歌的"降维打击"遗产

从Borg到K8s:谷歌的"降维打击"遗产

K8s调度器的基因来自Borg,谷歌内部2003年上线的容器系统。Borg调度器用C++写了20万行代码,支撑谷歌搜索、Gmail、YouTube。K8s重写时用Go语言,核心逻辑压缩到2万行。

但有些东西没继承。Borg有"抢占式调度"——低优先级任务随时被高优先级踢走,K8s直到1.19(2020年)才引入PriorityClass,且默认关闭。Borg的"资源估计"能预测任务实际用量,自动超卖,K8s依赖用户声明的request/limit,经常浪费或挤爆。

谷歌研究员John Wilkes在2015年的Borg论文里坦承:「Borg的复杂性是历史债务,K8s有机会重新设计。」但重新设计也意味着舍弃——K8s调度器更简单,也更"傻"。

云厂商的托管K8s服务(EKS、GKE、ACK)都在填这个坑。AWS的Karpenter(2021年开源)绕过默认调度器,直接对接EC2 Auto Scaling,按实际资源需求动态扩缩节点。阿里云的Koordinator(2022年开源)把Borg的资源画像和混部能力补了回来。

这些项目有个共同特点:不碰K8s核心代码,用调度框架的扩展点或CRD(自定义资源)实现。保持上游兼容,又能差异化竞争。

2024年K8s调度器最大的变动是多调度器(Multi-Scheduler)的成熟。一个集群跑多个调度器实例,各自负责不同租户或工作负载。字节跳动的实践是:在线服务用默认调度器,离线批处理用自研Gödel,AI训练用Volcano,三套并行,互不干扰。

这打破了"一个集群一个调度器"的假设,也让K8s的架构更像Borg的分布式设计。

K8s 1.30(2024年4月)的发布说明里,调度器团队列了17项增强,其中"动态资源分配"(DRA)预览版最值得关注——它让GPU、FPGA等异构资源的调度从"黑箱猜谜"变成显式声明。英伟达和谷歌联合提交了实现,预计1.32正式可用。

当你下次kubectl apply时,那个100毫秒内完成的调度决策,背后是经过20年迭代、数百万行代码、无数次生产事故淬炼的系统。而它最有趣的地方在于:设计文档写得明明白白,但多数人从未读过。

你的集群调度器跑的是默认配置,还是改过权重、插过插件?有没有遇到过Pod死活调度不上去、最后发现是某个隐性污点(Taint)在作怪的情况?