Kubernetes 调度
NodeSelector、亲和性、反亲和性、污点容忍、拓扑分布和调度失败排查。
Kubernetes 调度
调度器负责为 Pending Pod 选择运行节点。它会先过滤不满足条件的节点,再对候选节点打分,最后将 Pod 绑定到得分最高的节点。
调度影响因素
| 因素 | 说明 |
|---|---|
| requests | 节点可分配资源必须满足 Pod requests |
| nodeSelector | Pod 只能调度到匹配标签的节点 |
| nodeAffinity | 更灵活的节点亲和性规则 |
| podAffinity | 让 Pod 调度到已有目标 Pod 所在的拓扑域 |
| podAntiAffinity | 让 Pod 避开已有目标 Pod 所在的拓扑域 |
| taints / tolerations | 节点排斥不容忍污点的 Pod |
| topologySpreadConstraints | 控制 Pod 在拓扑域中的分布 |
| PVC / StorageClass | 存储拓扑可能限制可调度节点 |
nodeSelector
给节点打标签:
kubectl label node worker-01 node-role=frontend
Pod 指定节点标签:
spec:
# 通过节点标签做最简单的硬性调度约束。
nodeSelector:
# 只调度到带有 node-role=frontend 标签的节点。
node-role: frontend
nodeSelector 简单直接,但表达能力有限。
Node Affinity
硬性节点亲和性:
affinity:
# 节点亲和性,根据节点标签选择节点。
nodeAffinity:
# 硬性规则,不满足时 Pod 不会被调度。
requiredDuringSchedulingIgnoredDuringExecution:
# 节点选择条件列表。
nodeSelectorTerms:
# 一个 term 内的 matchExpressions 需要同时满足。
- matchExpressions:
# 匹配节点标签 key。
- key: node-role
# In 表示标签值必须在 values 列表中。
operator: In
# 允许的标签值。
values:
- frontend
软性节点亲和性:
affinity:
nodeAffinity:
# 软性规则,不满足也可以调度,但满足时得分更高。
preferredDuringSchedulingIgnoredDuringExecution:
# 偏好权重,范围 1-100。
- weight: 100
# 偏好条件。
preference:
matchExpressions:
- key: zone
operator: In
values:
- zone-a
Pod 亲和性
Pod Affinity 根据已有 Pod 的标签来决定新 Pod 应该调度到哪些拓扑域。它常用于让业务 Pod 靠近依赖服务,减少跨节点或跨可用区访问延迟。
让当前 Pod 尽量调度到已有 app=redis 的节点上:
affinity:
# Pod 亲和性,根据已有 Pod 的标签选择拓扑域。
podAffinity:
# 软性规则,满足时优先调度到对应拓扑域。
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
# Pod 亲和条件。
podAffinityTerm:
# 匹配已有 Pod 的标签。
labelSelector:
matchLabels:
app: redis
# 拓扑域。hostname 表示同一节点。
topologyKey: kubernetes.io/hostname
硬性 Pod 亲和性:
affinity:
podAffinity:
# 硬性规则,不满足时 Pod 不会被调度。
requiredDuringSchedulingIgnoredDuringExecution:
# 匹配已有 Pod 的标签。
- labelSelector:
matchLabels:
app: redis
# 拓扑域。hostname 表示同一节点。
topologyKey: kubernetes.io/hostname
字段说明:
| 字段 | 说明 |
|---|---|
labelSelector | 匹配已有 Pod 的标签 |
topologyKey | 拓扑域标签,例如节点、可用区、地域 |
requiredDuringSchedulingIgnoredDuringExecution | 硬性规则,不满足就不调度 |
preferredDuringSchedulingIgnoredDuringExecution | 软性偏好,不满足也可以调度 |
使用建议:
- 优先使用软性亲和,避免依赖 Pod 不存在时导致 Pending。
topologyKey: kubernetes.io/hostname表示靠近到同一节点。topologyKey: topology.kubernetes.io/zone表示靠近到同一可用区。- 不要为了“靠近”牺牲可用性,关键服务通常更需要分散。
Pod 反亲和性
让同一应用副本尽量分散:
affinity:
# Pod 反亲和性,根据已有 Pod 的标签避开拓扑域。
podAntiAffinity:
# 软性规则,满足时尽量避开对应拓扑域。
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
# Pod 反亲和条件。
podAffinityTerm:
# 匹配已有 Pod 的标签。
labelSelector:
matchLabels:
app: order-api
# 拓扑域。hostname 表示尽量不要和匹配 Pod 落在同一节点。
topologyKey: kubernetes.io/hostname
生产服务建议至少按节点分散,关键服务可以按可用区分散。
Pod 亲和性和反亲和性的差异:
| 类型 | 目标 |
|---|---|
podAffinity | 靠近匹配标签的 Pod |
podAntiAffinity | 避开匹配标签的 Pod |
污点和容忍
给节点添加污点:
kubectl taint nodes worker-01 dedicated=infra:NoSchedule
Pod 添加容忍:
# 容忍列表,允许 Pod 调度到带有匹配污点的节点。
tolerations:
# 污点 key。
- key: dedicated
# Equal 表示 key 和 value 都要匹配。
operator: Equal
# 污点 value。
value: infra
# 污点效果,需要和节点污点 effect 匹配。
effect: NoSchedule
污点效果:
| effect | 说明 |
|---|---|
NoSchedule | 不允许新 Pod 调度到该节点 |
PreferNoSchedule | 尽量不调度到该节点 |
NoExecute | 不容忍的已有 Pod 也会被驱逐 |
移除污点:
kubectl taint nodes worker-01 dedicated=infra:NoSchedule-
拓扑分布
控制副本在不同拓扑域中均匀分布:
# 拓扑分布约束,控制副本在不同拓扑域的分散程度。
topologySpreadConstraints:
# 单个拓扑域与其他拓扑域之间允许的最大副本数差值。
- maxSkew: 1
# 拓扑域标签。hostname 表示按节点分布。
topologyKey: kubernetes.io/hostname
# 不满足分布约束时禁止调度。
whenUnsatisfiable: DoNotSchedule
# 只统计匹配该标签的 Pod。
labelSelector:
matchLabels:
app: order-api
常用拓扑键:
kubernetes.io/hostname
topology.kubernetes.io/zone
topology.kubernetes.io/region
调度失败排查
kubectl describe pod <pod-name> -n default
kubectl get events -n default --sort-by='.lastTimestamp'
kubectl get nodes --show-labels
kubectl describe node <node-name>
常见事件和处理:
| 事件 | 处理方向 |
|---|---|
Insufficient cpu | 降低 requests 或扩容节点 |
Insufficient memory | 降低 requests、扩容节点或拆分服务 |
node(s) had untolerated taint | 添加 toleration 或移除节点污点 |
node(s) didn't match Pod's node affinity/selector | 检查节点标签和亲和性配置 |
pod has unbound immediate PersistentVolumeClaims | 检查 PVC、PV、StorageClass |
didn't match pod anti-affinity rules | 调整反亲和性或增加节点 |
调度策略建议
- 普通应用先设置合理 requests,再考虑亲和性。
- 不要滥用硬性亲和性,容易造成 Pending。
- 基础设施组件使用污点和容忍隔离。
- 多副本服务使用反亲和性或拓扑分布提升可用性。
- 有状态服务要同时考虑存储拓扑。
- 生产变更前先用
kubectl describe pod查看历史调度事件。