容器化之后服务器选型变了吗?三个最容易买多的地方
容器化之后服务器选型变了吗?三个最容易买多的地方
摘要:很多团队容器化之后买服务器还是按以前的思路来——CPU核数、内存大小、磁盘容量,跟裸机时代一样算。但容器化之后资源利用率模型变了,有些地方你买的配置比实际需要的多了一倍甚至更多。本文从实际项目经验出发,讲清楚容器化之后服务器选型的三个变化。
关键词:容器化、Docker、K8s、服务器选型、资源规划
分类:运维 / IDC / K8s
从一个"买多了"的故事说起
一个客户做了容器化改造,原来3台裸机跑3个服务,改成K8s之后来找我买服务器搭集群。
他的需求单是这么写的:
Master节点:1台 8核16G
Worker节点1:1台 16核32G(跑Web服务,原来占一台16核32G)
Worker节点2:1台 16核32G(跑API服务,原来占一台16核32G)
Worker节点3:1台 16核32G(跑后台任务,原来占一台16核32G)
思路很直接:原来每个服务占一台16核32G,容器化之后还是按这个配置买。
我问他:“你这些服务现在CPU实际用多少?”
他登上去看了下:
Web服务:CPU均值25%,内存用了8G
API服务:CPU均值15%,内存用了6G
后台任务:CPU均值10%(偶尔飙到60%),内存用了4G
三个服务加起来实际需要的资源:CPU峰值6核左右、内存18G。
他要买4台机器总共64核128G。
多花了将近一倍的钱。
容器化之后最大的变化就是资源可以共享和超分。裸机时代每个服务独占一台机器,大部分时间CPU和内存是浪费的。容器化之后多个容器共享节点资源,利用率能上去,用不着按裸机时代的配置来买。
第一个最容易买多的地方:CPU
裸机时代的逻辑
裸机部署的时候,CPU核数是按峰值需求选型的。你的API服务高峰期要用到8核,那就买8核的机器。但高峰期可能一天就两三个小时,剩下21个小时CPU利用率不到20%。
8个核里有6个大部分时间在闲着。
容器化之后变了什么
K8s的requests机制允许CPU超分。Pod的requests是保底,limits是峰值。多个Pod共享节点的CPU,非高峰时段互相借用空闲的CPU。
resources:
requests:
cpu: "500m" # 保底0.5核
limits:
cpu: "2000m" # 峰值2核
一个8核节点,可以跑16个request=500m的Pod。实际运行中大部分Pod不会同时用满CPU,所以高峰期个别Pod可以burst到2核。
# 实际资源使用
kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 3200m 40% 12Gi 75%
CPU实际用了40%,但requests已经分配了70%。意味着有30%的CPU余量可以给Pod突发使用。
怎么选CPU
不要按裸机时代的峰值选。按以下方式估算:
节点CPU核数 = 所有Pod的requests之和 × 1.3(留30%余量)
# 估算示例
pods = [
{"name": "web", "cpu_request": "500m", "replicas": 2},
{"name": "api", "cpu_request": "1000m", "replicas": 2},
{"name": "worker", "cpu_request": "500m", "replicas": 1},
]
total_cpu = sum(
int(p["cpu_request"].replace("m", "")) / 1000 * p["replicas"]
for p in pods
)
# total_cpu = 0.5×2 + 1×2 + 0.5×1 = 3.5核
node_cpu = total_cpu * 1.3
# node_cpu ≈ 4.55核 → 选8核节点足够
如果按裸机思维,三个服务峰值各需要2-4核,可能买3台8核甚至16核。容器化后资源可以超分,8核节点可能就够了。
什么时候不能超分: 如果你的服务是计算密集型的(视频转码、数据分析、AI推理),CPU长期跑满,那不能超分,老老实实按实际需求配。
第二个最容易买多的地方:内存
裸机时代的问题
一个Java服务堆内存配了8G,操作系统和中间件再占4G,这台机器就得16G起步。实际上Java堆用了5G,剩下的3G在大部分时间是空的。
但内存不像CPU可以超分。Pod的内存request了多少就要预留多少。一个request=8Gi的Pod,节点必须有8Gi的可用内存才能调度进去。
容器化之后怎么优化
第一步:把应用的JVM内存调准。
很多Java服务的-Xmx是按"万一需要"设的。比如设8G,但实际峰值只用4G。
# 看JVM实际内存使用
jstat -gc <pid> | tail -1
S0C S1C S0U S1U EC EU OC OU MC MU
0.0 524288.0 0.0 432156.0 4194304.0 2097152.0 8388608.0 4194304.0 98304.0 87654.0
OU(老年代使用)约4G,老年代总量8G。一半是空的。
# JVM参数调整
# 原来
-Xmx8g -Xms8g
# 调整后(留20%余量)
-Xmx5g -Xms5g
堆内存从8G降到5G,Pod的memory request跟着降。
第二步:容器的memory request设对。
# 不好的做法:requests和limits设一样
resources:
requests:
memory: "8Gi"
limits:
memory: "8Gi"
# 更好的做法:requests设为实际使用量,limits留余量
resources:
requests:
memory: "4Gi" # JVM堆5G + 非堆/系统约1G → 但实际上容器内存limit要大于JVM
limits:
memory: "6Gi"
注意一个坑:容器的memory limit要大于JVM的堆内存。 JVM除了堆内存,还有非堆(Metaspace、线程栈、直接内存等)、以及操作系统层面的开销。经验公式:
容器memory limit ≈ JVM -Xmx × 1.3 ~ 1.5
-Xmx=5G的话,容器limit设6.5-7.5G比较安全。
第三步:多个服务混部。
不同服务的内存使用模式不同。Java服务内存在启动后基本稳定。Python/Node.js服务可能有内存泄漏,内存在缓慢增长。
把这些服务放在同一个节点上,Java服务的稳定内存占用加上Python/Node.js的弹性内存占用,比各自独占一台机器的总内存需求小。
裸机时代:
Java服务独占16G机器(用了10G)
Python服务独占8G机器(用了5G)
总计:24G
容器化混部:
两个服务放在同一台16G节点上
Java request 6G + Python request 3G = 9G
留7G余量,足够了
总计:16G
怎么选内存
节点内存 = 所有Pod的memory requests之和 × 1.3(留余量给系统组件和突发)
比裸机时代算出来的数字通常小30-50%。
第三个最容易买多的地方:磁盘
裸机时代
每个服务的服务器上都有本地磁盘——系统盘、数据盘、日志盘。一个服务配500G SSD是标配。
三台服务器就是1.5T的SSD。但实际上每台只用了100-200G。
容器化之后
容器是无状态的。应用跑在容器里,容器删了重建,数据不受影响(前提是数据存在外面)。
这意味着:
-
系统盘需求下降。 K8s节点的系统盘主要给OS和容器运行时用。镜像会占用一些空间但可以清理。100-200G对大部分节点够了。
-
应用数据不应该在本地。 日志走stdout到日志收集系统(EFK/Loki),不用存在本地。用户文件走对象存储。数据库有自己的独立存储。
-
只有需要持久化存储的服务才配大磁盘。 比如数据库(虽然前面说了数据库建议裸机)、Elasticsearch、Prometheus这些有状态的服务。
裸机时代:
3台服务器 × 500G SSD = 1.5T
实际使用:约500G
容器化后:
3个K8s节点 × 200G SSD = 600G
独立存储(NAS/对象存储):按需
实际总使用量差不多,但节点磁盘成本降了
容器镜像的空间管理
容器化之后有一个新的空间消耗:Docker镜像。
# 查看磁盘使用
docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 8 12.5GB 5.2GB (41%)
Containers 8 8 1.2GB 0B
Local Volumes 3 2 850MB 200MB
Build Cache 0 0 0B 0B
镜像积累多了会占不少空间。定期清理:
# 清理不用的镜像和容器
docker system prune -a
# K8s节点上用crictl
crictl rmi --prune
也可以配一个定时清理的CronJob。但更好的做法是CI/CD构建新镜像的时候自动清理旧版本,只保留最近3-5个版本。
一个真实的选型对比
那个客户的最终选型:
原始方案(按裸机思路):
Master:1台 8核16G
Worker:3台 16核32G
总计:56核112G
月费约6000-8000元
优化方案(按容器化思路):
Master:1台 4核8G
Worker:2台 8核32G
总计:20核72G
月费约3000-4000元
月费省了将近一半。
20核72G够用吗?算一下:
所有Pod的requests:
Web(×2):CPU 1核 + 内存 4G = CPU 2核 + 内存 8G
API(×2):CPU 1核 + 内存 3G = CPU 2核 + 内存 6G
Worker(×1):CPU 0.5核 + 内存 2G
Ingress Controller:CPU 0.5核 + 内存 0.5G
CoreDNS等系统组件:CPU 0.5核 + 内存 0.5G
合计:CPU 5.5核 + 内存 17G
两个Worker节点:
可分配 CPU:约14核(8×2-系统预留)
可分配内存:约56G(32×2-系统预留)
CPU使用率:5.5/14 = 39%(余量充足,高峰期可以burst)
内存使用率:17/56 = 30%(余量充足)
够用了。实际运行了几个月,CPU峰值到过60%,内存峰值到过50%,都在安全范围内。
什么时候不能省
上面说的都是"可以省"的场景。但有些情况不能省:
计算密集型服务。 CPU长期跑满(视频转码、数据分析、AI推理),不能超分。按实际需求选型。
内存敏感型服务。 数据库、Redis、Elasticsearch,内存要给足不能省。内存不够会导致性能急剧下降甚至OOM。
IO密集型服务。 数据库、日志处理,磁盘性能很重要。不能因为"容器化了磁盘需求就少了"就把SSD换成HDD。
生产环境的高可用节点。 至少2个工作节点,一个挂了另一个能顶上。不要为了省钱只买1个工作节点。
一个估算框架
容器化之后的服务器选型,按这个流程估算:
第一步:列出所有服务
每个服务的Pod数、CPU request、memory request
第二步:加总
总CPU requests = 各服务CPU之和
总memory requests = 各服务内存之和
第三步:考虑余量
节点CPU = 总CPU × 1.3(留30%给突发和系统组件)
节点内存 = 总内存 × 1.3
第四步:选节点规格
单节点规格要能让至少2-3个服务同时运行
不要选太大的节点(故障爆炸半径大)
不要选太小的节点(调度碎片严重)
第五步:确定节点数量
至少2个工作节点(高可用)
总需求 ÷ 单节点容量 + 1(冗余)
跟裸机时代最大的区别:按requests选而不是按limits选。 requests是保底需求,limits是峰值。多个服务的峰值不会同时到来,所以节点不需要按所有服务的峰值之和来配。
总结
容器化之后服务器选型有三个容易买多的地方:
| 项目 | 裸机时代 | 容器化之后 | 省多少 |
|---|---|---|---|
| CPU | 按峰值选型 | 按requests选,可超分 | 30-50% |
| 内存 | 按堆内存×2 | 精确配置JVM+容器内存 | 20-40% |
| 磁盘 | 每台服务500G SSD | 节点小盘+独立存储 | 30-50% |
核心原因就一个:容器化之后多个服务共享节点资源,利用率上去了,不需要为每个服务单独预留峰值资源。
但有几种情况不能省:计算密集型、内存敏感型、IO密集型。这些服务的资源不能超分,按实际需求选型。
先跑起来看实际资源使用数据,再根据数据调整配置。不要一开始就按最大需求买——K8s的好处之一就是可以随时加节点扩容。
有问题评论区聊。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)