Skip to content

调度与资源

Kubernetes 调度器的工作不是"找一台有空闲 CPU 的机器放上去"。它要看 requests 和节点可分配资源的匹配、节点选择器、亲和性和反亲和性、污点和容忍、拓扑分布、PVC 绑定位置、端口冲突,还要考虑优先级和抢占。

资源配置写不清楚时,调度器给的 Pod 分布可能和预期完全不同。节点被塞满、驱逐、HPA 扩缩不准,很多时候根因在 requests 和 limits 的设置上。

requests 和 limits

yaml
resources:
  requests:
    cpu: 200m       # 调度器计算节点容量时用的值,也是容器的最低保障
    memory: 256Mi
  limits:
    cpu: 1          # CPU 超过 limit 会被 CFS 限速,不会杀容器
    memory: 512Mi   # 内存超过 limit 可能触发 OOMKill

四者的作用和关系:

字段对调度的影响对运行的影响
requests.cpu调度器按这个值累加判断节点是否够放作为 CPU 份额的权重基准
requests.memory同上kubelet 驱逐时按内存 requests 排序
limits.cpu不影响调度CPU 使用超过此值被限速(throttle),容器继续运行
limits.memory不影响调度内存使用超过此值可能被 OOMKill

CPU 是可压缩资源,超了会慢但不会死。内存是不可压缩资源,超了会被杀。limits.memory 设得太低时 Pod 会被内核 OOM Killer 杀掉,重启计数增加,但 kubectl describe pod 里能看到 OOMKilled 的原因。

完全不写 requests 的 Pod 属于 BestEffort QoS,调度器给它的资源估算是零。节点看起来还有很多"剩余资源",实际上 BestEffort 的 Pod 已经塞满了节点。一旦有 Guaranteed 或 Burstable 的 Pod 需要资源,BestEffort 最先被驱逐。

QoS 等级

QoS条件驱逐优先级
Guaranteed每个容器的 CPU 和内存 request == limit最低
Burstable至少一个容器设置了 request 或 limit,但不满足 Guaranteed中等
BestEffort没有任何 request 或 limit最高(最先被驱逐)

查看 Pod 的 QoS:

bash
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.status.qosClass}{"\n"}'

节点内存压力上来时,kubelet 先驱逐 BestEffort,再 Burstable,最后 Guaranteed。同一 QoS 等级内,内存使用超过 request 更多的 Pod 优先被驱逐。

节点选择

最基础的方式是 nodeSelector

bash
kubectl label node k8s-node01 node-role=web
yaml
nodeSelector:
  node-role: web

nodeSelector 只能做简单的 key=value 匹配。更复杂的条件(如 key in (A, B)key existskey notin (X))需要 nodeAffinity。

亲和性和反亲和性

nodeAffinity 的表达能力比 nodeSelector 强:

yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: node-role
              operator: In
              values:
                - web
                - api        # 带这两个 role 之一的节点都可以

requiredDuringScheduling 是硬条件——不满足就 Pending。preferredDuringScheduling 是软倾向——节点满足更好,不满足也调度。两者的名字里都带 IgnoredDuringExecution,意思是调度之后节点标签变了,Pod 也不会被赶走。

podAntiAffinity 把同应用的副本分散到不同节点:

yaml
affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: web
          topologyKey: kubernetes.io/hostname  # 以节点为拓扑域尽量分散

topologyKey 决定了"分散"的粒度:kubernetes.io/hostname 是按节点分散,topology.kubernetes.io/zone 是按可用区分散。

污点和容忍

污点(Taint)和容忍(Toleration)控制的是"哪些 Pod 不能上这个节点"和"哪些 Pod 可以容忍这个节点上的限制"。

给节点加污点:

bash
kubectl taint nodes k8s-node01 dedicated=monitoring:NoSchedule

Pod 要上这个节点就必须写对应的容忍:

yaml
tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "monitoring"
    effect: "NoSchedule"

三种 effect 的区别:

effect对已运行 Pod对新 Pod
NoSchedule不影响不调度到这个节点(除非有容忍)
PreferNoSchedule不影响尽量不调度到这个节点
NoExecute不容忍的 Pod 立即被驱逐不调度到这个节点

控制平面节点默认带有 node-role.kubernetes.io/control-plane:NoSchedule 污点,普通业务 Pod 不会上去。实验集群为省资源会去掉污点,生产环境保留。

拓扑分布

Topology Spread Constraints 表达的语义和反亲和性接近,但更聚焦于"尽量均匀":

yaml
topologySpreadConstraints:
  - maxSkew: 1                # 同一拓扑域内 Pod 数量差值最多为 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway  # 不满足也不阻塞调度
    labelSelector:
      matchLabels:
        app: web

maxSkew 越小分布越均匀。whenUnsatisfiable: DoNotSchedule 会阻塞调度直到约束满足,写太严格时可能导致 Pod 长时间 Pending。

多可用区场景用 topologyKey: topology.kubernetes.io/zone,配合 maxSkew: 1 可以让副本尽量均匀分布在不同可用区。

PDB

PodDisruptionBudget 保护的是"主动(自愿)驱逐"时的可用性,比如 kubectl drain、节点维护、集群升级:

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web
  namespace: demo
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: web

PDB 不是阻止 Pod 被删,而是限制同一时间可以被自愿驱逐的 Pod 数量。副本数很少的服务(比如只有 2 个副本且 minAvailable: 2),drain 节点时会被卡住——因为驱逐任何一个副本都会让可用数低于 2。

调度失败排查

bash
kubectl describe pod <pod-name> -n <namespace> | grep -A20 Events
kubectl get nodes
kubectl describe node <node-name> | grep -A10 'Allocated resources'

describe pod 的 Events 字段是第一步,会直接写调度失败的原因。常见事件和方向:

事件关键词含义处理方向
Insufficient cpu所有匹配节点的 CPU requests 合计超了降低 requests 或加节点
Insufficient memory同上,内存同上
untolerated taintPod 没有容忍节点上的污点加 toleration 或移除节点污点
didn't match node affinity/selector节点标签不匹配亲和性规则核节点标签和 Pod 的 nodeSelector/nodeAffinity
unbound PVCPVC 还没绑定 PV查 PVC 状态和 StorageClass
node(s) had volume node affinity conflictPV 的节点亲和性与调度目标不一致云盘、Local PV 常见,盘在 A 区 Pod 要去 B 区
pod affinity/anti-affinity亲和性或反亲和性约束不满足核当前 Pod 的分布和约束条件

调度失败和节点资源不足是两条线。调度失败是"找不到合适的节点放下这个 Pod",节点资源不足是"所有节点都超负荷运行"。前者看 Events 和约束,后者看 kubectl top nodes 和驱逐记录。