Kubernetes 调度、驱逐与节点维护实战

Kubernetes 节点维护最常见的误区,是把 kubectl drain 当作一个没有前置条件的下线按钮。实际能否安全驱逐,取决于资源请求、拓扑分布、PDB、副本、优先级、本地存储和剩余容量。

本文从调度决策开始,讲清楚主动维护、节点压力驱逐和优雅关机之间的区别,并给出可执行的节点维护 Runbook。

一、调度器在决定什么

调度大致分为两个阶段:

  1. Filter:排除不满足资源、亲和性、污点、端口、卷拓扑等条件的节点;
  2. Score:在剩余节点中按资源、分布、亲和性等规则评分。

Pod Pending 时,不要先重启 scheduler,应先查看事件:

kubectl describe pod -n <namespace> <pod>
kubectl get events -n <namespace> --sort-by=.lastTimestamp

常见原因包括:

  • CPU、内存或扩展资源不足;
  • 未容忍节点污点;
  • nodeSelector 或 nodeAffinity 无匹配节点;
  • Pod 反亲和或拓扑分布无法满足;
  • PVC 拓扑冲突;
  • 主机端口冲突;
  • ResourceQuota 或 LimitRange;
  • 节点不可调度。

二、requests与limits决定调度质量

调度器主要根据 requests 安排资源,而不是应用的真实瞬时用量。

requests过小

  • 节点被过度装箱;
  • 运行时发生 CPU 争用和内存压力;
  • HPA 指标失真;
  • 维护时没有足够迁移空间。

requests过大

  • Pod 长期 Pending;
  • 节点利用率看似很低;
  • 容量碎片严重;
  • 扩容成本增加。

建议用历史 P95/P99、启动峰值和业务 SLO 共同决定,而不是复制统一模板。

三、QoS与OOM

QoS 类别包括 Guaranteed、Burstable 和 BestEffort。它能帮助判断内存压力下的风险,但 kubelet 的驱逐排序并不是简单地只按 QoS 名称。

检查:

kubectl get pod -n <namespace> <pod> \
  -o jsonpath='{.status.qosClass}{"\n"}'
kubectl describe node <node>
journalctl -k --since '-30 min' | grep -Ei 'oom|killed process'

区分两类现象:

  • 容器 cgroup 内 OOM:通常显示 OOMKilled;
  • 节点整体内存压力:可能触发 kubelet 驱逐或内核 OOM。

四、节点选择与污点

nodeSelector与nodeAffinity

nodeSelector 简单直接;nodeAffinity 支持必选与偏好条件。生产标签要有稳定语义,例如区域、机型、磁盘类型和资源池,不要把临时状态写成长期调度标签。

taint与toleration

污点用于排斥不合适的 Pod,容忍只是允许调度,不保证一定调度过去。

kubectl describe node <node> | grep -A5 Taints
kubectl taint node <node> maintenance=true:NoSchedule

临时维护污点使用后要记录和清理,避免节点恢复后仍无法接收业务。

五、拓扑分布与高可用

如果副本全部落在同一节点或同一可用区,副本数再多也不能抵御故障。

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: api

设计时需要权衡:

  • DoNotSchedule 能保证分布,但容量不足时会 Pending;
  • ScheduleAnyway 可提高可调度性,但可能牺牲故障域均衡;
  • 标签缺失的节点会导致意外结果;
  • PVC 和存储拓扑也必须匹配。

维护前应确认剩余故障域是否容得下被驱逐副本。

六、优先级与抢占

PriorityClass 可让高优先级 Pod 在资源不足时抢占低优先级 Pod,但它不是免费容量。

使用原则:

  • 只为真正关键的控制面或核心业务设置高优先级;
  • 不要让所有业务都成为最高优先级;
  • 结合配额限制高优先级对象;
  • 观察被抢占业务是否形成重启风暴;
  • 明确 preemptionPolicy。

抢占解决的是“谁先使用有限资源”,不是总体容量不足。

七、PDB能保护什么

PodDisruptionBudget 限制 API 发起的自愿中断,例如 drain。它不能阻止:

  • 节点硬件故障;
  • 内核崩溃;
  • 节点压力驱逐;
  • 直接删除 Pod 或 Deployment;
  • 某些控制器替换。
kubectl get pdb -A
kubectl describe pdb -n <namespace> <name>

常见错误:

  • 单副本业务设置 minAvailable: 1,导致永远无法 drain;
  • 多个 PDB 选择器重叠;
  • 副本本身不健康,可用数不足;
  • maxUnavailable 与滚动发布策略冲突;
  • PDB 有保护但没有足够容量重新调度。

八、理解三种“驱逐”

API发起的驱逐

drain 通过 Eviction API 请求终止 Pod,会考虑 PDB。

节点压力驱逐

kubelet 根据内存、磁盘、inode 和 PID 压力主动终止 Pod。硬驱逐可能不遵循 Pod 的正常终止宽限期,也不受 PDB 保护。

抢占

scheduler 为高优先级 Pod 选择牺牲低优先级 Pod,目标是释放调度资源。

三者触发方、保护机制和日志位置不同,排障时必须先分类。

九、节点压力阈值

常见信号:

  • memory.available;
  • nodefs.available;
  • nodefs.inodesFree;
  • imagefs.available;
  • imagefs.inodesFree;
  • pid.available。
kubectl describe node <node>
df -hT
df -ih
free -h
ps -e --no-headers | wc -l
journalctl -u kubelet --since '-30 min' --no-pager

不要只把阈值调得更低来消除告警。需要治理镜像、日志、emptyDir、进程泄漏和请求配置。

十、标准节点维护流程

1. 维护前评估

  • 是否有控制面、etcd 或关键 DaemonSet;
  • 是否有 Local PV;
  • 是否有单副本业务;
  • PDB 是否允许驱逐;
  • 剩余节点是否有足够 CPU、内存、GPU 和存储拓扑;
  • 维护窗口和回退条件;
  • 节点恢复后需要哪些验证。

2. cordon

kubectl cordon <node>
kubectl get node <node>

cordon 只阻止新 Pod 调度,不会迁移已有 Pod。

3. 查看节点工作负载

kubectl get pods -A -o wide --field-selector spec.nodeName=<node>
kubectl get pdb -A

标记有状态、单副本、Local PV、emptyDir、GPU 和长任务。

4. drain

kubectl drain <node> \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --timeout=30m

使用 --delete-emptydir-data 表示接受 emptyDir 数据丢失,必须事先确认。不要默认添加 --force 或 --disable-eviction。

5. 验证业务重新落位

kubectl get pods -A -o wide
kubectl get events -A --sort-by=.lastTimestamp

检查 Pending、CrashLoopBackOff、PVC 挂载、服务端点和业务指标。

6. 执行维护

完成内核、运行时、驱动、硬件或配置变更,保留操作日志。

7. 节点恢复

systemctl is-active kubelet containerd
kubectl get node <node> -o wide
kubectl uncordon <node>

恢复后检查 DaemonSet、网络、存储、DNS、监控和真实业务。

十一、优雅节点关机

Linux 节点可由 kubelet 配合 systemd 感知计划关机,并按普通 Pod 和关键 Pod 分阶段终止。

需要审查 kubelet 的:

  • shutdownGracePeriod;
  • shutdownGracePeriodCriticalPods。

同时注意操作系统自动更新工具可能修改关机等待逻辑。应在测试环境验证“系统关机—Pod终止—节点恢复”的完整路径,而不是只检查配置项存在。

十二、DaemonSet与静态Pod

drain 默认忽略 DaemonSet 管理的 Pod,因为删除后仍会在相同节点重建。维护时要判断:

  • CNI、CSI、日志和监控 DaemonSet 是否需要优雅停止;
  • 静态 Pod 是否属于控制面;
  • 节点重启后 DaemonSet 是否依赖外部镜像仓库;
  • 新内核或运行时是否与节点插件兼容。

控制面节点的维护必须逐台执行,并保持 etcd 仲裁与 API Server 可用。

十三、Local PV与长任务

Local PV

Pod 通常不能随意迁移到其他节点。维护前必须确认数据副本、恢复方式和节点可恢复时间。

训练、批处理和长任务

可考虑:

  • checkpoint;
  • Job 重试;
  • 维护前停止接收新任务;
  • 设置合理 terminationGracePeriodSeconds;
  • 在任务平台层做迁移和续跑。

Kubernetes 驱逐不会自动为业务生成可恢复检查点。

十四、Descheduler的定位

Descheduler 可以根据策略驱逐已经运行但分布不合理的 Pod,让 scheduler 重新安排。

使用前要明确:

  • 它会产生真实的 Pod 中断;
  • 仍受 PDB 和可调度容量约束;
  • 不应作为错误 requests 或标签体系的补丁;
  • 策略需要灰度、审计和速率控制。

适合处理节点利用不均、拓扑分布变化和污点策略调整后的存量 Pod。

十五、常见维护故障

drain返回429

通常表示 PDB 不允许更多中断。检查副本健康、PDB 选择器、可用数和滚动发布状态。

Pod驱逐后一直Pending

检查剩余容量、污点、亲和性、拓扑、PVC 和主机端口。节点维护前的模拟调度不足是常见原因。

Pod长时间Terminating

检查 finalizer、preStop、terminationGracePeriodSeconds、存储卸载、进程不响应和 API 连接。不要第一时间强制删除有状态 Pod。

uncordon后节点仍不接收Pod

检查额外污点、标签、节点条件、资源可分配量、调度器事件和 DaemonSet 健康。

十六、维护自动化

自动化脚本至少应实现:

  1. 变更单和审批校验;
  2. 节点与业务预检;
  3. PDB 和容量检查;
  4. cordon、drain;
  5. 超时即暂停;
  6. 执行维护;
  7. 节点健康和业务验证;
  8. uncordon;
  9. 观察窗口;
  10. 输出审计报告。

自动化不能只串联命令,必须包含停止条件和人工接管点。

十七、生产检查清单

调度治理

  • [ ] 资源 requests 有数据依据;
  • [ ] 关键业务跨节点或跨区分布;
  • [ ] 污点、标签和优先级有统一规范;
  • [ ] PDB 与副本和发布策略匹配;
  • [ ] Local PV 和特殊设备有明确策略。

维护前

  • [ ] 剩余容量足够;
  • [ ] PDB允许当前批次驱逐;
  • [ ] 有状态和长任务已确认;
  • [ ] 已定义超时与回退条件;
  • [ ] 控制面和etcd不会同时失去多数实例。

维护后

  • [ ] Node Ready且资源正常;
  • [ ] CNI、CSI、监控、日志DaemonSet恢复;
  • [ ] Pod无异常Pending或Terminating;
  • [ ] DNS、存储、入口和业务指标通过;
  • [ ] 临时污点和维护标记已清理。

参考资料

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-25 22:27:30

results matching ""

    No results matching ""