
每月初拿到云上账单,会议室里通常会出现同一个场景:总额大家都认,往下拆没人认。三年前我们的账单只有资源维度,一堆实例编号列在那里,运维看着像天书,业务部门看着像别人的事。后来做了一轮标签治理,把资源按系统和责任人对齐,账单终于能拆到每个系统头上。结论先说:标签治理的目标不是省钱,而是让每一笔花费都有归属,省下的钱是归属清楚之后的副产品。
那次开会印象很深,账单上有一笔明显偏高的对象存储费用,谁也说不出是哪个业务在用。运维猜是备份,开发猜是日志,后来翻了两天的访问日志才确认是一个早就下线的测试环境还在跑。这类事情发生的次数多了,大家对账单就失去兴趣,反正说不清。归属不清,优化就无从谈起,因为没人知道自己该为什么负责。
翻了一遍全部资源,标签问题集中成三种。缺失:近四成资源一个标签都没有,账号是几年前开通的,谁开的已经查不到。随意:有人写"生产",有人写"prod",有人写"线上",同一个环境三种写法,汇总的时候分成三行。不继承:实例打了标签,挂在它下面的云盘、快照、负载均衡没有,这些附属资源往往是账单里的隐形大头。
一开始把目标定成"降本两成",结果各部门开始砍资源,把测试环境关停、把备份周期缩短,短期数字好看,长期埋雷。后来把目标改成"每一笔费用都能说出归属",考核的是标签覆盖率和对账差异率。指标换了之后,行为也跟着变,大家开始认真讨论哪个系统该用什么规格,而不是一刀切地关停。
标签不追求多,先保证三层。系统层:这个资源属于哪个系统,用统一的系统代号,不用中文名,避免改名字带来的历史断档。环境层:生产、预发、测试三类,取值只允许三个。责任人层:一个主责人加一个备份人,用员工编号而不是姓名,避免重名和离职后的悬空。三层之外,项目、成本中心按需加,但前三层是硬要求,缺一个就算不规范。
规范要能被机器检查,否则一定走形。键名统一用小写加下划线,值限定在枚举范围内,系统代号从资产库里取,不允许现场手输。我们把这套规则做成了一个校验脚本,扫描时不符合规范的一律计入待整改清单,而不是"人工判断"。规则写进文档的同时也写进流水线,新建资源如果不合规,创建流程会提示。
标签治理的难点不在打标签,在于知道每个资源属于谁。我们的做法是三层来源:资产库里的系统清单是权威源,CMDB 里记录的部署关系作为辅助,剩下的靠账单异常值和访问日志反推。反推出来的结果不直接写进标签,而是生成待确认清单,发给各系统负责人确认,确认之后才批量写入。这个流程慢一点,但避免了自己给自己编归属。
治理不能上来就批量改。先跑一遍扫描,输出三张清单:完全无标签的资源、标签不合规的资源、标签合规但附属资源缺失的资源。第三张清单容易漏,因为主资源看着是规范的,账单超支却出在附属资源上。扫描脚本只读不写,跑多少次都安全,可以放进每日定时任务里,作为长期的合规监控。
批量写标签必须能预演。脚本设计成默认只打印将要执行的动作,加参数才真正写入,并且每条操作都记日志,便于回滚:
import csv, json, logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def load_rules(path):
"""规则文件:resource_id, system_code, env, owner, backup_owner"""
with open(path, encoding="utf-8") as f:
return {r["resource_id"]: r for r in csv.DictReader(f)}
def build_tags(rule):
return {
"system": rule["system_code"],
"env": rule["env"],
"owner": rule["owner"],
"owner_backup": rule["backup_owner"],
}
def apply_tags(client, rules, dry_run=True):
ok, skip, fail = 0, 0, 0
for rid, rule in rules.items():
tags = build_tags(rule)
if dry_run:
logging.info("[DRY] %s -> %s", rid, json.dumps(tags, ensure_ascii=False))
skip += 1
continue
try:
exist = client.describe_tags(rid) or {}
# 只补缺失键,不覆盖人工维护过的值
merged = {**tags, **{k: v for k, v in exist.items() if k in tags}}
client.tag_resource(rid, merged)
logging.info("[OK] %s -> %s", rid, json.dumps(merged, ensure_ascii=False))
ok += 1
except Exception as e:
logging.error("[FAIL] %s %s", rid, e)
fail += 1
logging.info("done ok=%d skip=%d fail=%d", ok, skip, fail)这段有三个刻意设计。默认预演,避免一条命令改掉几百个资源。只补缺失键、不覆盖已有值,因为有些标签是业务自己维护的,脚本不该抢方向盘。失败不中断,逐条记录,跑完看汇总,比中途退出更好排查。批量接口另有并发限制,实际使用时按每批五十个分批提交,中间留间隔。
打完标签,账单侧要做的是按标签聚合。云厂商的账单导出通常支持按标签分组,导出之后按系统汇总,再按责任人的成本中心归集。这里有个细节:共享资源,比如负载均衡、日志服务、镜像仓库,标签只能打到一个系统上,但实际被多个系统共用。我们的做法是给这类资源打上"共享"标记,按月按调用量或流量比例二次分摊,比例表放在资产库里,每季度复核一次。
治理过一次,不做预防,三个月后又会乱。做法是把标签注入到创建环节:基础设施即代码的模板里把三层标签设成必填变量,控制台创建的走一个申请单,申请单里必填系统、环境、责任人。这一步的推动成本比事后治理低得多,因为新建的时候人还知道自己在干什么。看板这块我们是放在搭贝上做的,每天从账单导出表同步一次,按系统和环境两个维度切片,谁点开都能看到自己的部分。
头三个月只看一个指标:标签覆盖率。每周公布一次各系统的覆盖率,不排序次,只列数字。覆盖率上到九成之后,才开始看准确率,也就是标签与实际归属是否一致。顺序反了会很难推,因为数据不准的时候,大家会争论"这个标签到底对不对",而不是先去补。
每月初开半小时的对账会,参加的是各系统负责人,议程只有两项:上月各系统花费、异常波动说明。会上不发次序,只发趋势。这个会开了一年多,效果是负责人开始主动关注自己的花费,有人提前一个月就说"下个月我们要上新的采集节点,成本会涨"。预算意识是这样长出来的,不是靠制度压出来的。
资源会迁移、会重建、会被复制,复制出来的资源经常带着旧标签。我们遇到过测试环境从生产快照克隆出来,标签也一起克隆,账单上出现了两个"生产"。之后加了条规则:每月扫描一次标签与资产库的差异,不一致的生成工单。规则不复杂,能挡住大部分漂移。
标签覆盖率从六成到九成七,账单对账差异率从两成降到三个点以内。更实际的变化是讨论方式:以前开会问"这个月怎么涨了",现在问"订单系统这个月涨了两成,是不是加了节点"。成本从一笔说不清的总账,变成了几条能追到人的明细。省下来的钱我们也算过,大概一成多,但这不是重点,重点是再也不用为账单开两小时的会。
标签治理这件事,规则简单、执行琐碎。四句话可以概括:三层标签先立住、扫描只读批量先预演、共享资源单独分摊、新建环节默认合规。做到这四条,账单就从一份看不懂的清单,变成一张能指导决策的表。
问:标签和资源组,哪个更适合做成本分摊?
答:标签更灵活,一个资源可以同时带系统、环境、责任人多个维度,适合多角度分析;资源组更刚性,一个资源只属于一个组,适合做权限边界。实践中常把两者结合:资源组管权限和责任主体,标签管分析和分摊。只靠资源组,遇到共享资源就无法拆分;只靠标签,权限控制又不够硬。
问:历史资源标签缺失严重,从哪批先补?
答:按账单金额从高到低补,先补花费占前八成的资源,再补剩下的。这样投入产出比高,也能快速让对账会有数据可讲。补的时候按系统成批推进,不要按资源类型推进,因为按类型推进需要多个负责人同时确认,协调成本高。
问:脚本批量写标签会不会误改业务标签?
答:会,所以要做三件事:默认预演、只补缺失键、写入前先读现有值。另外把脚本的写权限限制在运维组的服务账号上,业务侧只给只读。每次执行保留完整日志,出问题可以按资源编号逐条回滚,这几条做到,风险基本可控。
问:共享资源的成本怎么分摊才合理?
答:先按可观测的用量分摊,比如日志服务按写入量、负载均衡按流量、镜像仓库按存储占用,用量数据都能从监控里取。取不到用量的,按各系统资源数的比例分摊,比例每季度复核一次。分摊规则要写进文档并公示,让被分摊的部门知道算法,争议会少很多。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。