1.3 机器准备:轻量与 CVM 怎么选

场景:一台机器跑网关,选什么规格

选型定了自研,下一步是买机器。我们的情况是:月调用 8000 万次,峰值 QPS 约 800,预算有限,而且团队只有一个运维(兼半个后端)。摆在面前的选择是轻量应用服务器与标准云服务器 CVM 两条路,另外还有云数据库 Redis 与 CVM 自建 Redis 的纠结。每个岔路口都踩过,这里把经验一次性说透。

轻量应用服务器的卖点是便宜、开箱即用、带宽包月,控制台填几下就能跑,适合个人小站。缺点也很明显:规格固定、不能自由升降配、不能组集群、没有弹性公网 IP。我们第一台网关机器确实先用轻量顶了两个月,彼时日调用量才三十万,跑得稳稳的。但很快撞到天花板——单机带宽上限卡在那里没法升,且没法和另一台机器组容灾,想做个故障演练都无从下手。正式上线前我们换成了标准 CVM,理由有三个:能绑定弹性公网 IP 方便容灾切换、能自由升配应对增长、能与云监控深度联动做自动化告警。

多说一句为什么容灾和弹性 IP 的优先级这么高。我们最早的可用性方案其实是 DNS 切换:一台挂了改解析指向另一台。听起来简单,实际上 TTL、客户端缓存、浏览器固执的 DNS 记忆,都会让切换慢得难以接受,我们实测过一次,切换十分钟了还有三成流量打到已经死掉的机器。弹性公网 IP 加内网 CLB 的组合,把切换时间压缩到了秒级,肉眼可见的差别。这笔钱不能省。

方案:两张规格对照表

我们按当时的调用规模做过测算,第一张表是服务器形态的对比,先看结论再看道理:

对比项 轻量应用服务器 标准 CVM
适用阶段 日调用量 50 万以下 日调用量 50 万以上
带宽 固定带宽包月 按量或包年包月,可调
升配降配 不支持 随时升降配
弹性公网 IP 不支持 支持,容灾切换必需
集群与负载均衡 不支持 与 CLB 联动组集群
价格 低 略高

第二张表是 Redis 的选型。网关的所有状态(令牌桶余量、签名防重放记录、响应缓存、灰度白名单)都留在 Redis 里,它是整个网关的单点,绝不能省:

方案 优点 缺点
云数据库 Redis 主从自动容灾、扩容点一下、有监控大盘 月租成本
CVM 自建 Redis 零增量成本 单点、备份与容灾全要自己写

我们选了云数据库 Redis 的主从版。文档入口 cloud.tencent.com/document/product/239,实例买在同可用区,走内网域名连接,延迟稳定在两毫秒以内。

配置:机器与网络

我们选了 4 核 8G 的 CVM 作网关机,同可用区另购一台 2 核 4G 备用。操作系统统一 Debian,装 PHP 8.2 与 OPcache,Web 服务器用 Nginx 转发 PHP-FPM。CVM 的文档入口是 cloud.tencent.com/document/product/213,从选购到安全组配置都在这里。

Nginx 这层只做一件正事:把所有请求转给网关入口,自己绝不碰业务。配置里特别注意把客户端真实 IP 通过 X-Forwarded-For 透传给 PHP,否则限流和审计只能看到 127.0.0.1:

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/tls/fullchain.pem;
    ssl_certificate_key /etc/tls/privkey.pem;

    location / {
        fastcgi_pass   unix:/run/php/php8.2-fpm.sock;
        fastcgi_param  SCRIPT_FILENAME /var/www/gateway/public/index.php;
        fastcgi_param  X-Forwarded-For $proxy_add_x_forwarded_for;
        include        fastcgi_params;
        fastcgi_param  HTTPS on;
    }
}

安全组只开 443 与 22(22 限制来源 IP),Redis 走内网域名连接,公网入口一律关闭。顺手提一句新机器的优惠,腾讯云经常有 腾讯云产品特惠 活动,云服务器、云数据库、CDN 都有折扣,刚起步的项目可以省一笔。

配置进阶:三套环境与命名规范

机器到位后我们又规划了三套环境,各自独立 CVM、共用一套 Redis 实例的独立 db 库位,避免环境间互相污染。生产、压测、联调三套环境的命名规范统一,所有机器以 gw-prod-01、gw-bench-01、gw-dev-01 这类格式命名,看一眼监控面板就知道是谁:

环境 规格 数据来源 用途
生产 4 核 8G × 2 真实流量 对外服务
压测 4 核 8G × 1 CLS 历史日志回放 变更验证
联调 2 核 4G × 1 抽样请求 新功能调试

压测环境后来成了网关变更的守门员,任何限流或路由改动上线前必须在压测环境跑十分钟回放比对,这个流程在第二篇之后会反复提到。联调环境给新同事练手用,随便折腾不心疼,把破坏性操作隔离在生产线之外,是团队成长很重要的一环。

踩坑:被忽略的带宽与连接数

第一个坑是带宽计量。我们一开始按包月带宽买的 5Mbps,网关流量大头是小响应包,QPS 一高带宽分分钟跑满,接口开始随机超时。诡异的是机器 CPU 才百分之二十,完全看不出问题,最后盯监控的出网流量曲线才发现峰值天天顶到天花板。后来改成按量计费峰值 20Mbps,成本反而更可控——按量只在你真的需要时收钱。

第二个坑是 PHP-FPM 进程数:默认 5 个 worker 根本顶不住 800 QPS(多亏大部分请求被缓存挡住了,否则早炸了),调到 pm.max_children = 64、pm.type = dynamic 之后才稳。调参的依据是压测,不是网上抄来的数字。

第三个坑是云数据库 Redis 的连接数上限。限流与缓存都连 Redis,PHP-FPM 开 64 个 worker 每个都可能持连接,轻松突破默认连接数配额,报错打到日志里才看到。解决方案是 predis 启用长连接加连接池,并把非关键路径的 Redis 操作降级为容错——缓存挂了也别让接口挂,大不了回源慢一点。具体做法是在 \Predis\Client 初始化时指定 exceptions => false,然后封装一个 redisSafe 助手,调用失败统一返回 false 并打告警,调用方拿到 false 就走降级分支。

第四个坑更隐蔽:内网 DNS。云数据库 Redis 的内网域名解析偶尔会慢,PHP 每次请求都解析一次域名,耗时被放大到毫秒级。解决方式是 predis 配置里开启连接持久化并把解析结果缓存到本地 hosts 的兜底逻辑,配合监控告警,解析慢的请求占比从百分之二降到了零。

小结

日调用 50 万以下轻量服务器够用,之上选标准 CVM——图的是升降配、弹性 IP 与集群能力三条命。缓存与限流这类状态一律上云数据库 Redis,别自建,单点状态自建等于把炸弹握在手里。带宽按量计费比包月更适合突发流量大的网关,PHP-FPM 进程数要跟着压测结果调,别拍脑袋。机器层是网关的地基,地基稳了上面的代码才有意义。预算紧张就先轻量顶一阵,业务起来再换 CVM,迁移成本并不高,一个晚上就能搞定。

规格选择上再给一个具体参考。800 QPS 峰值、平均响应 20 毫秒的场景,按 Little 定律粗算并发连接约 16 个,PHP-FPM 开 64 个 worker 留足余量,4 核 8G 机器实测 CPU 占用约六成。这个配比我们压测验证过,见第 5.2 节。如果你们的接口平均响应要 100 毫秒以上,同样的 QPS 需要的 worker 数要乘上去,机器也得加码,别照抄数字。

再补一个采购节奏的经验。机器别一次买满预估的峰值,先按当前峰值的 1.5 倍买,剩余用量走按量计费兜着,等下半年流量趋势确认了再转包年。我们吃过一次亏:年初照年度预估直接包年买了六台,结果年中业务调整流量腰斩,六台机器空转了大半年。容量决策与采购决策要分开——容量按数据算,采购按趋势分步走。

最后强调一个小工具习惯:把每次升降配与迁移操作写成脚本而非手工。我们的 CVM 变更脚本带 dry-run 模式,先模拟执行再真改,半年内十几次扩缩容零事故。机器层的变更频率不高,但每一次都是全站影响,工具化这事儿再怎么强调都不过分。

延伸阅读