RuleSyntax


https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/
https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
https://prometheus.io/docs/prometheus/latest/querying/basics/
https://prometheus.io/docs/prometheus/latest/querying/operators/
https://prometheus.io/docs/prometheus/latest/querying/functions/
https://docs.victoriametrics.com/victoriametrics/vmalert/
https://github.com/google/re2/wiki/Syntax

1. Rule File Structure#

当前 rule 文件是 Prometheus/vmalert 兼容格式。文件顶层是 groups,每个 group 里放一组 recording rules 或 alerting rules;interval 是这个 group 多久评估一次,expr 是 PromQL/MetricsQL 表达式。

groups:
  - name: aws-alb.recording.rules
    interval: 1m
    rules:
      - record: aws:alb:request_count:sum5m
        expr: |
          sum by (environment, dimension_LoadBalancer) (
            sum_over_time(aws_applicationelb_request_count_sum[5m])
          )

  - name: aws-alb.alerting.rules
    interval: 1m
    rules:
      - alert: AwsAlbElb5xxRateHigh
        expr: aws:alb:elb_5xx_rate:ratio5m > 0.01
        for: 5m
        labels:
          severity: P1
          service: alb
        annotations:
          summary: "ALB 5xx rate is high"
Key Meaning
groups rule group list
name group name, file 内唯一即可
interval evaluation interval,例如 1m
rules 当前 group 下的 rule list
record recording rule,计算结果会写成一个新 metric
alert alerting rule,表达式返回的每条 series 都会变成一条 alert
expr PromQL/MetricsQL 表达式
for 条件持续多久才 firing;之前是 pending
labels alert 静态标签,用于 routing/grouping,例如 severity/team/service
annotations 给人看的上下文,例如 summary、dashboard、runbook
`expr: `

intervalfor 的关系:

recording group:
    interval: 1m
    每 1 分钟执行一次 recording rule
    每次把 expr 的结果写成 record 指定的新 metric

alerting group:
    interval: 1m
    每 1 分钟执行一次 alerting rule 的 expr

for: 5m:
    不是 5 分钟里有一次满足就 firing
    而是这条 alert series 必须连续保持 true 满 5 分钟
    在 firing 之前状态是 pending
    中间任何一次 evaluation 变成 false,这个 pending 计时会重置

Example timeline:

interval = 1m
for = 5m

00:00 expr=true     alert enters pending
00:01 expr=true     pending 1m
00:02 expr=true     pending 2m
00:03 expr=true     pending 3m
00:04 expr=true     pending 4m
00:05 expr=true     firing

if one evaluation becomes false:

00:00 expr=true     alert enters pending
00:01 expr=true     pending 1m
00:02 expr=false    pending reset, alert inactive
00:03 expr=true     pending starts again
00:04 expr=true     pending 1m
00:05 expr=true     pending 2m

所以 interval: 1m + for: 5m 可以理解成每 1 分钟检查一次,表达式需要连续约 5 分钟都满足。实际 firing 时间会受 evaluation 时间点、数据延迟、rule reload 影响,不要理解成“固定恰好检查 5 次”。

如果希望某次 evaluation 只要满足就立即触发,不要把 for 写成 interval 的时间,而是不要写 for,或者显式写 for: 0m

Immediate firing sample:

groups:
  - name: api.alerting.rules
    interval: 1m
    rules:
      - alert: ApiDown
        expr: up{job="order-api"} == 0
        labels:
          severity: P1
          service: order-api

Timeline:

interval = 1m
for is not set

00:00 expr=false    inactive
00:01 expr=true     firing immediately
00:02 expr=true     still firing
00:03 expr=false    resolved

对比 for: 1m

interval = 1m
for = 1m

00:00 expr=true     alert enters pending
00:01 expr=true     firing

所以 for: 1m 不是“出现一次就触发”,而是至少要持续到下一次 evaluation 仍然满足。P1 需要立即触发时可以不写 for,但建议表达式本身带 traffic guard 或多条件判断,避免单点毛刺。

2. Reading An Expression#

先按这个顺序读:metric selector -> range/function -> aggregation -> arithmetic -> comparison -> vector matching。

aws:alb_targetgroup:target_5xx_rate:ratio5m > 0.05
and on (environment, dimension_LoadBalancer, dimension_TargetGroup)
aws:alb_targetgroup:request_count:sum5m >= 100
and on (environment, dimension_LoadBalancer, dimension_TargetGroup)
aws:alb_targetgroup:target_5xx_count:sum5m >= 10

意思是:同一个 environment + LoadBalancer + TargetGroup 上,5xx 比例大于 5%,并且 5 分钟请求数至少 100,并且 5xx 次数至少 10。后两个条件是 traffic guard,避免低流量下 1 个错误就触发高比例告警。

3. Metric Selector#

最基础的 selector 是 metric_name{label matcher}。AWS/YACE 的 CloudWatch 维度通常会变成 dimension_xxx label,例如 dimension_TableNamedimension_Operationdimension_LoadBalancer

aws_dynamodb_successful_request_latency_p95{
  dimension_TableName!="",
  dimension_Operation=~"GetItem|PutItem|UpdateItem|DeleteItem"
}

Sample input:

aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="orders", dimension_Operation="GetItem"} 0.012
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="orders", dimension_Operation="PutItem"} 0.031
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="orders", dimension_Operation="Scan"} 0.180
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="", dimension_Operation="GetItem"} 0.009
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="users", dimension_Operation="UpdateItem"} 0.044
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="users", dimension_Operation="BatchGetItem"} 0.070

After selector:

aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="orders", dimension_Operation="GetItem"} 0.012
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="orders", dimension_Operation="PutItem"} 0.031
aws_dynamodb_successful_request_latency_p95{environment="prod", dimension_TableName="users", dimension_Operation="UpdateItem"} 0.044

被过滤掉的 series:

dimension_Operation="Scan":
    不匹配 GetItem|PutItem|UpdateItem|DeleteItem

dimension_TableName="":
    被 dimension_TableName!="" 排除

dimension_Operation="BatchGetItem":
    不在 regex 列表里
Matcher Meaning
label="value" label 精确等于 value
label!="value" label 不等于 value
label=~"regex" label 匹配 RE2 regex
label!~"regex" label 不匹配 RE2 regex
dimension_TableName!="" 只保留 table name 非空的 series,常用于排除无维度聚合数据

注意:Prometheus label regex matcher 是 fully anchored,job=~"node" 只匹配完整值 node;要包含匹配要写 job=~".*node.*"label="" 会匹配空值,也可能匹配没有这个 label 的 series,所以排除缺失/空值常用 label!=""

4. Range Vector And Duration#

[5m] 把 instant vector 变成 range vector,意思是“每条 series 最近 5 分钟内实际存在的样本”。sum_over_time(metric[5m])rate(counter[5m]) 这类函数需要 range vector。

Syntax Meaning
metric instant vector,当前评估时间点的值
metric[5m] range vector,最近 5 分钟内实际采集到的样本
for: 10m 表达式连续 10 分钟为 true 才 firing
interval: 1m 每 1 分钟评估一次 rule

[5m] 不是固定取 5 个点,而是按时间窗口取样本。采集间隔是 1 分钟时,通常能看到约 5 个样本;采集间隔是 3 分钟时,5 分钟窗口里可能只有 1 到 2 个样本。

metric_name{label="value"}:
    metric name + labels 选中一条或多条 time series

metric_name{label="value"}[5m]:
    每条 series 最近 5 分钟内的 samples

sum_over_time(metric_name{label="value"}[5m]):
    对每条 series 自己的 5 分钟 samples 求和

常见 duration:30s1m5m10m1h。CloudWatch/YACE 很多指标 5 分钟一个点,for 不要太短,否则容易受采集延迟影响。

5. Aggregation#

Aggregation 用来把多条 series 合并成更有业务意义的粒度。by (...) 指定哪些 label 被保留;没写在 by 里的 label 会被聚合掉。

sum by (environment, dimension_QueueName) (
  sum_over_time(aws_sqs_number_of_messages_deleted_sum{dimension_QueueName!=""}[5m])
)

Sample input at current evaluation time:

aws_sqs_number_of_messages_deleted_sum{environment="prod", dimension_QueueName="orders"}[5m]
    10 12 8 15 5

aws_sqs_number_of_messages_deleted_sum{environment="prod", dimension_QueueName="payments"}[5m]
    3 7 0 9 1

aws_sqs_number_of_messages_deleted_sum{environment="uat", dimension_QueueName="orders"}[5m]
    1 2 0 3 4

aws_sqs_number_of_messages_deleted_sum{environment="prod", dimension_QueueName=""}[5m]
    100 120 110 90 80

这里的 10 12 8 15 5 表示同一条 time series 在最近 5 分钟窗口内采集到的 samples,不是 5 条不同 metric。

metric name:
    aws_sqs_number_of_messages_deleted_sum

labels:
    environment="prod"
    dimension_QueueName="orders"

range:
    [5m] 表示当前评估时间往前 5 分钟

samples:
    10 12 8 15 5
    如果采集间隔是 1m,可以理解成 5 个 1m period 的值
    如果采集间隔是 3m,5m 窗口里可能只有 1 到 2 个值

对 CloudWatch/YACE 的 *_sum 计数类 metric 来说,每个 sample 通常是一个 CloudWatch period 内的 Sum 值。所以上面的例子可以理解为:orders 队列最近 5 个 period 分别删除了 10 / 12 / 8 / 15 / 5 条消息。

Selector first:

aws_sqs_number_of_messages_deleted_sum{dimension_QueueName!=""}[5m]

After selector:

aws_sqs_number_of_messages_deleted_sum{environment="prod", dimension_QueueName="orders"}[5m]
    10 12 8 15 5

aws_sqs_number_of_messages_deleted_sum{environment="prod", dimension_QueueName="payments"}[5m]
    3 7 0 9 1

aws_sqs_number_of_messages_deleted_sum{environment="uat", dimension_QueueName="orders"}[5m]
    1 2 0 3 4

Then sum_over_time(...):

sum_over_time(aws_sqs_number_of_messages_deleted_sum{dimension_QueueName!=""}[5m])

Result:

{environment="prod", dimension_QueueName="orders"} 50
{environment="prod", dimension_QueueName="payments"} 20
{environment="uat", dimension_QueueName="orders"} 10

Then sum by (environment, dimension_QueueName) (...):

{environment="prod", dimension_QueueName="orders"} 50
{environment="prod", dimension_QueueName="payments"} 20
{environment="uat", dimension_QueueName="orders"} 10

这个例子里每个 queue 只有一条 series,所以 sum by 后数值没有变化。真实数据里如果还带有 jobinstanceaccount_idregion 等 label,sum by (environment, dimension_QueueName) 会把这些 label 聚合掉,只保留每个环境下每个 queue 的总数。

With extra labels:

after sum_over_time:
    {environment="prod", dimension_QueueName="orders", instance="yace-1"} 30
    {environment="prod", dimension_QueueName="orders", instance="yace-2"} 20

after sum by (environment, dimension_QueueName):
    {environment="prod", dimension_QueueName="orders"} 50
Function When To Use
sum by (...) count/request/error/backlog 总量,例如 5xx count、SQS messages
avg by (...) utilization/latency statistic 的普通视图,例如 CPU%、memory%、p95 metric 已经由 CloudWatch 算好
max by (...) 关注最坏实例/最大延迟/最大 lag,例如 replica lag
min by (...) 关注最小健康容量,例如 ALB healthy host count 要看最小值是否跌到 0

保留 label 的原则:保留能定位 owner 和资源的 label,例如 environmentdimension_TableNamedimension_QueueNameinstance;不要保留高基数且排障价值低的 label。

6. Functions Used Here#

当前 rule 主要用这些函数。

Function Input Meaning Example
sum_over_time(x[5m]) range vector 最近 5 分钟所有样本求和 CloudWatch *_sum 计数类指标
rate(counter[5m]) counter range vector counter 每秒速率,自动处理 reset node CPU、disk IO counter
clamp_min(x, 1) instant vector 把小于 1 的值抬到 1,避免除 0 error_count / clamp_min(request_count, 1)

CloudWatch/YACE 计数类 metric 名里常见 *_sum,它不是 Prometheus counter,而是 CloudWatch period 内的 Sum 统计值,所以常用 sum_over_time(metric_sum[5m]) 得到窗口内总数。node exporter 的 node_cpu_seconds_total 是 Prometheus counter,所以用 rate(...[5m])

rate(counter[5m]) 用来把持续递增的 counter 转成“每秒速率”。counter 的原始值是累计总数,不是每分钟的新增数。

Sample counter:

scrape_interval = 10s:
    5 分钟窗口里大约 31 个点
    300s / 10s + 1 = 31

http_requests_total{service="order-api", status="200"}[5m]
    t+000s    1000
    t+010s    1010
    t+020s    1020
    t+030s    1030
    t+040s    1040
    t+050s    1050
    t+060s    1060
    t+070s    1070
    t+080s    1080
    t+090s    1090
    t+100s    1100
    t+110s    1110
    t+120s    1120
    t+130s    1130
    t+140s    1140
    t+150s    1150
    t+160s    1160
    t+170s    1170
    t+180s    1180
    t+190s    1190
    t+200s    1200
    t+210s    1210
    t+220s    1220
    t+230s    1230
    t+240s    1240
    t+250s    1250
    t+260s    1260
    t+270s    1270
    t+280s    1280
    t+290s    1290
    t+300s    1300

Meaning:

1000:
    进程启动以来累计处理了 1000 个请求

1300:
    5 分钟后累计处理了 1300 个请求

increase over 5m:
    1300 - 1000 = 300 requests

rate over 5m:
    300 / 300s = 1 request/second

Query:

rate(http_requests_total{service="order-api", status="200"}[5m])

Result:

{service="order-api", status="200"} 1

如果想要“最近 5 分钟总共多少请求”,用 increase 更直观:

increase(http_requests_total{service="order-api", status="200"}[5m])

Result:

{service="order-api", status="200"} 300

Counter reset:

http_requests_total{service="order-api"}[5m]
    1000 1060 1120 20 80 140

这里进程重启后 counter 从 1120 变成 20rate / increase 会处理这种 reset,不会直接用 140 - 1000 得到负数。

Same samples with different functions:

input:
    scrape_interval = 10s

    http_requests_total{service="order-api", status="200"}[5m]
        t+000s    1000
        t+010s    1010
        t+020s    1020
        t+030s    1030
        t+040s    1040
        t+050s    1050
        t+060s    1060
        t+070s    1070
        t+080s    1080
        t+090s    1090
        t+100s    1100
        t+110s    1110
        t+120s    1120
        t+130s    1130
        t+140s    1140
        t+150s    1150
        t+160s    1160
        t+170s    1170
        t+180s    1180
        t+190s    1190
        t+200s    1200
        t+210s    1210
        t+220s    1220
        t+230s    1230
        t+240s    1240
        t+250s    1250
        t+260s    1260
        t+270s    1270
        t+280s    1280
        t+290s    1290
        t+300s    1300

http_requests_total{service="order-api", status="200"}:
    1300
    当前评估时间点的最新累计值

http_requests_total{service="order-api", status="200"}[5m]:
    1000 1010 1020 1030 1040 1050 1060 1070 1080 1090
    1100 1110 1120 1130 1140 1150 1160 1170 1180 1190
    1200 1210 1220 1230 1240 1250 1260 1270 1280 1290 1300
    最近 5 分钟窗口内这条 series 的 samples

rate(http_requests_total{service="order-api", status="200"}[5m]):
    (1300 - 1000) / 300s = 1 request/second
    适合告警和 dashboard 展示 QPS / RPS

increase(http_requests_total{service="order-api", status="200"}[5m]):
    1300 - 1000 = 300 requests
    适合展示最近 5 分钟总请求数

sum_over_time example, using CloudWatch/YACE period sum metric:
    aws_sqs_number_of_messages_deleted_sum{dimension_QueueName="orders"}[5m]
        10 12 8 15 5

    sum_over_time(...[5m])
        10 + 12 + 8 + 15 + 5 = 50

    meaning:
        orders queue 最近 5 个 period 一共删除了 50 条消息

avg_over_time example, using gauge:
    node_memory_used_percent{instance="node-1"}[5m]
        70 72 75 73 80

    avg_over_time(...[5m])
        (70 + 72 + 75 + 73 + 80) / 5 = 74

    meaning:
        node-1 最近 5 分钟平均内存使用率是 74%

max_over_time example, using gauge / statistic value:
    http_request_duration_seconds_p95{service="order-api"}[5m]
        0.20 0.35 0.80 0.40 0.30

    max_over_time(...[5m])
        max(0.20, 0.35, 0.80, 0.40, 0.30) = 0.80

    meaning:
        order-api 最近 5 分钟内最差的 p95 延迟是 0.80s

结论:

counter:
    用 rate 看每秒速率
    用 increase 看窗口内增长总量
    不要用 sum_over_time 计算请求数

gauge:
    用 avg_over_time / max_over_time / min_over_time 看窗口内统计值

容易混淆:

range selector:
    [5m] 是时间窗口,不是固定 5 个点
    点数由 scrape interval / CloudWatch period / 采集延迟决定

sum_over_time:
    对同一条 series 在时间窗口内的 samples 求和
    适合 CloudWatch/YACE 的 *_sum 计数类 period value
    不适合 CPU utilization / latency p95 这类 gauge 或统计值直接求和

sum by:
    对同一时间点的多条 series 做 label 维度聚合
    例如把 instance / job / region 聚合掉,只保留 queue 或 table 维度

counter:
    Prometheus 原生 counter 通常用 rate(counter[5m]) 或 increase(counter[5m])
    不要用 sum_over_time(counter[5m]) 计算请求数

gauge:
    CPU / memory / latency p95 这类值通常用 avg_over_time / max_over_time
    不要看到 [5m] 就默认套 sum_over_time

7. Arithmetic And Comparison#

表达式可以做四则运算和比较。比较结果仍然是 vector:每条满足条件的 series 被保留,不满足的被丢掉。

aws:alb:elb_5xx_count:sum5m / clamp_min(aws:alb:request_count:sum5m, 1)
aws:rds_cluster:cpu_utilization:avg > 85
node:memory_used_percent > 90
Operator Meaning
+ - * / 数学运算
> < >= <= == != 比较;告警通常用这些产生 firing series
0.05 ratio 5%;如果表达式是 percent,则写 5
100 * x 把 ratio 转成 percent

写 rule 前先确认单位:ALB error rate 如果 recording rule 名是 ratio5m,阈值 5% 写 0.05;CPU utilization 是 percent,阈值 85% 写 85;latency 有些 AWS metric 单位是 seconds,有些 dashboard 显示成 ms,必须看原始 metric 单位。

8. Vector Matching: and on / or on#

PromQL 里两个 vector 做二元运算时,默认按完全相同的 label set 匹配。实际 rule 经常需要指定“只按这些 label 匹配”,这就是 on (...)

aws:ecs_service:task_deficit:avg > 0
and on (environment, dimension_ClusterName, dimension_ServiceName)
aws:ecs_service:desired_task_count:avg > 0

意思是:左边找出 task deficit 大于 0 的 service;右边只保留 desired task 大于 0 的 service;and on (...) 表示只有左右两边在这些 label 上能匹配时,才保留左边那条 series。这个写法可以避免 desired=0 的停用服务误报。

三个条件就继续链式写:

A
and on (environment, dimension_QueueName) B
and on (environment, dimension_QueueName) C

or on (...) 常用于补 0 或保留另一侧 series。例如 ALB target 5xx count 缺失时,可以用 request_count 乘 0 补出同 label 的 0 值,避免除法结果缺 series。

(
  aws:alb_targetgroup:target_5xx_count:sum5m
  or on (environment, dimension_LoadBalancer, dimension_TargetGroup)
  0 * aws:alb_targetgroup:request_count:sum5m
)
/ clamp_min(aws:alb_targetgroup:request_count:sum5m, 1)

9. Regex Used In These Rules#

Prometheus 使用 RE2 regex,不支持 lookahead/lookbehind/backreference。常用写法如下。

Regex Meaning
`GetItem PutItem
.*node.* 包含 node
`/boot($ /.*)`
`/boot($ /.*)
`tmpfs devtmpfs

Regex 在 YAML 双引号里要注意反斜杠转义。当前规则里大部分 regex 没有 \d 这类转义,所以直接写即可;如果需要匹配字面点号,写 "api\\.example\\.com"

10. Recording Rule Naming#

Recording rule 是给复杂表达式取一个稳定 metric 名,方便 dashboard 和 alert 复用。命名建议读起来像“资源:指标:统计窗口/统计方式”。

aws:alb:request_count:sum5m
aws:alb_targetgroup:target_5xx_rate:ratio5m
aws:rds_cluster:aurora_replica_lag:max
node:cpu_used_percent:avg5m

常用 suffix:sum5m 表示窗口总数;rate5m 表示 5 分钟速率;avg/max/min/p95 表示统计方式;ratio 表示 0 到 1 的比例;percent 表示 0 到 100 的百分比。

11. Alert Rule Design#

一个好的 alert expression 通常包含 symptom、scope、threshold、duration、noise guard。

- alert: AwsCloudFront5xxErrorRateHigh
  expr: |
    aws:cloudfront_distribution:5xx_error_rate:avg > 1
    and on (environment, dimension_DistributionId)
    aws:cloudfront_distribution:requests:sum5m >= 1000
  for: 5m
  labels:
    severity: P1
    service: cloudfront
  annotations:
    summary: "CloudFront 5xx error rate is high"
Part Example Why
symptom 5xx_error_rate > 1 真正影响用户或系统健康的现象
scope by (environment, dimension_DistributionId) 告警能定位资源
threshold > 1 与单位一致,1 表示 1% 而不是 0.01
duration for: 5m 避免单点毛刺
guard requests >= 1000 低流量时避免比例误报

12. AWS/YACE Metric Patterns#

YACE 会把 CloudWatch metric 和 dimension 展开成 Prometheus label。写 rule 时要先确认 dashboard 或 Explore 里的真实 label,不要凭 AWS 控制台名字猜。

AWS Dimension YACE Label Example
LoadBalancer dimension_LoadBalancer
TargetGroup dimension_TargetGroup
ClusterName dimension_ClusterName
ServiceName dimension_ServiceName
DBClusterIdentifier dimension_DBClusterIdentifier
CacheClusterId dimension_CacheClusterId
QueueName dimension_QueueName
TableName dimension_TableName
Operation dimension_Operation
DistributionId dimension_DistributionId

CloudWatch statistic 也要看清楚:_sum 常用于计数窗口求和;_average 用于平均利用率;_maximum 用于最坏值,例如最大 lag;_minimum 用于最小健康容量,例如 healthy host count 是否跌到 0。

13. Host Rule Patterns#

node exporter rule 和 AWS CloudWatch rule 不一样,它基于操作系统 counter/gauge。

100 - (
  avg by (environment, instance) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
  ) * 100
)

这个 CPU 表达式表示:idle CPU 每秒速率的平均值乘 100 得到 idle percent,再用 100 - idle 得到 used percent。

100 * (
  1 - (
    node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
  )
)

这个 memory 表达式表示:可用内存占总内存比例越低,已使用比例越高。disk rule 需要排除 pseudo filesystem 和临时 mountpoint,否则会有大量无意义告警。

14. Write A New Rule#

写新 rule 可以按这个模板走。

1. 在 Grafana Explore 找到真实 metric name 和 labels。
2. 判断 metric 类型:CloudWatch sum / CloudWatch average / Prometheus counter / gauge。
3. 决定告警粒度:ALB、target group、queue、table、instance。
4. 用 recording rule 固化复杂表达式,并保留定位资源需要的 labels。
5. alert rule 只写清楚 threshold、traffic guard、for、labels、annotations。
6. 检查单位:ratio/percent、seconds/milliseconds、bytes/GB、count/rate。
7. 用 promtool 或 vmalert API 检查 rule 是否加载和评估成功。

最小模板:

groups:
  - name: example.recording.rules
    interval: 1m
    rules:
      - record: resource:metric:stat
        expr: |
          sum by (environment, resource_label) (
            sum_over_time(raw_metric_sum{resource_label!=""}[5m])
          )

  - name: example.alerting.rules
    interval: 1m
    rules:
      - alert: ResourceMetricHigh
        expr: |
          resource:metric:stat > THRESHOLD
        for: 5m
        labels:
          severity: P1
          service: service-name
        annotations:
          summary: "Resource metric is high"

15. Common Mistakes#

Mistake Correct Approach
ratio 当 percent 0.05 是 5%;5 是 500%
把 percent 当 ratio CloudFront 5xxErrorRate 如果单位是 percent,1% 写 1
label 写错 先查真实 label,例如 DynamoDB 是 dimension_Operation 不是 operation
聚合后丢定位 label sum by (...) 里保留 queue/table/instance 等资源 label
低流量比例误报 error rate rule 加 request count guard
对 counter 用普通差值 Prometheus counter 用 rate/increase;CloudWatch sum 用 sum_over_time
for 太短 CloudWatch 指标通常考虑采集周期和延迟
regex 以为是 contains label regex fully anchored,contains 要写 .*xxx.*
用错 average/max/min 看告警语义:平均趋势用 average,最坏实例用 max,最小健康容量用 min

16. Quick Checks#

promtool check rules *.yml
curl -s http://vmalert:8880/api/v1/rules
curl -s http://vmalert:8880/api/v1/alerts

如果 lastEvaluation0001-01-01T00:00:00Z,通常表示这个 rule 还没有被当前 vmalert 实例实际评估过,常见原因是文件没被加载、group 被禁用、vmalert 刚启动还没评估、或 API 查到的不是正在运行的 rule group。