1.3 机器准备:轻量应用服务器够不够

场景:预算表上只有两千块

选型定了自研,接下来最现实的问题:机器。老板给的月预算是两千块出头,还要留出 MySQL 和存储的钱。我们第一反应是看看云厂商的各种套餐,最后落在轻量应用服务器上——价格透明、套餐固定带宽,不用像 CVM 那样一项项勾配置。

先说结论:对我们的场景完全够用,而且绰绰有余。但够用的前提是把账算清楚,这一节就是教你怎么算这笔账。

方案:按角色选规格

我们的分工是 master 一台、worker 两台起步,MySQL 用云数据库,快照放对象存储。机器的负载特征很不一样:master 吃内存(调度和去重指纹库都在内存里跑),worker 吃网络和少量 CPU,控制台静态文件干脆全丢 CDN。按这个特征选规格:| 角色 | 规格 | 月费(当时价) | 负载依据 | |---|---|---|---| | master | 2 核 4G 4M | 约 90 元 | 调度加指纹库,内存是瓶颈 | | worker x2 | 2 核 4G 4M | 约 90 元/台 | 抓取是 IO 密集,CPU 占用低 | | MySQL | 1 核 1G 基础版 | 约 80 元 | 写入为主,并发低 | | COS | 按容量计费 | 约 30 元起 | 快照为主,低频存储便宜 | | 合计 | | 约 380 元/月 | 量翻倍前都扛得住 |

这张表里最值得注意的不是价格,而是合计那一行——机器费用只占总成本的四成,剩下的在数据库、存储和带宽上。很多团队做预算时只盯着机器,上线两个月后就开始为存储账单吃惊。数据库之所以买云数据库而不是在轻量上自建 MySQL,是因为备份和高可用值这个差价,我们四个人里没有 DBA。

选轻量而不是 CVM 的原因就一条:套餐制省心。固定带宽、固定月费,不用担心流量突刺把账单打飞。轻量应用服务器的官方文档入口是 cloud.tencent.com/document/product/1207,套餐和价格都以那里为准;新用户可以蹲一下活动页,比如这个轻量应用服务器特惠入口,我们第一台机器就是活动价拿下的。正文里写的价格只是当时下单时的记忆,别直接照抄。

套餐到期续费的价格和首单往往不一样,这个差别在 5.3 节的成本复盘里专门算过,这里只提醒一句:做年度预算时按续费价算,别按首单价算,否则下半年会很难看。

有人会问 master 和 worker 为什么不合并成一台。单机原型阶段确实是一台,3.1 节会讲拆开的理由——简单说是一旦要做断点续爬和横向扩容,调度与执行混在一起会让状态管理复杂度翻倍。预算紧的时候可以先合并跑着,架构上留好拆分接口即可。

规格上还有两个细节值得说。一是轻量应用服务器的套餐带宽是共享出口,多台 worker 同时全速跑时各自能拿到的不一定有幸达到标称值,所以我们在 worker 上做了主动限速(3.2 节),既是礼貌也是自保。二是轻量默认带的系统盘是 SSD,做 Go 编译和日志写入足够,但容量小,大文件一律走数据盘或直接进 COS。这两个细节后来都进了我们的部署检查清单,新机器上线先逐条过一遍。

配置与代码:让机器自己证明自己够用

我们对 worker 的并发做了实测,数据值得参考:在一台 2 核 4G 的轻量机器上,并发从 10 提到 20 时吞吐接近翻倍,提到 40 时只涨了三成,提到 80 时反而下降——目标站开始限速,超时重试拖垮了整体效率。所以我们的 worker 默认并发卡在 20,给半夜的竞争对手留足余量。这个实验在 3.2 节有完整的限速推导。

另外说一下高配机器的诱惑。有一阵子我们想直接上 8 核 16G 的机型,一台顶三台,管理成本低。算了一笔账:单机版本省的是机器数,但单点故障的风险被放大了——一台挂了等于损失三分之一到全部的采集能力。分布式系统里机器多反而是优势,故障域小,摘掉一台继续跑。最终我们选择多台小机器的路线,这个决定在 5.1 节监控告警设计时证明非常正确。

顺带一个反直觉的经验:采集场景下机器的性能常常不是瓶颈,网络才是。同样一台 2 核 4G,下载延迟 200 毫秒的站和 50 毫秒的站,吞吐能差三倍。所以机器规划里有一项隐形工作:给目标站分片区,把延迟相近的站点分给同一台 worker,减少连接在同一台机器上忽快忽慢。这招在 3.1 节的分片策略里详细讲。

顺便算算电费之外容易被忽略的那部分:每台机器还要分摊快照存储、日志存储和监控指标的存储成本。这些小钱单看不起眼,合起来在 5.3 节的成本表里能占到一成。预算表上只列机器价是新手常见的错误。

买机器之前我们先做了压力采样:把 Python 老脚本的历史数据当样本,估算单页平均下载 80KB、解析加清洗约 30 毫秒 CPU 时间,单 worker 开 20 个并发连接时 CPU 占用不到百分之四十。有了这些数字,2 核 4G 的选择就不是猜的。补充一个判断依据:worker 是 IO 密集型,CPU 基本都在等网络,所以核心数不用多;master 是计算密集型,指纹比对和优先级排序都吃单核性能,所以宁可贵一点也不将就。

上线后我们还写了个很小的资源采样任务,每分钟把负载写进监控:

func reportLoad(ctx context.Context) {
    t := time.NewTicker(time.Minute)
    for {
        select {
        case <-ctx.Done():
            return
        case <-t.C:
            cpu := readLoadAvg()
            mem := readMemUsage()
            metrics.Emit("worker.load", map[string]string{
                "host": hostname, "cpu": cpu, "mem": mem,
            })
        }
    }
}

这套采样数据在 5.1 节监控告警里直接复用,也是 5.3 节成本复盘的依据——花多少钱得知道花在哪台机器上。轻量后台自带云监控的 CPU 与带宽图表,够看大盘;自定义指标补充业务维度,两者的分工在 5.1 节有完整的告警规则表。

部署流程也一并标准化:所有机器统一 Ubuntu 系统、统一装 Docker、统一把日志目权限交给非 root 用户。当时顺手做的标准化,在第三篇加 worker 机器时省了大量时间——新机器到货后跑一个初始化脚本就能加入集群,这就是后来 3.1 节里 worker 横向扩容能半小时搞定的重要原因。

怎么说呢,机器准备这事看起来是采购,其实是架构决策。每一条规格选择的背后,都是对未来半年负载和故障形态的预判。

踩坑:三个让机器账单教我们的事

第一个坑是带宽按流量计费的套餐。我们贪便宜选过一款流量计费套餐,月中一次全量铺底跑了 200GB 流量,账单直接翻番。吃一堑长一智:铺底这类大流量操作必须走可控窗口,或者临时升级带宽包。这事的教训后来写进了 5.3 节的操作清单。

第二个坑是系统盘大小。默认系统盘对编译 Go 项目绰绰有余,但 worker 本地会缓存一小段原始 HTML 用于重试,没注意就写满了。后来把缓存目录挪到数据盘并加了自动清理。

第三个坑是地域选择。图省事买了离业务最近的地域,结果有个采集对象的服务器在另一个地域,每次请求都绕远路,延迟翻倍。采集场景下出口带宽和到目标站的延迟比机器规格更重要,买之前先 ping 一下目标站。

第四个坑是顺手买了包年。图包年折扣直接付了一年,结果三个月后架构从单机拆成多机,那台机器变成了纯 master,配置过剩。中小项目建议先按月买,架构稳定再转包年。这也呼应了 1.2 节的观点:早期保持可退出的灵活性比省那点折扣重要。

还有一个不算坑但值得分享的决定:我们把 master 和其中一台 worker 买了同一规格的机型。好处是任何一台机器坏了,另一台可以直接顶上——配置相同、镜像通用,灾备能力是白捡的。多花不了多少钱,但出事的时候能救命。5.2 节有一次大规模封禁危机,就是靠这台备用机切流量熬过去的。

小结

轻量应用服务器对中小规模的采集平台是完全够用的起点:月费几百块、套餐透明、扩容就是升级套餐或加机器。真正的成本风险不在机器本身,而在带宽计费方式和存储增长速度,这在 5.3 节会展开。规格选择的核心是分清角色负载:worker 看带宽、master 看内存和单核性能、数据库看并发。机器定了,下一节把 Go 库与云产品的地图对齐,正式进入开发。如果你想看轻量服务器在建站场景的更多玩法,可以参考 lighthouse-site-guide。## 延伸阅读