简介:《容器云平台容量规划及管理优化.pdf》面向容器云平台架构师、运维工程师及PaaS平台设计人员,聚焦互联网与证券行业资源使用时段集中、利用率偏低、容量规划困难等现实痛点,系统梳理从用户体验、资源管理到需求预测的完整优化思路。资源包共1个PDF文件,大小约708KB,内容围绕租户与管理员的资源视图设计、多集群全局资源视角、Kubernetes与PaaS、云管、DevOps及服务治理的整合展开,并深入讲解计算、持久化存储与网络资源的容量评估方法。文中结合证券行业上午9至10点资源高峰的典型场景,剖析平台组件与Kubernetes组件资源占用、微服务拆分浪费、基础镜像选型等易被忽视的细节,给出弹性扩容与避免过度浪费的平衡策略。目前已有65人学习,适合希望提升资源利用率、降低运营成本并构建灵活分配机制的从业者参考。
1. 容器云平台容量规划:为什么你的集群总是“一半浪费、一半排队”
很多团队上容器云平台的头一年都会经历同一个剧本:业务方抱怨 Pod 调度不上去、扩容慢,运维侧一看监控,整体 CPU 利用率长期趴在 15% 上下,内存更夸张,一堆节点跑着个位数负载。于是开始加节点,加完发现利用率更低了,成本却翻了一倍。这不是玄学,是容量规划没做。
容器云平台的容量规划,本质是回答三个问题:现在有多少可分配资源、业务真实需要多少、未来什么时候会不够。它跟传统虚拟机容量规划最大的区别在于——容器的资源单位是 request/limit,调度看 request,实际跑起来看 usage,三者之间的差值就是浪费和风险的来源。管理优化则是在规划落地之后,持续把 request 和真实 usage 之间的偏差收窄,让调度更准、扩缩容更及时、成本更可控。
这套东西适合谁?适合已经有一套 Kubernetes 或基于 K8s 的容器云平台、节点规模超过 20 台、开始被成本或调度问题困扰的团队。如果你还在单机跑 Docker Compose,这篇可以先收藏,等集群起来再看。下面按“先搞清楚资源账本 → 再动手采集和建模 → 然后落地优化 → 最后避坑”的顺序讲,每一步都给可复现的命令和参数。
2. 先把资源账本算清楚:request、limit 与真实 usage 的三本账
容量规划翻车,十有八九是账本没对齐。容器云平台里同时存在三套资源数字,很多人只盯着其中一套,结果就是“监控看着很闲,调度却说没资源”。
2.1 三本账分别是什么,谁在用它做决策
第一本账是request,声明在 Pod 的resources.requests里。调度器(kube-scheduler)只认这个值,它决定 Pod 能不能被放到某个节点上。节点可分配资源减去所有已调度 Pod 的 request 总和,就是调度视角的剩余量。
第二本账是limit,声明在resources.limits里。它决定容器运行时(containerd 或 Docker)给容器设多大的 cgroup 上限。CPU 超了会被 throttle,内存超了会被 OOMKill。注意:limit 不参与调度决策。
第三本账是真实 usage,来自 cAdvisor / metrics-server / Prometheus 采集到的实际消耗。它才是业务真正吃掉的资源。
三本账的关系通常是:request ≥ usage(否则会被频繁驱逐或 throttle),limit ≥ request(否则配置本身有问题)。浪费就藏在request - usage这个差值里,风险藏在usage逼近limit的那些时刻。
提示:很多团队只配 limit 不配 request,K8s 会把 request 默认等于 limit,调度器按 limit 算账,集群看起来“很满”,实际 usage 很低,这是最典型的隐性浪费。
2.2 用 kubectl 和 PromQL 把三本账拉出来
先看单个命名空间下所有 Pod 的 request/limit 配置情况:
# 列出所有 Pod 的 request/limit,按命名空间过滤 kubectl get pods -n production -o custom-columns=\ 'NAME:.metadata.name,\ CPU_REQ:.spec.containers[*].resources.requests.cpu,\ CPU_LIM:.spec.containers[*].resources.limits.cpu,\ MEM_REQ:.spec.containers[*].resources.requests.memory,\ MEM_LIM:.spec.containers[*].resources.limits.memory'这条命令用custom-columns把关键字段直接打平,方便你快速扫一遍有没有明显不合理的配置。参数说明:-n production换成你的业务命名空间;[*]表示取所有容器,如果一个 Pod 有多个容器,输出会用逗号分隔,需要人工核对。
再看节点维度的可分配资源和已分配 request:
# 查看每个节点的可分配资源与已分配 request kubectl describe nodes | grep -A 5 "Allocated resources"输出里Requests那一行就是调度视角的占用率。如果这个百分比长期高于 70% 但实际 CPU usage 低于 20%,说明 request 虚高,需要往下调。
真实 usage 用 PromQL 拉,假设你已经部署了 Prometheus:
# 按命名空间统计 CPU 真实使用率(相对 request) sum(rate(container_cpu_usage_seconds_total{container!="POD"}[5m])) by (namespace) / sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace)这个查询算的是“真实 CPU 使用量 / request 总量”,结果如果普遍低于 0.3,就是明确的 request 过高信号。[5m]是采样窗口,生产环境建议用[30m]或[1h]平滑掉毛刺。
2.3 把三本账对齐成一张容量基线表
光看单个指标没用,要把节点、命名空间、业务线三个维度的三本账汇总成一张基线表。我一般会按下面这个结构整理,每周更新一次:
| 维度 | 可分配 CPU | 已分配 request | 真实 usage(P95) | request/usage 比 | 风险标记 |
|---|---|---|---|---|---|
| 节点池 A | 64 核 | 52 核 | 14 核 | 3.7 | request 虚高 |
| 节点池 B | 128 核 | 110 核 | 96 核 | 1.15 | 接近饱和 |
| 命名空间 order | 32 核 | 28 核 | 9 核 | 3.1 | 需下调 request |
| 命名空间 search | 32 核 | 30 核 | 29 核 | 1.03 | 高风险 |
这张表的价值在于:一眼能看出哪些池子是“假忙”,哪些是“真忙”。节点池 B 和 search 命名空间就是需要优先处理的,前者要扩容或迁移,后者要优化 request 精度。节点池 A 和 order 命名空间则是“账面紧张、实际浪费”,下调 request 就能释放调度空间,不用加机器。
真实 usage 一定要用 P95 或 P99,不要用平均值。平均值会被大量低峰时段拉低,掩盖掉业务高峰时的真实压力。我见过用平均值做规划、结果大促当天集体 OOM 的血泪案例。
3. 容量建模:从历史数据算出“该给多少”而不是“拍多少”
账本对齐之后,下一步是回答“request 到底该设多少”。拍脑袋设 request 是容量规划最大的敌人,要么设高了浪费,要么设低了被 throttle。这一章讲怎么用历史数据建模,把 request 算出来。
3.1 用 P95/P99 分位数定 request,而不是用峰值
最朴素也最可靠的方法:取业务过去 7~14 天真实 usage 的 P95 分位数作为 CPU request,P99 作为内存 request。为什么 CPU 用 P95、内存用 P99?因为 CPU 是可压缩资源,短时超了只是被 throttle,影响可控;内存是不可压缩资源,超了直接 OOMKill,必须留更厚的安全垫。
用 PromQL 算某个 Deployment 过去 14 天的 CPU P95:
# 过去 14 天某 Deployment 的 CPU P95(单位:核) quantile_over_time(0.95, sum(rate(container_cpu_usage_seconds_total{ namespace="production", pod=~"order-service-.*", container!="POD" }[5m])) by (pod)[14d:5m] )参数说明:quantile_over_time的第一个参数是分位数,0.95 就是 P95;[14d:5m]表示在 14 天范围内以 5 分钟为步长采样;pod=~"order-service-.*"是正则匹配,换成你的业务前缀。算出来的值向上取整到 0.1 核,就是建议的 CPU request。
内存同理,把container_cpu_usage_seconds_total换成container_memory_working_set_bytes,分位数改成 0.99,注意单位是字节,要除以1024^3换算成 GiB。
3.2 用 VPA 的 recommender 做自动化建议
手动算分位数适合少量核心服务,服务多了就得靠工具。Vertical Pod Autoscaler(VPA)的 recommender 组件就是干这个的,它会持续分析历史 usage,给出 request/limit 的建议值。
部署 VPA 后,创建一个VerticalPodAutoscaler对象,先跑updateMode: "Off"模式,只出建议不改配置:
apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: order-service-vpa namespace: production spec: targetRef: apiVersion: apps/v1 kind: Deployment name: order-service updatePolicy: updateMode: "Off" # 只给建议,不自动改updateMode: "Off"是关键,生产环境千万别一上来就用Auto,VPA 自动改 request 会触发 Pod 重建,业务高峰期重建就是事故。跑一周后看建议:
kubectl describe vpa order-service-vpa -n production输出里的Recommendation段会给出lowerBound、target、upperBound三个值。target就是建议的 request,upperBound可以作为 limit 的参考。我一般取target作为新 request,取upperBound的 1.2 倍作为 limit。
3.3 把建模结果落成配置:一次安全的 request 调整流程
算出建议值之后,不能直接改线上 Deployment。我一般走这个流程:
第一步,在预发环境用建议值跑 3 天,观察有没有 throttle 和 OOM。第二步,生产环境灰度,先改 10% 的副本,用kubectl rollout控制节奏。第三步,观察 24 小时,重点看container_cpu_cfs_throttled_seconds_total和container_memory_working_set_bytes两个指标。第四步,全量滚动更新。
# 灰度调整:先改 10% 副本的 request kubectl set resources deployment order-service \ --requests=cpu=500m,memory=1Gi \ --limits=cpu=1000m,memory=2Gi \ -n production # 观察 throttle 情况 kubectl top pods -n production -l app=order-servicekubectl set resources会触发滚动更新,--requests和--limits同时给,避免出现 request 大于 limit 的非法配置。改完之后用kubectl top看实时 usage,配合 Prometheus 看 throttle 速率。如果 throttle 明显上升,说明 request 调得太低,回滚即可。
注意:调整 request 会触发 Pod 重建,务必避开业务高峰,并且确认 PDB(PodDisruptionBudget)配置正确,否则可能一次性重建过多副本导致服务不可用。
4. 管理优化落地:扩缩容、超卖与成本的三方平衡
容量规划解决“给多少”,管理优化解决“怎么动态调”。静态 request 再准,也扛不住业务流量的潮汐变化。这一章讲三个最实用的优化手段:HPA 精细调参、节点超卖、以及成本归因。
4.1 HPA 的 3 个必调参数:别让扩缩容变成震荡
Horizontal Pod Autoscaler(HPA)默认配置在生产环境几乎一定会震荡——扩了又缩、缩了又扩。核心要调三个参数:
第一个是--horizontal-pod-autoscaler-sync-period,默认 15 秒,建议保持默认或调到 30 秒,太短会放大指标毛刺。
第二个是--horizontal-pod-autoscaler-downscale-stabilization,默认 5 分钟,建议调到 10~15 分钟。缩容比扩容更需要谨慎,因为缩容后如果流量反弹,重新扩容需要时间。
第三个是 HPA 对象里的behavior字段,用stabilizationWindowSeconds和policies精确控制扩缩速率:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-service-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-service minReplicas: 3 maxReplicas: 30 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 # 目标利用率,不是 request 的百分比 behavior: scaleUp: stabilizationWindowSeconds: 30 policies: - type: Percent value: 50 periodSeconds: 60 # 每分钟最多扩 50% scaleDown: stabilizationWindowSeconds: 600 policies: - type: Pods value: 2 periodSeconds: 120 # 每两分钟最多缩 2 个averageUtilization: 60表示当平均 CPU 使用率达到 request 的 60% 时触发扩容。这个值不要设太高(比如 80%),否则扩容来不及;也不要太低(比如 30%),否则长期多跑副本浪费成本。60% 是我在多数在线服务上验证过的平衡点。
4.2 节点超卖:用 request 和 limit 的差值换密度
节点超卖的本质是:调度器按 request 算账,但容器实际能用到 limit。如果一批服务的 usage 远低于 request,就可以把节点的 request 总量配置得比实际资源高,提升部署密度。
K8s 节点超卖通过 kubelet 的--system-reserved和--kube-reserved参数控制,但更常用的是在节点池层面设置不同的超卖比。比如在线服务节点池超卖比 1.5,离线任务节点池超卖比 3.0。
具体做法是给节点打标签,然后用 nodeSelector 或 affinity 把不同业务调度到不同池子:
# 给节点打超卖比标签 kubectl label node node-01 node-pool=online-oversell-1.5 kubectl label node node-02 node-pool=batch-oversell-3.0 # Deployment 里指定节点池 # spec.template.spec.nodeSelector: # node-pool: online-oversell-1.5超卖比不是越高越好。CPU 超卖 3 倍以上,一旦多个服务同时冲高,节点会严重 throttle;内存超卖超过 1.5 倍,OOM 风险急剧上升。我的经验值:在线服务 CPU 超卖 1.5~2.0、内存 1.0~1.2;离线任务 CPU 可以到 3.0~4.0,内存 1.5~2.0。
4.3 成本归因:把账单摊到每个命名空间
管理优化最终要回答“钱花在哪了”。用 Prometheus 的kube_pod_container_resource_requests乘以节点单价,就能算出每个命名空间的月度成本:
# 按命名空间估算 CPU 月度成本(假设单核月成本 30 元) sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace) * 30这个数字是“按 request 计费”的口径,反映的是调度占用成本。再算一个“按 usage 计费”的口径,两者对比就能看出哪个团队申请了资源却没用起来。我一般每月出一张表,把两个口径的差值发给各业务线,推动他们主动下调 request。这比运维单方面改配置有效得多,因为成本压力直接传导到了业务方。
5. 避坑与排查:容量规划里最容易翻车的 5 个场景
这一章全是踩过的坑,每条按“现象 → 原因 → 解决”写,照着排查能省不少时间。
现象一:节点显示 request 已满,但实际 CPU 利用率不到 10%。原因:大量 Pod 只配了 limit 没配 request,K8s 默认把 request 等于 limit,调度器按 limit 算账。解决:用kubectl get pods -o json批量检查resources.requests为空的 Pod,补上合理的 request。可以用 LimitRange 在命名空间级别设默认值,防止新 Pod 再出现这种情况。
现象二:HPA 频繁扩缩,副本数像心电图。原因:averageUtilization设得太低(比如 40%),或者指标采集窗口太短,业务正常波动就触发扩缩。解决:把目标利用率调到 60%~70%,同时把scaleDown.stabilizationWindowSeconds调到 600 秒以上。如果还是震荡,检查是不是多个 HPA 同时管一个 Deployment。
现象三:调整 request 后服务大面积重启,可用性掉底。原因:直接改了 Deployment 的 request,触发全量滚动更新,而 PDB 没配或配得太宽松,导致同时重建过多副本。解决:改之前先确认 PDB 的minAvailable至少是副本数的 70%,然后用kubectl rollout pause暂停,分批手动恢复。灰度期间盯紧kube_deployment_status_replicas_available。
现象四:内存 request 按 P99 设了,还是偶尔 OOM。原因:P99 只覆盖了 99% 的时间点,剩下 1% 的极端峰值没覆盖到,而内存是不可压缩的,一次峰值就 OOM。解决:内存 request 在 P99 基础上再乘 1.2~1.3 的安全系数,或者直接取max_over_time的峰值。同时检查是不是有内存泄漏,用container_memory_working_set_bytes的长期趋势判断。
现象五:节点超卖后,夜间批量任务把在线服务挤爆。原因:在线和离线混部在同一个节点池,离线任务夜间冲高,抢占了在线服务的 CPU。解决:用 nodeSelector 把在线和离线分到不同节点池,或者用 K8s 的PriorityClass给在线服务设高优先级,配合preemptionPolicy让离线任务可被抢占。超卖比也要分开设,离线池可以更激进。
6. 进阶技巧:用 descheduler 做持续再平衡,让容量规划“活”起来
容量规划不是一次性项目,集群跑久了必然出现碎片——有的节点 request 快满了但 usage 很低,有的节点 Pod 很少却因为亲和性调不走。这时候需要 Descheduler 做持续再平衡。
Descheduler 是一个周期性运行的组件,它根据策略驱逐不合理的 Pod,让调度器重新调度。最常用的三个策略:
apiVersion: descheduler/v1alpha2 kind: DeschedulerPolicy strategies: LowNodeUtilization: enabled: true params: nodeResourceUtilizationThresholds: thresholds: cpu: 20 # 低于 20% 视为低利用 memory: 20 targetThresholds: cpu: 70 # 高于 70% 视为高利用 memory: 70 RemoveDuplicates: enabled: true PodLifeTime: enabled: true params: maxPodLifeTimeSeconds: 604800 # 7 天LowNodeUtilization会把高利用节点上的 Pod 迁到低利用节点,RemoveDuplicates打散同一 Deployment 集中在单节点的副本,PodLifeTime定期重建长生命周期 Pod 让配置生效。参数上,thresholds和targetThresholds之间要留足缓冲,否则会来回迁移。我一般设 20/70,中间 50 个百分点的缓冲带足够稳定。
Descheduler 默认是DryRun模式,先跑一周看它会驱逐哪些 Pod,确认无误再改成实际执行。执行频率建议 10~15 分钟一次,太频繁会干扰正常调度。
验证再平衡效果,看两个指标:节点间 CPU request 的标准差(越小越均衡),以及kube_pod_status_scheduled的 P99 延迟(反映调度压力)。标准差下降 30% 以上,说明再平衡生效了。
最后说个我自己的习惯:每次调整容量配置前,先在一个隔离的命名空间用同样的配置跑一遍压测,确认 request/limit 组合在峰值下不 throttle、不 OOM,再上生产。这个习惯帮我挡掉了至少三次可能的事故。容量规划没有一劳永逸,只有持续观测、小步调整。希望帮到你。
本文还有配套的精品资源,点击获取