上周帮一家做在线教育的客户查故障,用户反馈直播课频繁卡顿、延迟高,后台监控显示 CPU 利用率忽高忽低,但单台机器负载并不爆表。一通排查下来,问题出在资源调度策略上——Kubernetes 的 HorizontalPodAutoscaler(HPA)只盯着 CPU 平均值,没考虑流量突发时 Pod 分布不均,结果 3 台节点里有 2 台挤了 8 个高并发课程服务 Pod,另一台空跑着 3 个低负载管理 Pod。
调度器“看不见”的资源争抢
很多人以为只要集群总资源够,调度就稳。其实不然。比如 YARN 或 K8s 的默认调度器,在 Pod 或 Container 启动时只检查节点是否有足够 CPU 和内存“额度”,却不会细看当前节点的网络带宽是否已被占满、磁盘 IO 是否被日志写入拖慢、甚至 NUMA 节点内存是否跨区访问。一个典型的例子:某次大促前扩容了 10 个订单处理 Pod,全部被调度到同一物理机的同一个 NUMA 节点上,结果 Redis 客户端频繁超时——不是 CPU 不够,是本地内存带宽打满了。
亲和性与反亲和性配置错位
曾见过一套微服务系统把所有网关、认证、限流组件全设成 requiredDuringSchedulingIgnoredDuringExecution 强制同节点部署,美其名曰“降低延时”。结果某天认证服务因 GC 暂停 2 秒,整个节点上的网关也跟着抖动,用户登录直接 503。更隐蔽的是反亲和性漏配:订单服务 Pod 默认允许同节点部署,而它依赖的 MySQL 连接池又用了连接复用,导致多个 Pod 共享少数几个长连接,一旦连接中断,批量重连风暴瞬间打垮数据库代理层。
资源请求(requests)与限制(limits)倒挂
这是最常被忽略的硬伤。开发习惯性写:
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "2Gi"
cpu: "2"看着好像留了余量,实则埋雷。K8s 调度器只按 requests 分配节点,64Mi 内存能塞进任何机器,但运行中一旦内存涨到 1.5Gi,就会触发 OOM Kill;而 CPU limit 设为 2 核,实际业务峰值需要 1.8 核持续运行,结果被 throttled,响应时间从 80ms 拉到 1200ms。真实案例:某支付回调服务因这个配置,凌晨批量对账时反复重启,对账失败率飙升。
节点污点(Taint)与容忍(Toleration)误用
为隔离测试流量,运维给几台节点打了 env=test:NoSchedule 污点,但忘了给测试环境的 Deployment 加对应容忍。结果所有测试 Pod 全被调度到生产节点上,半夜压测直接把核心交易链路的 IO 打满。还有一次,DBA 给数据库节点加了 dedicated=db:NoExecute,但应用侧 Toleration 写成了 dedicated=database:NoExecute(少了个 b),调度器直接无视,关键数据服务跑到了中间件节点上,磁盘写入延迟翻倍。
自定义调度器插件失效
某金融客户用上了自研的“按地域就近调度”插件,用来保证用户请求落到离自己最近的缓存集群。上线半年一直正常,直到某次内核升级后,插件调用的 cgroups v1 接口被废弃,插件静默降级成默认调度器,结果上海用户请求被分到北京节点,缓存命中率从 92% 掉到 63%,接口平均耗时多出 47ms。