一个内部工单系统从物理机迁到虚拟服务器,负责的同事按"以前那台机器差不多"的思路提了 4 核 8G 的配置,存储随手选了 100G 通用盘,网络直接用默认带宽。上线头一周风平浪静,第二周开始出问题:早高峰 CPU 跑到 95%,磁盘写入延迟从 2ms 涨到 40ms,日志里时不时冒出内存溢出。排查下来没有一行代码是错的,问题全在规格规划上——核数不够扛并发,内存没给页缓存和堆外留余量,存储的 IOPS 上限只有几百,网络带宽在批量导出时被打满。这类事故我见过太多次,虚拟服务器规划这件事,难的不是点几下鼠标开一台机器,而是把业务需求翻译成 CPU、内存、存储、网络四个维度的具体数字,并且给每个数字找到依据。下面这套方法,是我在多个项目里反复校准过的,从估算公式到实测手段到上线后的调参阈值都有,适合刚接手基础设施工作的开发者,也适合需要自己开机器做压测的后端同学。
1. 先算业务账:虚拟服务器规划真正的起点在哪
1.1 规格申请单上最该填的不是核数,而是业务画像
很多人拿到"开一台虚拟服务器"的任务,第一反应是打开配置页面看套餐。这个顺序反了。正确的顺序是先写清楚这台机器要干什么,再倒推规格。我习惯在工作笔记里先列三张表:第一张是负载类型表,标明这台机器跑的是 Web 服务、数据库、缓存、消息队列、批处理还是文件服务,一台机器只跑一种角色最好,混合部署会让后面所有估算失真;第二张是流量特征表,写清楚日均请求量、峰值 QPS、峰值出现的时段、单次请求的平均响应体大小;第三张是数据特征表,写清楚每日新增数据量、数据保留周期、是随机小 IO 为主还是顺序大 IO 为主。
这三张表填完,很多参数自己就浮出来了。举个例子,一个日均 20 万次请求的接口服务,平均响应体 30KB,那每天的出网流量大约是 6GB,峰值时段如果集中在两小时内,峰值带宽需求就能直接算。反过来,如果只有"我要一台服务器"这句话,任何配置都是拍脑袋。
我见过最典型的错误是把开发和测试环境的规格直接搬到生产。开发环境一天几百个请求,4 核 4G 绰绰有余,生产环境峰值几千 QPS,同样的配置必然崩。环境之间的负载差异往往是数量级的,不是百分之几十。
1.2 基线数据从哪来:三种可用的取数方式
规划要有数据支撑,但新项目上线前哪来的数据?实践中我常用三种取数方式,按可靠性从高到低排列。
第一种是历史系统实测。如果是从旧系统迁移,直接抓旧机器的监控曲线最有说服力。至少要取连续 14 天的数据,覆盖工作日和周末,重点看 P95 和 P99 而不是平均值。平均值这个东西特别会骗人,一台机器平均 CPU 20%,听起来很闲,但仔细看曲线会发现每天有两个小时稳定跑到 90%,那这两个小时就是你的真实瓶颈。
第二种是本地压测。在开发机上用压测工具跑出单请求的 CPU 耗时和内存增量,再乘以预估 QPS。这种方法精度一般,因为开发机的性能和生产虚拟机的性能不可比,但用来判断数量级够用。
第三种是同类系统类比。找公司内部业务量级相近的系统,看它的规格和实际负载率。这种方法最粗糙,但比完全拍脑袋强得多。用类比法的时候记得打个折扣,因为每个系统的实现质量差异很大,一个写得好的服务和一个 N+1 查询满天飞的服务,同样的 QPS 下资源消耗能差三倍。
1.3 峰值系数与冗余系数:把"够用"翻译成"留有余量"
估算出基础资源量之后,必须乘两个系数。峰值系数解决的是"平均到峰值"的放大问题,冗余系数解决的是"故障时单点扛全量"的问题。
峰值系数看业务形态。内部管理系统通常 2 到 3 倍,面向公众的电商类业务在大促期间能到 8 到 10 倍,定时任务型负载可能是 20 倍以上的瞬时尖峰。冗余系数则跟部署架构有关:如果是三节点集群,单节点故障时另外两台各承担 1.5 倍负载,冗余系数取 1.5 就够;如果是双节点,故障时单台要扛 2 倍,冗余系数必须到 2;单机部署没有任何冗余可言,系数只能靠加大本身配置来兜底。
这两个系数不是拍出来的,是架构决定的。我通常会在规划文档里明确写出"本规格按 N+1 冗余设计,单节点故障时 CPU 峰值预计达到 75%",把假设摆到台面上,后面出问题也容易定位是假设错了还是实现错了。
2. CPU 规划的实质:vCPU 数字背后的算力折扣
2.1 物理核、超线程与 vCPU 的换算关系
虚拟服务器上看到的核数是 vCPU,它和物理核不是一对一。主流平台的换算逻辑是:一颗开启超线程的物理 CPU 核,暴露为 2 个 vCPU。也就是说你买到 8 vCPU,底层可能只占用了 4 个物理核的超线程资源。
这个区别在计算密集型场景下影响很大。超线程的原理是让两个线程共享一个物理核的执行单元,当一个线程在等内存时另一个线程可以填进来。对于访存密集、分支预测失败多的负载,超线程能带来 20% 到 30% 的吞吐提升;但对于纯浮点运算、编译、视频转码这类把执行单元吃满的负载,两个超线程抢同一套执行单元,实际算力可能只有单线程的 1.2 倍左右。
所以估算 CPU 时我心里有个换算表:Web 服务、API 网关这类负载,vCPU 基本可以当物理核用;计算密集型负载,我会把 vCPU 数除以 1.6 到 1.8 再评估实际算力。这不是精确科学,但能避免"买了 16 核还是跑不动"的困惑。
| 负载类型 | vCPU 与物理核的等效折算 | 说明 |
|---|---|---|
| Web/API 服务 | 1 : 0.9 左右 | 大量等待 IO,超线程收益明显 |
| 数据库 | 1 : 0.8 左右 | 混合负载,缓存命中时偏计算 |
| 编译/转码 | 1 : 0.6 左右 | 执行单元饱和,超线程收益有限 |
| 加密计算 | 1 : 0.55 左右 | 纯计算,几乎无等待 |
2.2 用单请求 CPU 耗时倒推核数
最靠谱的核数估算方法是抓住"单请求消耗多少 CPU 时间"这个指标。公式很朴素:
所需 vCPU 数 = 峰值 QPS × 单请求 CPU 耗时(秒)× 冗余系数
假设一个订单查询接口,压测下来单请求平均消耗 8 毫秒 CPU 时间,峰值 QPS 500,冗余系数 1.5,那么 500 × 0.008 × 1.5 = 6 核。再往上取到 8 核,留出一点系统开销和突发空间。
这里有个容易忽略的点:单请求 CPU 耗时要用 P95 的值而不是平均值。因为 CPU 是全核并行处理的,理论上 500 QPS 均匀分布时总耗时是固定的,但请求不是均匀的,长尾请求会拉长排队。用 P95 的耗时算出的是"保证 95% 请求不排队"的核数,比平均值更贴近真实体验。
获取单请求 CPU 耗时的方法,可以看应用层的性能剖析数据,也可以用压测时观察虚拟机的 CPU 使用率反推:压测 100 QPS 时 CPU 使用率涨了 40%(单核 100% 计),那么单请求大约消耗 4 毫秒,这个反推方法粗糙但快捷。
2.3 超分比:便宜背后的争抢代价
虚拟化平台普遍支持 CPU 超分,也就是把物理核卖给多个租户,超分比从 1:1 到 1:8 都有可能。共享型实例便宜,就是因为超分比高。这意味着当邻居机器忙起来的时候,你的 vCPU 可能拿不到完整的时间片,表现出来的症状是"CPU 使用率显示 60%,但请求就是慢"。
判断一台机器有没有被超分影响,看两个指标:CPU 就绪等待时间(steal time)和 CPU 使用率与吞吐的比值。steal time 在 Linux 下用 top 命令能看到,在 %Cpu 那一行里的 st 列,持续超过 3% 就说明你的 vCPU 在等物理 CPU。另一个信号是同样的吞吐量,CPU 使用率莫名涨了 20%,而代码没改。
我的建议很直接:数据库、缓存、实时计算这类对延迟敏感的负载,一定选独享型或超分比 1:1 到 1:2 的规格;普通的 Web 前端、离线任务,用共享型完全没问题。别为了省那点成本,把 MySQL 放在超分比 1:8 的机器上,后面排队排到怀疑人生。
2.4 一个反直觉的实测:核数翻倍吞吐不涨
我在一个模拟项目里做过对比:同一个 Java 服务,把 vCPU 从 4 核加到 8 核,压测峰值 QPS 只从 3200 涨到 3600,涨幅 12%。原因是通过线程池最大值配的是 200,而单请求里有大量串行等待,真正的瓶颈在数据库连接池,只有 20 个连接,数据库端每次查询 15 毫秒,20 个连接的理论上限就是 1300 QPS 左右,服务端再加核也只是多开几个线程一起等。
这个案例说明,CPU 规划不能脱离整条链路。规划之前先找瓶颈在哪一段——如果瓶颈在下游存储或数据库,加 CPU 是纯浪费钱。判断方法很简单,压测时同时看应用服务器的 CPU 使用率和数据库的 CPU、连接数、慢查询数量,哪一段先饱和,瓶颈就在哪一段。
3. 内存规划:操作系统和运行时悄悄吃掉的那部分
3.1 三层内存占用:系统、运行时、业务
很多人算内存只算业务数据,比如"我的缓存有 2GB,那 4G 机器够了"。实际情况是内存被三层结构瓜分:最底层是操作系统自身和内核页缓存,中间层是运行时环境(JVM、Python 解释器、Node 运行时)的固定开销,最上层才是业务数据。
操作系统这层,Linux 最小也要留 512MB 到 1GB 给内核、基础进程和页缓存。页缓存看着是"被占用",其实是可回收的,但页缓存太小会导致磁盘读频繁穿透,性能明显下降。我给数据库和文件服务类机器留的页缓存空间通常是总内存的 15% 到 25%。
运行时这层,JVM 的固定开销包括:Metaspace(默认无上限,实际常见 128MB 到 256MB)、代码缓存(几十 MB)、线程栈(每线程默认 1MB,200 个线程就是 200MB)、直接内存(Netty、NIO 用的堆外缓冲,常见 256MB 到 1GB)、GC 自身需要的额外空间。这些加起来轻松占掉 1GB 以上。Python 和 Node 相对轻,但也有几百 MB 的常驻。
3.2 JVM 堆大小到底该给多少
Java 服务的内存规划有个经验区间:堆内存占总内存的 50% 到 65%,不要超过 70%。原因在于堆外还有一大堆东西要放,而且堆内的对象在 GC 时需要额外的空间做复制和整理。
举个具体例子,一台 8GB 内存的机器跑 Spring Boot 服务。我通常这么分配:堆 4.5GB(-Xms4g -Xmx4g,固定大小避免动态扩缩带来的抖动),Metaspace 上限 512MB,直接内存上限 1GB(-XX:MaxDirectMemorySize=1g),线程栈按 300 线程 × 1MB 算 300MB,剩下的 1.7GB 留给操作系统、页缓存和碎片余量。这个配比在多数业务下都跑得比较稳。
堆给太小会频繁 GC,给太大会让单次 Full GC 的停顿时间变长。一个粗略的判断:堆在 8GB 以内时,G1 的停顿通常能控制在 200ms 以内;堆超过 16GB,就需要更仔细地调 Region 大小和暂停目标。如果不知道自己的服务堆该多大,可以先按"常驻活跃对象大小的 2 到 3 倍"给,再通过 GC 日志里的存活对象曲线校正。
3.3 缓存类服务的内存预留:fork 那一刻的隐形炸弹
跑 Redis 这类缓存服务时,只按数据量算内存是危险的。Redis 执行持久化时会 fork 出子进程,虽然用的是写时复制,但只要主进程在持久化期间有写入,被修改的内存页就会被复制一份,最坏情况下内存占用接近翻倍。
我的做法是把maxmemory设在物理内存的 60% 到 70%,留足 30% 以上给 fork。一台 16GB 的机器,maxmemory给 10GB 左右。如果数据量确实超过这个数,正确做法是分片而不是硬塞。同时把vm.overcommit_memory设为 1,避免 fork 时因为内存分配检查失败而报错。
另一个常被忽略的是日志和临时文件。应用频繁打印日志、生成临时文件、做文件上传落盘,这些都会消耗页缓存和磁盘空间,间接影响内存可用量。给这类机器规划内存时,我会额外留出 1GB 到 2GB 的缓冲。
3.4 swap 开不开:分场景给结论
这个问题争论了很多年。我的结论是按角色分:数据库和缓存坚决不开,或者只开很小的 swap 并把vm.swappiness调到 1;普通 Web 应用可以开 2GB 到 4GB 的 swap 作为防崩溃的最后一道保险。
不开 swap 的理由是性能,一旦热点数据被换出到磁盘,延迟从微秒级变成毫秒级,数据库的查询响应会直接崩掉。但完全不开的代价是,内存一旦短时超限,内核的 OOM Killer 会直接杀掉进程,服务硬重启。对 Web 应用来说,被换出一点内存只会慢一些,被杀掉却是直接不可用,所以留一点 swap 更划算。
配置上,vm.swappiness=1意味着内核只在内存极度紧张时才使用 swap,平时基本不碰。这个值设在 10 以下,既能保留兜底能力,又不会让系统主动把热数据挪到磁盘。
4. 存储选型:容量只是入场券,IOPS 和吞吐才是天花板
4.1 容量估算要算三笔账
容量规划最常见的错误是按"当前数据量 × 2"给,结果三个月就满了。完整的容量账要算三笔。
第一笔是数据本身的增长。取当前数据量,按历史增长率外推。如果系统还没上线,就按业务预估:每天新增多少条记录、每条记录多大、索引占多少(通常数据量的 30% 到 50%)、保留多久。一个有索引的关系型数据库,实际占用往往是裸数据量的 2 到 3 倍。
第二笔是日志和临时文件。应用日志按每天 1GB 到 5GB 估,访问日志更大,加上 GC 日志、慢查询日志、临时导出文件,我通常预留总容量的 15% 到 20%。这些文件如果是"只写不删",几个月就能吃掉几十 GB。
第三笔是快照和备份。如果在这台机器上做本地快照,快照本身也占空间;备份文件如果落在本地盘,更要算进去。通常预留 20% 左右。
算完三笔再加 20% 的安全水位,就是初始容量。真实环境中,磁盘使用率超过 80% 就应该开始考虑扩容,超过 90% 很多系统会出问题——数据库写入失败、日志轮转失败、临时文件创建失败,连锁反应很快。
4.2 IOPS、吞吐、延迟:三个指标对应三种负载
存储性能有三个维度,不同的负载卡在不同的维度上。
随机小 IO 看 IOPS。数据库的索引查找、元数据操作、小文件的读写,每次都是 4KB 到 16KB 的随机读写,瓶颈在每秒能完成多少次 IO。一块普通云盘的 IOPS 可能在 3000 到 5000,高性能盘能到 2 万以上。如果跑 MySQL 且每天有几百万次查询,3000 IOPS 是肯定不够的。
顺序大 IO 看吞吐。日志写入、文件上传下载、备份、大数据处理,都是大块顺序读写,瓶颈在 MB/s。这时候 IOPS 再高也没用,关键看带宽。
延迟看单次操作耗时。这个指标对数据库最敏感,尤其是写操作,因为写要等落盘确认。普通网络盘的写延迟在 1ms 到 5ms,本地 SSD 能到 0.1ms。延迟高的盘,即使 IOPS 够,事务型负载的响应时间也会被拖长。
下面这张表是我做选型时的对照参考,数值是常见区间的粗略量级:
| 存储类型 | 典型 IOPS | 典型吞吐 | 写延迟 | 适用场景 |
|---|---|---|---|---|
| 普通网络盘 | 3000~5000 | 100~150 MB/s | 2~5 ms | 日志、文件、低频访问 |
| 高性能网络盘 | 2万~5万 | 300~500 MB/s | 1~2 ms | 中小型数据库、缓存持久化 |
| 本地 SSD | 5万~10万 | 500 MB/s~1 GB/s | 0.1~0.3 ms | 高并发数据库、搜索索引 |
| 独立数据盘阵列 | 10万以上 | 1 GB/s 以上 | 0.5~1 ms | 大型数据库、分析型负载 |
本地盘的性能优势明显,但有个致命问题:它绑定在单台物理机上,机器故障数据就没了,而且不能跨机迁移。所以本地盘只适合做主从架构里可以重建的副本节点,或者临时计算用的缓存盘。
4.3 用 fio 摸清一块盘的真实底线
不看规格书,直接测。规格书上的 IOPS 往往是理想条件下的峰值,实际表现受块大小、队列深度、读写比例影响很大。我用 fio 做两轮测试,一轮测随机读写,一轮测顺序读写。
# 随机读写混合测试,模拟数据库负载 fio --name=randrw --ioengine=libaio --direct=1 --rw=randrw \ --bs=4k --iodepth=32 --numjobs=4 --size=2G --runtime=60 \ --group_reporting # 顺序写测试,模拟日志和大文件写入 fio --name=seqwrite --ioengine=libaio --direct=1 --rw=write \ --bs=1M --iodepth=16 --numjobs=1 --size=4G --runtime=60 \ --group_reporting看结果里的iops、bw和lat三个值。重点看lat的 p99 而不是平均值——平均值好看但 p99 很差,说明有偶发的长尾延迟,对数据库来说这是灾难。我遇到过一次,某块盘的 IOPS 跑到了标称值的 90%,但 p99 延迟是平均值的 30 倍,原因是同一批物理盘上的其他租户在做大规模写入,这种邻居噪声只能通过换盘或换可用区解决。
4.4 分区与文件系统:几个少走弯路的默认选择
分区规划上,我的习惯是至少分三个区:系统盘 40GB 到 60GB,只放操作系统和应用;数据盘单独一块,挂载到/data,放数据库文件、上传文件、日志;如果做容器化,再加一块给容器运行时和镜像存储。分开的好处是系统盘被日志写满时不会拖垮数据,扩容时也只需要动数据盘。
文件系统选择上,通用场景用 ext4 足够;数据量特别大、目录特别多(比如百万级文件)用 xfs 更合适,它的目录索引效率更高;对象存储或大数据场景用专门的分布式文件系统。挂载参数上,数据库盘的noatime一定要加,避免每次读文件都更新访问时间,白白多出一次写 IO。
云环境的云盘还有一类细节:小容量盘的性能是打折的。很多平台按容量阶梯给 IOPS,100GB 的盘可能只有 1000 多 IOPS,500GB 以上才给到标称上限。所以如果性能需求高,宁可开大一点的盘,哪怕用不满容量。
5. 网络配置:默认值往往是最不合适的那个
5.1 带宽估算:从请求大小倒推
网络带宽的估算公式也很直接:
所需带宽(Mbps) = 峰值 QPS × 平均响应大小(MB)× 8 ÷ 目标利用率
假设峰值 QPS 800,平均响应 80KB(约 0.078MB),目标利用率 70%,那么 800 × 0.078 × 8 ÷ 0.7 ≈ 713 Mbps。这个数字说明 100Mbps 的公网带宽肯定不够,需要至少 1Gbps 的内网带宽,公网侧可能还需要配负载均衡分摊。
三个易错点:一是只算了出向流量没算入向,文件上传类业务入向流量可能更大;二是忽略了重传,网络质量差的时候 TCP 重传会让实际带宽消耗涨 10% 到 20%;三是忘了 CDN 和对象存储可以卸载大量静态资源流量,把图片、静态文件挪到对象存储后,服务器本身需要的带宽可能直接降一个数量级。
公网带宽通常按峰值计费或按流量计费,成本差异很大。按带宽计费的适合流量平稳的业务,按流量计费适合有明显波谷的业务。这个选择要在规划阶段就定下来。
5.2 内网与外网:物理隔离的价值
虚拟服务器一般至少两块网卡或者两个网络平面:内网和公网。内网走机器之间的通信,包括数据库访问、缓存访问、服务间调用;公网走对用户的出入口。分开的核心价值有两个。
第一是性能和成本。内网带宽通常免费且带宽大(万兆甚至更高),公网带宽贵且受限。把数据库、缓存、消息队列全部走内网,只在入口层用公网,能省下大量成本。
第二是安全边界。数据库、缓存、内部管理接口只绑内网地址,公网根本访问不到,这是最基本的一道防线。安全组规则上,我的习惯是默认拒绝所有入向,然后按需放通——Web 端口只对负载均衡放通,SSH 只对跳板机或办公网段放通,数据库端口只对应用服务器网段放通。这个"白名单"模式一开始配置麻烦,但能挡掉绝大多数扫描和暴力尝试。
5.3 连接数与内核参数:小配置引发的大问题
网络性能问题里,有相当一部分不是带宽不够,而是连接数或内核参数配置不当。
TIME_WAIT 堆积是最常见的。作为服务端主动关闭连接多的场景(比如短连接 API),大量连接处于 TIME_WAIT 状态会占满本地端口。默认net.ipv4.ip_local_port_range是 32768 到 60999,只有不到 3 万个端口,高并发短连接场景很快耗尽。调整方式是扩大端口范围并允许复用:
net.ipv4.ip_local_port_range = 10240 65000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535另一个是ulimit -n,文件描述符上限。默认 1024 对现代服务来说太小,每个连接占一个 fd,几千并发就爆了。系统级要改/etc/security/limits.conf,进程级在服务启动脚本里设置,容器化环境还要注意容器的 fd 限制。
MTU 的问题相对隐蔽。默认 1500,如果做隧道封装或者跨可用区通信,路径 MTU 可能小于 1500,导致大包分片甚至丢包。表现是"小请求正常,大响应超时",排查的时候用ping -s逐步加大包大小测试就能定位。
6. 完整推演:把一个业务需求变成规格清单
6.1 场景设定:一个日订单十万的内部交易系统
拿一个我经手过的模拟项目做完整推演。业务是内部交易系统:日均 10 万笔订单,其中 80% 集中在 9 点到 18 点,峰值时段(比如上午 10 点到 11 点)占全天流量的 15%,也就是 1.5 万笔订单在 1 小时内完成,约 4.2 笔每秒。每笔订单涉及 6 到 8 次接口调用(查商品、查库存、算价格、下单、写日志等),所以峰值 QPS 约 30。每个接口平均响应体 20KB。
先别急着说"这点量随便一台机器就行"。规划的价值不只在于算大,也在于算准——算准了才能既不浪费也不冒险。
6.2 CPU 与内存的逐项计算
CPU 方面,压测单个接口平均消耗 6 毫秒 CPU,峰值 QPS 30,那么 30 × 0.006 = 0.18 核。这个数字小得离谱,说明纯从应用逻辑算完全用不到 1 核。但实际规格不能这么给,因为要考虑:操作系统基础开销约 0.2 核,定时任务和批处理占 0.3 核,日志和监控代理占 0.1 核,然后按最保守的冗余系数 2 计算,最终 2 到 4 核是合理的。再考虑未来业务翻三倍的预期,选 4 核。
内存方面,JVM 应用,常驻活跃对象预估 800MB,按 2.5 倍给堆就是 2GB,加上 Metaspace 256MB、直接内存 512MB、线程栈 200MB、系统和页缓存 1GB,合计约 4GB。选 8GB 留出余量,实际使用率大概在 60% 左右,属于健康的范围。
如果这个系统还带一个 Redis 缓存,缓存数据预估 2GB,那 Redis 至少要 4GB 内存(按 60% 的 maxmemory 比例),最好单独一台机器,不要和应用混部,避免互相抢内存。
6.3 存储与网络的规格推导
存储方面,订单数据每天 10 万条,每条记录加索引约 2KB,每天 200MB,一年约 70GB,保留三年就是 210GB。加上订单日志、操作审计、临时导出文件,再算上快照和 20% 安全水位,初始给 500GB 比较合适。性能上,订单系统的写入是随机小 IO,每次下单至少 3 次写(订单主表、明细、状态),峰值 4 笔每秒就是 12 次写 IOPS,这个负载很低,普通盘完全够用。但如果这个系统还提供报表查询,报表的聚合查询会产生大量随机读,那就需要考虑高性能盘。
网络方面,峰值 QPS 30 × 20KB × 8 = 4.8 Mbps,加 70% 利用率系数后约 7 Mbps。这个量很小,但因为是对内服务,走内网万兆链路毫无压力。如果还要对外提供文件下载,那要另外算,一个 10MB 的文件被 100 个用户同时下载就是 8Gbps,这种场景必须上对象存储加 CDN,不能让虚拟服务器直接扛。
最终规格清单:应用服务器 4 vCPU / 8GB / 系统盘 60GB + 数据盘 500GB / 内网万兆;缓存服务器 2 vCPU / 4GB / 系统盘 40GB。另外配一个同规格的备用节点,组成双节点加负载均衡的架构。
6.4 上线后第一次校准:看什么、调什么
上线后第一周是校准窗口期。我会盯五个指标:CPU 使用率的 P95、内存可用量(available而不是free)、磁盘 IOPS 和 p99 延迟、网络带宽峰值、应用层的 P99 响应时间。
如果 CPU 的 P95 长期在 30% 以下,说明规格给大了,可以考虑降配;如果在 70% 到 85%,属于合理区间;超过 85% 且伴随响应时间上升,就该扩容了。内存看available,低于总内存的 15% 就要警惕,尤其是它持续下降的趋势——这通常意味着有内存泄漏或缓存无上限增长。
磁盘方面,重点看 IOPS 是否接近规格上限的 80%,以及 p99 延迟是否显著高于均值。网络看峰值带宽利用率和重传率,重传率超过 1% 就要查链路质量。
7. 长期运维视角:扩容阈值、监控盲区与成本回收
7.1 把扩容决策变成一条可执行的规则
"感觉该扩容了"不是规则。我给每个资源维度设一个明确的阈值和观察窗口,写进运维文档里:
- CPU:P95 连续 3 天超过 70%,触发扩容评估;连续 7 天超过 80%,直接扩容。
- 内存:
available连续 24 小时低于 15%,或者 7 天内出现 3 次以上 OOM 记录,扩容。 - 磁盘:使用率超过 75% 触发告警和清理,超过 85% 扩容,不留到 90%。
- 存储性能:IOPS 使用率超过 80% 或 p99 延迟超过 20ms 持续 1 小时,评估升级盘型。
- 网络:峰值带宽利用率超过 70% 持续 3 天,扩容带宽或加 CDN。
这些阈值不是绝对的,但比没有阈值强得多。关键是要有观察窗口,因为单次尖峰和持续高负载是两回事,前者扩容是浪费,后者不扩是冒险。
7.2 那些监控面板上看不见的盲区
默认的监控面板通常只展示 CPU、内存、磁盘、网络四个基础指标,但真正导致故障的往往是面板上看不见的东西。
第一个盲区是磁盘 inode。小文件特别多的系统(比如邮件队列、缓存文件、容器镜像层),可能出现"磁盘还有空间但写不进去"的情况,因为 inode 用完了。df -i能看到 inode 使用率,规划时如果预计小文件数量在百万级,要选择支持大 inode 数量的文件系统或者提前调整。
第二个盲区是文件描述符。前面提过,但要强调它在监控上的隐蔽性——CPU、内存、磁盘全部正常,服务却开始报"too many open files"。这个指标要单独采集。
第三个盲区是连接数。数据库连接池耗尽、TCP 连接数打满、TIME_WAIT 堆积,这些都不会直接反映在四个基础指标上,需要针对性地采集。
第四个盲区是时钟和 DNS。虚拟机的时钟漂移会导致证书校验失败、分布式锁失效、日志时间错乱;DNS 解析慢或失败会导致服务间调用莫名超时。这两个问题排查起来极其费时,但在规划阶段配好 NTP 同步和本地 DNS 缓存就能大幅降低概率。
7.3 缩容与成本回收:规划的另一半
规划不只是往上加。业务有波峰波谷,很多机器的资源在大部分时间里是闲置的。定期做一次资源盘点,把长期低负载的机器降配,能省下可观的成本。
我的做法是每个季度看一次资源使用报告,找出连续 30 天 CPU P95 低于 25%、内存 available 高于 50% 的机器,逐个确认是否需要降配。降配前要确认三件事:业务是否有明确的增长预期、是否有季节性波动、冗余是否足够。有些系统平时闲着,但大促时是主力,这种机器降配后在大促前要升回来,来回折腾的成本可能超过省下的钱。
还有一种回收方式是把多台低负载的小机器合并。比如五台 2 核 4G 的机器各跑一个内部小工具,CPU 都在 10% 以下,合并到一台 8 核 16G 上用容器隔离,既省成本又便于统一运维。但合并的前提是这些服务之间没有强隔离要求,且故障域可以接受——合并之后一台机器挂了会同时影响五个服务,这个风险要评估清楚。
规划这件事做久了会发现,最难的从来不是记住那些公式和参数,而是养成"先量化再决策"的习惯。我早期也干过凭经验拍配置的事,运气好没出问题,但那种"够用就好"的心态在业务量涨上来之后一定会付出代价。现在每次开机器之前,我都会强迫自己在文档里写下这台机器的负载画像、估算过程、假设条件和扩容阈值,哪怕只有半页纸。这份记录在半年后回头看的时候,价值比配置本身大得多——它能告诉你当初为什么这么选,也能在出问题时快速定位是哪个假设失效了。