深色模式
计算成本优化
计算通常是账单里最大的一块。优化路径有三条:把规格调对(rightsizing)、把计费模式选对(预留/竞价)、把运行时长压对(弹性)。
适用环境
- 云主机或 Kubernetes 集群
- 有 ≥ 30 天的 CPU/内存监控
- 工作负载可分为"常驻型"和"可中断型"
bash
# 盘点当前规格与请求量
kubectl get nodes -o custom-columns='NAME:.metadata.name,CPU:.status.capacity.cpu,MEM:.status.capacity.memory'
kubectl top nodes1
2
3
2
3
操作步骤
1. Rightsizing:先算出真正需要的规格
promql
# 近 30 天容器 CPU 使用 P95
quantile_over_time(0.95,
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace, pod)[30d:5m]
)1
2
3
4
2
3
4
promql
# 近 30 天内存使用 P95
max_over_time(container_memory_working_set_bytes{container!=""}[30d:5m])1
2
2
text
建议 requests = P95 使用量 × 1.2
建议 limits = 峰值使用量 × 1.5(或按语言特性调整)1
2
2
对比当前 requests 与建议值的差距,差距越大节省空间越大。
bash
# 用工具自动给出建议
kubectl get deploy -A -o json \
| jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name) \(.spec.template.spec.containers[].resources.requests)"' | head -201
2
3
2
3
2. 处理"大 requests 小使用"的典型浪费
yaml
# 常见错误:requests 直接等于 limits,且都设得很大
resources:
requests: {cpu: "4", memory: "8Gi"} # 实际只用 0.5 核
limits: {cpu: "4", memory: "8Gi"}
# 修正后
resources:
requests: {cpu: "1", memory: "2Gi"} # 贴近真实使用,提升装箱率
limits: {cpu: "2", memory: "4Gi"} # 留突发空间1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
注意:CPU limits 会导致 CFS throttling,延迟敏感服务可以不设 CPU limits 或设得宽松;内存 limits 必须设(防 OOM 影响邻居)。
3. 计费模式选择:三层组合
text
基座负载(24×7 稳定) → 预留实例/节省计划,换长期折扣
波动负载(白天高夜间低)→ 按量 + 弹性伸缩
可中断负载(批处理、CI、渲染)→ 竞价实例,价格最低但会被回收1
2
3
2
3
判断方法:
promql
# 看某服务近 30 天的副本数波动,波动小则适合预留
min_over_time(count(kube_pod_info{namespace="trade"})[30d:1h])
max_over_time(count(kube_pod_info{namespace="trade"})[30d:1h])1
2
3
2
3
text
预留覆盖率建议 = 基座负载 / 总负载,一般 60%~80%
覆盖率过高 → 夜间低谷浪费;过低 → 折扣拿不到1
2
2
4. 竞价实例的正确用法
yaml
# K8s 中用 nodeSelector/toleration 把可中断任务调度到竞价节点
apiVersion: batch/v1
kind: Job
metadata:
name: nightly-report
spec:
template:
spec:
nodeSelector:
node.kubernetes.io/instance-type: spot
tolerations:
- key: spot
operator: Equal
value: "true"
effect: NoSchedule
restartPolicy: OnFailure1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
竞价实例前提:任务可重试、状态外置、有 2 分钟中断预警处理逻辑、预留按需兜底容量。
竞价实例不适合有状态与延迟敏感服务
竞价实例会被提前回收(通常几十秒到 2 分钟通知)。数据库、消息队列、在线服务放在竞价节点上会造成数据丢失或请求失败。
5. 弹性:按负载自动伸缩时长
bash
# 夜间调低 HPA 下限(CronJob 定时执行)
kubectl patch hpa order-api -p '{"spec":{"minReplicas":2}}'
# 早高峰前调回
kubectl patch hpa order-api -p '{"spec":{"minReplicas":8}}'1
2
3
4
2
3
4
text
开发/测试环境夜间与周末关机:
一周 168 小时,工作时段约 45 小时 → 可省约 70% 时长1
2
2
6. 架构层面的单位成本优化
text
1. 提升单机性能(语言/运行时/算法)→ 同样流量更少核
2. 缓存命中率提升 → 减少后端计算与 DB 压力
3. 异步化削峰 → 按平均而非峰值配置
4. 冷热分离 → 归档查询不必占在线资源1
2
3
4
2
3
4
promql
# 缓存命中率是单位成本的关键杠杆
sum(rate(cache_hits_total[5m])) / (sum(rate(cache_hits_total[5m])) + sum(rate(cache_misses_total[5m])))1
2
2
7. 估算收益
text
节省 = Σ (原规格单价 - 新规格单价) × 时长
+ 预留折扣 × 基座用量
+ 竞价价差 × 可中断用量
+ 缩容时长 × 单价
例:30 个服务平均 requests 是实际使用的 3 倍
理论装箱率可从 30% 提到 65%,节点数可减约一半1
2
3
4
5
6
7
2
3
4
5
6
7
验证
bash
# 调规格后必须验证
kubectl top pods -A | sort -k3 -rn | head
# 1) CPU throttling 未上升
# 2) P99 延迟未恶化
# 3) OOM 数量为零
kubectl get events -A --field-selector reason=OOMKilling1
2
3
4
5
6
2
3
4
5
6
promql
# throttling 检查
rate(container_cpu_cfs_throttled_seconds_total[5m])1
2
2
常见坑
一次性把所有服务调紧
批量调小 requests 后如果出现性能问题,很难定位是哪个服务。应分批(每批 5 个)+ 逐批观察 24 小时。
CPU limits 设太小引发限流
limits.cpu 会在 100ms 周期内被硬限流,导致 P99 抖动甚至超时。延迟敏感服务建议只设 requests,或 limits 至少 2 倍于 requests。
预留买太多
预留是承诺消费,用不满就是纯浪费。基座负载应按"近 30 天最小用量"而非平均用量估算。
竞价节点没有按需兜底
竞价池被抢空时若无按需节点兜底,任务会一直 pending。必须保留少量按需容量 + 中断重试机制。