1. 调度器核心机制解析
kube-scheduler作为Kubernetes集群的大脑,其节点选择算法直接决定了Pod的运行效率和资源利用率。在实际生产环境中,我们经常需要面对这样的场景:一个需要GPU资源的AI训练Pod被错误调度到普通计算节点,导致整个训练任务失败。理解调度器的工作原理,正是避免这类问题的关键。
1.1 调度流程全景图
当一个新的Pod被创建时,kube-scheduler会按照以下流程进行处理:
-
过滤阶段(Filtering):首先排除所有不满足Pod硬性要求的节点。比如Pod声明需要8核CPU,那么当前可用CPU不足8核的节点会被立即淘汰。这个阶段主要检查:
- 节点资源是否充足(CPU/Memory/GPU等)
- 节点是否满足Pod的nodeSelector/nodeAffinity要求
- 节点是否存在污点(Taint)且Pod没有对应容忍(Toleration)
- 节点是否处于可调度状态(NotReady、DiskPressure等)
-
打分阶段(Scoring):对通过过滤的节点进行评分。评分标准包括:
- 资源平衡度(避免节点资源使用严重不均衡)
- 镜像本地化程度(节点是否已缓存所需容器镜像)
- 亲和性匹配度(与podAffinity/antiAffinity的匹配程度)
- 自定义策略(用户通过Extender添加的评分规则)
实际经验:在大规模集群中,过滤阶段可以快速缩减候选节点范围,是性能优化的重点。我们曾通过优化过滤策略,将5000节点集群的调度延迟从2秒降低到200毫秒。
1.2 核心调度策略详解
1.2.1 最少请求优先(LeastRequestedPriority)
这个策略的评分公式为:
code复制score = (cpu((capacity - requested)/capacity) + memory((capacity - requested)/capacity))/2 * 10
其中requested是该节点上所有Pod申请资源的总和,capacity是节点总资源。这个策略倾向于选择资源剩余更多的节点,实现负载均衡。
1.2.2 平衡资源分配(BalancedResourceAllocation)
该策略的计算方式为:
code复制score = 10 - abs(cpuFraction - memoryFraction)*10
cpuFraction和memoryFraction分别是CPU和内存的使用比例。这个策略追求的是节点的CPU和内存使用率尽可能接近,避免出现CPU用满但内存空闲,或者相反的情况。
1.2.3 镜像本地化(ImageLocalityPriority)
对于需要拉取大型容器镜像的场景(比如深度学习训练),这个策略能显著提升Pod启动速度。其评分规则是:
- 如果节点已存在Pod所需的所有镜像:最高分(10分)
- 部分镜像存在:按已有镜像大小比例给分
- 没有所需镜像:0分
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高级调度实战技巧
2.1 亲和性与反亲和性配置
在实际生产环境中,我们经常需要控制Pod的分布策略。比如:
- 服务高可用:将同一服务的Pod分散在不同可用区
yaml复制affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [web-server]
topologyKey: topology.kubernetes.io/zone
- 数据本地化:将计算任务调度到存储所在的节点
yaml复制affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [data-service]
topologyKey: kubernetes.io/hostname
2.2 自定义调度器开发
当内置调度策略无法满足需求时,可以通过三种方式扩展:
- 调度器Extender:通过webhook方式扩展过滤和打分逻辑
go复制type ExtenderArgs struct {
Pod *v1.Pod
Nodes []*v1.Node
NodeNames *[]string
}
type ExtenderFilterResult struct {
Nodes []*v1.Node
NodeNames *[]string
Error string
}
- 多调度器共存:为特殊Pod配置不同的调度器
yaml复制apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
schedulerName: gpu-scheduler
containers:
- name: cuda-container
image: nvidia/cuda
- 完全自定义调度器:实现watch机制和绑定API
python复制def scheduler_loop():
watcher = watch.Watch()
for event in watcher.stream(client.list_pods_to_schedule):
pod = event['object']
nodes = filter_nodes(pod)
best_node = score_nodes(pod, nodes)
bind_pod_to_node(pod, best_node)
3. 性能优化与问题排查
3.1 大规模集群调度优化
当集群规模超过1000节点时,调度器可能成为性能瓶颈。我们通过以下方案解决:
- 分区调度:将集群划分为多个调度分区
bash复制kube-scheduler --policy-config-file=policy.json
policy.json配置示例:
json复制{
"kind": "Policy",
"predicates": [
{"name": "RegionMatches", "argument": {"serviceRegions": ["east"]}}
]
}
- 缓存优化:调整调度器缓存大小
yaml复制apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: NodeResourcesFit
args:
ignoredResources: ["example.com/special-resource"]
3.2 常见调度问题排查
- Pod一直处于Pending状态
bash复制kubectl describe pod <pod-name> | grep -A 10 Events
典型原因包括:
- 没有满足资源要求的节点(检查节点资源)
- 不满足亲和性规则(检查nodeAffinity)
- 存在污点限制(检查Taint和Toleration)
- 调度延迟过高
bash复制kubectl get --raw /metrics | grep scheduler_
关键指标:
- scheduler_pending_pods
- scheduler_scheduling_algorithm_duration_seconds
- scheduler_binding_duration_seconds
- 资源碎片问题
bash复制kubectl top nodes
kubectl describe nodes | grep -A 5 Allocated
解决方案:
- 设置合适的Pod资源请求(request)
- 使用Descheduler定期重平衡
- 配置合适的Pod优先级
4. 面试深度问题解析
4.1 典型面试题剖析
问题: 当多个Pod同时需要调度时,kube-scheduler如何处理竞争?
要点解析:
- 调度队列采用优先级排序(PriorityClass)
- 对于相同优先级的Pod:
- 先创建的Pod先调度(FIFO)
- 可通过设置queueSort插件修改排序策略
- 调度过程是串行的,通过调度周期(scheduling cycle)和绑定周期(binding cycle)分离
问题: 如何保证关键Pod一定能被调度?
解决方案:
- 使用PriorityClass提升优先级
yaml复制apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
- 配置抢占机制(preemption)
yaml复制apiVersion: v1
kind: Pod
metadata:
name: important-pod
spec:
priorityClassName: high-priority
containers:
- name: nginx
image: nginx
4.2 调度器扩展实践
场景: 需要根据节点实时负载(非Kubernetes管理的指标)进行调度
实现方案:
- 通过Metrics Server收集自定义指标
- 实现自定义调度插件:
go复制type LoadAwarePlugin struct{}
func (p *LoadAwarePlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
nodeLoad := getNodeLoad(nodeInfo.Node())
if nodeLoad > threshold {
return framework.NewStatus(framework.Unschedulable, "high load")
}
return nil
}
func (p *LoadAwarePlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
load := getNodeLoad(nodeName)
return 100 - load, nil
}
- 注册插件到调度器配置:
yaml复制apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
plugins:
filter:
enabled:
- name: LoadAware
score:
enabled:
- name: LoadAware
pluginConfig:
- name: LoadAware
args:
threshold: 80
在万级节点的生产集群中,我们通过这种自定义调度策略将节点CPU利用率标准差从35%降低到15%,显著提升了资源利用效率。
