K8s学习笔记:Pod调度详解
本文是自己的学习笔记
1、Pod调度流程
1.1、时序图
下面是创建pod的时序图。

- kubectl 向API Server 提交 Pod。例如下面的yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
执行kubectl apply -f pod.yaml。请求发送给 API Server。API Server 进行权限验证和格式检查,通过后将 Pod 的配置信息(元数据)写入 etcd 数据库。此时 Pod 的状态是 Pending。
-
通知变更:etcd 数据发生变化后,API Server 通过 Watch(长连接监听)机制,把“有一个新 Pod 待调度”的消息通知给 Kube-Scheduler。
计算节点:Scheduler 收到通知,在本地计算出最适合该 Pod 的工作节点(Node)。
etcd 第二次写入:Scheduler 将计算结果(例如 nodeName: node-1)发送给 API Server。API Server 再次将这个绑定关系更新写入 etcd。 -
通知节点:etcd 的数据再次更新,API Server 察觉到后,立刻通知目标节点上的 Kubelet。Kubelet 接单:Kubelet 发现这个 Pod 分配给了自己,开始在节点上大干一场:
调用 CRI(容器运行时):先拉起 Pause 容器(初始化网络和存储),再拉取业务镜像并启动业务容器。
调用 CNI(网络插件):为 Pod 配置集群 IP 地址。
调用 CSI(存储插件):挂载所需的持久化数据卷。
etcd 第三次写入:当节点上的容器全部成功运行后,Kubelet 将 Pod 的最新状态(Running 以及分配到的 Pod IP)汇报给 API Server。API Server 最后一次将状态写入 etcd。
1.2、核心总结
etcd 是账本,记录 Pod 从出生到运行的每一次状态变更。
API Server 是前台兼打字员,负责接收请求,并把结果写进 etcd 账本。
Scheduler 和 Kubelet 是流水线工人,它们盯着 API Server(看账本变化),账本上一旦出现自己的任务,就立刻认领并干活。
2、调度算法
在调度流程中,Scheduler的核心任务就是从集群中挑出一个最合适的节点(Node)来运行新创建的 Pod。
为了做到这一点,Scheduler 的调度算法主要分为三个核心阶段:过滤(Filtering/Predicates)、打分(Scoring/Priorities) 和 绑定(Binding)。
2.1、 过滤阶段(Filtering / Predicates)
这个阶段俗称“硬限制审查”。Scheduler 会遍历集群中所有的节点,剔除掉那些完全不符合 Pod 运行最低要求的节点。如果一个节点在这个阶段被一票否决,就不会进入下一步。
常见的核心过滤策略包括:
- 资源检查(PodFitsResources):检查节点的剩余 CPU 和内存是否满足 Pod 的 requests 设定的值。
- 端口冲突检查(PodFitsHostPorts):如果 Pod 声明了使用宿主机的某个特定端口(hostPort),检查该节点上这个端口是否已被占用。
- 卷冲突检查(NoDiskConflict / MaxCSIVolumeCount):检查 Pod 挂载的存储卷在节点上是否冲突,或者是否超过了节点能挂载的单机最大云盘数量。
- 节点亲和性过滤(NodeAffinity):检查节点是否匹配 Pod 定义的 nodeSelector 或 nodeAffinity 硬限制(requiredDuringSchedulingIgnoredDuringExecution)。
- 污点与容忍度检查(PodToleratesNodeTaints):检查节点上是否有污点(Taints),而 Pod 是否拥有对应的容忍度(Tolerations)。如果 Pod 无法容忍该污点,则该节点被过滤。
注意:如果所有节点在过滤阶段全被淘汰,Pod 就会保持 Pending 状态,并在事件(Events)中报出 FailedScheduling。
2.2、 打分阶段(Scoring / Priorities)
通过过滤阶段的节点,说明“能用”,但不见得“最好”。打分阶段就是“软意愿评估”。Scheduler 会运行一系列打分算法,给每个合格的节点打分(通常是 0-100 分),最终总分最高的节点胜出。
主要的核心打分维度包括:
-
资源利用率平衡(打分权衡):这里有两个互斥的策略,集群一般会根据业务场景二选一:
LeastRequestedPriority(空闲优先,默认偏向):资源的空闲比例越高,得分越高。这能让 Pod 均匀地打散到各个节点上,避免单机压力过大(适合追求高可用和性能的场景)。
MostRequestedPriority(紧凑优先):资源的已使用比例越高,得分越高。这会把 Pod 尽量“塞进”已经在使用中的节点,从而把空闲节点腾出来(适合云上弹性伸缩、为了省钱做节约成本的场景)。 -
拓扑与位置分布:SelectorSpreadPriority(分散优先):尽量把属于同一个 Deployment 或 Service 的 Pod 分散到不同的节点或不同的可用区(AZ)上,防止一个节点挂了导致整个服务雪崩。
ImageLocalityPriority(镜像本地化):检查节点上是否已经下载了该 Pod 所需的容器镜像。如果有,得分更高。因为这样可以省去拉取镜像的时间,让 Pod 启动得极快。 -
NodeAffinityPriority(节点软亲和):匹配 Pod 的 preferredDuringSchedulingIgnoredDuringExecution 条件。满足“期望去这里”的节点加分。
InterPodAffinityPriority(Pod 间亲和/反亲和):如果 Pod 软亲和希望和某个现有的 Pod 在一起(方便内部通信),或者反亲和希望远离某个 Pod(比如两个高 CPU 的 Pod 别在一台机器上),满足这些软条件的节点会获得高分。
2.3、 选定与绑定阶段(Reserve & Binding)
当所有打分项乘以各自的权重(Weight)相加后,Scheduler 就会得出得分最高的节点:
确定节点:如果有多个节点得分并列第一,Scheduler 会使用随机或轮询机制挑出一个。
乐观预留(Reserve / Assume):为了追求极致的高并发调度性能,Scheduler 不会等数据写入 etcd 成功后再处理下一个 Pod。它会在内存中先假装(Assume)这个 Pod 已经在这个节点上了(先把节点的资源减掉),这就叫乐观锁预留。
异步绑定(Binding):真正向 API Server 发起 Patch 请求,把 Pod 的 spec.nodeName 字段改成选中的节点名。这个动作是异步的。一旦 API Server 写入 etcd 成功,接力棒就交给了对应节点上的 Kubelet。
2.4、总结
Kubernetes Scheduler 的设计精髓在于 先做减法(过滤),再做加法(打分)。过滤保证了“能活”,打分保证了“活得好”。随着 K8s 演进,现在它完全基于 Scheduling Framework(调度框架) 架构,把上述的每一个算法都解耦成了可插拔的插件(Plugin),企业可以非常方便地开发自己的自定义调度算法(比如基于 GPU 拓扑调度)。
3、 影响pod调度的因素
3.1、资源请求和限制
CPU / Memory / GPU 等资源:调度器只会将 Pod 分配给剩余未分配资源大于等于 Pod requests 的节点。
即使节点实际 CPU/内存空闲,但如果已被其他 Pod 的 requests 占满,Pod 也无法调度上去。
3.2、节点选择器(nodeSelector)
- nodeSelector:简单的键值对匹配,节点必须具备对应 Label。
比如四个node,两个被打上env_role:dev,另外两个被打上env_role:prod。当pod的设置如下,指定具体的env_role的值,那该pod只会被分配到1,2节点。
apiVersion: v1
kind: Pod
metadata:
name: nginx-ssd-pod
labels:
app: nginx
spec:
nodeSelector:
env_role: dev

3.3、节点亲和性 (nodeAffinity)
- requiredDuringSchedulingIgnoredDuringExecution:硬亲和(必须满足,否者 Pending)。
- preferredDuringSchedulingIgnoredDuringExecution:软亲和(尽量满足,加分项)。
apiVersion: v1
kind: Pod
metadata:
name: affinity-example-pod
labels:
app: my-app
spec:
containers:
- name: app
image: nginx:1.25
# ------------------------------------
# 影响调度的核心配置:节点亲和性
# ------------------------------------
affinity:
nodeAffinity:
# 1. 硬亲和性(Hard Affinity):必须满足
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: env
operator: In
values:
- prod
# 2. 软亲和性(Soft Affinity):尽量满足(加分项)
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80 # 权重 (1-100),得分越高越优先
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- us-east-1b
- operator: In:匹配 Key 的 Value 是否在指定的数组中(类似 SQL 的 IN)。
- 忽略执行(IgnoredDuringExecution):如果在 Pod 运行期间节点的标签发生了改变,不满足规则了,不会驱逐或终止正在运行的 Pod。
- weight(权重):取值范围为 1–100。调度器会在优选(Scoring)阶段为满足条件的节点额外加上响应的权重得分,最终综合得分最高的节点将被选定。
- 常用操作符 (operator)
In / NotIn:值在/不在指定列表里。
Exists / DoesNotExist:检查节点的某个 Label 键是否存在/不存在(不需要设置 values)。
Gt / Lt:数值的大于/小于比较。
3.3、节点污点与 Pod 容忍度 (Taints & Tolerations)
污点(Taint) 是 Kubernetes 集中管理节点“排斥力”的核心机制。
如果说 nodeSelector 和 nodeAffinity 是 Pod 主动选择 哪些 Node(吸引力),那么 Taint(污点) 就是 Node 主动拒绝 哪些 Pod(排斥力)。
污点是在Node上配置的,而容忍度是在Pod上配置的。给Node增加污点后,如果Pod的配置无法容忍这个污点,那Pod就不会被分配给这个Node。
3.3.1、污点的组成结构
一个污点由三部分组成:Key=Value:Effect
- Key:自定义的键名(如 gpu、node-role)。
- Value:自定义的值(可为空)。
- Effect(作用效果):定义了不容忍该污点的 Pod 会受到怎样的排斥行为。
3.3.2、三大 Effect(作用效果)详解
这是污点最关键的概念,决定了调度的行为模式:
| Effect 名称 | 调度阶段行为(新 Pod) | 运行阶段行为(已有 Pod) | 典型应用场景 |
|---|---|---|---|
NoSchedule | 硬拦截:无法调度到该节点 | 不影响:继续正常运行 | 专用节点隔离(如 GPU 节点、Master 节点) |
PreferNoSchedule | 软排斥:尽量不调度到该节点 | 不影响:继续正常运行 | 倾向性隔离、资源充沛时的优化部署 |
NoExecute | 硬拦截:无法调度到该节点 | 立即驱逐:清理现有 Pod | 节点故障避险、节点维护搬迁 |
3.3.3、命令和配置示例
- 给Node增加污点
# 格式:kubectl taint nodes <node-name> key=value:Effect
# 示例:标记 node-01 为专用 GPU 节点
kubectl taint nodes node-01 gpu=nvidia:NoSchedule
# 示例:标记 node-02 准备维护(驱逐现有 Pod)
kubectl taint nodes node-02 maintenance=true:NoExecute
- 给node删除污点
# 删除 node-01 上的 gpu 污点
kubectl taint nodes node-01 gpu=nvidia:NoSchedule-
- 在 Pod YAML 中声明容忍度 (tolerations)
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: cuda-container
image: nvidia/cuda:12.0.0-base-ubuntu22.04
# ------------------------------------
# 配置容忍度
# ------------------------------------
tolerations:
# operator是equal,必须 key、value 和 effect 完全相同的node才能被容忍。
- key: "gpu"
operator: "Equal"
value: "nvidia"
effect: "NoSchedule"
# operator是exists,只要节点存在 key="gpu" 的污点,无论 value 是什么都容忍。
- key: "gpu"
operator: "Exists"1
effect: "NoSchedule"
# 容忍一切,不填 key 且操作符为 Exists,代表容忍该节点上的所有污点(通常用于全局 Agent,如 DaemonSet 中的日志收集或网络插件组件)。
tolerations:
- operator: "Exists"
更多推荐
所有评论(0)