跳到主要内容
版本:1.3.0

预算告警与通知

当支出超过预算限额的指定百分比时,AISIX Cloud 预算告警会通知你。你可以用它在阻断型预算开始拒绝流量前提醒运维人员,或监控仅告警预算。

前提条件

开始前,请准备以下内容:

  • 至少一个现有预算
  • 用作组织通知渠道的 Webhook URL 或 Slack Incoming Webhook URL。

配置通知渠道

通知渠道是组织级告警目标,在 Notifications 视图中管理。组织内触发的每个告警都会发送到所有已启用的渠道。

支持两种渠道类型:

  • Webhook:控制面会将每个告警作为 JSON POST 请求发送到你的 URL。可通过这种方式与事件处理工具、聊天系统或其它接受 HTTP 回调的系统集成。Webhook 渠道还可以自定义请求体添加请求头
  • Slack:设置 Slack Incoming Webhook URL 后,告警会以易读的 Slack 消息发送。

创建并测试渠道:

  1. 打开 Notifications,然后选择 New channel
  2. Name 中输入名称,选择 Type,并填写 URL
  3. 保持选中 Enabled,然后选择 Create channel
  4. 为新渠道选择 Test,并确认目标已收到测试通知。

渠道 URL 是只写字段。创建渠道后,控制面仅显示类似 https://hooks.slack.com/*** 的脱敏形式。编辑渠道时将 URL 留空会保留已存储的 URL。

禁用渠道会停止后续投递。删除渠道会保留其历史投递记录;仍在队列中等待投递的通知会被标记为失败。

允许私有网络目标

你可以保存解析到私有、环回或链路本地地址的渠道 URL,但默认情况下,对该 URL 的测试和告警投递会失败。这可以防止共享控制面上由操作人员提供的 URL 访问内部端点。

对于 Webhook 接收器或 Slack 代理位于内网的本地部署控制面,请在控制面 API 上设置 AISIX_CLOUD_NOTIFY_ALLOW_PRIVATE_URLS=true。对应的 Helm Chart 配置项为 api.notifyAllowPrivateURLs

配置告警阈值

每个预算都包含一个告警阈值列表,阈值以预算限额的百分比表示。按照以下步骤为每个预算配置阈值:

  1. 打开 Budgets 视图,并选择要编辑的预算。
  2. Alert thresholds (%) 中输入一个或多个整数百分比,多个值之间用逗号分隔。例如,设置 80, 100 可分别在接近限额和达到限额时收到告警。
  3. 保存预算。

最多可输入 20 个不重复的阈值,取值范围为 1 到 200。若留空,控制面将使用 80

每个阈值在每个预算周期内只触发一次。当支出超过月度预算的 80% 时,控制面会向每个已启用的渠道发送一次告警。同一周期内不会重复发送 80% 告警,但其它已配置阈值可以分别触发告警。周期重置后,每个阈值都可以再次触发。对于按团队成员设置的预算(each member in a team),每个成员会独立触发告警。

对于阻断型预算,高于 100% 的阈值通常不会达到,因为后续请求会在达到限额时被拒绝。不过,低于限额时已准入的请求可能在完成时使支出超过限额,并发请求也可能造成超额。出现这类情况时,高于 100% 的阈值可以触发。

控制面会持续评估支出。即使之后没有更多流量,支出超过阈值后通常也会在 30 秒内发出告警。

Webhook 载荷

默认情况下,Webhook 渠道会收到以下 JSON 文档形式的告警:

{
"event": "budget_threshold",
"dedup_key": "budget_threshold:6f6d…:…:1782864000:80",
"org_id": "1f0c…",
"budget_id": "6f6d…",
"budget_name": "payments-team monthly",
"scope": "team",
"scope_ref": "9a2b…",
"subject_name": "payments-team",
"threshold_pct": 80,
"percent": 82.3,
"spent_cents": 8230,
"limit_cents": 10000,
"period": "month",
"period_start": "2026-07-01T00:00:00Z",
"triggered_at": "2026-07-21T09:00:00Z"
}

投递采用至少一次语义:重试或多渠道告警可能会多次到达同一接收器。同一次触发的所有投递都使用相同的 dedup_key。如果接收器必须对每个告警只处理一次,请使用该字段去重。Slack 渠道会以文本消息的形式呈现相同信息。

请求的 Content-Type 始终为 application/json

自定义请求体

在 Webhook 渠道上设置 Body template,即可发送你自定义结构的请求体,而不是默认文档。你可以借此把告警直接投递到要求特定消息格式的聊天平台。该字段留空则发送默认文档。

模板使用 Go text/template,变量写作 {{ .name }}。可用变量如下:

变量说明
.event事件类型,取值为 budget_threshold
.dedup_key同一次触发的所有投递共用的标识。
.org_id组织 ID。
.budget_id预算 ID。
.budget_name预算显示名称。
.scope预算作用域,例如 orgteammember
.scope_ref作用域对象的 ID。
.subject_name作用域对象的名称。
.threshold_pct触发的阈值,整数百分比。
.percent支出占限额的百分比。
.spent_cents支出金额,单位为分。
.limit_cents预算限额,单位为分。
.period预算周期,例如 month
.period_start当前周期的开始时间,RFC 3339 格式。
.triggered_at告警触发时间,RFC 3339 格式。
.message告警的单行可读摘要。

前 15 个变量就是默认文档中的字段;.message 是额外提供的便捷变量,例如 Budget 'payments-team monthly' (team 'payments-team') reached 82.3% of its month limit: spent $82.30 of $100.00

模板中可以使用一个函数 json,它将值渲染为 JSON 字面量,并对字符串完成加引号和转义。嵌入任何值时都建议使用它:

{"msg": {{ .message | json }}}

在引号内直接插值——{"msg": "{{ .message }}"}——同样可以渲染,但不建议这样写:组织自己设置的预算名或团队名中可能包含引号或反斜杠,此时模板渲染出的内容将不是合法 JSON。这会使该次投递永久失败,告警随之丢失。

模板需遵守以下规则:

  • 渲染结果必须是合法 JSON,且不超过 64 KiB。
  • 引用表格之外的变量名会报错,而不是渲染为空字符串。
  • rangetemplateblock 会被拒绝。每个变量都是单个字符串或数字,既没有可遍历的内容,也没有可调用的第二个模板。ifwith 可以使用。
  • 保存渠道时,控制面会用一个示例告警渲染该模板;如果渲染失败或结果不是 JSON,保存会被拒绝。
  • 如果模板在保存时能渲染、但在真实告警上渲染失败,该次投递会立即结束,不会重试。
  • 与请求头的值不同,模板不是只写字段:读取渠道时会原样返回。请把凭据放在请求头中,不要放进模板正文。
  • Body templateHeaders 仅 Webhook 渠道支持。将已有的 Webhook 渠道改为 slack 类型时,若这两个字段仍有值,请求会被拒绝——请在同一次请求中清空它们。

聊天平台示例

飞书(Lark)自定义机器人

{"msg_type": "text", "content": {"text": {{ .message | json }}}}

钉钉自定义机器人

{"msgtype": "text", "text": {"content": {{ .message | json }}}}

企业微信群机器人

{"msgtype": "text", "text": {"content": {{ .message | json }}}}

如果不使用 .message,而要自行拼接文本:

{"msgtype": "text", "text": {"content": {{ printf "Budget %s reached %d%% (%.1f%%) of its %s limit" .budget_name .threshold_pct .percent .period | json }}}}

添加请求头

在 Webhook 渠道上设置 Headers,即可在每次投递时附带额外的请求头,例如 Bearer Token、共享签名密钥或租户标识。凭据应当放在这里,因为请求头的值是只写的。

请求头需遵守以下规则:

  • 最多 16 个请求头。
  • 名称必须是合法的 HTTP 请求头名称,长度不超过 128 个字符;任意两个名称不能仅大小写不同。
  • 值的长度不超过 4096 个字符,且不能包含任何控制字符——不仅仅是换行符。
  • Content-TypeContent-LengthHostTransfer-EncodingConnection 会被拒绝(忽略大小写匹配),这些请求头由控制面自行设置。

读取渠道时会返回已配置的请求头名称,但每个值都被替换为 ***,因此「读取后修改再写回」可以直接使用:为某个名称提交 *** 会保留该名称下已存储的值(名称匹配忽略大小写)。如果为一个没有已存储值的名称提交 ***,请求会被拒绝,否则该请求头的值就真的变成了 ***。提交空对象会清除所有已配置的请求头。

验证投递

Notifications 视图中的 Delivery log 会记录组织实际触发的告警。每条记录包含渠道、状态(pendingdeliveredfailed)、尝试次数、最后一次错误和完整载荷。

发送失败后会按逐步增加的间隔自动重试:首次失败后 1 分钟,然后依次为 5 分钟、15 分钟、1 小时和 6 小时。连续 6 次失败后,投递会被标记为 failed,并保留在日志中供检查。而重试无法解决的失败——例如请求体模板无法渲染——则会让该次投递立即结束。

如果失败的这次尝试收到了 HTTP 响应,记录中还会包含该响应的状态码和响应体。当状态码本身不足以说明被拒绝的原因时,响应体往往是唯一的线索。响应体按目标返回的原样保留,最多 1 MiB,但该渠道自己发出的凭据会被替换为 ***:渠道 URL(完整 URL,以及仅路径和查询参数的形式),以及任意长度不小于 8 个字符的已配置请求头的值——因为目标常常会把它们回显在错误信息里。长度更短的请求头值不做替换,否则会把无关内容也一并遮蔽。

投递列表中只携带响应体的前 8 KiB;在记录上选择 Load full response,即可通过 GET /notification_deliveries/{delivery_id} 获取完整内容。投递成功时不会记录响应,未收到响应的尝试(例如超时、地址被拒绝或域名无法解析)同样不会记录。

测试发送不会出现在投递日志中。它是同步操作,不会重试,其结果直接返回给控制台:包含实际发出的请求体,以及在收到响应时(无论成功还是失败)目标返回的状态码和响应体(按同样规则脱敏)。在正式依赖某个请求体模板前,可以用它确认模板渲染出的内容符合预期。

备注

当部署允许私有网络目标时(AISIX_CLOUD_NOTIFY_ALLOW_PRIVATE_URLS),测试发送会把目标的响应体返回给任何有权编辑通知渠道的人。在操作人员不完全可信的控制面上启用该选项前,请考虑这一点。

通过 AISIX Cloud Admin API 管理告警

通知渠道属于 AISIX Cloud Admin API:

  • GET /notification_channels
  • POST /notification_channels
  • GET /notification_channels/{channel_id}
  • PATCH /notification_channels/{channel_id}
  • DELETE /notification_channels/{channel_id}
  • POST /notification_channels/{channel_id}/test

预算告警阈值对应预算资源的 alert_thresholds 字段。有关请求和响应结构,请参阅 AISIX Cloud Admin API 参考

后续步骤

接下来可阅读日志记录与审计,调查预算行为背后的请求和控制面变更。如需理解驱动预算评估的支出记录,请参阅用量上报