Kubernetes-调度器(1)
在 Kubernetes 中,调度器(Scheduler)是集群的 “大脑”,负责将 Pod 合理分配到集群节点上;而亲和性(Affinity)则是精细控制 Pod 调度位置的核心工具。本文将结合我在离线单节点环境下的完整实战,从原理到代码,带你彻底掌握调度器与亲和性的配置、排障与最佳实践。
🎯 一、Kubernetes 调度器核心原理
1.1 调度器的工作流程
Kubernetes 默认调度器 kube-scheduler 的核心任务是为每个待调度 Pod 选择最优节点,分为以下阶段:
- 过滤(Filtering):通过一系列 “预选策略” 排除不满足条件的节点(如资源不足、节点污点)。
- 打分(Scoring):对通过过滤的节点按 “优选策略” 打分,得分最高的节点被选中。
- 绑定(Binding):将 Pod 与选中的节点绑定,完成调度。
1.2 自定义调度器的价值
默认调度器满足大多数场景,但在离线、单节点或特殊业务需求下,自定义调度器可以:
- 绕过 DNS 依赖,直接通过 API 绑定 Pod。
- 实现更灵活的节点选择逻辑(如固定调度到特定节点)。
- 适配无
jq、无网络的极端离线环境。
1.3 无依赖自定义调度器实战
在离线环境中,我们可以用纯 Shell 脚本实现一个轻量自定义调度器,核心逻辑如下:
#!/bin/bash
SERVER='localhost:8001'
while true; do
# 获取待调度 Pod(schedulerName=my-scheduler 且未分配节点)
PODS=$(kubectl --server $SERVER get pods -o custom-columns=NAME:.metadata.name,SCHEDULER:.spec.schedulerName,NODE:.spec.nodeName --no-headers | awk '$2 == "my-scheduler" && $3 == "<none>" {print $1}')
for PODNAME in $PODS; do
# 获取节点列表并随机选择
NODES=$(kubectl --server $SERVER get nodes -o custom-columns=NAME:.metadata.name --no-headers)
CHOSEN=$(echo "$NODES" | shuf -n 1)
# 绑定 Pod 到节点
curl --header "Content-Type:application/json" \
--request POST \
--data '{"apiVersion":"v1","kind":"Binding","metadata":{"name":"'$PODNAME'"},"target":{"apiVersion":"v1","kind":"Node","name":"'$CHOSEN'"}}' \
http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
echo "$(date +'%Y-%m-%d %H:%M:%S') - Assigned $PODNAME to $CHOSEN"
done
sleep 1
done
💡 关键优化:用
kubectl custom-columns+awk替代jq,纯原生工具适配离线环境;单节点环境下自动绑定到唯一节点172-16-2-47。
📋 二、亲和性与反亲和性深度解析
2.1 亲和性的设计目标
亲和性(Affinity)解决了 nodeSelector 功能单一的问题,支持:
- 软约束 / 硬约束:区分 “必须满足” 和 “尽量满足” 的调度规则。
- Pod 级亲和性:基于已有 Pod 的标签进行调度(如 “和 A Pod 同节点” 或 “不和 B Pod 同节点”)。
- 拓扑域控制:按节点、机房等拓扑维度进行调度。
2.2 核心类型与对比
| 类型 | 硬约束(Required) | 软约束(Preferred) |
|---|---|---|
| 节点亲和性(NodeAffinity) | 必须调度到匹配节点,否则 Pod Pending | 尽量调度到匹配节点,无匹配则忽略 |
| Pod 亲和性(PodAffinity) | 必须和指定 Pod 同拓扑域,否则 Pod Pending | 尽量和指定 Pod 同拓扑域,无匹配则忽略 |
| Pod 反亲和性(PodAntiAffinity) | 必须不和指定 Pod 同拓扑域,否则 Pod Pending | 尽量不和指定 Pod 同拓扑域,无匹配则忽略 |
2.3 硬反亲和性单节点实战
在单节点环境下,硬反亲和性的行为需要特别注意:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-2
topologyKey: kubernetes.io/hostname
⚠️ 单节点特性:如果集群中存在
app=pod-2的 Pod,当前 Pod 会永久 Pending(无其他节点可选);若不存在则正常调度。
🛠️ 三、常见问题与排障指南
3.1 YAML 语法错误
现象:部署时提示 error converting YAML to JSON: yaml: line X: did not find expected '-'。原因:列表项缺少 - 前缀或缩进错误。解决:
- 确保
requiredDuringSchedulingIgnoredDuringExecution等列表项前有-。 - 统一使用 2 个空格缩进,避免 Tab 与空格混用。
3.2 自定义调度器无法绑定 Pod
现象:调度器日志显示 curl 失败,Pod 一直处于 Pending。原因:未启动 kubectl proxy 或 API 权限不足。解决:
# 启动 API 代理
kubectl proxy --port=8001 &
# 验证 API 连通性
curl http://localhost:8001/version
3.3 亲和性规则不生效
现象:Pod 未按亲和性规则调度。原因:标签不匹配、拓扑域错误或硬约束无法满足。解决:
# 检查节点标签
kubectl get nodes --show-labels
# 检查 Pod 标签
kubectl get pods --show-labels
# 查看调度事件
kubectl describe pod <pod-name> | grep "Events" -A 10
📊 四、生产环境最佳实践
4.1 调度器选型
- 默认调度器:满足 90% 以上场景,无需额外维护。
- 自定义调度器:仅在特殊需求下使用(如离线环境、固定节点调度)。
- 第三方调度器:如 Volcano、Scheduler-Plugins,适合复杂业务场景。
4.2 亲和性配置原则
- 优先软约束:除非必须,否则用
preferredDuringSchedulingIgnoredDuringExecution避免 Pod Pending。 - 拓扑域选择:多可用区场景下用
topologyKey: topology.kubernetes.io/zone实现跨机房容灾。 - 标签规范:统一标签命名(如
app、role),确保亲和性规则可维护。
4.3 离线环境适配
- 镜像本地化:将所有镜像提前导入节点,避免拉取失败。
- 工具无依赖:用
kubectl、awk、shuf替代jq等第三方工具。 - 单节点验证:在单节点环境下测试硬约束,确保极端场景下的稳定性。
🎓 五、总结与思考
通过本次离线单节点环境的实战,我们不仅掌握了调度器与亲和性的核心原理,更深入理解了 Kubernetes 调度系统的灵活性与复杂性。在实际生产中,我们需要:
- 平衡灵活性与稳定性:自定义调度器带来灵活性,但增加了维护成本;默认调度器稳定,但功能有限。
- 精细化调度控制:亲和性是一把双刃剑,合理使用可以提升资源利用率,滥用则会导致调度失败。
- 离线场景预案:在无网络、无依赖的环境下,原生工具与 Shell 脚本是最可靠的选择。
掌握调度器与亲和性,不仅能让你在 Kubernetes 运维中更游刃有余,更能为大规模集群的资源优化与容灾设计打下坚实基础。
更多推荐


所有评论(0)