背景

在 Kubernetes 集群运维中,Pod 的调度策略与资源管理是保障服务稳定性与资源利用率的核心课题。调度器(kube-scheduler)负责为每个新建 Pod 选择最优节点,而资源管理则决定了 Pod 运行时能获得的 CPU 和内存保障。本文从调度策略配置、Resource QoS 层级、资源限制落地三个层面,介绍一套可落地的实践方法。

一、Pod 调度策略配置

1. 节点亲和与反亲和

通过 nodeAffinitypodAntiAffinity 可精确控制 Pod 的部署位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
  • requiredDuringSchedulingIgnoredDuringExecution:硬限制,调度时必须满足
  • preferredDuringSchedulingIgnoredDuringExecution:软限制,优先满足
  • topologyKey: kubernetes.io/hostname:表示Pod应分布在不同节点上,避免单点故障

2. 污点与容忍(Taints & Tolerations)

节点可以通过污点拒绝调度没有对应容忍的 Pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 为专用节点打上污点
kubectl taint nodes node-type=special NoSchedule:NoExecute

# Pod 声明容忍
spec:
tolerations:
- key: "node-type"
operator: "Equal"
value: "special"
effect: "NoSchedule"
- key: "node-type"
operator: "Equal"
value: "special"
effect: "NoExecute"
tolerationSeconds: 300 # 允许被驱逐前运行300秒

3. 优先级与抢占

通过 PriorityClass 实现关键业务的调度优先级:

1
2
3
4
5
6
7
8
9
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 100000
globalDefault: false
---
spec:
priorityClassName: high-priority

调度器在低优先级 Pod 无法调度时,会尝试驱逐(preemption)已调度的低优先级 Pod,为高优先级 Pod 腾出空间。

二、Resource QoS 层级

Kubernetes 根据 Pod 声明的 resource requests/limits,将 Pod 划分为三个 QoS 等级:

QoS 等级 判断条件 调度保障 资源压缩策略
Guaranteed requests == limits(CPU和内存均满足) 最高 最后被压缩
Burstable 有 requests 但不满足 Guaranteed 中等 介于两者之间
BestEffort 未声明任何 requests/limits 无保障 最先被压缩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Guaranteed 示例
resources:
requests:
cpu: "500m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "256Mi"

# Burstable 示例
resources:
requests:
cpu: "200m"
memory: "128Mi"
limits:
cpu: "1"
memory: "512Mi"

建议:将核心服务 Pod 声明为 Guaranteed 等级,避免在资源紧张时被过度压缩。

三、Cgroups 层面的资源限制

Kubernetes 对 Pod 的资源限制,最终通过 Linux Cgroups 实现。以内存限制为例:

1
2
# 查看某 Pod 的 Cgroups 路径
cat /sys/fs/cgroup/memory/kubepods/burstable/pod<UID>/memory.limit_in_bytes

常见问题:内存 limit 触发 OOM

当 Pod 内存使用超过 limits 时,Linux 会触发 OOM Killer 杀死进程,Kubernetes 随后重启容器。常见根因:

  1. JVM/Node.js 等运行时未感知容器内存限制-XX:MaxRAMFraction=1(JDK 8u191+)或 Node --max-old-space-size 需显式配置
  2. 进程间内存泄漏:通过 dmesg -T | grep OOMkubectl describe pod 查看事件
  3. Page Cache 占用过高:内存 limits 包括缓存,需结合 memory.soft_limit_in_bytes 调整

CPU limit 的特殊行为

CPU 以 CFS(Completely Fair Scheduler)周期调度,设置为整数(如 2)时会导致进程饥饿。更推荐的方式:

1
2
3
4
5
resources:
limits:
cpu: "2000m" # 建议不超过 2000m(即 2 核)
requests:
cpu: "500m"

建议:对延迟敏感的服务,尽量只设 CPU requests 而不设 limits,或将 limits 设为较高值(如 requests 的 4 倍),避免触发 CPU throttling。

四、实践建议

  1. 摸清家底:使用 kubectl top nodes 了解集群整体资源使用情况
  2. 分层声明:核心有状态服务(Guaranteed)、中间件(Guaranteed/Burstable)、无状态服务(Burstable/BestEffort)
  3. 配置 LimitRange:在 Namespace 级别设置默认 requests/limits,防止未声明资源的新 Pod 抢占过多资源:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 256Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "4"
memory: 4Gi
  1. 持续调优:通过 kubectl describe node 观察 Pod 打散情况,定期 review requests 与实际使用量的比值(overcommit ratio)。

总结

Kubernetes 的调度与资源管理是一个需要持续优化的过程。调度策略解决的是”Pod 放在哪”的问题,Resource QoS 解决的是”Pod 能跑多稳”的问题,而 Cgroups 则是这两者在 Linux 内核层面的最终落地。运维团队应建立规范的资源声明标准,结合监控与调优实践,在保障服务稳定性的同时最大化集群资源利用率。