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_sumrequest_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 告警必须是"需要立刻爬起来处理"的级别。

标签:Prometheus