1.2 自建存储 vs COS:一笔账算清

场景:一台闲置服务器引发的幻想

拆完需求,第一股冲动就是自建。手里正好有台 4 核 8G 的闲置云服务器,挂块硬盘每月也不过几百块。我拿出计算器一按,1TB 硬盘几个月就能买断,COS 一年下来还要交存储费,这不明摆着自建便宜吗?这种买断思维特别有说服力,因为它只算看得见的钱,而钱一旦以每月多少的形式出现,人就会觉得它免费。

这个念头持续了大概两天,就被三件事打碎:一是带宽,云服务器默认只有很小的公网带宽,下个 500MB 的文件要等一个多小时,团队五个人根本没法用;二是容量,1TB 听着多,设计稿和视频塞起来很快,半年就满,扩容等于换机器迁移数据,还得停机一夜;三是可靠性,单机一块盘,坏了全完,多年的设计稿跟着陪葬。自建存储的钱不在硬件上,都在带宽、冗余和运维的暗处,而这些钱按月递增,永远不会停止。我后来把这套心理活动讲给朋友听,发现每个亲手搭过存储的人都有同一段心路:先是觉得自己买硬件赚了,然后被带宽教做人,最后在某个深夜的故障后彻底想通。

方案:两条路的对账单

我把两条路的开销摊在一张表里,按团队真实用量(约 500GB 存储、月下载 200GB、50 人使用)估算。为了公平,自建那一列我按四年折旧、含人工的方式算,不刻意夸大:

对比项 自建存储 对象存储 COS
首期投入 服务器加硬盘约 4000 元 0 元,按量付费
带宽 需升级到 20Mbps,月费 800 元起 按流量计费,或走 CDN 更便宜
扩容 换机器、迁移数据,停机一夜 控制台改配置,无感扩容
可靠性 单机约 99%,看运气 服务协议 99.99% 起步,多副本
运维 我自己,半夜报警自己起 云厂商兜底,我只管调用
技术栈 要写存储层、备份脚本、监控 写配置和调用,代码量极小

再看一笔一年的明细账,把每一项钱都摆到桌面上。注意自建的带宽是固定开销,用不用都要交,而云上不用就不花钱:

费用项 自建(元/年) COS(元/年)
硬件折旧 6000 0
带宽 9600 0(走 CDN 回源计费)
CDN 加速 不适用 约 1800
存储费 0(已买断) 约 1200
备份与冗余 约 2000(第二块盘) 0(多副本已含)
运维工时 无法估,按一次故障两天算 0
合计 17600 起 约 3000

账算到这份上,还要补一个决策维度:什么情况下我会真的选自建?我也认真想过这个问题:

场景 更优选择 理由
个人小站、几 GB 文件 自建无妨 带宽够用,宕机无后果
团队协作、持续增长 对象存储 扩容和可靠性是刚需
内网隔离、合规要求 自建或私有化 数据不能出门
突发大流量下载 云上按量 固定带宽扛不住峰值

结论很明显:自建的便宜是幻觉,省下的是明面的存储费,交出去的是带宽、冗余和半夜的睡眠。买断的硬件只会贬值,而云存储的单价年年都在降。算账时别忘了把新客优惠算进去,可以先看一眼 腾讯云产品特惠,存储包和流量包的新客价往往比官网列表低不少,第一年成本还能再降一截。

配置:让存储后端随时可切换

虽说选了 COS,我还是在后端留了一层抽象,方便哪天想换回自建或迁移到别的云。Go 里这事不复杂,定义接口即可,实现细节关门之后随便换。这一层后来证明是整个后端最值钱的设计之一:

// Store 定义存储后端的最小行为,和具体厂商解耦
type Store interface {
    // 生成上传地址与凭证
    SignedUpload(ctx context.Context, key string, size int64) (*Upload, error)
    // 生成带签名的下载地址
    SignedDownload(ctx context.Context, key string, ttl time.Duration) (string, error)
    // 检查对象是否存在(秒传与对账用)
    Head(ctx context.Context, key string) (int64, error)
}

// CosStore 实现 Store,内部封装 COS SDK
type CosStore struct{ client *cos.Client }

// Upload 是返回给客户端的直传凭证
type Upload struct {
    URL    string        // 客户端直传的目标地址
    Token  string        // 临时密钥信息
    Expire time.Duration // 有效期
}

上线半年后这层抽象真的救了我一次:某区域 COS 抖动,上传成功率掉到一半以下,我把配置切到另一个桶,只改了初始化代码,接口不变,调用方零感知。如果当初把 COS SDK 的调用散落在 handler 各处,这次切换至少要改两天,而且必然漏改。平时写代码时,要求所有和 COS 打交道的代码都必须经过 Store 接口,单元测试里注入一个 LocalStore 实现,走本地目录,CI 里跑完整流程不花一分钱云上费用。这个习惯让重构变成一件安全的事。

踩坑:账单里那些看不见的角落

第一是请求费用。COS 除了存储费和流量费,还有按请求次数计费的一项,删文件、列目录、分片上传的每一次接口调用都算钱。我们早期分片设得太小,1GB 文件切了上千片,初始化、上传、完成各一次请求,光请求费就比流量费还高,详尽的分片策略在 2.3 大文件分片一节。分片大小和请求次数之间有个甜蜜点,值得上线后盯一周账单反复调。

第二是存储类型错配。为省钱我把桶里所有文件设成了低频存储,结果频繁读取的文件每次下载都有额外取回费,算下来比标准存储还贵。存哪类文件用哪类存储,得看访问频率,不能一刀切,热数据用标准存储,冷数据才归档,这部分在 5.2 生命周期一节展开。

第三是欠费停服。小项目没有财务提醒,某天流量突增把余额刷掉,桶直接被停,整个网盘 404,所有用户同时来找我。后来我在云监控里配了余额告警,余额低于阈值就发通知,还会同时发给团队群,这一招在 5.3 监控告警里细讲。

第四是 NAT 网关费。有人传文件传到一半去查账单,发现费用蹭蹭涨,原因是早期测试时把回源地址配错了,所有下载都在绕远路,流量费按最贵的路径计。配置类的东西改一行就是钱,上线前必须逐项核对费用路径。

第五是出口流量费的方向感。云上的流量费分上传和下载两个方向,上传进对象存储通常免费,下载才收费,而 CDN 回源到 COS 的那一段也要单独算。有一阵子我搞不清三段费用谁收谁,优化了半天成本反而涨了,搞得啼笑皆非。后来我把整条链路的费用画成一张流向图贴在墙上,每次调配置前先看一眼图,再没犯过方向错误。

最后补一段给纠结者的心里话。我见过很多团队在自建与上云之间摇摆半年,服务器买了又退,方案写了又废,最后什么都没上线。其实选哪条路都不致命,致命的是一直选。先用最糙的方式跑起来,让真实用户和真实账单替你做决定,这比任何 PPT 里的对比表都管用。我们的需求表和对账单都还挂在 wiki 上,每次新人加入都会被这篇文档拦下问一堆问题,回答完这些问题,他就理解了这个系统为什么是今天这个样子。

自建方案还有一笔账容易漏算:硬件折旧后的迁移。硬盘三年必换、服务器五年淘汰,换新又是一轮采购加停机窗口。云端按月付费永远用新硬件,这笔隐性成本加进去,自建的价格优势进一步缩水。

我们的结论不是自建一定差,而是小团队的时间不该花在造存储轮子上。如果公司有专门的运维团队与机房资源,结论可能不同。技术选型永远因地制宜,但算账的方法是相通的。

小结

自建还是上云,本质是买断还是租用。租到的是带宽、可靠性和运维时间,买断的是贬值资产。对我这种五人小团队来说答案没有悬念:把存储交给 COS,把人力省下来写业务。这笔账算清之后,后面所有技术选型都没再纠结过,因为争论的终点永远是一张对账表,而不是谁嗓门大。如果你正处在选型的当口,我建议你也花一小时把这张表填出来——填表的过程会逼你直面自己到底有多少用户、多少流量、多少运维时间,很多自我感动式的技术情怀在数字面前会自动消失。最后提醒一句,表格里的数字不必精确到个位,量级对了结论就对,过度精确反而让决策拖延,先用粗估上线,有了真实账单再回来修这张表,5.4 成本复盘一节就是修过三轮之后的结果。

延伸阅读