AWS 万亿账单异常暴露的真正问题:告警响了,却没有触发行动

2026-07-22 34 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

一次账单计算系统的配置变更,让部分 AWS 客户连续超过 24 小时看到数十亿乃至数万亿美元的预估费用。更值得工程团队警惕的不是数字有多夸张,而是 AWS 内部告警已经发现异常,却既没有停止账单生成,也没有通知值班工程师。最终,客户升级反馈在 4.5 小时后才真正推动处置。

这起事件说明:监控检测到异常,只完成了可靠性工作的前半段。告警必须连接到明确的处置动作、独立的通知路径和可验证的升级机制,否则它只是日志系统里的另一条记录。

检测、通知和控制是三套不同能力

一个完整的异常处理链路至少包含三个环节:

  1. 检测:识别账单是否偏离历史范围、业务规模或物理上限。
  2. 通知:把事件送到真正有人响应的渠道,并在无人确认时自动升级。
  3. 控制:阻止异常数据继续传播到客户界面、发票、付款或内部财务自动化流程。

本次事件中,检测环节显然工作了,但后两个环节没有形成有效闭环。工程上常见的错误,是把“指标进入告警状态”等同于“事故已经有人处理”。实际上,告警规则、通知目标、值班系统和自动阻断逻辑可能分别由不同团队维护,任何一处配置错误都会让链路失效。

对账单系统而言,控制措施也不应只有“继续发布”或“关闭整个平台”两种选择。更稳妥的状态机可以包含:

  • 正常发布经过校验的预估账单;
  • 异常时冻结最后一个可信值,并标记数据更新时间;
  • 将可疑结果隔离到待审核队列;
  • 暂停付款、资源停机等不可逆的下游自动化;
  • 保留人工覆盖入口和完整审计记录。

尤其要避免根据单一账单异常自动关闭生产资源。一个虚假的万亿账单已经很糟糕,如果它进一步触发大规模停机,数据错误就会升级为真实业务事故。

平台级缓解措施也会制造监控盲区

事件处置期间,预算提醒和成本异常提醒被平台级禁用。这可能减少错误通知,但也意味着客户暂时失去了原本依赖的成本防线。系统进入缓解状态后,风险并没有消失,只是从“错误告警”转变为“真实异常可能无人发现”。

因此,关键控制不应完全依赖同一个故障域。可以这样实践:

  • 在独立账户或外部调度平台运行成本哨兵;
  • 将通知发送到独立的邮件、PagerDuty、Opsgenie 或其他值班通道;
  • 保存每日成本快照,避免只相信当前 API 返回值;
  • 同时检查账单金额、资源用量和业务吞吐量;
  • 对告警通道执行定期合成测试,确认消息确实到达值班人员。

“独立”并不只是把另一条 CloudWatch Alarm 放进同一个账户。如果数据源、身份系统、通知服务和管理权限都相同,它仍可能与主链路一起失效。

可以这样实践:部署一个外部成本哨兵

下面的 Python 脚本通过 AWS Cost Explorer 获取当月累计未混合成本,并同时检查绝对上限和相对基线。它可以在开发机、CI 定时任务或独立运维账户中运行。

运行前需要安装 boto3,配置具有 ce:GetCostAndUsage 权限的 AWS 凭证,并把 BASELINE_USD 改成该账户正常的月度成本基线。设置 SNS_TOPIC_ARN 后,异常会额外发送到 SNS;不设置时脚本仍会通过非零退出码通知外部调度器。

python -m pip install boto3
export BASELINE_USD=12000
export MAX_MULTIPLIER=4
export ABSOLUTE_LIMIT_USD=100000
export SNS_TOPIC_ARN='arn:aws:sns:us-east-1:123456789012:external-cost-alerts'
python cost_sentinel.py

将以下内容保存为 cost_sentinel.py

import json
import os
import sys
from datetime import date, timedelta
from decimal import Decimal

import boto3

baseline = Decimal(os.environ["BASELINE_USD"])
max_multiplier = Decimal(os.getenv("MAX_MULTIPLIER", "4"))
absolute_limit = Decimal(os.getenv("ABSOLUTE_LIMIT_USD", "100000"))
topic_arn = os.getenv("SNS_TOPIC_ARN")

start = date.today().replace(day=1)
end = date.today() + timedelta(days=1)  # Cost Explorer 的 End 不包含当天

ce = boto3.client("ce", region_name="us-east-1")
response = ce.get_cost_and_usage(
    TimePeriod={"Start": start.isoformat(), "End": end.isoformat()},
    Granularity="MONTHLY",
    Metrics=["UnblendedCost"],
)

amount = sum(
    Decimal(item["Total"]["UnblendedCost"]["Amount"])
    for item in response["ResultsByTime"]
)
threshold = min(baseline * max_multiplier, absolute_limit)
anomalous = amount > threshold

result = {
    "period_start": start.isoformat(),
    "observed_usd": str(amount),
    "threshold_usd": str(threshold),
    "anomalous": anomalous,
}
print(json.dumps(result, indent=2))

if anomalous:
    message = "AWS cost sentinel detected an anomaly: " + json.dumps(result)
    if topic_arn:
        boto3.client("sns").publish(
            TopicArn=topic_arn,
            Subject="External AWS cost anomaly",
            Message=message,
        )
    sys.exit(2)

这个脚本是第二层防线,不是真正独立的数据证明。它仍然读取 AWS Cost Explorer,因此计算平面返回错误数据时也可能看到错误金额。实际部署时,应把结果与资源数量、请求量、存储增长和前一日快照交叉验证。例如,费用增长一万倍而实例数量与流量基本不变,应被识别为“账单数据可疑”,而不是立即执行资源清理。

把告警当成需要测试的生产功能

成本告警通常只在费用异常时才被关注,因此很容易长期处于未验证状态。团队可以每月注入一次不会影响真实账单的合成事件,并记录以下服务级指标:

  • 检测延迟:异常出现到规则触发用了多久;
  • 通知延迟:规则触发到值班人员收到消息用了多久;
  • 确认延迟:多久有人确认并开始调查;
  • 升级成功率:第一响应人未确认时,是否通知下一层;
  • 控制有效性:可疑数据是否停止进入付款和资源治理流程。

还应为“告警系统被禁用”本身创建告警。预算提醒、异常检测器或通知订阅被暂停时,外部监控应把它当作高优先级配置变化,而不是安静地接受监控能力下降。

采用时的检查清单

这起事件最实用的结论不是再增加几条阈值规则,而是检查从异常到行动的整条链路:

  • 告警是否绑定了有效且有人负责的通知目标;
  • 无人确认时是否会自动升级;
  • 是否存在独立于主计费平台的成本监控;
  • 异常账单能否被冻结、隔离或回退到最后可信值;
  • 下游系统是否会因单一成本信号执行不可逆操作;
  • 平台级禁用告警时,是否有替代监控和明确的恢复期限;
  • 团队是否定期验证告警真的能把人叫醒。

一个可靠的告警系统不能只证明“机器发现过问题”。它必须证明正确的人及时收到消息,并且系统在人工介入前不会继续放大错误。


相关推荐