服务端5月27日 18:01
Prometheus Alertmanager 告警怎么配置?线上服务出问题却收不到告警,或者告警多到看不过来——这是很多团队上 Prometheus 后遇到的典型问题。核心原因往往出在两个环节:告警规则写得不准,或者 Alertmanager 路由配置没理顺。下面从规则定义到通知分发,把完整链路讲清楚。
## 告警规则:Prometheus 端定义触发条件
告警规则写在独立的规则文件中,由 Prometheus 负责评估。一条规则的核心要素:
- **expr**:PromQL 表达式,定义什么条件算异常
- **for**:条件持续多久才触发,避免瞬时抖动误报
- **labels**:附加标签,供 Alertmanager 路由和分组使用
- **annotations**:告警描述,出现在通知内容里
示例——CPU 使用率超过 80% 持续 5 分钟:
```yaml
groups:
- name: node_alerts
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
team: infra
annotations:
summary: "{{ $labels.instance }} CPU 使用率过高"
description: "当前值 {{ $value }}%,阈值 80%"
```
再补一个内存告警的例子,实际生产中 CPU 和内存往往配对出现:
```yaml
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: critical
team: infra
annotations:
summary: "{{ $labels.instance }} 内存使用率过高"
description: "当前值 {{ $value }}%,阈值 85%"
```
规则文件在 `prometheus.yml` 中通过 `rule_files` 加载:
```yaml
rule_files:
- "alerts/*.yml"
```
### for 子句的工作机制
`for` 不是简单的延迟。Prometheus 内部对每条规则维护三个状态:
1. **Inactive**:表达式不满足,无告警
2. **Pending**:表达式满足,但还没持续到 `for` 指定的时间
3. **Firing**:表达式满足且持续了 `for` 时间,告警真正触发并推送给 Alertmanager
理解这个状态机有助于排查告警"延迟触发"的问题——如果 `for` 设了 10m,但指标在 9 分钟时恢复正常又再次超限,Pending 计时器会重置。
## 连接 Alertmanager:让告警有去处
Prometheus 自身不发送通知,需要把告警推给 Alertmanager。在 `prometheus.yml` 中配置:
```yaml
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
```
如果部署了多个 Alertmanager 实例做高可用,直接列出所有目标即可:
```yaml
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager-1:9093
- alertmanager-2:9093
- alertmanager-3:9093
```
Prometheus 会向所有实例推送告警,Alertmanager 内部通过 Gossip 协议同步状态,确保同一条告警不会重复发送通知。
配置完成后,Prometheus 会将 Firing 状态的告警持续推送到 Alertmanager。
## Alertmanager 路由:决定谁收到什么通知
Alertmanager 的核心逻辑是「收到告警 → 分组 → 路由 → 抑制/静默检查 → 发送通知」。路由配置决定了告警最终走向哪个接收器。
路由是一个树状结构:根节点是默认路由,子节点通过标签匹配来覆盖默认行为。告警从根节点进入,深度优先遍历,匹配到第一个符合条件的节点就停下来处理。
基础路由示例:
```yaml
route:
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'default'
routes:
- match:
severity: critical
receiver: 'oncall'
repeat_interval: 1h
- match:
team: infra
receiver: 'infra-team'
```
四个时间参数的含义:
- **group_wait**:收到该组第一条告警后等多久再发通知,目的是攒一批一起发
- **group_interval**:同组后续告警的最小发送间隔
- **repeat_interval**:同一条告警重复通知的最小间隔
- **group_by**:按哪些标签分组,相同标签值的告警合并为一条通知
子路由 `routes` 支持按标签匹配,实现不同级别的告警走不同通道。
### match 和 match_re 的区别
- **match**:精确匹配,标签值必须完全相等
- **match_re**:正则匹配,标签值满足正则表达式即可
```yaml
routes:
- match_re:
service: nginx|apache
receiver: 'web-team'
- match_re:
service: mysql|mongodb|redis
receiver: 'db-team'
```
当一个告警可能匹配多条子路由时,默认只走第一条匹配到的。如果需要一条告警同时发送给多个接收器,在子路由中加上 `continue: true`:
```yaml
routes:
- match:
severity: critical
receiver: 'oncall'
continue: true
- match:
team: infra
receiver: 'infra-team'
```
这样 critical 级别的告警会同时发给 oncall 和 infra-team。
## 接收器配置:通知发到哪里
Alertmanager 支持多种通知渠道,包括 Email、Slack、PagerDuty、Webhook、企业微信、钉钉等:
```yaml
receivers:
- name: 'default'
email_configs:
- to: 'ops@example.com'
from: 'alertmanager@example.com'
smarthost: 'smtp.example.com:587'
- name: 'oncall'
webhook_configs:
- url: 'https://hooks.example.com/alert'
send_resolved: true
- name: 'infra-team'
email_configs:
- to: 'infra@example.com'
```
`send_resolved: true` 表示告警恢复时也发通知,生产环境建议开启。
### 通知模板自定义
默认通知格式信息量有限,可以通过 Go Template 自定义通知内容。在全局配置中指定模板文件路径:
```yaml
templates:
- '/etc/alertmanager/templates/*.tmpl'
```
模板中可以引用告警的 Labels 和 Annotations,灵活组织通知内容。
## 告警抑制:高优先级告警压制低优先级
当集群整体故障时,不需要再收到该集群上每个服务的低级别告警。抑制规则实现这个逻辑:
```yaml
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'cluster']
```
含义:当同一个集群同一种告警存在 critical 级别时,warning 级别的不再单独通知。`equal` 列表是判断「同一种告警」的依据。
也可以同时定义多条抑制规则,覆盖不同场景:
```yaml
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'cluster']
- source_match:
alertname: 'NodeDown'
target_match:
severity: 'warning'
equal: ['instance']
```
第二条规则表示:当某个节点宕机时,该节点上的所有 warning 级别告警都抑制掉,因为它们大概率是节点宕机的连锁反应。
## 告警静默:维护窗口免打扰
计划内维护期间可以通过 API 创建静默规则,匹配到的告警不会发送通知:
```bash
curl -X POST http://alertmanager:9093/api/v2/silences \
-H 'Content-Type: application/json' \
-d '{
"matchers": [
{"name": "cluster", "value": "prod-east", "isRegex": false}
],
"startsAt": "2026-05-27T02:00:00Z",
"endsAt": "2026-05-27T06:00:00Z",
"createdBy": "ops-team",
"comment": "Planned maintenance"
}'
```
也可以在 Alertmanager Web UI(默认 9093 端口)中可视化创建和管理静默规则。
静默与抑制的区别:抑制是配置文件中静态定义的规则,随 Alertmanager 启动生效;静默是运行时动态创建的,适合临时场景,到期自动失效。
## 全局配置与 resolve_timeout
Alertmanager 的全局配置中有一个容易忽略的参数 `resolve_timeout`:
```yaml
global:
resolve_timeout: 5m
```
含义是:如果 Alertmanager 在 5 分钟内没有收到某条告警的更新(即 Prometheus 不再推送该告警),就认为该告警已恢复。这个机制是告警自动恢复的兜底策略——正常情况下 Prometheus 会主动发送 resolved 事件,但如果 Prometheus 重启或网络中断,resolved 事件可能丢失,此时 `resolve_timeout` 就起作用了。
生产环境建议根据告警的重要程度调整:关键告警可以设长一些(15m-30m),避免因短暂断连导致误报恢复。
## 配置验证与常见问题
修改 Alertmanager 配置后,用 `amtool` 检查语法:
```bash
amtool check-config alertmanager.yml
```
也可以用 `amtool` 测试路由匹配结果,确认一条告警会走哪个接收器:
```bash
amtool config routes test --config.file alertmanager.yml \
severity=critical team=infra alertname=HighCPUUsage
```
几个生产环境常见的坑:
- **告警一直 Firing 不恢复**:检查 `for` 时间是否过长,或 PromQL 表达式本身是否有问题;另外确认 `resolve_timeout` 是否合理
- **收不到通知**:确认 Prometheus 能连通 Alertmanager,检查路由匹配条件是否正确,用 `amtool config routes test` 验证
- **通知太频繁**:增大 `repeat_interval`,启用 `group_by` 合并同类告警
- **静默未生效**:确认 matchers 的标签名和值与告警标签完全一致,注意大小写敏感
- **子路由不生效**:检查是否因为前面的子路由已经匹配,后面被跳过了;需要同时匹配时加 `continue: true`
- **HA 部署下重复通知**:确认多个 Alertmanager 实例之间网络互通,Gossip 协议正常同步标签
Prometheus
Prometheus 是一个开源的监控和报警工具集,它最初是由 SoundCloud 创建的,现在是云原生计算基金会(CNCF)下的一个项目。Prometheus 被设计用于监控和报警在动态容器环境中的多个服务,但它同样适用于传统的硬件和软件监控。

服务端5月27日 18:01
如何优化 Prometheus 的存储和性能?## Prometheus 存储架构基础
Prometheus 使用自研的 TSDB(Time Series Database)作为本地存储引擎。数据写入流程为:先写入内存中的 Head Block,同时通过 WAL(Write-Ahead Log)保证持久性;Head Block 满两个时间窗口后被持久化为磁盘上的 Block;后台 Compaction 进程定期合并小 Block 并清理过期数据。
每个 Block 由以下部分组成:
- **chunks/**:存储实际的时间序列数据点,使用 Facebook Gorilla 压缩算法,16 字节的数据点可压缩至平均 1.37 字节
- **index**:倒排索引,支持按标签快速查询时间序列
- **meta.json**:Block 元信息
- **tombstones**:删除标记,删除操作不会立即清除数据,而是记录标记等待下次 Compaction 时清理
理解这个架构是进行存储优化的前提。
## 数据保留策略配置
通过启动参数控制本地数据的保留时间和磁盘上限:
```yaml
# prometheus.yml 或启动参数
storage:
tsdb:
retention.time: 15d # 数据保留时长,默认 15d
retention.size: 50GB # 磁盘使用上限,达到后自动清理最旧数据
```
两个参数同时配置时,任一条件触发都会清理数据。生产环境建议同时设置,防止磁盘打满。
关键原则:本地存储只保留近期热数据,长期存储需求交给 remote write 后端处理。
## 标签基数控制
标签基数(Label Cardinality)是影响 Prometheus 存储和查询性能最关键的因素。每一个唯一的标签组合都会产生一条独立的时间序列,基数爆炸会导致内存飙升、查询变慢、磁盘膨胀。
**必须避免的高基数标签:**
- 用户 ID、请求 ID、会话 ID
- 原始 IP 地址
- 未截断的 URL 路径
```yaml
# 使用 metric_relabel_configs 在采集阶段丢弃或重写高基数标签
scrape_configs:
- job_name: 'my-app'
metric_relabel_configs:
- source_labels: [__name__]
regex: 'go_memstats_.*'
action: drop # 丢弃不需要的指标
- source_labels: [path]
regex: '/api/v1/.*'
replacement: '/api/v1/:path' # 合并路径,降低基数
target_label: path
```
排查高基数指标的方法:
```promql
# 查看当前时间序列总数
count({__name__=~".+"})
# 按指标名分组统计序列数,找出最大的
topk(20, count by (__name__)({__name__=~".+"}))
```
## 采集间隔优化
采集频率直接影响数据写入量和存储占用。合理分层设置采集间隔:
```yaml
global:
scrape_interval: 30s # 全局默认值
scrape_timeout: 10s
scrape_configs:
- job_name: 'critical-service'
scrape_interval: 15s # 核心服务用短间隔
scrape_timeout: 10s
- job_name: 'batch-job'
scrape_interval: 60s # 后台任务用长间隔
scrape_timeout: 15s
```
注意事项:
- `scrape_timeout` 不能大于 `scrape_interval`
- 采集间隔从 15s 改为 30s,存储量直接减半
- 不重要的指标可以单独配置更长的间隔
## WAL 压缩与写入优化
WAL(Write-Ahead Log)是数据持久性的保障,但默认未压缩时会占用大量磁盘空间,崩溃恢复也较慢。
```bash
# 启用 WAL 压缩(Prometheus 2.20+ 默认开启)
--storage.tsdb.wal-compression
# 控制写入队列大小(Prometheus 2.29+)
--storage.tsdb.head-chunks.write-queue-size=0 # 默认 0 表示同步写入
# 设为非 0 值(如 1000)可异步写入,降低写入延迟但增加内存使用
```
WAL 压缩使用 Snappy 算法,可将 WAL 体积缩减约 50%,CPU 开销极小。一旦开启,无法回退到 2.11 之前的版本。
如果 WAL 异常增长,通常是 remote write 消费跟不上或 Compaction 失败导致,排查方法:
```bash
# 检查 WAL 目录大小
du -sh /data/prometheus/wal/
# 检查 Compaction 是否正常
curl -s http://localhost:9090/metrics | grep prometheus_tsdb_compactions_failed_total
```
## Recording Rules 预计算
对于复杂的聚合查询或频繁使用的仪表盘,Recording Rules 可以将计算结果预存为新指标,显著降低查询时的 CPU 和内存压力。
```yaml
groups:
- name: http_request_rules
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum by (job) (rate(http_requests_total[5m]))
- record: method:http_requests:rate5m
expr: sum by (method) (rate(http_requests_total[5m]))
- record: http:request_duration_seconds:p99
expr: histogram_quantile(0.99, sum by (le, job) (rate(http_request_duration_seconds_bucket[5m])))
```
使用原则:
- 仪表盘反复执行的聚合查询都应该转为 Recording Rule
- 规则名称采用 `level:metric:operations` 的命名约定
- 规则的 `interval` 不应小于全局 `scrape_interval`
## Remote Write 调优
Remote Write 是将 Prometheus 数据远程写入长期存储后端(Thanos、VictoriaMetrics、Mimir 等)的机制。配置不当会导致 WAL 堆积和内存溢出。
```yaml
remote_write:
- url: 'http://thanos-receive:19291/api/v1/receive'
queue_config:
max_samples_per_send: 500 # 每批发送样本数
batch_send_deadline: 5s # 批次等待最大时间
max_shards: 100 # 最大并发分片数
min_shards: 1 # 最小分片数
capacity: 2500 # 队列容量
write_relabel_configs:
- source_labels: [__name__]
regex: 'go_.*'
action: drop # 远端不需要的指标可在发送前丢弃
```
调优要点:
- 分片数(shards)根据吞吐量动态调整,`max_shards` 设为预估峰值即可
- 如果远端持续写入失败超过 2 小时,WAL 会被 Compaction 截断,未发送数据将丢失
- 监控关键指标:`prometheus_remote_storage_samples_failed_total`、`prometheus_remote_storage_samples_pending`
## 查询性能优化
### 使用标签过滤缩小范围
```promql
# 差:全量扫描后过滤
sum(http_requests_total) by (job)
# 好:在查询时就限定范围
sum(http_requests_total{job="api-server", env="prod"}) by (method)
```
### 控制查询时间窗口
大范围查询(如 30 天)会扫描大量 Block。建议:
- 仪表盘默认显示 1-6 小时
- 需要更长范围时使用 Recording Rules 预聚合的数据
- 利用 API 的 `step` 参数控制返回点数
### 避免高开销函数
- `rate()` 和 `irate()` 的区间选择不宜过长,通常 `[5m]` 或 `[1m]`
- 避免对高基数指标使用 `group_left` 做 1:N 的 join
- `histogram_quantile()` 尽量配合 Recording Rules 预计算
## 监控 Prometheus 自身
生产环境必须对 Prometheus 自身进行监控和告警:
```yaml
# 关键监控指标
- alert: PrometheusTSDBCompactionFailing
expr: increase(prometheus_tsdb_compactions_failed_total[5m]) > 0
- alert: PrometheusWALCorruptions
expr: increase(prometheus_tsdb_wal_corruptions_total[5m]) > 0
- alert: PrometheusRemoteWriteFailures
expr: increase(prometheus_remote_storage_samples_failed_total[5m]) > 0
- alert: PrometheusHighCardinality
expr: prometheus_tsdb_head_series > 1000000
```
同时关注以下运行指标:
- `prometheus_tsdb_head_samples_appended_total`:写入速率
- `prometheus_target_interval_length_seconds`:采集间隔偏差
- `process_resident_memory_bytes`:实际内存占用
- `prometheus_tsdb_compaction_duration_seconds`:压缩耗时
## 长期存储与架构扩展
单机 Prometheus 的本地存储有上限,大规模场景需要架构层面的扩展:
**Thanos 方案:**
- Sidecar 模式对现有部署侵入最小,周期性将 Block 上传到对象存储
- Receive 模式支持多 Prometheus remote write 汇聚
- Store Gateway 提供对历史数据的查询能力
**VictoriaMetrics 方案:**
- 专有压缩算法,压缩比可达 Prometheus 的 10 倍
- 单节点部署即可替代 Prometheus + 远端存储的组合
- 完全兼容 PromQL
**Grafana Mimir 方案:**
- 支持 Multi-Tenant,适合平台级部署
- 与 Grafana 生态深度集成
选择建议:中小规模优先考虑 VictoriaMetrics,多租户平台选 Mimir,需要兼容现有对象存储选 Thanos。
## 磁盘与硬件建议
- 使用 SSD 存储 TSDB 数据目录,HDD 在高写入负载下 Compaction 性能极差
- 磁盘剩余空间保持 30% 以上,Compaction 需要额外临时空间
- 内存分配参考:每百万活跃时间序列约需 1-2 GB 内存
- Kubernetes 部署时建议设置合理的 requests 和 limits,避免 OOM Kill
## 总结
Prometheus 存储优化是一个从基数控制到架构扩展的系统性工程。核心思路是:在采集端过滤无用指标、控制标签基数;在存储端合理配置保留策略、启用 WAL 压缩;在查询端善用 Recording Rules、避免全表扫描;在架构层通过 remote write 实现冷热分离、长期存储。每个环节的优化都建立在对 TSDB 工作原理的理解之上,监控 Prometheus 自身的关键指标则是发现和预防问题的最后一道防线。前端5月27日 18:00
如何将 Prometheus 与 Grafana 集成?有哪些最佳实践和常见坑点?## Prometheus 与 Grafana 集成的架构原理
Prometheus 负责时序数据的采集、存储和告警,Grafana 负责数据的可视化呈现。两者通过 HTTP API 交互:Grafana 作为客户端向 Prometheus 发起 PromQL 查询请求,Prometheus 返回时间序列数据,Grafana 再将数据渲染为图表。理解这个数据流是做好集成的前提。
核心数据链路:应用暴露 /metrics 接口 → Prometheus 通过 pull 模型定时抓取 → 数据存入 TSDB → Grafana 通过 PromQL 查询 → 仪表盘可视化 + 告警。
## 集成配置详解
### 添加 Prometheus 数据源
在 Grafana 中进入 Configuration → Data Sources → Add data source,选择 Prometheus 类型,填写以下关键配置:
```json
{
"name": "Prometheus",
"type": "prometheus",
"url": "http://prometheus:9090",
"access": "proxy",
"isDefault": true,
"jsonData": {
"httpMethod": "POST",
"timeInterval": "15s",
"customQueryParameters": ""
}
}
```
关键参数说明:
- **httpMethod**: 推荐设为 POST,对于大范围查询性能更好
- **timeInterval**: 与 Prometheus 的 scrape_interval 保持一致,避免数据对齐问题
- **access**: 生产环境建议用 proxy 模式,由 Grafana 后端代理请求,避免暴露 Prometheus 地址
也可以通过 provisioning 配置文件自动注册数据源:
```yaml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
access: proxy
isDefault: true
jsonData:
httpMethod: POST
timeInterval: 15s
editable: true
```
### 验证数据源连通性
添加完成后,点击 Save & Test,Grafana 会发送一个查询请求验证连通性。如果报错,排查以下常见问题:
- 网络不通:检查 Prometheus 是否可达(curl http://prometheus:9090/api/v1/status/config)
- 跨域问题:proxy 模式下由 Grafana 后端代理,不存在跨域;direct 模式下需浏览器直连,需配置 CORS
- 认证问题:如果 Prometheus 启用了 basic auth 或 TLS,需要在数据源配置中补充凭证
## 常用 PromQL 查询示例
### CPU 使用率
```promql
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
```
### 内存使用率
```promql
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
```
### 磁盘使用率
```promql
(1 - (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes)) * 100
```
### 网络流量
```promql
rate(container_network_receive_bytes_total[5m])
```
### Kubernetes Pod 重启次数
```promql
sum by (namespace, pod) (increase(kube_pod_container_status_restarts_total[1h]))
```
### HTTP 请求错误率(5xx 占比)
```promql
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100
```
## 变量与模板配置
变量是构建可复用仪表盘的核心能力,避免为每个实例、命名空间重复创建面板。
### 常用变量定义
| 变量名 | 类型 | Query | 说明 |
|--------|------|-------|------|
| instance | Query | label_values(up, instance) | 选择监控实例 |
| namespace | Query | label_values(kube_pod_info, namespace) | 选择 K8s 命名空间 |
| interval | Interval | 30s,1m,5m,15m,1h | 控制查询步长 |
| datasource | Datasource | Prometheus | 支持多数据源切换 |
在面板查询中使用变量语法:
```promql
# 按 instance 变量过滤
rate(node_cpu_seconds_total{instance="$instance", mode!="idle"}[5m])
# 按 namespace 变量过滤
sum by (pod) (rate(container_cpu_usage_seconds_total{namespace="$namespace"}[5m]))
```
### $__rate_interval 的使用
Grafana 7.2+ 推荐使用 $__rate_interval 替代手动指定区间:
```promql
rate(node_cpu_seconds_total{mode="idle"}[$__rate_interval]) * 100
```
$__rate_interval 会自动计算为 max(scrape_interval * 4, dashboard_refresh_interval),确保 rate 函数始终有足够的数据点,避免断图。
## 仪表盘设计与组织
### 仪表盘分层
生产环境建议采用三层仪表盘架构:
- **概览层(Overview)**:展示系统全局健康状态,使用 Stat 面板 + 红黄绿阈值,一眼发现问题
- **服务层(Service)**:按服务/应用维度展开,包含请求量、延迟分布、错误率等 SLI 指标
- **实例层(Instance)**:下钻到具体实例,展示 CPU、内存、磁盘 IO、网络等资源详情
### 面板类型选择
| 场景 | 推荐面板类型 | 说明 |
|------|-------------|------|
| 时间序列趋势 | Time Series | 默认首选,支持多条线叠加 |
| 当前值/状态 | Stat | 显示最新值,配合阈值变色 |
| 百分位分布 | Heatmap | 适合延迟分布可视化 |
| 排行/Top N | Bar Chart | 展示资源占用 Top 排名 |
| 表格数据 | Table | 适合多维指标对比 |
### 告警面板配置
在面板下方添加 Alert 区域,设置 Evaluate every(评估频率)和 For(持续时间),避免瞬时抖动触发告警。
## Recording Rules 优化查询性能
当仪表盘中存在耗时较长的聚合查询时,Recording Rules 可以预先计算并存储结果,大幅降低查询延迟。
```yaml
groups:
- name: cpu_rules
interval: 30s
rules:
- record: job:cpu_usage:rate5m
expr: 100 - (avg by (job) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
- name: http_rules
interval: 30s
rules:
- record: job:http_error_rate:rate5m
expr: sum by (job) (rate(http_requests_total{status=~"5.."}[5m])) / sum by (job) (rate(http_requests_total[5m])) * 100
```
在 Grafana 中直接查询预计算指标:
```promql
job:cpu_usage:rate5m{job="my-service"}
```
Recording Rules 命名规范建议:`level:metric:operations`,例如 `job:cpu_usage:rate5m`,便于识别层级和计算逻辑。
## 告警配置与集成
### Grafana Alerting vs Prometheus Alertmanager
两者可以独立使用,也可以组合:
- **Grafana Alerting**:配置简单,直接在仪表盘上设置,支持 Unified Alerting 统一管理多数据源告警,适合简单场景
- **Prometheus Alertmanager**:功能更强大,支持告警分组(group_by)、抑制(inhibit_rules)、静默(silences)、路由(routes),适合大规模告警管理
### Prometheus 告警规则示例
```yaml
groups:
- name: node_alerts
rules:
- alert: HighCpuUsage
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is {{ $value }}% (threshold: 85%)"
- alert: DiskSpaceLow
expr: (1 - (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes)) * 100 > 90
for: 10m
labels:
severity: critical
annotations:
summary: "Disk space low on {{ $labels.instance }}"
description: "Disk usage is {{ $value }}% (threshold: 90%)"
```
### Alertmanager 路由配置
```yaml
route:
group_by: [alertname, cluster]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: default-slack
routes:
- match:
severity: critical
receiver: pagerduty-critical
repeat_interval: 1h
- match:
severity: warning
receiver: default-slack
receivers:
- name: default-slack
slack_configs:
- channel: #monitoring
send_resolved: true
- name: pagerduty-critical
pagerduty_configs:
- service_key: <your-key>
```
### Grafana 告警通知渠道
Grafana 支持多种通知渠道:邮件、Slack、Webhook、钉钉、企业微信、PagerDuty 等。在 Alerting → Contact Points 中配置,并在 Notification Policies 中设置路由规则。
## 高可用与生产级部署
### Prometheus 高可用方案
单点 Prometheus 存在单点故障风险,生产环境常见两种高可用方案:
- **多实例并行**:部署两个以上相同配置的 Prometheus 实例,各自独立抓取和存储数据,Grafana 配置多个数据源并用 Load Balance 模式查询
- **远程写入(Remote Write)**:Prometheus 将数据远程写入 Thanos / Cortex / Mimir 等长期存储后端,Grafana 从统一存储查询
### Grafana 高可用
Grafana 本身无状态,多个实例共享同一个数据库(MySQL/PostgreSQL)即可实现高可用。注意关闭告警的 HA 降级(设置 `ha_peer_name`)。
### 数据保留策略
```yaml
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
# 启动参数
--storage.tsdb.retention.time=30d
--storage.tsdb.retention.size=50GB
```
短期数据保留在 Prometheus 本地 TSDB,长期数据通过 Remote Write 归档到对象存储。
## 导入社区仪表盘
Grafana 官方维护了大量开源仪表盘模板:
- 访问 grafana.com/grafana/dashboards 搜索
- 常用 ID:Node Exporter Full(1860)、Kubernetes 集群监控(7249)、Spring Boot Statistics(12900)
- 在 Grafana 中通过 Dashboards → Import → 输入 ID 即可导入
导入后需要根据实际环境调整变量和查询,避免指标名称不匹配导致面板无数据。
## 常见坑点与排障
1. **rate 函数区间过短**:scrape_interval 为 15s 时,rate(xxx[1m]) 可能因数据点不足出现断图,建议区间至少为 4 倍 scrape_interval 或使用 $__rate_interval
2. **时区不一致**:Prometheus 使用 UTC,Grafana 默认跟随浏览器时区,告警时间判断需注意转换
3. **标签冲突**:不同 job 采集的相同指标可能标签不一致,导致查询结果缺失,建议统一标签规范
4. **大范围查询超时**:查询 30 天以上数据时容易超时,应使用 Recording Rules 预聚合,或配置 Prometheus 的 --query.timeout 参数
5. **Dashboard JSON 版本不兼容**:Grafana 大版本升级后,旧仪表盘 JSON 格式可能变化,升级前做好备份服务端5月27日 17:56
如何在 K8s 中部署 Prometheus 监控?Prometheus 是 Kubernetes 生态中最主流的监控方案。本文覆盖 Helm 快速部署和 Prometheus Operator 生产级部署两种方式,并给出 ServiceMonitor 配置、自动发现、常用指标和排错要点。
## 一、部署方式选择
| 方式 | 适用场景 | 复杂度 |
|------|----------|--------|
| Helm + kube-prometheus-stack | 快速体验、测试环境 | 低 |
| Prometheus Operator | 生产环境、需要 CRD 管理 | 中 |
两种方式都推荐部署到独立命名空间(如 `monitoring`),避免与业务负载混用。
## 二、Helm 快速部署
### 2.1 安装 kube-prometheus-stack
kube-prometheus-stack 是社区维护的一体化 Chart,打包了 Prometheus、Alertmanager、Grafana 及常用 Exporter。
```bash
# 添加仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 安装到 monitoring 命名空间
helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --create-namespace
```
### 2.2 验证部署状态
```bash
kubectl get pods -n monitoring
# 预期看到 prometheus-operator、prometheus-prometheus、alertmanager、grafana 等 Pod 均为 Running
```
### 2.3 访问 Grafana 仪表盘
```bash
# 端口转发访问 Grafana
kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring
# 浏览器打开 http://localhost:3000,默认账号 admin/prom-operator
```
安装完成后 Grafana 已内置 Kubernetes 集群监控仪表盘,无需额外配置。
## 三、Prometheus Operator 部署(生产推荐)
Prometheus Operator 通过 CRD 管理 Prometheus 实例,无需手动维护配置文件,是生产环境的推荐方案。
### 3.1 核心 CRD 说明
| CRD | 作用 |
|-----|------|
| Prometheus | 定义 Prometheus 实例,指定副本数、资源限制、存储卷 |
| Alertmanager | 定义告警管理器实例 |
| ServiceMonitor | 声明式配置监控目标,按 Label 选择 Service |
| PodMonitor | 直接按 Pod Label 选择监控目标(跳过 Service) |
| PrometheusRule | 管理告警规则和记录规则 |
### 3.2 部署 Operator
```bash
# 使用 kubectl apply 安装 Operator 及 CRD
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/main/example/rbac/prometheus-operator-deployment.yaml
```
### 3.3 创建 Prometheus 实例
```yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: k8s
namespace: monitoring
spec:
replicas: 2
serviceAccountName: prometheus-k8s
serviceMonitorSelector:
matchLabels:
team: frontend
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 4Gi
storage:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
```
关键参数说明:
- `replicas: 2` — 生产环境建议至少 2 副本实现高可用
- `serviceMonitorSelector` — 只匹配带有对应 Label 的 ServiceMonitor
- `storage` — 必须配置 PersistentVolume,否则重启后数据丢失
## 四、ServiceMonitor 配置监控目标
ServiceMonitor 是 Operator 模式下配置采集目标的核心资源,通过 Label 选择器自动发现 Service。
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
team: frontend # 需与 Prometheus 的 serviceMonitorSelector 匹配
spec:
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
interval: 30s
path: /metrics
namespaceSelector:
matchNames:
- default
```
配置要点:
- `labels.team` 必须与 Prometheus CR 的 `serviceMonitorSelector` 匹配,否则不会被抓取
- `namespaceSelector` 指定从哪些命名空间发现 Service
- `interval` 不宜设置过短(< 15s),避免对目标服务造成压力
## 五、Kubernetes 自动发现
除 ServiceMonitor 外,也可通过原生 Prometheus 配置实现 Pod 自动发现:
```yaml
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)
replacement: ${1}
```
使用方式:在 Pod 的 annotation 中添加 `prometheus.io/scrape: "true"` 和 `prometheus.io/port: "9090"`,Prometheus 即可自动采集。
**建议**:Operator 模式下优先使用 ServiceMonitor,自动发现适用于无法修改 CRD 的场景。
## 六、常用指标速查
| 类别 | 指标名 | 用途 |
|------|--------|------|
| 容器 CPU | `container_cpu_usage_seconds_total` | 计算 CPU 使用率 |
| 容器内存 | `container_memory_working_set_bytes` | 实际使用内存(OOM 判定依据) |
| Pod 状态 | `kube_pod_status_phase` | Pod 运行状态统计 |
| 节点内存 | `node_memory_MemAvailable_bytes` | 节点可用内存 |
| 网络流量 | `container_network_receive_bytes_total` | 容器入站流量 |
CPU 使用率计算示例(PromQL):
```
rate(container_cpu_usage_seconds_total{container!="",pod!=""}[5m])
```
## 七、生产环境注意事项
1. **资源限制必须设置** — Prometheus 内存占用随时间序列数增长,不设上限会 OOM 并影响节点上其他 Pod
2. **持久化存储不可省略** — 默认使用 emptyDir,Pod 重启数据全部丢失,必须配置 `volumeClaimTemplate`
3. **采集间隔不宜过短** — 15s 是下限推荐值,大规模集群建议 30s-60s
4. **使用 recording rules 降频** — 对高频查询的指标用 recording rule 预计算,减少实时查询压力
5. **告警规则分优先级** — P0 级告警走即时通知(PagerDuty/电话),P1/P2 走邮件/IM
### 常见排错
| 现象 | 原因 | 解决 |
|------|------|------|
| Target 显示 0/0 active | ServiceMonitor Label 不匹配 | 检查 `team` Label 是否与 Prometheus CR 的 selector 一致 |
| Prometheus Pod CrashLoopBackOff | 内存不足 | 增大 `resources.limits.memory` |
| 指标数据缺失 | 采集目标未暴露 /metrics | 检查 Service 端口和 annotation |服务端5月27日 17:53
Prometheus 和 Zabbix、Nagios 监控系统有什么区别?Prometheus 与 Zabbix、Nagios 是运维领域最常用的三类监控系统,但它们的设计哲学和适用场景截然不同。选错工具不仅浪费团队精力,还可能在故障发生时错过关键告警。本文从架构模型、数据存储、告警机制、适用场景四个维度拆解差异,帮你做出正确选择。
## 架构模型:Pull vs Push 的根本分歧
Prometheus 采用 Pull 模式,主动从目标拉取指标数据。这意味着每个被监控的服务只需暴露一个 /metrics 端点,Prometheus server 定时抓取即可。这种设计天然适合 Kubernetes 等动态环境——新 Pod 上线后自动被服务发现纳入监控,无需手动配置。
Zabbix 支持 Push 和 Pull 混合模式。Agent 主动上报数据,也支持 Server 主动查询。这种灵活性使其能覆盖网络交换机、打印机等无法运行 Agent 的设备,通过 SNMP、ICMP 等协议被动采集。
Nagios 以被动检查为核心,依赖插件执行检查命令并返回状态码(OK/WARNING/CRITICAL)。架构上属于"检查执行器"而非"数据采集器",不存储历史指标曲线,只记录状态变更事件。
核心区别:Prometheus 关注"指标值是多少",Nagios 关注"状态是否正常",Zabbix 两者兼顾。
## 数据存储:时序数据库 vs 关系型数据库
Prometheus 内置时序数据库(TSDB),数据以时间序列形式压缩存储,查询效率极高,但单机存储容量有限(默认保留15天)。长期存储需配合 Thanos 或 Cortex 扩展。
Zabbix 使用 MySQL/PostgreSQL 等关系型数据库,支持海量历史数据存储,适合需要长期趋势分析的场景。但数据量增长后查询性能会下降,需要定期做数据分区和归档。
Nagios 不存储指标时序数据,仅保留状态变更日志。如果需要历史曲线,必须额外集成 PNP4Nagios 或 Graphite 等组件。
## 查询与告警能力
Prometheus 的 PromQL 是专为时序数据设计的查询语言,支持丰富的聚合、运算和预测函数。例如预测磁盘何时写满可以用 predict_linear() 函数,这在 Zabbix 和 Nagios 中难以实现。告警通过 Alertmanager 管理,支持告警分组、抑制、静默和路由分发。
Zabbix 内置告警引擎,触发器表达式灵活,支持邮件、短信、Webhook 等多种通知渠道。可视化也内置,无需额外部署 Grafana。但查询能力相对有限,复杂分析需要写脚本或导出数据。
Nagios 告警依赖插件返回值,配置依赖文件定义,修改告警阈值需要编辑配置文件并重载服务。缺少统一的告警管理平台,在大规模部署下维护成本高。
## 可视化与服务发现
Prometheus 自带简易 UI,生产环境普遍配合 Grafana 使用。服务发现原生支持 Kubernetes、Consul、DNS 等多种机制,新服务自动注册,无需人工干预。
Zabbix 内置仪表板和拓扑图,开箱即用,对非技术用户更友好。自动发现功能也较为成熟,但配置复杂度高于 Prometheus 的声明式配置。
Nagios 可视化能力最弱,界面风格停留在早期 Web 风格。社区有替代方案如 Check MK,但增加了技术栈复杂度。
## 如何选择:基于场景的决策
如果你的环境以 Kubernetes 和容器为主,Prometheus 是唯一的选择。CNCF 生态的监控组件(如 kube-state-metrics、node-exporter)都以 Prometheus 格式输出指标,集成零成本。
如果需要监控传统数据中心——物理服务器、网络设备、存储阵列——Zabbix 更合适。它的 SNMP/IPMI 支持和内置可视化能显著降低运维门槛。
如果你的监控需求简单,只需要知道"服务是否存活",Nagios 足够应对。它轻量、稳定,插件生态成熟,但扩展性差,不适合大规模或动态环境。
混合方案在实践中很常见:Prometheus 负责容器和云原生应用的指标监控,Zabbix 负责传统基础设施,两者通过 Grafana 统一展示面板,Alertmanager 和 Zabbix 分别处理各自领域的告警。这种组合在中小团队中广泛使用。服务端5月27日 17:51
Prometheus 安全认证怎么配置?Basic Auth 与 RBAC 实战Prometheus 默认不启用认证,9090 端口一旦暴露,任何人都能访问 /metrics 和 /api/v1/query,造成监控数据泄露甚至配置被篡改。下面从 scrape 认证、服务端防护、K8s RBAC 三个层面说明如何配置 Prometheus 的安全认证和访问控制。
## 一、Scrape 侧认证:让 Prometheus 访问受保护的 Target
### 1.1 Basic Auth
当 Target(如 pushgateway 或其他 exporter)启用了 Basic Auth 时,Prometheus 抓取时需携带用户名密码:
```yaml
scrape_configs:
- job_name: 'pushgateway'
basic_auth:
username: admin
password: <your-password>
static_configs:
- targets: ['localhost:9091']
```
若需从文件读取密码,使用 `password_file` 替代 `password`,避免密钥明文写入配置。
### 1.2 Bearer Token
在 Kubernetes 环境中,Prometheus 使用 ServiceAccount Token 访问 kube-apiserver:
```yaml
scrape_configs:
- job_name: 'kubernetes-apiservers'
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
kubernetes_sd_configs:
- role: endpoints
```
### 1.3 TLS 双向认证
当 Target 要求客户端证书时:
```yaml
scrape_configs:
- job_name: 'etcd'
scheme: https
tls_config:
ca_file: /etc/prometheus/tls/ca.crt
cert_file: /etc/prometheus/tls/client.crt
key_file: /etc/prometheus/tls/client.key
insecure_skip_verify: false
```
## 二、服务端防护:保护 Prometheus 自身的 UI 和 API
Prometheus 从 2.24 版本开始支持内置 Basic Auth 和 TLS,通过 `web.config.file` 参数加载。
### 2.1 生成 bcrypt 密码哈希
```bash
# 安装工具
apt-get install -y python3-bcrypt
# 生成哈希
python3 -c "
import bcrypt
print(bcrypt.hashpw(b'your-secure-password', bcrypt.gensalt()).decode())
"
# 输出类似:$2b$12$Wxn...
```
### 2.2 编写 web-config.yml
```yaml
basic_auth_users:
admin: '$2b$12$Wxn...' # 上一步生成的哈希
tls_config:
cert_file: /etc/prometheus/tls/cert.pem
key_file: /etc/prometheus/tls/key.pem
```
验证配置文件语法:
```bash
promtool check web-config web-config.yml
```
### 2.3 启动时加载
```bash
prometheus --config.file=/etc/prometheus/prometheus.yml --web.config.file=/etc/prometheus/web-config.yml
```
启动后访问 http://localhost:9090 将弹出 Basic Auth 登录框,未认证请求返回 401。
### 2.4 Docker 部署示例
```yaml
# docker-compose.yml
services:
prometheus:
image: prom/prometheus:v2.53.0
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--web.config.file=/etc/prometheus/web-config.yml'
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./web-config.yml:/etc/prometheus/web-config.yml
ports:
- '9090:9090'
```
### 2.5 Nginx 反向代理认证(适用 kube-prometheus-stack)
当前 kube-prometheus-stack 不直接支持内置 Basic Auth,可通过 Ingress + Nginx 实现:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prometheus-ingress
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: prometheus-basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
rules:
- host: prometheus.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: prometheus-operated
port:
number: 9090
```
创建对应的 Secret:
```bash
htpasswd -c auth admin
kubectl create secret generic prometheus-basic-auth --from-file=auth -n monitoring
```
## 三、Kubernetes RBAC:限制 Prometheus 的访问范围
### 3.1 创建 ServiceAccount
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
```
### 3.2 定义 Role(最小权限)
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: prometheus
namespace: monitoring
rules:
- apiGroups: ['']
resources: ['pods', 'services', 'endpoints']
verbs: ['get', 'list', 'watch']
```
仅授权 monitoring 命名空间下的资源读取,不授予写权限和跨命名空间权限。
### 3.3 绑定 Role 和 ServiceAccount
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prometheus
namespace: monitoring
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
```
### 3.4 ClusterRole(如需跨命名空间监控)
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: ['']
resources: ['nodes', 'pods', 'services', 'endpoints']
verbs: ['get', 'list', 'watch']
- nonResourceURLs: ['/metrics']
verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
```
## 四、Grafana 对接待认证的 Prometheus
配置 Basic Auth 后,Grafana 数据源需同步修改:
```yaml
# grafana datasources config
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
basicAuth: true
basicAuthUser: admin
secureJsonData:
basicAuthPassword: your-secure-password
editable: true
```
## 五、安全加固清单
| 措施 | 说明 |
|------|------|
| 启用 Basic Auth | 防止未授权访问 UI 和 API |
| 启用 TLS | 防止传输层窃听 |
| 网络策略隔离 | Kubernetes NetworkPolicy 限制 Pod 间访问 |
| 防火墙规则 | 仅允许 Grafana/Alertmanager 所在网段访问 9090 |
| 密钥不硬编码 | 使用 password_file 或 Kubernetes Secrets |
| 定期轮换密钥 | 每 90 天更换 Basic Auth 密码和 TLS 证书 |
| 审计日志 | 通过请求日志监控异常访问模式 |
| 及时更新版本 | 关注 Prometheus 安全公告,修补已知漏洞 |服务端5月27日 17:50
Prometheus Recording Rules 和 Alerting Rules 怎么选?Prometheus 支持两种规则类型:Recording Rules 和 Alerting Rules。两者都通过 PromQL 表达式定期评估,但用途完全不同。Recording Rules 用于预计算并持久化查询结果,Alerting Rules 用于监控指标并在满足条件时触发告警。理解两者的区别与联动方式,是写出高质量 Prometheus 规则的前提。
## Recording Rules:预计算提升查询性能
Recording Rules 的核心作用是将复杂或高频的 PromQL 表达式预先计算好,把结果存为新的时间序列。这样仪表盘和查询直接读取预计算结果,无需每次实时计算。
### 为什么需要 Recording Rules
当 Dashboard 每隔几秒刷新一次,而背后是一个涉及大量时间序列聚合的 PromQL 表达式时,每次实时计算会给 Prometheus 带来明显压力。Recording Rules 把这种计算从每次查询时提前到定期评估时,查询变成简单的指标读取,速度大幅提升。
另一个典型场景是跨团队共享指标。基础设施团队可以把基础聚合结果录制成新指标,应用团队直接基于这些录制指标构建自己的查询,避免重复计算。
### 配置示例
```yaml
groups:
- name: api_recording_rules
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum by (job) (rate(http_requests_total[5m]))
- record: job:request_errors:rate5m
expr: sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
```
`interval: 30s` 表示该组规则每 30 秒评估一次。每条规则的 `record` 字段指定新指标的名称,`expr` 字段定义计算表达式。评估完成后,`job:http_requests:rate5m` 和 `job:request_errors:rate5m` 就像普通指标一样可以被查询和引用。
### 命名规范
Recording Rules 的命名直接影响可读性和可维护性。推荐遵循 `level:metric:operations` 的格式:
- `level`:聚合维度,如 `job`、`instance`、`cluster`
- `metric`:原始指标名称,如 `http_requests`、`request_errors`
- `operations`:计算方式,如 `rate5m`、`sum`、`avg`
例如 `job:http_requests:rate5m` 表示按 job 聚合的 HTTP 请求 5 分钟速率。保持一致的命名规范,能让团队快速理解每条录制指标的含义。
## Alerting Rules:监控指标触发告警
Alerting Rules 用于定义告警条件。当 PromQL 表达式的结果满足条件时,Prometheus 会生成告警并推送到 Alertmanager,由 Alertmanager 负责分组、抑制、静默和通知路由。
### 配置示例
```yaml
groups:
- name: api_alerting_rules
rules:
- alert: HighErrorRate
expr: job:request_errors:rate5m / job:http_requests:rate5m > 0.05
for: 5m
keep_firing_for: 10m
labels:
severity: critical
annotations:
summary: "High error rate on {{ $labels.job }}"
description: "Error rate is {{ $value | humanizePercentage }}"
```
关键字段说明:
- `alert`:告警名称,需在同一个 group 内唯一
- `expr`:PromQL 表达式,结果非零即视为触发。这里引用了前面 Recording Rules 生成的指标
- `for`:条件持续满足多久后才真正触发告警。比如 `for: 5m` 意味着错误率连续 5 分钟超过 5% 才告警,避免瞬时抖动导致的误报
- `keep_firing_for`:告警触发后,即使条件恢复,仍继续保持 firing 状态一段时间,防止告警频繁闪烁
- `labels`:附加到告警的标签,用于 Alertmanager 的路由和分组
- `annotations`:告警的描述信息,支持模板变量,在通知中展示
### 与 Alertmanager 的集成
Prometheus 自身只负责生成告警,通知的分组、去重、路由由 Alertmanager 完成。在 `prometheus.yml` 中配置 Alertmanager 地址:
```yaml
alerting:
alertmanagers:
- static_configs:
- targets:
- 'alertmanager:9093'
```
Alertmanager 根据告警的 `labels` 决定路由到哪个接收方(邮件、Slack、PagerDuty 等),并支持抑制(inhibit)和静默(silence)策略。
## 核心区别
| 特性 | Recording Rules | Alerting Rules |
|------|----------------|----------------|
| 目的 | 预计算查询结果,存为新时间序列 | 监控指标,满足条件时触发告警 |
| 输出 | 生成新的时间序列数据 | 生成告警记录,推送至 Alertmanager |
| 存储 | 结果写入 TSDB | 不写入新时间序列 |
| 性能影响 | 减少实时查询压力,提升 Dashboard 速度 | 规则评估本身有开销,但告警推送开销极小 |
| 典型消费者 | Dashboard、其他规则、Ad-hoc 查询 | Alertmanager、通知渠道 |
| 关键参数 | `record`、`expr`、`interval` | `alert`、`expr`、`for`、`labels`、`annotations` |
一个容易忽略的区别:Recording Rules 的结果是持久化的时间序列,可以通过范围查询获取历史数据;Alerting Rules 的告警状态是瞬时的,一旦恢复就不再存在。
## 两者联动:用 Recording Rules 为 Alerting Rules 服务
这是实际生产中最常见的模式:先用 Recording Rules 预计算复杂指标,再让 Alerting Rules 引用这些预计算结果。
这样做的好处:
- 告警规则的 `expr` 更简洁易读,降低维护成本
- 预计算结果可复用,同一组录制指标能支撑多个告警规则
- 避免 Alerting Rules 在评估时执行复杂计算,减少评估超时风险
- 录制指标同时可用于 Dashboard 展示,一套计算多处使用
上面的示例中,`HighErrorRate` 告警规则直接引用了 `job:request_errors:rate5m` 和 `job:http_requests:rate5m` 两个录制指标,`expr` 只需做一次简单除法,清晰且高效。
## 规则管理实践
### 规则文件组织
将 Recording Rules 和 Alerting Rules 分文件管理,便于维护:
```yaml
# prometheus.yml
rule_files:
- /etc/prometheus/rules/recording/*.yml
- /etc/prometheus/rules/alerting/*.yml
```
同一 group 内的规则按顺序评估,共享相同的评估时间戳。不同 group 之间并行评估。
### 语法验证
部署前用 `promtool` 检查规则文件语法:
```bash
promtool check rules /etc/prometheus/rules/*.yml
```
这能捕获缩进错误、无效的 PromQL 表达式、缺少必填字段等常见问题。
### 评估性能监控
Prometheus 自身暴露了规则评估的指标,关注以下两个:
- `prometheus_rule_evaluation_duration_seconds`:规则评估耗时
- `prometheus_rule_group_last_duration_seconds`:规则组评估耗时
如果某个 group 的评估耗时持续超过其 `interval`,需要考虑拆分 group 或优化表达式。
### 级联告警设置
为规则文件设置 `limit`,防止单个 group 产生过多序列或告警拖垮 Prometheus:
```yaml
groups:
- name: api_alerting_rules
limit: 100
rules:
# ...
```
当序列或告警数量超过 limit 时,该 group 内所有规则产出的数据都会被丢弃,这是一种保护机制,需要在监控中对 limit 触发进行告警。服务端5月27日 17:49
Prometheus 生产环境怎么做?从架构到告警的实战经验Prometheus 上线跑起来不难,难的是跑稳、跑快、不漏报不误报。这篇文章把生产环境踩过的坑和验证过的方案整理出来,覆盖架构选型、指标设计、告警治理、性能调优和安全加固五个方面。
## 架构选型:单实例扛不住怎么办
单实例 Prometheus 在万级指标以下完全够用,问题出在规模增长之后。内存通常先成为瓶颈——Prometheus 每 2 小时将内存中的 TSDB 数据落盘一次,落盘前所有数据都在内存里,查询范围越大内存占用越高。
**什么时候该拆分**:当单个实例的 `prometheus_tsdb_head_series` 超过 500 万,或者 P95 查询延迟超过 2 秒,就该考虑分片或联邦了。
**长期存储方案对比**:
| 方案 | 适用场景 | 优点 | 缺点 |
|------|----------|------|------|
| Thanos Sidecar | 已有 Prometheus,需要全局查询 | 改动最小,兼容现有部署 | Sidecar 和 Prometheus 同生命周期,单点风险仍在 |
| Thanos Receive | 写入量大、需要实时远程写入 | 解耦采集和存储,支持多租户 | 架构复杂度高,依赖对象存储 |
| Cortex/Mimir | 超大规模、多租户 | 完全分布式,读写分离 | 运维成本最高,组件多 |
生产环境中 Thanos 是最主流的选择,原因很简单:渐进式迁移——先加 Sidecar 实现全局查询,再按需加 Receive 和 Compactor,不需要一步到位。
**资源规划的经验值**:每 100 万 active series 大约需要 4GB 内存和 2 核 CPU,预留 50% 余量应对突发。数据保留策略建议本地保留 15 天(`--storage.tsdb.retention.time=15d`),长期数据通过 Thanos 写入对象存储。
## 指标设计:90% 的问题出在标签上
指标命名和标签设计直接决定查询效率和告警质量。
**命名三原则**:下划线分隔、包含应用名、用标准单位后缀(`_bytes`、`_seconds`、`_total`)。例如 `http_request_duration_seconds_sum` 比 `request_time` 清晰得多。
**标签的最大坑是高基数**。什么是高基数?一个标签的取值超过 1 万种就是高基数。最常见的踩坑:
- 用 `user_id` 做标签——每个用户一个标签值,10 万用户就是 10 万 cardinality
- 把请求参数放进标签——`path="/api/user/123"` 每个路径一个值
- 用 `container_id` 做标签——容器重建后 ID 变化,基数无限增长
高基数标签不会让 Prometheus 崩溃,但会让它变慢。`prometheus_tsdb_head_series` 暴涨、查询超时、OOM killer 介入,往往根因就是一个高基数标签。
**检测方法**:用 `topk(10, count by (__name__) ({__name__=~".+"}))` 找出基数最高的指标,逐个排查是否该用日志而非指标来承载。
**四种指标类型的选择逻辑**:
- **Counter**(只增不减):请求总数、错误总数。查询时用 `rate()` 或 `increase()` 计算速率
- **Gauge**(可增可减):当前内存、CPU 使用率、队列深度。直接查询当前值
- **Histogram**(客户端分桶):延迟、响应大小。用 `histogram_quantile()` 计算 P50/P95/P99
- **Summary**(客户端计算分位数):和 Histogram 类似但分位数在客户端预计算,不可聚合。生产环境优先用 Histogram
一个常见误区:对 Counter 用 `avg()` 而不是 `rate()`——Counter 是累计值,直接取平均毫无意义。
## 告警治理:少即是多
告警过多比没有告警更危险,因为人们会对噪音免疫,真正严重的问题被淹没。
**分级策略**:
- **P0/Critical**:需要立即响应,比如服务完全不可用、数据丢失。通知方式:电话 + PagerDuty
- **P1/Warning**:需要关注但不致命,比如磁盘使用率 80%、延迟上升 50%。通知方式:Slack/飞书
- **P2/Info**:仅记录,不需要人工干预。通知方式:日志
**抑制规则**:同一实例的 Critical 告警应该抑制 Warning 告警,避免雪崩式通知:
```yaml
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'instance']
```
**告警路由的实用配置**:
```yaml
route:
group_by: ['alertname', 'cluster']
group_wait: 30s # 首次告警等待 30s 聚合同组
group_interval: 5m # 同组新告警间隔 5 分钟
repeat_interval: 4h # 未恢复的告警 4 小时重发一次
receiver: 'slack-default'
routes:
- match:
severity: critical
receiver: 'pagerduty'
repeat_interval: 1h # Critical 告警 1 小时重发
```
**告警规则的常见反模式**:
- 用绝对值而非比率做阈值——`memory_usage_bytes > 8589934592` 在不同规格机器上完全不同,应该用 `memory_usage_bytes / memory_limit_bytes > 0.9`
- 缺少 `for` 持续时间——瞬时抖动就触发告警,应该加 `for: 5m` 确认问题持续
- 告警条件太紧——`for: 1m` 加上 99% 阈值,每周都在误报
## 性能调优:让 Prometheus 跑得快
**采集优化**:
- 采集间隔不要一刀切。基础设施指标 15s 采集一次足够,业务指标可以 30s 甚至 60s
- 用 `metric_relabel_configs` 在采集端过滤不需要的指标,而不是在查询时过滤:
```yaml
scrape_configs:
- job_name: 'myapp'
metric_relabel_configs:
- source_labels: [__name__]
regex: 'go_.*'
action: drop # 丢弃所有 go_ 开头的运行时指标
```
- Recording Rules 预计算高频查询,把耗时 10 秒的 PromQL 变成毫秒级:
```yaml
groups:
- name: api_performance
interval: 30s
rules:
- record: api:request_duration_seconds:p99
expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, method))
```
**查询优化**:
- 限制查询时间范围——不要在 Grafana 面板里默认查 30 天,大范围查询用 Thanos Query
- 用标签过滤缩小数据集——`http_requests_total{method="GET"}` 比 `http_requests_total` 快得多
- 避免 `group_left` 和大范围 `rate()` 组合查询,这是内存杀手
**存储优化**:
- `--storage.tsdb.wal-compression` 开启 WAL 压缩,减少磁盘占用
- 定期用 `promtool tsdb analyze` 检查高基数指标和标签
- 本地 SSD 是底线,机械硬盘上 Prometheus 基本不可用
**监控 Prometheus 自身**:
```promql
# 是否存活
up{job="prometheus"}
# 采集延迟——超过 0 说明采集不过来
prometheus_target_scrape_pool_sync_length_seconds:quantile
# 规则评估耗时——超过 1 秒需要优化
prometheus_rule_evaluation_duration_seconds:quantile
# WAL 重放耗时——重启后恢复时间
prometheus_tsdb_wal_replay_duration_seconds
```
## 安全加固:默认配置是裸奔
Prometheus 默认没有任何认证机制,直接暴露在网络上等于裸奔。
**基本认证**:用 `basic_auth_users` 配置用户名密码,密码用 bcrypt 哈希而不是明文:
```bash
# 生成 bcrypt 哈希
htpasswd -nbBC 10 admin '' | tr -d ':\n' | sed 's/$2y/$2a/'
```
```yaml
basic_auth_users:
admin: '$2a$10$...' # bcrypt 哈希,不是明文密码
```
**TLS 加密**:Prometheus 原生不支持 TLS,需要用反向代理(Nginx/Envoy)或者在 Prometheus 前面加一个 oauth2-proxy。
**网络隔离**:
- Kubernetes 环境下用 NetworkPolicy 限制只有 Grafana 和 Alertmanager 能访问 Prometheus
- 不要把 Prometheus 端口暴露到公网
- 使用 Service Mesh(如 Istio)的 mTLS 自动加密内部流量
**配置管理**:
- 所有配置文件纳入 Git 版本控制,变更走 PR 审核流程
- 用 Prometheus Operator 或 kube-prometheus-stack 管理配置,而不是手动修改 YAML
- Secret 不要提交到 Git,用 Sealed Secrets 或 External Secrets Operator 管理
## 写在最后
生产环境的 Prometheus 运维核心就三件事:控制基数、治理告警、监控自身。架构选型根据规模渐进式升级,不要一上来就上 Thanos 全家桶;指标设计阶段就要想清楚标签的取值范围,高基数标签事后改的成本远高于事前规划;告警宁缺毋滥,P0 告警必须是"需要立刻爬起来处理"的级别。服务端5月27日 17:49
Prometheus Remote Write 和 Remote Read 怎么配置?Prometheus 自带的本地 TSDB 存储有容量和时效的限制,当数据量增长或需要跨集群查询时,就要借助 Remote Write 和 Remote Read 将数据外接到远程存储。这两个接口是 Prometheus 官方定义的标准协议,所有兼容的存储后端都能无缝对接。
## Remote Write 的工作流程
Prometheus 采集到指标后,样本先写入本地 WAL(预写日志),Remote Write 组件从 WAL 中批量读取新样本,经过队列缓冲和分片后,以 Protobuf 编码通过 HTTP POST 发送到远端端点。整个过程是异步的,即使远端暂时不可用,队列也会按退避策略重试,不会阻塞采集。
**典型配置**:
```yaml
remote_write:
- url: "http://remote-storage:9201/api/v1/write"
basic_auth:
username: "user"
password: "pass"
queue_config:
capacity: 10000
max_shards: 50
min_shards: 1
max_samples_per_send: 1000
batch_send_deadline: 5s
min_backoff: 30ms
max_backoff: 100ms
write_relabel_configs:
- source_labels: [__name__]
regex: 'expensive_.*'
action: drop
```
**队列参数解析**:
- `capacity` — 内存队列中缓存的样本上限,超出会阻塞写入
- `max_shards` / `min_shards` — 动态分片的上下限,分片越多吞吐越高,但内存和 CPU 开销也越大
- `max_samples_per_send` — 每次请求发送的最大样本数,值越大吞吐越高,但单次失败代价也更大
- `batch_send_deadline` — 即使样本数未达到 `max_samples_per_send`,超过此时间也会强制发送
- `min_backoff` / `max_backoff` — 发送失败后的重试退避范围,避免雪崩式重试
`write_relabel_configs` 可以在发送前过滤或改写标签,比如丢弃 `expensive_` 前缀的高基数指标,减少远程存储的压力。
## Remote Read 的工作流程
当用户在 Prometheus UI 或 Grafana 发起查询时,如果本地 TSDB 没有对应数据,Prometheus 会向 `remote_read` 端点发送 Protobuf 编码的查询请求,远端存储返回匹配的时间序列样本,Prometheus 将其与本地数据合并后返回给用户。
**典型配置**:
```yaml
remote_read:
- url: "http://remote-storage:9201/api/v1/read"
read_recent: true
basic_auth:
username: "user"
password: "pass"
```
`read_recent: true` 表示优先从本地读取近期数据,只向远端请求本地没有的历史数据,能显著降低查询延迟。设为 `false` 则所有数据都从远端获取。
## 主流远程存储后端
| 后端 | 特点 |
|------|------|
| Thanos | 基于 OSS/S3 的长期存储,支持全局查询视图,社区活跃 |
| VictoriaMetrics | 高性能压缩,兼容 Remote Write 协议,部署简单 |
| Cortex | 多租户架构,适合大规模集群,依赖组件较多 |
| M3DB | 分布式时序数据库,Uber 开源,擅长高基数场景 |
| InfluxDB | 生态成熟,支持多种写入协议 |
| TimescaleDB | 基于 PostgreSQL,适合已有 PG 运维经验的团队 |
## 常见问题与最佳实践
**内存增长**:开启 Remote Write 后 Prometheus 内存通常增加约 25%,因为需要在内存中缓存序列 ID 到标签值的映射。建议监控 `prometheus_remote_storage_queue_length`,如果队列持续积压,需要调大 `max_shards` 或检查网络。
**数据丢失风险**:如果 Prometheus 异常退出且 WAL 未及时同步,极端情况下可能丢失少量数据。确保 `queue_config` 中的重试和退避参数合理配置。
**性能监控指标**:
- `prometheus_remote_storage_queue_length` — 当前队列深度
- `prometheus_remote_storage_failed_samples_total` — 发送失败累计次数
- `prometheus_remote_storage_succeeded_samples_total` — 发送成功累计次数
- `prometheus_remote_storage_highest_timestamp_in_seconds` — 已成功发送的最大时间戳
**标签过滤**:始终用 `write_relabel_configs` 过滤不需要的高基数指标,避免远程存储膨胀和查询变慢。
**网络压缩**:Remote Write 默认使用 Snappy 压缩,部分后端也支持 gzip。确保远端支持对应压缩格式以减少带宽占用。
## 配置思路总结
1. 先确定长期存储后端(VictoriaMetrics 入门最简单)
2. 配置 `remote_write`,用 `write_relabel_configs` 过滤高基数指标
3. 按实际吞吐调整 `queue_config`,从小分片起步逐步扩容
4. 配置 `remote_read` 并开启 `read_recent: true`
5. 在 Grafana 中添加 Remote Write 相关的面板,持续监控队列和失败率服务端5月27日 17:44
Prometheus 指标类型怎么选?Counter Gauge Histogram Summary 区别与用法Prometheus 监控的核心不是采集数据,而是用对指标类型。选错了类型,要么查询结果不准,要么聚合不出你想要的维度。Counter、Gauge、Histogram、Summary 四种类型各有明确的使用场景,搞清楚它们的差异是写出可靠监控规则的第一步。
## Counter:只增不减的累计值
Counter 最简单的理解:出租车计价器。数字只会往上走,不会倒退(除非进程重启归零)。
典型场景:HTTP 请求总数、错误发生次数、任务完成数。这些值天然只会累加。
```promql
# 计算 QPS——最近 5 分钟每秒平均请求数
rate(http_requests_total[5m])
# 计算增长量——最近 1 小时新增了多少请求
increase(http_requests_total[1h])
```
Counter 的查询几乎离不开 `rate()` 或 `increase()`。直接看 Counter 的原始值意义不大——"总共处理了 100 万个请求"本身不能告诉你系统现在健不健康,但"每秒处理 500 个请求"就能。
定义 Counter 指标的方式:
```go
var httpRequestCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"},
)
```
注意 `NewCounterVec` 的 `Vec` 后缀——它支持按 label 拆分维度(按方法、路径、状态码分别统计),这在实战中非常常用。
**容易踩的坑**:把本该用 Gauge 的值(比如当前在线用户数)定义成 Counter。在线用户数会减,Counter 不能减,强行用 Counter 记录会导致 rate() 计算出负值,图表出现锯齿状异常。
## Gauge:可增可减的瞬时值
Gauge 像温度计——当前多少就是多少,可以升高也可以降低。
典型场景:当前内存使用量、CPU 使用率、队列深度、在线连接数。这些值的"当前值"本身就有意义,不需要算变化率。
```promql
# 直接看当前值
node_memory_available_bytes
# 看过去 1 小时的峰值
max_over_time(node_memory_available_bytes[1h])
# 看过去 30 分钟的平均值
avg_over_time(node_cpu_seconds_total[30m])
```
Gauge 用 `max_over_time`、`min_over_time`、`avg_over_time` 这类函数。注意:**不要对 Gauge 用 `rate()`**——`rate()` 计算的是增长率,而 Gauge 的值波动本身不表示增长趋势,算出来的结果没有意义。
```go
var tempGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "room_temperature_celsius",
Help: "Current room temperature",
},
[]string{"room"},
)
// 设置值
tempGauge.WithLabelValues("server-room").Set(23.5)
```
Gauge 可以用 `Set()` 直接设置任意值,也可以用 `Inc()` / `Dec()` 增减,这是和 Counter 的本质区别。
## Histogram:服务端算分位数的分布
当你需要知道"95% 的请求在多长时间内完成"时,就需要 Histogram。
Histogram 的工作原理:定义一组 bucket(桶的边界),每个观测值落入对应的桶中。比如配置 bucket 为 `[0.1, 0.5, 1, 2.5, 5, 10]`,一个耗时 0.3 秒的请求会同时被计入 `le=0.5`、`le=1`、`le=2.5`... 所有比它大的桶里。
```go
var requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration",
Buckets: []float64{0.1, 0.5, 1, 2.5, 5, 10},
},
[]string{"method"},
)
```
Histogram 会自动产生三个指标:
- `http_request_duration_seconds_bucket{le="0.5"}` — 落入各桶的累计计数
- `http_request_duration_seconds_sum` — 所有观测值之和
- `http_request_duration_seconds_count` — 观测总次数
用 `histogram_quantile` 在服务端计算分位数:
```promql
# P95 延迟
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
```
Histogram 的关键优势:**可以聚合**。多个实例的 bucket 数据可以在 Prometheus 服务端合并计算,适合分布式系统。代价是分位数值是近似值(受 bucket 粒度影响),bucket 配得太粗会导致误差大。
**bucket 怎么配**:先观察数据的实际分布,再设定桶边界。初始可以用 Prometheus 默认的桶(适合 Web 请求延迟),上线后根据 P99 的实际范围调整。桶的数量建议 8-12 个,太少了精度差,太多了存储膨胀。
## Summary:客户端算分位数的精确值
Summary 和 Histogram 解决同样的问题——算分位数,但计算位置不同:Summary 在客户端(你的应用里)直接算好分位数再上报。
```go
var rpcDuration = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "rpc_duration_seconds",
Help: "RPC call duration",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
},
[]string{"service"},
)
```
`Objectives` 定义了你关心哪些分位数及可接受的误差。`0.9: 0.01` 表示 P90 的误差在 1% 以内。
Summary 产出的指标:
- `rpc_duration_seconds{quantile="0.9"}` — P90 延迟(客户端已算好)
- `rpc_duration_seconds_sum` — 总和
- `rpc_duration_seconds_count` — 总次数
直接查询即可:
```promql
# P99 延迟,无需 histogram_quantile
rpc_duration_seconds{quantile="0.99"}
```
**但 Summary 有个致命限制:不能跨实例聚合**。你有 10 个 Pod,每个 Pod 自己算了 P99,你没法把这 10 个 P99 合并成全局 P99。数学上,分位数的分位数不是总体的分位数。所以 Summary 只适合单实例场景,或者你只关心每个实例自己的延迟分布。
## Histogram 还是 Summary?
这是面试和实战中最高频的问题,核心差异就一点:**分位数在哪里算**。
| 对比项 | Histogram | Summary |
|--------|-----------|---------|
| 分位数计算位置 | Prometheus 服务端 | 应用客户端 |
| 可聚合 | 可以(跨实例合并计算) | 不可以 |
| 精确度 | 近似值(受 bucket 粒度影响) | 可控误差(由 Objectives 设定) |
| 客户端开销 | 低(只计数) | 较高(需要维护分位数流式计算) |
| 适用场景 | 分布式系统、多实例 | 单实例、需要精确分位数 |
选择逻辑很简单:多实例分布式系统用 Histogram,单实例或需要精确值用 Summary。大多数生产环境选 Histogram,因为可聚合是刚需。
## 四种类型怎么选
决策路径:先问"这个值是只增不减的吗"——是就用 Counter,不是就问"当前瞬时值本身有意义吗"——有意义用 Gauge,没意义再问"需要算分位数吗"——需要就问"要多实例聚合吗"——要聚合用 Histogram,不要用 Summary。
举几个实战中的对应关系:
- API 请求总数 → Counter(只增不减)
- 当前活跃连接数 → Gauge(可增可减,当前值有意义)
- 请求延迟分布 → Histogram(需要分位数 + 聚合)
- 单进程内部方法耗时 → Summary(单实例精确分位数)
Prometheus 服务端本身其实不区分这四种类型——所有时间序列在服务端都是一样的存储格式。类型信息只存在于客户端库中,目的是约束你用正确的 API 记录数据、用正确的函数查询数据。选对类型,查询才有意义;选错了,数据采集了也用不上。