告警弹出来的时候说,staging 环境连不上了。第一反应是:关我什么事。四秒之后第二个念头冒出来——那客户为什么在推特上说登不进去? 顺着这条线查下去,结果比想象中难看:这个被叫做"测试环境"的东西,已经给真实用户提供服务好几个月了。一条被遗忘的 DNS 记录、一个从旧营销页面跳转过来的重定向、还有一批在演示环节被开通账号、之后再也没被迁走的客户。它不是"变成了生产环境",它是 对一部分人来说就是生产环境 ——这两件事本质上一样,只是默认配置更糟。 审计翻出来的三样东西 第一样,两年前"就复制这一次"的生产数据库转储。真实的姓名、真实的邮箱、真实的数据行。一份生产数据的副本,无论主机名写着什么,它都是生产级别的存在。 第二样,所有人都心知肚明的宽松规则。调试接口开着,管理后台靠一个路径而不是密码来防护,还有一个测试账号的密码躺在异地的一块白板照片里。这些在生产环境里叫事故,在测试环境里叫方便——直到真有活人住了进来。 第三样,没有备份、没有告警、没有演练。没人会给一件戏服做备份。然后这件戏服里住进了租客。 现在每次规划会都要重申一条规则:生产环境不是一个名字,它是用户数据所在的地方。 按数据分类,不按主机名分类 任何装着真实用户数据的东西,都要按生产标准对待——备份、告警、鉴权、打补丁——哪怕它叫 staging、叫 demo、叫 bob-box。真实数据要么经过清洗才能进来,要么干脆别进来;"就快速导一次"这种做法被明令禁止。 测试环境本身也改成了按设计即用即弃:每晚死掉重建,启动时清洗,每周五删除。一个每周都会死掉的环境,攒不下秘密、漂移、租客,也攒不下口口相传的"惯例"。 实现方式是一段夜间脚本:先彻底删掉旧的 staging,再重新创建一个,指定镜像、SSH 密钥、2 核 CPU、4G 内存、40G 磁盘,并挂上一份 cloud-init 配置。这份配置在启动时拉取清洗过的数据转储,解压后导入数据库,再重启应用。 在 Krova Cloud 上,这种生命周期之所以负担得起,是因为按分钟计费,而一个停止状态的 Cube 只收磁盘费用——周五删除是默认状态,不是一个特殊事件。 让环境撒谎的那些门 孤儿 DNS 记录是测试环境"长出"租客的主要途径。每周跑一次检查:遍历域名清单,逐个查 CNAME 记录,只要还有记录指向 staging,那就是一扇门,直接打印出来。 再加上分类检查——直接问 staging 它手里握着什么,而不是问它叫什么名字。一条 SQL 查用户表里邮箱不以 @example.com 结尾的记录数,只要这个数字大于零,就说明 staging 在骗你。再查一下监听端口,如果有调试端口在监听,那就是一个长着测试环境外壳的生产级伤口。 几个必须承认的现实 合成数据有真实感税。有些 bug 只在真实形状的数据上才会咬人——400 个字符的姓名、地址栏里的 emoji。所以做法是:用一套更小、更古怪的合成数据集,加上偶尔经过清洗的真实样本,并且像审查代码一样审查这些样本。完美不在菜单上,分类才是。 小团队负担不起完全对等。没关系。规则不是"测试环境必须等于生产环境",而是"任何真实的东西都不能住在规则是假装的地方"。一句话,任何规模都能执行。 即用即弃的测试环境也修不好生产环境。生产环境仍然需要真正的墙:默认不开放公网 IP、每个 Cube 独立内核、权限收敛的密钥。这里谈的是分类错误,不是要拆掉城堡。 环境会撒谎,数据不会。去问问你的 staging 手里握着什么——别看主机名,看内容。如果答案里包含真实姓名、真实邮箱,或者一个从被遗忘的门里溜达进来的客户,那你根本没有测试环境。你有的是一套穿着戏服、无人监控的生产环境。 我们这套戏服穿了几个月。客户从没察觉。这不是什么值得夸的事,这才是最吓人的地方。 特别

about image