Appearance
调度与资源
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=webyaml
nodeSelector:
node-role: webnodeSelector 只能做简单的 key=value 匹配。更复杂的条件(如 key in (A, B)、key exists、key 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:NoSchedulePod 要上这个节点就必须写对应的容忍:
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: webmaxSkew 越小分布越均匀。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: webPDB 不是阻止 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 taint | Pod 没有容忍节点上的污点 | 加 toleration 或移除节点污点 |
didn't match node affinity/selector | 节点标签不匹配亲和性规则 | 核节点标签和 Pod 的 nodeSelector/nodeAffinity |
unbound PVC | PVC 还没绑定 PV | 查 PVC 状态和 StorageClass |
node(s) had volume node affinity conflict | PV 的节点亲和性与调度目标不一致 | 云盘、Local PV 常见,盘在 A 区 Pod 要去 B 区 |
pod affinity/anti-affinity | 亲和性或反亲和性约束不满足 | 核当前 Pod 的分布和约束条件 |
调度失败和节点资源不足是两条线。调度失败是"找不到合适的节点放下这个 Pod",节点资源不足是"所有节点都超负荷运行"。前者看 Events 和约束,后者看 kubectl top nodes 和驱逐记录。