独家实操|腾讯云助手实现测试资源生命周期治理(三):生产白名单与误删防护实战——修复 AI 脚本误匹配生产资源缺陷
系列导航:
- 第一篇:被遗忘的测试资源、资源登记与到期标记体系、自动回收脚本实现
- 第二篇:延期申请与审批流程——让人有"反悔机会"的回收机制
- 第三篇(本篇):生产白名单与误删防护实战——修复 AI 脚本误匹配生产资源的缺陷
一、差点删掉生产库的那次演练
回收脚本上线前的第一次演练,我们准备了一个影子环境:5 台测试 CVM + 1 台"伪装成测试机"的生产配置镜像(名称故意不规范,模拟真实世界的脏数据)。让腾讯云助手生成的回收脚本跑 dry-run,输出计划如下:
[dry-run] 拟回收 6 台: 1. cvm-test-teamA-01 到期 3 天前 env=test ✓ 2. cvm-test-teamB-04 到期 5 天前 env=test ✓ 3. ... 6. prod-db-proxy-bak 「到期 1 天前」env=(缺失)第 6 台。一台生产环境的数据库代理备份机。
事后排查,它踩中的是一连串"合理"的逻辑缺陷:
- 这台机器
env标签缺失(历史遗留,谁都没发现); - 脚本的到期判断逻辑是
expire-at < now,而这台机器根本没有biz:expire-at标签——AI 生成的代码里,tags.get("biz:expire-at", "0")的默认值是字符串 “0”; - 字符串
"0"与当前时间比较,Python 里int("0") < now恒成立 →“缺标签"被解读为"1970 年到期”; - 再叠加它名称里有个
bak,AI 的资源描述里写它是"备份类资源,长期无活跃",进入了回收候选。
一个默认值 + 一个脏标签,差点合谋删掉生产机器。这个案例完美诠释了本系列反复出现的主题:自动化回收的最大风险不是脚本删错"选中的资源",而是把不该选的资源选了进来。本篇讲三层白名单 + 缺陷修复,确保"选错人"这件事在结构上不可能发生。
二、修复一:标签缺失 fail-fast,禁止任何默认值
第一处修复是哲学层面的,和前面多租户系列的原则完全一致:隔离/安全关键信息缺失时必须快速失败,禁止回落默认值。
# 修复前(AI 初版)——缺标签被静默解释为"1970 年到期"expire=int(tags.get("biz:expire-at","0"))# 修复后 —— 缺关键标签直接跳过回收 + 高优告警,绝不猜defget_expiry(tags:dict):raw=tags.get("biz:expire-at")ifrawisNone:raiseTagMissingError("biz:expire-at")try:expire=int(raw)except(TypeError,ValueError):raiseTagInvalidError(f"biz:expire-at={raw!r}")returnexpiredefrecycle_candidate(ins):try:returnget_expiry(ins.tags)<now()exceptTagErrorase:alert(f"[SKIP]{ins.id}标签异常({e}),已跳过回收,请人工处理",priority="high")returnFalse原则归纳:"信息不足"和"明确到期"是两种完全不同的状态,前者跳过+告警,后者才进回收流程。宁可漏回收(浪费点钱),不可错回收(删掉生产)。这就是回收场景的 default-deny。
三、修复二:三层白名单,正反都要堵
只有"跳过异常"还不够——那台代理机的标签齐全的话照样可能混进候选。所以回收执行前必须过三层白名单,任何一层命中即拦截:
PROD_WHITELIST={# 第 1 层:环境层 —— 只有显式 env=test 才可回收(白名单语义,防"默认是测试")"env":{"test","dev"},# 第 2 层:身份层 —— 生产资源 ID / 名称 / IP 硬清单(黑名单语义,最后防线)"ids":{"ins-proddb-01","ins-proddb-02","ins-prod-db-proxy-bak"},"names":["prod-*","*-prod-*","prod_*"],"ips":["10.1.0.0/16"],# 第 3 层:属性层 —— 生产特征字段(数据库、带宽包、备案域名绑定等)"attrs":{"is_prod_domain_bound":True,"bandwidth_package":True},}defpass_whitelist(ins)->tuple[bool,str]:# 层 1:env 必须显式为 test/dev(缺失/其他值一律拒绝)ifins.tags.get("env")notinPROD_WHITELIST["env"]:returnFalse,f"env={ins.tags.get('env')!r}not in test/dev"# 层 2:身份硬清单ifins.idinPROD_WHITELIST["ids"]:returnFalse,f"hit whitelist id:{ins.id}"ifany(fnmatch.fnmatch(ins.name,p)forpinPROD_WHITELIST["names"]):returnFalse,f"hit whitelist name pattern:{ins.name}"ifip_in_cidrs(ins.ip,PROD_WHITELIST["ips"]):returnFalse,f"hit whitelist ip:{ins.ip}"# 层 3:生产属性特征forattr,expectedinPROD_WHITELIST["attrs"].items():ifgetattr(ins,attr,None)==expected:returnFalse,f"hit prod attribute:{attr}"returnTrue,"ok"三层的设计逻辑值得展开:
- 层 1 是白名单语义:可回收的环境要显式声明(test/dev),env 缺失 = 不可回收。如果反过来写黑名单(“env != prod 就回收”),任何标签 typo(
pord、PROD)都会直接命中回收——那台代理机就是因为这个逻辑差点阵亡; - 层 2 是显式硬清单:关键生产资源逐台登记,模式匹配兜住命名规律的(
prod-*)。注意它不是第一道防线而是最后一道——万一标签全错,名称和 IP 还能救命; - 层 3 是语义特征:有些生产资源名字干净、标签也对(被误打的 env=test),但属性露馅——绑了生产域名、挂了带宽包。属性层是 AI 建议补的:我们让它"扮演攻击者,给一台生产资源伪装成测试资源混进回收清单",它给出的伪装方案(改标签、改名字)都被前两层挡住后,提出了"绑定生产域名"这条它自己也绕不开的特征。
四、修复三:执行前的"双重人审 + 双因子确认"
白名单之外,回收的执行动作本身还有两道流程闸门:
- 批量上限:单次回收执行超过 20 台必须拆批——大量回收意味着要么是大清理(该有人盯着)要么是逻辑出错了;
- 双因子确认:
terminate/release_attachments级别动作,执行前需要平台值班人 + 资源所属团队 TL 双方在审批卡片上确认。注意这里审批的是执行清单本身(含全部资源明细),不是抽象的"批准回收"——人必须看到具体删什么。
配合第一篇的三重闸门(环境/白名单/保护期),完整防线变成:
候选生成:标签校验(缺失 fail-fast,禁止默认值) ↓ 三层白名单:env 显式声明 → 身份硬清单 → 生产属性特征 ↓ 保护期校验:停机缓冲期内禁止 terminate ↓ 执行闸门:批量上限 + 双因子确认(人看明细清单) ↓ 执行后:回收结果回报 + 全量审计日志五、演练体系:白名单不是配完就完事
我们把这整套防线做成了每周自动演练:在影子环境里随机混入"伪装生产资源"(改标签、改名字、绑生产属性三种伪装各一批),跑完整回收流水线,断言全部伪装资源被拦截:
defweekly_drill():shadow=build_shadow_env(decoys=[fake_prod(drop_env_tag=True),# 去 env 标签fake_prod(fake_env="test"),# 伪装 envfake_prod(fake_env="test",clean_name=True),# 标签名双伪装fake_prod(fake_env="test",prod_domain=True),# 三重伪装,靠属性层拦截])plan=recycle_pipeline(shadow,dry_run=False)assertall(d.idinplan.blockedfordinshadow.decoys),"生产伪装资源未被全部拦截!"assertlen(plan.recycled)==len(shadow.expired_test),"正常回收不应误伤"四次演练抓过两个回归 bug:一次是白名单改版后names模式匹配的大小写问题(PROD-*没被prod-*匹配挡住,fnmatch 需要显式 casefold),一次是新同学加"快速回收"接口时绕过了层 3 校验。防线会被日常迭代悄悄打穿,演练是唯一能持续验证它还活着的方式。
六、系列总结
三篇合起来,测试资源生命周期治理的完整闭环:
- 登记:创建即打 TTL 标签,无标签不创建,存量走待认领(第一篇);
- 回收:三级递进(通知→停机→释放),停机缓冲拦截 43% 误回收(第一篇);
- 延期:自动核验 + 递减配额 + 审批分层,"还在用"必须过数据这一关(第二篇);
- 防误删:标签缺失 fail-fast、三层白名单(env 白名单语义 + 身份硬清单 + 生产属性)、批量上限、双因子确认、每周伪装演练(本篇)。
其中最想传递的一条经验:自动化回收系统的安全设计,重心不在"怎么删得准",而在"怎么保证删不到不该删的"——前者是效率问题,后者是敢不敢开自动化的前提。AI 生成的脚本在效率维度表现优秀,但"缺标签默认 1970 年到期"这类默认值缺陷,恰恰是它最稳定复现的盲区,每一次安全关键路径都必须人工审到默认值这一层。
系列完结。测试资源治理季度收益:资源池缩 41%、月省约 4.2 万、零误删。做 FinOps 和资源治理的同学,评论区聊聊你们敢不敢在生产开自动回收。