机制四要素
可见性
每个业务线负责人每月都能看到自己的账单构成、环比变化和优化建议清单。看不见就不会管。
责任制
每个成本中心指定 owner,异动超过阈值需要在例会上说明原因。不是问责,是让优化有人推。
自动化
闲置资源扫描、非生产定时启停、未打标拦截、预算告警,能自动的都自动化,不靠人记。
激励
把节省下来的预算部分返还给业务线用于技术投入,比单纯考核降本指标更有效。
月度运营节奏
-
每月 1-3 日
账单结算与报表生成
上月账单出账,自动生成各业务线成本报表并推送给 owner。
-
每月 5 日
异动分析
识别环比涨幅超过 15% 的成本项,逐项归因:是业务增长、架构变更还是配置失误。
-
每月 8 日
FinOps 例会
各业务线 owner 参加,过异动项、过优化建议、认领本月优化任务。控制在 45 分钟内。
-
每月 10-25 日
优化执行
按认领的任务推进优化,进展在群里同步,遇阻由 FinOps 负责人协调。
-
每月 28 日
效果复盘
统计本月完成的优化项与实际节省额,更新到累计节省看板。
-
每季度末
承诺折扣复盘
评估覆盖率与利用率,决定是否加购或调整,并做下季度预算规划。
成本异动自动告警
"""成本异动检测与告警。
每天凌晨执行,对比昨日与前 7 天同期的日均成本,
按业务线维度找出异动并推送到企业微信/钉钉。
"""
from __future__ import annotations
import json
import os
from datetime import date, timedelta
import boto3
import urllib3
ce = boto3.client("ce", region_name="us-east-1") # Cost Explorer 只在 us-east-1
http = urllib3.PoolManager()
WEBHOOK = os.environ["ALERT_WEBHOOK"]
SPIKE_PCT = float(os.environ.get("SPIKE_PCT", "20"))
MIN_DELTA_USD = float(os.environ.get("MIN_DELTA_USD", "50"))
def daily_cost_by_tag(start: date, end: date, tag_key: str = "CostCenter") -> dict[str, dict[str, float]]:
"""返回 {日期: {业务线: 成本}}。"""
resp = ce.get_cost_and_usage(
TimePeriod={"Start": start.isoformat(), "End": end.isoformat()},
Granularity="DAILY",
Metrics=["UnblendedCost"],
GroupBy=[{"Type": "TAG", "Key": tag_key}],
)
out: dict[str, dict[str, float]] = {}
for result in resp["ResultsByTime"]:
day = result["TimePeriod"]["Start"]
out[day] = {}
for group in result["Groups"]:
raw = group["Keys"][0]
name = raw.split("$", 1)[1] or "__UNTAGGED__"
out[day][name] = float(group["Metrics"]["UnblendedCost"]["Amount"])
return out
def detect_spikes() -> list[dict]:
today = date.today()
yesterday = today - timedelta(days=1)
baseline_start = today - timedelta(days=8)
data = daily_cost_by_tag(baseline_start, today)
y_key = yesterday.isoformat()
if y_key not in data:
return []
yesterday_cost = data[y_key]
baseline_days = [d for d in data if d != y_key]
spikes = []
for cc, cost in yesterday_cost.items():
history = [data[d].get(cc, 0.0) for d in baseline_days]
if not history:
continue
avg = sum(history) / len(history)
if avg <= 0:
continue
delta = cost - avg
pct = 100.0 * delta / avg
if pct >= SPIKE_PCT and delta >= MIN_DELTA_USD:
spikes.append({
"cost_center": cc,
"yesterday": round(cost, 2),
"baseline_avg": round(avg, 2),
"delta": round(delta, 2),
"pct": round(pct, 1),
})
return sorted(spikes, key=lambda s: s["delta"], reverse=True)
def notify(spikes: list[dict]) -> None:
if not spikes:
return
lines = ["## 成本异动提醒",", f"检测日期:{date.today() - timedelta(days=1)}","]
for s in spikes:
lines.append(
f"- **{s['cost_center']}**:${s['yesterday']} "
f"(7 日均值 ${s['baseline_avg']},↑{s['pct']}%,"
f"多支出 ${s['delta']})"
)
lines += ["", "请对应负责人在今日内确认原因。"]
payload = {"msgtype": "markdown", "markdown": {"content": "\n".join(lines)}}
http.request(
"POST", WEBHOOK,
body=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
)
def lambda_handler(event, context): # noqa: ARG001
spikes = detect_spikes()
notify(spikes)
return {"spike_count": len(spikes), "spikes": spikes}
同时设置绝对金额门槛和百分比门槛,避免小金额波动刷屏。开发环境从 $1 涨到 $2 也是 100%,但没人关心。