当我们通过 kubectl apply 创建 Pod 时,一个核心问题随之产生:这个 Pod 最终是如何被确定运行在哪一个具体节点上的?这背后正是 Kubernetes调度器(kube-scheduler) 的核心工作。
调度器的核心职责:决定“跑在哪”
首先需要明确一个关键区分:
- 控制器(Controller) 负责:需要创建多少个 Pod 副本。
- 调度器(Scheduler) 负责:每个 Pod 应该被调度到哪个 Node 上运行。
调度器只专注于一件事:为那些尚未指定节点的 Pod,从集群中筛选出一个最合适的节点。一旦做出绑定决定,结果通常是不可逆的。
何时触发调度?
一个 Pod 必须满足以下条件,才会被放入调度器的待调度队列:
- Pod 对象已被成功创建。
spec.nodeName 字段为空(即未指定节点)。
- 没有被之前的调度失败记录“锁死”。
满足这些条件的 Pod 被称为“Pending 且 Unschedulable”的候选 Pod,调度器会持续监听这类对象。
调度三阶段:筛选、打分与绑定
调度器为一个 Pod 选择节点的决策流程可以清晰地分为三个步骤:筛选(Filter) → 打分(Score) → 绑定(Bind)。
1. 筛选阶段:剔除不合格节点
此阶段的目标是进行硬性过滤,将所有“不能运行该 Pod”的节点剔除。常见的筛选条件(谓词)包括:
- 资源请求:节点的可用 CPU 和内存是否满足 Pod 的
requests。
- 节点选择器与亲和性:是否符合
nodeSelector 或 nodeAffinity 的规则。
- 污点与容忍度:Pod 是否能够容忍(
tolerations)节点上的污点(taints)。
- Pod 间亲和/反亲和:是否符合与其他 Pod 的亲和或反亲和规则。
- 其他约束:如端口冲突、卷挂载限制等。
筛选阶段是非此即彼的,不满足条件的节点会被直接淘汰,不会进入后续流程。
2. 打分阶段:选择最优节点
通过筛选的节点都“能运行”该 Pod。打分阶段的目标是从这些合格节点中,选出一个“最好”的。
调度器会基于一系列评分策略(优先级函数)为每个节点计算分数,考量维度包括:
- 资源平衡:倾向于将 Pod 调度到资源更空闲的节点(如
LeastRequestedPriority)。
- 负载均衡:避免 Pod 过度集中。
- 亲和性权重:实现
podAffinity 或避免 podAntiAffinity。
- 拓扑分布:例如,将 Pod 均匀分布到不同的可用区(Zone)。
最终,得分最高的节点胜出。这些复杂的调度规则和资源管理,是构建健壮 云原生/IaaS 应用基础设施的关键。
3. 绑定阶段:落实调度决策
调度器将最终的选择结果(即节点名称)写回 Pod 对象的 spec.nodeName 字段。此刻起:
- 该 Pod 的调度周期结束,调度器对其的责任完成。
- 目标节点上的 kubelet 组件监听到此绑定事件,开始接管并执行 Pod 的创建流程,拉取镜像、启动容器。
常见问题:Pod 为何持续 Pending?
绝大多数 Pod 卡在 Pending 状态的原因,早在筛选阶段就已经决定了。常见“卡死”原因包括:
- Pod 申请的资源配置(
requests)远超集群中任何节点的可用资源。
- 未给 Pod 配置容忍度以匹配目标节点的污点。
nodeAffinity 或 nodeSelector 配置错误,无节点满足标签要求。
- Pod 使用的 PVC 无法成功绑定 PV。
- 节点上所需端口已被占用。
诊断的第一步总是使用命令 kubectl describe pod <pod-name>,查看 Events 部分,调度失败的原因通常会明确记录。
关键概念与最佳实践
requests 是调度依据:调度器仅根据 requests 进行资源判断,limits 不影响调度决策。requests 是 Pod 获取节点资源的“入场券”。
- 亲和性是“软硬兼施”的约束:
nodeAffinity 分为强制约束(requiredDuringSchedulingIgnoredDuringExecution)和偏好约束(preferredDuringSchedulingIgnoredDuringExecution)。误用强制约束可能导致 Pod 无法调度。
- 污点是节点的“拒客令”:节点上的污点会阻止不能容忍它的 Pod 被调度上来。这是控制 Pod 部署位置(例如,不让普通业务 Pod 调度到 Master 节点)的核心机制。
- 调度器遵循规则而非理解业务:调度器本身不会理解你的应用是延迟敏感型还是计算密集型。它只严格地执行你设定的调度规则。如果没有规则,它默认会基于内置策略(如资源平衡)进行决策。
高级调度与扩展性
Kubernetes 调度器的能力是可扩展的,这支撑了其多样化的业务场景:
- 调度框架插件:可以通过实现不同的插件来扩展筛选和打分逻辑。
- 自定义调度器:可以开发并运行与默认调度器并存的自定义调度器,用于特定工作负载。
- Operator 级调度:一些有状态应用的 Operator 可能会在更复杂的逻辑层面影响或决定 Pod 的调度位置。
总结
简而言之,调度器决定 Pod 的“出生地”,而控制器管理 Pod 的“生命周期与数量”。理解调度器的工作原理,是掌握 Kubernetes 如何将工作负载“落地”到物理基础设施的关键,也是高效进行 运维/DevOps 和集群管理的基础。Pod 一旦绑定节点,除非主动干预,否则通常不会“搬家”。
|