华为云 CCE 弹性伸缩实战:让 Kubernetes 集群跟着流量自己长大、自己瘦下来
一、为什么需要弹性伸缩:先算一笔账
一个面向用户的 Web 服务,流量很少是均匀的:白天高、深夜低;大促、营销活动、新版本发布时会出现几倍甚至十几倍的峰值。如果按峰值长期预留副本和节点,低谷期的资源利用率常常只有 10%~20%,等于一直在为用不到的算力付费;如果按均值配置,峰值一到,请求排队、响应变慢甚至超时,严重时还会触发服务雪崩。弹性伸缩要解决的,就是让供给跟上需求的变化:流量上来时自动加副本、加节点,流量下去后自动收回来。
在 Kubernetes 里,弹性伸缩分三个层次:Pod 副本数(HPA)、单个 Pod 的资源规格(VPA)、集群节点数量(节点池自动伸缩)。三者不是互相替代,而是各管一摊。下面以华为云容器引擎 CCE 为例,把这三层串起来做一次完整演练。
二、演练准备:一个可被伸缩的应用
弹性伸缩的前提是“应用本身能被伸缩”。先准备一个无状态的示例 Web 应用,用 Deployment 部署,Service 对外暴露:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-web
spec:
replicas: 2
selector:
matchLabels:
app: demo-web
template:
metadata:
labels:
app: demo-web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
这里有三个关键点,缺一不可:
- requests 必须写。HPA 判断 CPU 利用率的分母就是 requests;不写 requests,HPA 根本算不出利用率,等于没有刻度。
- limits 要留余量。limits 是上限,设得过低会在高峰时被限流;设得过高又会让单个 Pod 占满节点。一般建议 limits 是 requests 的 2~5 倍,再根据压测结果调整。
- 就绪探针(readinessProbe)必须配。新扩出来的 Pod 只有通过探针才会被 Service 加入后端,否则流量会被发到还没准备好的 Pod 上,等于“人到了,活还没法干”。
另外确认集群已安装 metrics-server 插件(CCE 集群默认提供),它是 HPA 的“眼睛”,负责采集 Pod 的 CPU、内存使用量。可以用 kubectl top pods 验证:能看到各 Pod 的实时用量,说明指标链路是通的。
三、第一层:HPA——按指标自动增减副本
HPA(Horizontal Pod Autoscaler)是最常用的一层。它的逻辑很直白:每隔一段时间看一次指标,算出期望副本数,公式是:期望副本数 = 当前副本数 ×(当前指标值 ÷ 目标指标值),向上取整。比如当前 2 个副本、平均 CPU 利用率 90%、目标 60%,期望副本数就是 2 × 90 / 60 = 3 个。
下面这份配置把目标定在 CPU 平均利用率 60%、内存 70%,副本数在 2~10 之间浮动,并用 behavior 字段控制扩缩容的节奏:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demo-web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: demo-web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
几个容易踩的点:
- minReplicas 不要设成 1。至少保留 2 个副本,单个节点故障或 Pod 重建时服务才不会中断;多副本也是滚动发布和缩容时不丢流量的底气。
- 扩容要快、缩容要慢。上面配置里,扩容不等待、每分钟最多翻倍;缩容则有 300 秒稳定窗口,每分钟最多缩掉一半。流量高峰往往几分钟内就起来了,等不起;流量回落则常常是抖动,缩太快会引起“扩—缩—扩”的反复横跳。
- 多指标是“或”关系。CPU 和内存只要有一个达到目标,就会触发扩容,取的是两者算出的期望副本数里较大的那个。所以 memory 目标设得过低时,一个内存小高峰也可能把副本数拉上去,调阈值要结合压测看。
- 别指望 HPA 秒级生效。从指标采集、计算、创建 Pod,到镜像拉取、探针通过,整个链路通常需要几十秒到几分钟。对突发流量,HPA 是“救火”,不是“预防”——真正可预期的峰值(比如定时营销活动)要提前用定时伸缩或手动扩容打底。
验证方法:用压测工具对 Service 持续发请求,例如在集群里起一个压测 Pod,或者用 hey、wrk 从集群外打流量。观察 kubectl get hpa -w,能看到 TARGETS 从 60% 附近爬升、REPLICAS 从 2 逐步加到 4、6;停止压测后,副本数在稳定窗口过后慢慢回落,说明整条链路是通的。
四、第二层:VPA——自动调整单个 Pod 的资源规格
有些应用扩副本解决不了问题:单 Pod 的内存需求本身就会随数据量增长,或者 CPU 峰值是串行的、并行度加不上去。这时要用到 VPA(Vertical Pod Autoscaler),它按历史用量自动建议、甚至自动改写 Pod 的 requests 和 limits。
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-web-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: demo-web
updatePolicy:
updateMode: "Initial"
updateMode 有三种,要按场景选:
- Off:只出建议、不改资源,适合先观察一段时间,看看推荐值靠不靠谱。
- Initial:只在 Pod 创建时按推荐值设置 requests,运行中不重建,风险最小。
- Auto:运行中发现规格不匹配就重建 Pod。效果最彻底,但重建意味着 Pod 重启,有状态或长连接的服务要慎用。
VPA 有两条红线:第一,不要对同一个工作负载同时开 HPA(按 CPU/内存)和 VPA 的 Auto 模式——HPA 按利用率算副本数,VPA 又在改 requests 里的分母,两边互相干扰,副本数和规格会一起抖动。第二,VPA 的推荐值依赖历史数据,新上线的应用样本不足时建议可能偏大或偏小,先用 Off 模式跑一两个业务周期再决定。
五、第三层:节点池自动伸缩——集群节点也不够用了怎么办
HPA 把副本数拉到 10 个,但节点上已经没有空闲 CPU 和内存了,新 Pod 只能 Pending。副本数的天花板,最终是节点数。所以第三层是节点池自动伸缩:CCE 里在节点池上开启弹性伸缩、设好节点数量上下限,当 Pod 因资源不足无法调度时,自动创建节点;当节点长期空闲、且上面的 Pod 可以被安全驱逐时,自动缩掉节点。
配节点池自动伸缩时,要注意四件事:
- 上下限要和配额、预算一起算。节点上限不是越大越好,它对应的是真实的 ECS 配额和费用。提前在控制台确认目标可用区的 ECS 配额够不够,顺手给配额和费用设个告警。
- 缩容要给 Pod 留“退路”。给关键应用配 PodDisruptionBudget(PDB),比如 minAvailable: 1,保证缩容驱逐时至少留一个副本在线;有状态或挂了本地盘的 Pod,节点缩容可能驱逐失败,要单独评估。
- 新节点加入需要时间。节点创建、初始化、拉取镜像通常要几分钟,扩容不是即时的。应对可预期的峰值,提前扩节点或把常用镜像做预热,比临时扩容更稳。
- 节点规格要和工作负载匹配。节点池里节点规格差异太大时,调度器可能把大规格 Pod 集中到某类节点上,自动伸缩的判断也会变复杂。一般一个节点池保持同一种规格,特殊需求用单独的节点池加污点(taints)隔离。
六、把三层串起来:一套能落地的组合策略
单用任何一层都不完整。比较稳妥的组合是:HPA 负责分钟级的副本伸缩,节点池自动伸缩负责兜底资源供给,VPA 的 Off/Initial 模式负责定期校准 requests 的基准值,再叠加定时伸缩应对可预期的峰值:
- 日常:HPA(CPU 60%、内存 70%)+ 节点池自动伸缩,minReplicas 2、节点数下限保证基础容量。
- 大促/活动前:提前把 minReplicas 和节点数下限抬高,活动结束后再降回来;也可以用 CCE 的 CronHPA 插件做定时伸缩,让扩容在活动开始前半小时自动发生。
- 每月复盘:看 VPA 的推荐值和实际用量分布,把明显偏大或偏小的 requests 改掉——requests 定得准,HPA 的判断才准,节点利用率也才上得去。
- 成本视角:只在低谷期看“省了多少钱”容易跑偏。更合理的算法是对比同等可用性下的固定配置成本:比如固定 6 副本 × 3 节点,与弹性配置下日均 3 副本 × 2 节点、在峰值时自动顶到 10 副本的成本,前者多花的就是弹性省下来的。把弹性伸缩和云监控 CES 的告警、配额告警配在一起,副本数或节点数冲到上限时能第一时间收到通知,避免“悄悄扩到上限、流量还在涨”的被动局面。
七、排障清单:伸缩不工作时先查这几处
- HPA 的 TARGETS 一直显示 unknown:先看 metrics-server 是否正常,
kubectl top pods能不能出数;再看 Deployment 有没有写 requests——没写 requests 是最常见的原因。 - 副本数涨不上去:检查 maxReplicas 是不是设得太小;检查节点资源够不够,
kubectl describe pod看新 Pod 是不是 Pending;检查配额够不够。 - 副本数反复横跳:多半是缩容太激进或指标抖动。把 scaleDown 的 stabilizationWindowSeconds 拉长(比如 300 秒),缩容策略改成按百分比逐步缩;如果业务本身的 CPU 就是锯齿状,考虑换用自定义指标(比如队列长度、每秒请求数)做 HPA,而不是只看 CPU。
- 节点扩出来了,Pod 还是 Pending:看节点是不是加了污点、Pod 有没有对应的容忍(tolerations);看节点池的节点规格是否满足 Pod 的 requests;看镜像拉取是不是卡住了。
- 缩容时业务抖动:检查 PDB 配得对不对,检查 Pod 有没有配置 preStop Hook 和 terminationGracePeriodSeconds,让旧 Pod 在退出前有时间处理完手头的请求、从 Service 后端摘除。
八、总结:落地前逐项核对
弹性伸缩不是“装个 HPA 就完事”,而是一套组合:requests 写准、探针配好、HPA 管副本、节点池管节点、VPA 管基准、定时伸缩管可预期的峰值。落地之前,可以按这张清单逐项核对:metrics-server 正常、requests/limits 已设置、就绪探针已配置、minReplicas 不小于 2、扩容快缩容慢、PDB 已配置、节点池上下限与配额已确认、压测验证过扩缩容全过程。把这些做扎实,集群才能在流量高峰时自己长大、在低谷时自己瘦下来,而你只需要看监控、收告警,不用在深夜手动改副本数。
- 点赞
- 收藏
- 关注作者
评论(0)