Kubernetes调度
kube-scheduler 是 Kubernetes 集群的默认调度器,负责为 Pod 选择最佳节点。它解决的核心问题是:节点资源参差不齐,新 Pod 该放哪——人工分配不现实,调度器按"过滤 + 评分"两阶段自动决策。
调度流程
- 过滤(Filtering):排除不满足硬性条件的节点——资源不够、端口冲突、不容忍节点污点的节点直接出局
- 评分(Scoring):对剩余节点按策略打分(默认倾向资源均衡),选最高分节点绑定
绑定是"写入决定"而非"物理操作":调度器把 Pod 与节点写进 etcd,真正启动容器的是节点上的 kubelet。
节点选择器
使用 nodeSelector 将 Pod 调度到带有特定标签的节点——这是最简单的定向调度手段:给节点打标签,Pod 声明"只去带这个标签的节点"。
节点标签管理
# 添加标签
kubectl label nodes node1 disktype=ssd
# 查看标签
kubectl get nodes --show-labels
# 删除标签
kubectl label nodes node1 disktype-Pod 使用节点选择器
spec:
nodeSelector:
disktype: ssd # 匹配标签为 disktype=ssd 的节点
containers:
- name: app
image: app:v1nodeSelector 的边界
nodeSelector 只能做"标签值精确相等"匹配,表达不了"磁盘要快且最好是专属节点"这类组合条件;且只要没有节点满足标签,Pod 就一直 Pending。更灵活的节点亲和性(nodeAffinity)支持范围、排除等表达式,复杂场景应使用它。
污点和容忍
Taint(污点)和 Toleration(容忍)配合使用,控制 Pod 调度到特定节点。污点是打在节点上的"排斥标记"(有污点的节点默认不接新 Pod),容忍是 Pod 上的"豁免声明"(声明我能忍受这个污点)。用途和 nodeSelector 相反:nodeSelector 是 Pod 挑节点,污点是节点挑 Pod。
污点效果
| 效果 | 说明 | 对已有 Pod 的影响 |
|---|---|---|
| NoSchedule | 不调度新 Pod | 不影响 |
| PreferNoSchedule | 尽量不调度 | 不影响 |
| NoExecute | 不调度并驱逐 | 会驱逐 |
设置污点
# 添加污点
kubectl taint nodes node1 key=value:NoSchedule
# 添加 NoExecute 污点(会驱逐已有 Pod)
kubectl taint nodes node1 key=value:NoExecute
# 删除污点(末尾加 -)
kubectl taint nodes node1 key=value:NoSchedule-Pod 容忍配置
spec:
tolerations:
# 精确匹配
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
# 通配符匹配
- key: "node-type"
operator: "Exists"
effect: "NoExecute"容忍配置说明:
| 字段 | 说明 |
|---|---|
| key | 污点 key |
| operator | Equal(值匹配)或 Exists(存在即可) |
| value | 污点值(operator 为 Equal 时需要) |
| effect | NoSchedule / PreferNoSchedule / NoExecute |
| tolerationSeconds | NoExecute 效果的持续时间 |
常见场景
专用节点(只运行特定应用): 给节点打 NoSchedule 污点,只有声明容忍的应用才能调度上去:
kubectl taint nodes node1 dedicated=app:NoScheduleMaster 节点保护: 控制面节点默认带 node-role.kubernetes.io/master:NoSchedule 污点,普通业务 Pod 不会占用控制面资源:
kubectl taint nodes master node-role.kubernetes.io/master:NoSchedule有状态应用的节点亲和性: 数据库等对磁盘敏感的应用固定到专属节点:
kubectl taint nodes node1 dedicated=mysql:NoSchedule资源限制
为容器设置 CPU 和内存的请求(requests)和限制(limits)。requests 是调度承诺(调度器按它选节点,选完就预留),limits 是运行上限(运行时超了要受罚)。
requests vs limits
| 字段 | 作用 | 说明 |
|---|---|---|
| requests | 调度依据 | K8s 据此选择有足够资源的节点 |
| limits | 运行上限 | 超过会被限制或 OOM Kill(内存耗尽时被内核强制杀死) |
spec:
containers:
- name: app
image: app:v1
resources:
limits:
cpu: "500m" # 上限,可突破(短暂)
memory: "256Mi" # 上限,不可突破(OOM)
requests:
cpu: "100m" # 调度参考
memory: "64Mi" # 调度参考单位:
| 资源 | 单位 | 说明 |
|---|---|---|
| CPU | m(毫核)或核 | 100m = 0.1 核 |
| Memory | Ki / Mi / Gi | 1024 进制 |
CPU 可超卖、内存不可超卖
CPU 是可压缩资源:容器超过 CPU limits 只是被降速节流,进程继续运行;内存是不可压缩资源:超过内存 limits 只能被内核 OOM Kill,所以内存 limits 必须按真实峰值设置,别指望"超了降速"。
建议
- requests 设置为实际使用量
- limits 设置为 requests 的 2-4 倍
- 避免 limits 过大导致资源浪费
ResourceQuota
命名空间级别的资源总量限制,所有 Pod 的 requests/limits 总和不得超过配额,超限的资源创建请求会被拒绝:
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota
spec:
hard:
requests.cpu: "4"
requests.memory: "4Gi"
limits.cpu: "8"
limits.memory: "8Gi"LimitRange
容器默认资源限制(未指定时自动应用),避免"忘了写 requests/limits"的 Pod 无限占用节点:
apiVersion: v1
kind: LimitRange
metadata:
name: limits
spec:
limits:
- type: Container
default:
cpu: "200m"
memory: "128Mi"
defaultRequest:
cpu: "100m"
memory: "64Mi"最佳实践
- 为所有容器设置 resource requests
- 使用 LimitRange 设置命名空间默认值
- 使用 ResourceQuota 限制命名空间总量
常用命令
# 查看节点资源使用
kubectl top nodes
# 查看 Pod 资源使用
kubectl top pods
# 查看调度相关事件
kubectl get events --field-selector involvedObject.name=pod-name
# 查看 Pod 调度决策
kubectl describe pod <pod-name> | grep -A 5 "Events:"