资源不够:手动加 Pod 加不过来
业务高峰一来,CPU 飙满、请求排队,你手动改 Deployment 的副本数、kubectl apply,再盯着看有没有压下去——峰值一过又得手动缩回来,不然白白浪费钱。这种手动伸缩在流量平稳时还行,但流量是波峰波谷的,半夜流量突然暴涨你不可能爬起来敲命令。这就是为什么 K8s 里有一套自动伸缩机制。
K8s 的弹性伸缩分两层:Pod 层自动加副本(HPA),节点层自动加机器(Cluster Autoscaler)。这一篇把这两层都讲清楚。
手动伸缩适合小流量稳定业务;波峰波谷大的业务,必须上自动伸缩。
- HPA按 CPU/内存/自定义指标自动增减 Pod 副本。
- 资源配额限制每个服务能用多少 CPU 内存,防争抢。
- 节点伸缩Pod 排不下时自动加一台机器。
- 冷启动新 Pod 刚起来还不能立刻接流量。
HPA:按指标自动伸缩 Pod
HPA(水平 Pod 自动伸缩)盯着一个指标——比如 CPU 使用率——超过阈值就加副本,低于阈值就减。配起来大概长这样:
# HPA 关键思路
- 目标:CPU 平均使用率保持在 60% 左右
- 最低副本数:2(保底)
- 最高副本数:10(上限,别无限加)
- 指标:容器 CPU 利用率
这里有个前提:每个 Pod 必须声明了 requests(申请的资源),不然 HPA 没法算利用率。所以想上 HPA,先把每个 Deployment 的 resources.requests 配齐。
资源配额:别让一个服务吃光整节点
集群里很多服务跑在同一批节点上,如果没有限制,一个失控的服务可能把整台机器的 CPU 吃光,邻居全被拖死。资源配额(ResourceQuota)就是给每个命名空间画个预算:
配额的两个作用:
- limits:单个 Pod 最多用多少(防止吃满)
- requests:调度时按这个值占座(保证它有资源跑)
- 命名空间级:整个项目最多用多少 CPU/内存
很多人只配 requests 不配 limits,结果一个 Pod 内存泄漏把节点拖垮。requests 决定它能不能跑起来,limits 决定它最多能闹多大——两个都要配。
节点自动伸缩:Pod 排不下时补机器
HPA 把 Pod 副本从 2 加到 10,但如果集群里没空闲节点了,新 Pod 会一直卡着起不来。这时候就要靠节点自动伸缩(Cluster Autoscaler)——它发现有 Pod 排不下,就去云厂商开一台新机器加进集群;反过来节点长期闲着,它会再退掉:
两层伸缩配合:
1. HPA 先扩 Pod 副本
2. 节点不够 → Cluster Autoscaler 加机器
3. 流量回落 → HPA 缩 Pod
4. 节点长期空 → 退机器
注意节点伸缩比 Pod 伸缩慢很多——开一台新机器要几分钟。所以HPA 的扩容要比流量预测略激进,给节点伸缩留出补位时间。
冷启动:刚扩完还得等
HPA 加了新 Pod,但新 Pod 不是立刻就能接流量的——它要拉镜像、启动应用、连数据库、加载缓存,整个过程可能要几十秒到几分钟。在这段时间里,流量还在涨,旧 Pod 已经快扛不住了,新 Pod 又没就绪。这就是冷启动问题。
缓解办法:尽量保持一个保底副本数(minReplicas)略高于平峰需求,不要把下限设得太低;应用启动速度本身也要优化,镜像做小、预热缓存,缩短就绪时间。
伸缩抖动:别来回扩缩
还有个坑叫伸缩抖动:流量一涨立刻扩,一回落立刻缩,结果副本数在 3 和 6 之间反复横跳,每次扩缩都带冷启动开销,反而更慢。
HPA 有一个稳定窗口(stabilization window)——缩容时不会马上缩,而是观察一段时间再决定。扩容要快、缩容要慢,这是经验法则。这样流量小幅波动不会来回折腾。
一套最小自动伸缩方案
如果你现在 K8s 还是手动调副本,按这个最小路径:
第一步:给每个服务配 resources
- requests 和 limits 都配好
第二步:给核心服务加 HPA
- minReplicas=2, maxReplicas=10
- 目标 CPU 60%
第三步:开节点自动伸缩
- 让排不下的 Pod 能补上机器
第四步:观察一两周
- 看抖动严不严重、冷启动够不够快
- 微调阈值和稳定窗口
弹性伸缩不是设完就完事,要靠真实流量反复调。但「先上 HPA 解决手动加副本」这一步,已经能让你从半夜爬起来盯流量的痛苦里解脱出来。配合前面讲的可观测和容量压测,整个集群的资源利用和扛峰值能力就上来了。