一、调度器工作原理
Kubernetes 调度器(kube-scheduler)负责为 Pod 选择最佳运行节点,核心流程:
plaintext
┌─────────────────────────────────────────────────────────────────────┐
│ Pod 创建到调度完整流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. API Server 接收 Pod 创建请求 │
│ ↓ │
│ 2. Pod 进入调度队列(Scheduling Queue) │
│ ↓ │
│ 3. 调度器执行预选(Filtering)→ 筛选可用节点 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 预选条件: 资源充足 | 端口可用 | 选择器匹配 | 容忍匹配 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ 4. 调度器执行优选(Scoring)→ 节点打分排序 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 打分维度: 资源利用率 | 亲和性 | 数据局部性 | 优先级 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ 5. 绑定(Binding)→ 将 Pod 绑定到最优节点 │
│ ↓ │
│ 6. Kubelet 在目标节点拉取并启动容器 │
│ │
└─────────────────────────────────────────────────────────────────────┘
调度决策受多种因素影响:优先级、资源请求、亲和性、污点等。
二、PriorityClass 优先级类
2.1 核心概念
PriorityClass 定义 Pod 的调度优先级,数值越大优先级越高。高优先级 Pod 可抢占低优先级 Pod 的资源。
plaintext
┌─────────────────────────────────────────────────────────────────────┐
│ PriorityClass 抢占机制 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 高优先级 Pod (priority=1000) │
│ │ │
│ ├─→ 调度失败(无合适节点) │
│ │ │
│ └─→ 抢占低优先级 Pod 资源 ──→ 驱逐低优先级 Pod ──→ 调度成功 │
│ ↓ │
│ 低优先级 Pod (priority=100) │
│ 被驱逐后重新进入调度队列 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.2 系统内置 PriorityClass
表格
| 名称 | 优先级值 | 用途 |
|---|---|---|
| system-cluster-critical | 2000000000 | 集群关键组件(kube-system) |
| system-node-critical | 2000001000 | 节点关键组件(kubelet 等) |
注意:数值超过 10 亿的为系统保留,不要自定义这么高的值。
2.3 自定义 PriorityClass
yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 10000
globalDefault: false
description: "生产环境高优先级应用"
---
apiVersion: v1
kind: Pod
metadata:
name: high-priority-app
spec:
priorityClassName: high-priority
containers:
- name: app
image: nginx:latest
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
三、nodeSelector 节点选择器
最简单的调度方式,通过标签筛选节点。
bash
# 为节点添加标签
kubectl label node k8s-node-1 disk-type=ssd
kubectl label node k8s-node-2 disk-type=HDD
# 查看节点标签
kubectl get nodes --show-labels
yaml
apiVersion: v1
kind: Pod
metadata:
name: ssd-app
spec:
nodeSelector:
disk-type: ssd # 只会调度到带此标签的节点
containers:
- name: app
image: nginx:latest
四、nodeAffinity 节点亲和性
比 nodeSelector 更强大的节点选择机制,支持硬限制 和软偏好。
4.1 两种策略对比
表格
| 类型 | 行为 | 等同于 |
|---|---|---|
| requiredDuringSchedulingIgnoredDuringExecution | 硬限制,不满足则不调度 | nodeSelector 必选 |
| preferredDuringSchedulingIgnoredDuringExecution | 软偏好,尽量满足 | nodeSelector 首选 |
4.2 YAML 示例
yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-zone-app
spec:
affinity:
nodeAffinity:
# 硬性要求:必须运行在 amd64 架构节点
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
# 软性偏好:优先选择 zone=prod 的节点
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values:
- prod
- weight: 20
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
containers:
- name: app
image: nginx:latest
五、podAffinity 与 podAntiAffinity
5.1 核心概念
- podAffinity:让 Pod 倾向于与某些 Pod 部署在同一区域(同一节点/可用区)
- podAntiAffinity:让 Pod 倾向于与其他 Pod 分开部署
5.2 YAML 示例
yaml
apiVersion: v1
kind: Pod
metadata:
name: web-server
spec:
affinity:
# 希望与 redis Pod 部署在一起
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname # 同一节点
# 避免与其他 web-server 部署在同一节点
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-server
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx:latest
topologyKey 常用值:
kubernetes.io/hostname:同一节点topology.kubernetes.io/zone:同一可用区topology.kubernetes.io/region:同一地域
六、Taints 污点与 Tolerations 容忍
6.1 污点效果类型
表格
| 效果 | 行为 |
|---|---|
| NoSchedule | 不调度新 Pod 到此节点(不影响已有 Pod) |
| PreferNoSchedule | 尽量不调度,不强制 |
| NoExecute | 驱逐已有 Pod,且不再调度新 Pod |
6.2 污点管理命令
bash
# 添加污点(节点不可调度)
kubectl taint node k8s-node-1 dedicated=apps:NoSchedule
# 添加污点(节点不可调度,含特殊值)
kubectl taint node k8s-node-1 key=value:NoExecute --overwrite
# 移除污点
kubectl taint node k8s-node-1 dedicated-
# 查看节点污点
kubectl describe node k8s-node-1 | grep Taints
6.3 Pod 容忍配置
yaml
apiVersion: v1
kind: Pod
metadata:
name: tolerating-app
spec:
tolerations:
# 容忍指定污点
- key: "dedicated"
operator: "Equal"
value: "apps"
effect: "NoSchedule"
# 容忍所有 NoSchedule 污点
- key: "dedicated"
operator: "Exists"
effect: "NoSchedule"
# 容忍所有污点(慎用)
- operator: "Exists"
# 容忍 NoExecute 污点,300秒后驱逐
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300
containers:
- name: app
image: nginx:latest
七、资源限制与调度
调度器根据 requests 评估节点可用资源,而非 limits。
yaml
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx:latest
resources:
requests:
memory: "1Gi" # 调度依据:节点需满足此资源
cpu: "500m"
limits:
memory: "2Gi" # 硬限制:超过则 OOM 或限流
cpu: "1000m"
plaintext
┌─────────────────────────────────────────────────────────────────────┐
│ requests vs limits 调度影响 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ requests (调度依据): │
│ - 调度器计算: Node 可分配资源 = 节点总资源 - 已调度 Pod requests │
│ - Pod 只有 requests ≤ 可分配资源 时才能调度 │
│ │
│ limits (运行时限制): │
│ - 不影响调度决策 │
│ - 仅在容器运行时生效(CPU 限流 / 内存 OOM) │
│ │
│ ⚠️ 如果 requests 未设置: │
│ - 默认等于 limits(若有定义) │
│ - 或默认为 0(完全不受控,风险极高) │
│ │
└─────────────────────────────────────────────────────────────────────┘
八、常见问题与排查
8.1 Pod 一直处于 Pending 状态
bash
# 1. 查看 Pod 详情和事件
kubectl describe pod <pod-name>
# 2. 检查是否有可用节点
kubectl get nodes
# 3. 检查节点资源
kubectl describe node <node-name> | grep -A 5 "Allocated resources"
# 4. 检查调度器日志
kubectl logs -n kube-system kube-scheduler-<pod> --tail=100
常见原因:
- 节点资源不足(requests > 可用资源)
- nodeSelector/nodeAffinity 条件无匹配节点
- 污点无对应容忍
8.2 抢占成功但原 Pod 未被驱逐
bash
# 检查被抢占 Pod 的调度状态
kubectl get pod <pod-name> -o wide
kubectl describe pod <pod-name>
# 查看 Pod 优先级
kubectl get pod <pod-name> -o jsonpath='{.spec.priority}'
注意 :抢占后原 Pod 进入 Terminating 状态,需要等待 kubelet 真正删除才会调度新 Pod。
8.3 调度不均匀
bash
# 查看各节点 Pod 分布
kubectl get pods -o wide --sort-by='.spec.nodeName'
# 检查是否存在软亲和性权重问题
kubectl describe pod <pod-name> | grep -A 10 "Affinity"
九、最佳实践
-
合理设置 PriorityClass 值:生产环境建议 1000-10000,避免占用系统优先级
-
避免资源 requests 过小:设置为实际需求的 80-90%,预留弹性空间
-
优先使用 nodeAffinity 而非污点:污点影响范围大,affinity 更精细
-
生产环境禁用
PreferNoSchedule:该策略不强制,可能导致调度不符合预期 -
DaemonSet 配合容忍使用:确保关键组件能在带污点的 master 节点运行
-
PodAntiAffinity 谨慎使用 :配合
topologyKey: topology.kubernetes.io/zone防止单点故障 -
调度失败及时排查:优先检查事件(kubectl describe)和调度器日志
-
监控调度延迟:调度耗时过长可能影响业务响应,及时优化调度器配置