面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 05月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 分别处理各自领域的告警。这种组合在中小团队中广泛使用。
服务端阅读 05月27日 17:51

Prometheus 安全认证怎么配置?Basic Auth 与 RBAC 实战

Prometheus 默认不启用认证,9090 端口一旦暴露,任何人都能访问 /metrics 和 /api/v1/query,造成监控数据泄露甚至配置被篡改。下面从 scrape 认证、服务端防护、K8s RBAC 三个层面说明如何配置 Prometheus 的安全认证和访问控制。一、Scrape 侧认证:让 Prometheus 访问受保护的 Target1.1 Basic Auth当 Target(如 pushgateway 或其他 exporter)启用了 Basic Auth 时,Prometheus 抓取时需携带用户名密码: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: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: endpoints1.3 TLS 双向认证当 Target 要求客户端证书时: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 和 APIPrometheus 从 2.24 版本开始支持内置 Basic Auth 和 TLS,通过 web.config.file 参数加载。2.1 生成 bcrypt 密码哈希# 安装工具apt-get install -y python3-bcrypt# 生成哈希python3 -c "import bcryptprint(bcrypt.hashpw(b'your-secure-password', bcrypt.gensalt()).decode())"# 输出类似:$2b$12$Wxn...2.2 编写 web-config.ymlbasic_auth_users: admin: '$2b$12$Wxn...' # 上一步生成的哈希tls_config: cert_file: /etc/prometheus/tls/cert.pem key_file: /etc/prometheus/tls/key.pem验证配置文件语法:promtool check web-config web-config.yml2.3 启动时加载prometheus --config.file=/etc/prometheus/prometheus.yml --web.config.file=/etc/prometheus/web-config.yml启动后访问 http://localhost:9090 将弹出 Basic Auth 登录框,未认证请求返回 401。2.4 Docker 部署示例# docker-compose.ymlservices: 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 实现:apiVersion: networking.k8s.io/v1kind: Ingressmetadata: 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:htpasswd -c auth adminkubectl create secret generic prometheus-basic-auth --from-file=auth -n monitoring三、Kubernetes RBAC:限制 Prometheus 的访问范围3.1 创建 ServiceAccountapiVersion: v1kind: ServiceAccountmetadata: name: prometheus namespace: monitoring3.2 定义 Role(最小权限)apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: prometheus namespace: monitoringrules: - apiGroups: [''] resources: ['pods', 'services', 'endpoints'] verbs: ['get', 'list', 'watch']仅授权 monitoring 命名空间下的资源读取,不授予写权限和跨命名空间权限。3.3 绑定 Role 和 ServiceAccountapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: prometheus namespace: monitoringroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: prometheussubjects: - kind: ServiceAccount name: prometheus namespace: monitoring3.4 ClusterRole(如需跨命名空间监控)apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: prometheusrules: - apiGroups: [''] resources: ['nodes', 'pods', 'services', 'endpoints'] verbs: ['get', 'list', 'watch'] - nonResourceURLs: ['/metrics'] verbs: ['get']---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: prometheusroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheussubjects: - kind: ServiceAccount name: prometheus namespace: monitoring四、Grafana 对接待认证的 Prometheus配置 Basic Auth 后,Grafana 数据源需同步修改:# grafana datasources configapiVersion: 1datasources: - 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 安全公告,修补已知漏洞 |
服务端阅读 05月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 把这种计算从每次查询时提前到定期评估时,查询变成简单的指标读取,速度大幅提升。另一个典型场景是跨团队共享指标。基础设施团队可以把基础聚合结果录制成新指标,应用团队直接基于这些录制指标构建自己的查询,避免重复计算。配置示例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、clustermetric:原始指标名称,如 http_requests、request_errorsoperations:计算方式,如 rate5m、sum、avg例如 job:http_requests:rate5m 表示按 job 聚合的 HTTP 请求 5 分钟速率。保持一致的命名规范,能让团队快速理解每条录制指标的含义。Alerting Rules:监控指标触发告警Alerting Rules 用于定义告警条件。当 PromQL 表达式的结果满足条件时,Prometheus 会生成告警并推送到 Alertmanager,由 Alertmanager 负责分组、抑制、静默和通知路由。配置示例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 地址: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 分文件管理,便于维护:# prometheus.ymlrule_files: - /etc/prometheus/rules/recording/*.yml - /etc/prometheus/rules/alerting/*.yml同一 group 内的规则按顺序评估,共享相同的评估时间戳。不同 group 之间并行评估。语法验证部署前用 promtool 检查规则文件语法: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:groups: - name: api_alerting_rules limit: 100 rules: # ...当序列或告警数量超过 limit 时,该 group 内所有规则产出的数据都会被丢弃,这是一种保护机制,需要在监控中对 limit 触发进行告警。
服务端阅读 05月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/P99Summary(客户端计算分位数):和 Histogram 类似但分位数在客户端预计算,不可聚合。生产环境优先用 Histogram一个常见误区:对 Counter 用 avg() 而不是 rate()——Counter 是累计值,直接取平均毫无意义。告警治理:少即是多告警过多比没有告警更危险,因为人们会对噪音免疫,真正严重的问题被淹没。分级策略:P0/Critical:需要立即响应,比如服务完全不可用、数据丢失。通知方式:电话 + PagerDutyP1/Warning:需要关注但不致命,比如磁盘使用率 80%、延迟上升 50%。通知方式:Slack/飞书P2/Info:仅记录,不需要人工干预。通知方式:日志抑制规则:同一实例的 Critical 告警应该抑制 Warning 告警,避免雪崩式通知:inhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'instance']告警路由的实用配置: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 在采集端过滤不需要的指标,而不是在查询时过滤:scrape_configs: - job_name: 'myapp' metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*' action: drop # 丢弃所有 go_ 开头的运行时指标Recording Rules 预计算高频查询,把耗时 10 秒的 PromQL 变成毫秒级: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 自身:# 是否存活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 哈希而不是明文:# 生成 bcrypt 哈希htpasswd -nbBC 10 admin '' | tr -d ':\n' | sed 's/$2y/$2a/'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 管理配置,而不是手动修改 YAMLSecret 不要提交到 Git,用 Sealed Secrets 或 External Secrets Operator 管理写在最后生产环境的 Prometheus 运维核心就三件事:控制基数、治理告警、监控自身。架构选型根据规模渐进式升级,不要一上来就上 Thanos 全家桶;指标设计阶段就要想清楚标签的取值范围,高基数标签事后改的成本远高于事前规划;告警宁缺毋滥,P0 告警必须是"需要立刻爬起来处理"的级别。
服务端阅读 05月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 发送到远端端点。整个过程是异步的,即使远端暂时不可用,队列也会按退避策略重试,不会阻塞采集。典型配置: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 将其与本地数据合并后返回给用户。典型配置: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。确保远端支持对应压缩格式以减少带宽占用。配置思路总结先确定长期存储后端(VictoriaMetrics 入门最简单)配置 remote_write,用 write_relabel_configs 过滤高基数指标按实际吞吐调整 queue_config,从小分片起步逐步扩容配置 remote_read 并开启 read_recent: true在 Grafana 中添加 Remote Write 相关的面板,持续监控队列和失败率
服务端阅读 05月27日 17:44

Prometheus 指标类型怎么选?Counter Gauge Histogram Summary 区别与用法

Prometheus 监控的核心不是采集数据,而是用对指标类型。选错了类型,要么查询结果不准,要么聚合不出你想要的维度。Counter、Gauge、Histogram、Summary 四种类型各有明确的使用场景,搞清楚它们的差异是写出可靠监控规则的第一步。Counter:只增不减的累计值Counter 最简单的理解:出租车计价器。数字只会往上走,不会倒退(除非进程重启归零)。典型场景:HTTP 请求总数、错误发生次数、任务完成数。这些值天然只会累加。# 计算 QPS——最近 5 分钟每秒平均请求数rate(http_requests_total[5m])# 计算增长量——最近 1 小时新增了多少请求increase(http_requests_total[1h])Counter 的查询几乎离不开 rate() 或 increase()。直接看 Counter 的原始值意义不大——"总共处理了 100 万个请求"本身不能告诉你系统现在健不健康,但"每秒处理 500 个请求"就能。定义 Counter 指标的方式: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 使用率、队列深度、在线连接数。这些值的"当前值"本身就有意义,不需要算变化率。# 直接看当前值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 的值波动本身不表示增长趋势,算出来的结果没有意义。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… 所有比它大的桶里。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 在服务端计算分位数:# 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 在客户端(你的应用里)直接算好分位数再上报。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 — 总次数直接查询即可:# P99 延迟,无需 histogram_quantilerpc_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 记录数据、用正确的函数查询数据。选对类型,查询才有意义;选错了,数据采集了也用不上。
服务端阅读 05月27日 17:42

Prometheus 性能优化与水平扩展怎么做?

Prometheus 单实例在百万级时间序列下会出现采集延迟、查询超时、OOM 等问题。本文从采集、存储、查询三个维度给出调优手段,再对比 Thanos、Mimir、VictoriaMetrics 三种水平扩展方案的选型建议。采集优化分级采集间隔根据业务重要性对 Target 设置不同的采集间隔。核心服务 15 秒,普通服务 30-60 秒,批量任务 120 秒。将全局 scrape_interval 从 15s 放宽到 30s,采样量直接减半。global: scrape_interval: 30sscrape_configs: - job_name: critical-service scrape_interval: 15s - job_name: batch-jobs scrape_interval: 120s过滤高基数指标高基数标签(如 userid、containerid)会令时间序列数量爆炸。用 metricrelabelconfigs 在采集端丢弃不需要的指标,从源头控制数据量。metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*|process_.*' action: drop - source_labels: [__name__] regex: 'container_.*_seconds_.*' action: drop同时设置 samplelimit 和 targetlimit 作为安全阀,防止误配置导致指标暴增。scrape_configs: - job_name: api sample_limit: 10000 target_limit: 100水平分片采集当单实例采集能力不足时,用 hashmod relabel 将 Target 分散到多个 Prometheus 实例,每个实例只抓取一部分。global: external_labels: scraper: '1'scrape_configs: - job_name: sharded relabel_configs: - source_labels: [__address__] modulus: 4 target_label: __tmp_hash action: hashmod - source_labels: [__tmp_hash] regex: '1' action: keepmodulus 设为 4 表示 4 个分片,每个实例 regex 匹配自己的编号。配合 external_labels 标识来源,后续通过 Thanos 或 Mimir 聚合全局视图。存储优化TSDB 调优开启 WAL 压缩和内存映射写入,降低磁盘 I/O 压力。--storage.tsdb.wal-compression=true--storage.tsdb.wal-segment-size=20MB--storage.tsdb.memory-map-on-write=true控制数据保留期,避免磁盘写满。生产环境通常 15-30 天,长期数据交给对象存储。--storage.tsdb.retention.time=15d--storage.tsdb.retention.size=80GBRemote Write 异步写入将数据异步写入远端存储,Prometheus 本地只保留短期热数据。建议在 Remote Write 前加一层缓存队列(如 Prometheus本身的 queue 配置),避免远端慢写入拖垮采集。remote_write: - url: http://thanos-receive:19291/api/v1/receive queue_config: max_samples_per_send: 10000 capacity: 50000 max_shards: 50查询优化Recording Rules 预计算将高频、耗时的 PromQL 查询预计算为 Recording Rule,Dashboard 和告警直接查询预计算结果,查询耗时从秒级降到毫秒级。groups: - name: precompute interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: namespace:cpu:usage expr: sum by (namespace) (rate(container_cpu_usage_seconds_total[5m]))查询参数调优--query.max-samples=50000000--query.timeout=2m--query.lookback-delta=5mmax_samples 防止单次查询扫过多数据;lookback-delta 控制无数据时的回看窗口,调小可减少扫描量。水平扩展方案选型单实例垂直扩展有上限。当时间序列超过 200 万、查询 QPS 超过 50、存储保留期超过 30 天时,需要引入水平扩展方案。方案对比| 维度 | Thanos | Grafana Mimir | VictoriaMetrics ||------|--------|---------------|-----------------|| 架构模式 | Sidecar 附加现有 Prometheus | 独立分布式集群 | 单节点或集群模式 || 迁移成本 | 最低,Sidecar 无侵入 | 需要部署完整微服务栈 | 中等,改 remote_write 地址即可 || 长期存储 | 支持 S3/GCS 等对象存储 | 支持 S3/GCS/Azure | 支持 S3/GCS || 多租户 | 基本支持 | 强隔离,适合平台团队 | 有限支持 || 全局查询 | Querier 聚合多实例 | Query Frontend + Distributor | vmselect 聚合 || 运维复杂度 | 中等(4-5 个组件) | 高(10+ 微服务) | 低(1-3 个组件) || 适用规模 | 中大型,已有 Prometheus | 大型企业,需多租户 | 中小型到中大型 || 存储效率 | 2-4 倍压缩 | 中等 | 5-10 倍压缩,最省磁盘 |Thanos 方案Thanos 通过 Sidecar 组件附着在现有 Prometheus 上,将 TSDB 块上传到对象存储实现长期保留,Querier 提供跨实例全局查询。适合已有 Prometheus 部署、希望低摩擦迁移的团队。核心组件:Sidecar(上传数据块)、Store Gateway(查询历史数据)、Compactor(压缩和降采样)、Querier(全局查询)、Query Frontend(查询缓存和分片)。Grafana Mimir 方案Mimir 是 Cortex 的继任者,提供完全分布式的多租户 Prometheus 兼容存储。2025 年底发布 v3.0,引入 Kafka 解耦架构。适合有平台团队、需要服务多业务线的大企业。注意:新部署不应选择 Cortex,直接用 Mimir。VictoriaMetrics 方案VictoriaMetrics 兼容 Prometheus API,单二进制部署即可运行。存储效率比原生 Prometheus 高 5-10 倍,资源占用低。500 节点 K8s 集群仅需 2-4 GB 内存,而 Prometheus 需要 8-16 GB。适合追求简单高效、中小规模到中大规模的团队。联邦架构联邦是 Prometheus 原生功能,用于层级化监控。子 Prometheus 采集各区域数据,父 Prometheus 通过 /federate 端点拉取聚合指标。scrape_configs: - job_name: federate scrape_interval: 15s honor_labels: true metrics_path: /federate params: match[]: - '{__name__=~"job:.*"}' static_configs: - targets: - prometheus-region-a:9090 - prometheus-region-b:9090联邦适合多数据中心、多区域的层级聚合场景,不建议用作主要水平扩展手段——它只拉取聚合结果,原始数据仍在子实例上。自身监控监控 Prometheus 自身的关键指标,及时发现瓶颈:# 采集速率rate(prometheus_tsdb_head_samples_appended_total[5m])# 查询延迟histogram_quantile(0.9, rate(prometheus_query_duration_seconds_bucket[5m]))# WAL 写入滞留rate(prometheus_tsdb_wal_writes_failed_total[5m])# 磁盘使用prometheus_tsdb_storage_blocks_bytes建议对这些指标设置告警:采集速率突降、P90 查询超过 10 秒、WAL 写入失败、磁盘使用超 80%。容量规划参考| 规模 | 活跃时间序列 | 内存 | CPU | 磁盘(30天) | 建议 ||------|-------------|------|-----|-------------|------|| 小型 | 1000 万 | 分布式 | 分布式 | 对象存储 | Mimir 分布式集群 |要点总结采集端通过分级间隔、过滤高基数指标和水平分片控制数据入口;存储端通过 WAL 压缩、内存映射和 Remote Write 分离冷热数据;查询端用 Recording Rules 预计算并调优查询参数。超出单实例能力时,中小团队首选 VictoriaMetrics 或 Thanos,大型企业选 Mimir。联邦架构用于多区域层级聚合,不替代水平扩展。持续监控 Prometheus 自身指标,在瓶颈出现前提前扩容。
服务端阅读 05月27日 17:41

Prometheus 支持哪些服务发现机制?如何配置?

Prometheus 支持多种服务发现机制,用于在动态环境中自动发现监控目标,无需手动维护目标列表。下面逐一说明各机制的原理、配置方式与适用场景。静态配置静态配置是最简单的方式,直接在 prometheus.yml 中写死目标地址,适合小规模或目标固定的场景:scrape_configs: - job_name: 'node' static_configs: - targets: ['192.168.1.10:9100', '192.168.1.11:9100'] labels: datacenter: 'dc1'目标变动时需要重启或 reload Prometheus,不适合频繁变化的动态环境。基于文件的服务发现filesdconfigs 通过读取外部 JSON 或 YAML 文件获取目标列表,Prometheus 会监听文件变化并自动更新:scrape_configs: - job_name: 'file' file_sd_configs: - files: - '/etc/prometheus/targets/*.json' refresh_interval: 5mJSON 文件格式示例:[ { "targets": ["10.0.0.1:9100", "10.0.0.2:9100"], "labels": {"env": "prod"} }]这种方式适合由外部脚本或 CMDB 系统定期生成目标列表的场景,Prometheus 不需要重启。Kubernetes 服务发现Kubernetes 是 Prometheus 最常用的部署环境,通过 kubernetessdconfigs 直接调用 Kubernetes API 发现集群资源: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_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__meta_kubernetes_pod_ip] action: replace target_label: __address__ replacement: $1:9104Kubernetes 角色类型| 角色 | 发现资源 | 典型场景 ||------|---------|---------|| pod | 所有 Pod | 直接采集 Pod 指标 || service | 所有 Service | 采集 Service 背后端点 || endpoints | Service 的 Endpoints | 最常用,配合注解使用 || endpointslices | EndpointSlices | K8s 1.21+ 替代 Endpoints || node | 所有 Node | 采集节点级指标 || ingress | 所有 Ingress | 监控 Ingress 延迟 |通过注解控制采集在 Pod 模板中添加注解即可被 Prometheus 自动发现:metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/metrics"RBAC 权限Prometheus 需要 ClusterRole 权限才能访问 K8s API:rules: - apiGroups: [""] resources: - nodes - services - endpoints - pods verbs: ["get", "list", "watch"]命名空间过滤限制发现范围,只采集特定命名空间的目标:kubernetes_sd_configs: - role: pod namespaces: names: - production - stagingPrometheus Operator 的 CRD 方式在 Kubernetes 环境中,Prometheus Operator 提供了更声明式的方式,通过 ServiceMonitor、PodMonitor 和 Probe 三种 CRD 定义采集目标,无需直接编写 relabel 配置:apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: my-appspec: selector: matchLabels: app: my-app endpoints: - port: metrics path: /metrics interval: 30sOperator 自动将这些 CRD 转换为 Prometheus 的 scrape_configs,是 Kubernetes 环境下的推荐方式。Consul 服务发现Consul 是常用的服务注册中心,Prometheus 通过 consulsdconfigs 查询 Consul Catalog 获取服务实例:scrape_configs: - job_name: 'consul' consul_sd_configs: - server: 'consul.example.com:8500' services: ['web', 'api'] tag_separator: ',' scheme: http token: 'your-acl-token'Consul 服务实例的元数据通过 __meta_consul_service_metadata_* 标签暴露,可以在 relabel 阶段提取为 Prometheus 标签。适合已有 Consul 基础设施的微服务架构。DNS SRV 服务发现通过 DNS SRV 记录发现目标,无需额外依赖,适合支持 SRV 记录的内网环境:scrape_configs: - job_name: 'dns' dns_sd_configs: - names: ['_prometheus._tcp.example.com'] type: SRV port: 9100也支持 A/AAAA 记录查询,将域名解析为 IP 列表。配置简单但不适用于频繁变化的场景。HTTP 服务发现Prometheus 2.28+ 引入的 httpsdconfigs,通过 HTTP 端点获取目标列表,返回格式与 file_sd 相同的 JSON:scrape_configs: - job_name: 'http' http_sd_configs: - url: 'http://config-server/targets' refresh_interval: 1m这是对文件服务发现的动态替代,适合通过自建配置中心或 CMDB API 提供目标的场景。云平台服务发现AWS EC2scrape_configs: - job_name: 'ec2' ec2_sd_configs: - region: us-east-1 access_key: AKIA... secret_key: xxx... filters: - name: tag:Environment values: [production]可以通过 filters 按 EC2 标签过滤实例,元标签 __meta_ec2_tag_* 提供 EC2 标签信息。Azure VMscrape_configs: - job_name: 'azure' azure_sd_configs: - subscription_id: 'your-subscription-id' tenant_id: 'your-tenant-id' client_id: 'your-client-id' client_secret: 'your-client-secret'GCEscrape_configs: - job_name: 'gce' gce_sd_configs: - project: 'my-gcp-project' - zone: 'us-central1-a'Relabel 配置详解Relabel 是服务发现的核心能力,在目标进入采集队列前对标签进行加工和过滤:| 动作 | 作用 | 常用场景 ||------|------|---------|| keep | 保留匹配的目标 | 只采集带特定注解的 Pod || drop | 丢弃匹配的目标 | 排除 kube-system 命名空间 || replace | 替换标签值 | 修改 address 加端口 || labelmap | 批量映射标签 | 将元标签映射为业务标签 || hashmod | 哈希取模 | 分片采集,多实例分担负载 || labeldrop | 删除标签 | 清理不需要的 __ 前缀标签 || labelkeep | 保留匹配的标签 | 只保留特定模式的标签 |常用元标签Kubernetes 环境中最常用的元标签:__meta_kubernetes_pod_name — Pod 名称__meta_kubernetes_namespace — 命名空间__meta_kubernetes_pod_annotation_prometheus_io_scrape — 采集注解__meta_kubernetes_pod_annotation_prometheus_io_path — 指标路径注解__meta_kubernetes_pod_annotation_prometheus_io_port — 端口注解__meta_kubernetes_service_name — Service 名称Consul 环境中的元标签:__meta_consul_service_metadata_* — 服务元数据__meta_consul_tags — 服务标签__meta_consul_dc — 数据中心调试服务发现当目标未按预期出现时,可以通过以下方式排查:在 Prometheus UI 的 Status → Targets 页面查看已发现的目标和标签。通过 API 查询当前服务发现结果:curl -s http://prometheus:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, discoveredLabels: .discoveredLabels}'查看服务发现过程中丢弃的目标(DISCOVERED 状态):curl -s http://prometheus:9090/api/v1/targets | jq '.data.droppedTargets | length'如何选择服务发现方式选择依据主要看部署环境:Kubernetes 集群内:优先用 kubernetessdconfigs 或 Prometheus Operator CRD有 Consul 注册中心:用 consulsdconfigs云平台原生:用对应的云 SD(ec2/azure/gce)有 CMDB 或配置中心:用 httpsdconfigs 或 filesdconfigs小规模静态环境:直接用 static_configs支持 SRV 的内网:可用 dnssdconfigs实际生产中,Kubernetes 环境占绝大多数,Kubernetes SD + Relabel 是最核心的组合。
服务端阅读 05月27日 17:40

Prometheus 故障排查怎么做?7 类常见问题逐个击破

Prometheus 线上跑着跑着突然 OOM 崩了,告警静悄悄没触发,采集数据大面积缺失——这些问题你可能都遇到过。下面按照实际排障频率,从最常见到最棘手,逐一拆解。Prometheus 无法启动启动失败通常是配置错误或资源冲突,按以下顺序排查:第一步:检查配置文件语法promtool check config /etc/prometheus/prometheus.ymlpromtool 是 Prometheus 自带的诊断工具,它会逐行解析配置并报出具体的行号和错误原因。常见的配置错误包括 YAML 缩进不一致、scrapeinterval 写成了字符串而非 duration 格式、以及 relabelconfigs 中引用了不存在的标签。第二步:检查端口占用lsof -i :9090ss -tlnp | grep 90909090 端口被其他进程占用是部署时的高频问题,尤其是在容器环境中未正确配置网络命名空间时。如果端口被占用,可以修改 --web.listen-address 参数指定其他端口。第三步:查看服务日志journalctl -u prometheus -f --no-pager -n 200日志中常见的关键错误信息:cannot load TSDB:TSDB 数据目录损坏或权限不足cannot create directory:数据目录权限问题,检查 Prometheus 进程的运行用户是否对数据目录有写权限cannot initialize notification queue:Alertmanager 地址不可达数据采集失败(Target Down)采集失败是日常运维中最频繁遇到的问题,核心排查思路是确认"Prometheus 能否访问到 Target 的 /metrics 端点"。确认 Target 状态在 Prometheus Web UI 的 /targets 页面可以看到每个 Target 的健康状态和最后错误信息。也可以用 PromQL 查询:up{job="your-job"}up == 1 表示正常,up == 0 表示采集失败。结合 scrape_samples_scraped 可以判断是否采集到了数据但数据为空。排查网络连通性curl -v http://target:port/metrics重点关注:连接超时:防火墙规则或安全组未放通403/401:认证配置不匹配404:metrics 路径不是默认的 /metrics,需要在 job 配置中指定 metrics_pathSSL 错误:证书过期或自签名证书未在 Prometheus 侧配置 tls_config检查认证配置scrape_configs: - job_name: 'secure-app' basic_auth: username: admin password: <secret> tls_config: ca_file: /etc/prometheus/certs/ca.crt insecure_skip_verify: false bearer_token: <secret>认证问题容易被忽略的细节:Bearer Token 过期后不会自动刷新,需要配合密钥管理工具轮转;insecure_skip_verify: true 虽然能解决证书校验失败,但不要在生产环境使用。查询性能慢复杂查询把 Prometheus UI 卡死,或者 Grafana 面板加载超时,这是数据量增长后的典型问题。优化查询语句的原则用标签过滤缩小范围,避免全量扫描:http_requests_total{job="api",method="GET"} 远优于 http_requests_total控制时间范围,尤其是 rate() 和 histogram_quantile() 这类聚合函数,范围越大计算量指数级增长避免 or 和 on 的复杂联合查询,拆分成多个简单查询分别执行用 Recording Rules 预计算将高频查询的中间结果提前算好,查询时直接读预计算结果:groups: - name: api_performance interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: job:http_requests:availability expr: | sum by (job) (rate(http_requests_total{code=~"2.."}[5m])) / sum by (job) (rate(http_requests_total[5m]))关键点:interval 要和查询的时间窗口匹配,太短浪费算力,太长数据延迟大。监控查询自身的性能# 查询耗时分布histogram_quantile(0.9, rate(prometheus_engine_query_duration_seconds_bucket[5m]))# 规则评估耗时topk(10, prometheus_rule_evaluation_seconds)如果发现某条规则评估耗时持续偏高,说明这条规则的表达式需要优化或者拆分。内存使用过高Prometheus 内存占用与活跃时间序列数量直接相关。一条经验值:每百万活跃序列大约需要 3-4 GB 内存。调整数据保留时间# 启动参数方式--storage.tsdb.retention.time=15d# 配置文件方式(Prometheus 2.40+)storage: tsdb: retention.time: 15d retention.size: 50GB同时设置 retention.time 和 retention.size,以先达到的阈值为准。这在磁盘空间有限的环境中尤为重要。过滤不必要的指标在数据源头过滤掉不需要的指标,比事后查询时过滤高效得多:scrape_configs: - job_name: 'app' metric_relabel_configs: - source_labels: [__name__] regex: 'go_.*' action: drop - source_labels: [__name__] regex: 'http_requests_.*' action: keepaction: drop 在前,action: keep 在后——先排除无用指标,再保留需要的,减少处理量。还可以用 labeldrop 去掉高基数标签来降低序列数。监控内存指标# 进程实际内存占用process_resident_memory_bytes# 活跃时间序列数——内存的核心驱动因素prometheus_tsdb_head_series# Head GC 耗时,频繁 GC 说明内存压力大rate(prometheus_tsdb_head_gc_duration_seconds_sum[5m])当 prometheus_tsdb_head_series 持续增长且接近历史峰值时,就要提前扩容或裁剪指标了。磁盘空间不足磁盘问题通常和内存问题相伴出现——数据量大,内存压力大,磁盘也容易满。检查数据目录大小du -sh /var/lib/prometheus/du -sh /var/lib/prometheus/chunks_head/ls -lhS /var/lib/prometheus/chunks_head/ 是写入最频繁的目录,通常也是增长最快的。如果 wal/ 目录异常大,说明 Head Compaction 没有正常执行。配置保留策略storage: tsdb: retention.time: 15d retention.size: 50GB当磁盘使用率超过 80% 时 Prometheus 会进入"节流模式",拒绝写入新数据。所以保留策略要留出安全余量。清理指定时间段的旧数据# 查看数据块信息ls -la /var/lib/prometheus/# 删除指定时间范围的数据块promtool tsdb delete-blocks /var/lib/prometheus/ --min-time=1704067200000 --max-time=1704153600000注意:delete-blocks 操作不可逆,执行前务必确认时间范围。生产环境建议先用 snapshot 备份再清理。告警不触发告警配置了却没触发,这是最危险的情况——你以为系统正常,其实只是监控系统本身出了问题。检查告警规则语法promtool check rules /etc/prometheus/rules/*.yml常见错误:for 持续时间设置过长(比如 for: 24h),导致短暂故障恢复后告警未触发;expr 中的标签选择器与实际指标标签不匹配。检查告警状态# 告警是否进入 Pending 或 Firing 状态ALERTS{alertname="your-alert"}# 告警触发次数ALERTS_FOR_STATE{alertname="your-alert"}在 Prometheus UI 的 /alerts 页面可以直接看到每条规则的状态。如果状态一直是 Inactive,说明表达式条件从未满足;如果是 Pending 但不进入 Firing,检查 for 持续时间是否过长。检查 Alertmanager 连接# 告警通知队列积压prometheus_notifications_queue_length# 告警发送失败次数rate(prometheus_notifications_errors_total[5m])# Alertmanager 连接状态prometheus_notifications_alertmanagers_discovered如果队列积压持续增长,说明 Alertmanager 处理速度跟不上,可能需要增加 Alertmanager 实例或检查下游通知渠道。Alertmanager 侧排查# 查看 Alertmanager 日志journalctl -u alertmanager -f# 检查静默规则amtool silence query --alertmanager.url=http://localhost:9093# 检查路由配置amtool config routes --alertmanager.url=http://localhost:9093容易被忽略的原因:路由配置中 match 或 match_re 写错导致告警被丢弃;静默规则覆盖了预期告警;inhibit_rules 抑制了低级别告警。TSDB 损坏TSDB 损坏通常发生在异常断电、磁盘故障或 OOM Kill 之后。诊断 TSDB 状态promtool tsdb analyze /var/lib/prometheus/输出包含:序列数、标签数、数据块数量。如果某个数据块目录为空或索引文件缺失,说明该块已损坏。修复 TSDBpromtool tsdb repair /var/lib/prometheus/repair 会尝试恢复损坏的索引和元数据文件,但不会恢复已丢失的原始数据。执行前建议先备份。备份与恢复# 创建快照(需要开启 --web.enable-admin-api)curl -XPOST http://localhost:9090/api/v1/admin/tsdb/snapshot# 备份到远程rsync -avz /var/lib/prometheus/snapshots/ /backup/prometheus/# 恢复数据——直接替换数据目录systemctl stop prometheusrm -rf /var/lib/prometheus/cp -r /backup/prometheus/latest/ /var/lib/prometheus/chown -R prometheus:prometheus /var/lib/prometheus/systemctl start prometheus预防措施# 定期检查 TSDB 完整性(加入 cron)0 3 * * * promtool tsdb analyze /var/lib/prometheus/ >> /var/log/tsdb-health.log 2>&1常见错误信息速查| 错误信息 | 原因 | 处理方式 ||---------|------|----------|| context deadline exceeded | 查询超过 query.timeout 限制 | 优化查询语句或增大超时时间 || out of order sample | 同一序列中出现了时间戳更早的样本 | 检查是否有多个实例用相同标签写入同一条序列 || duplicate series | 多个 Target 暴露了完全相同标签的序列 | 在 metric_relabel_configs 中添加唯一标识标签 || too many samples | 单次采集的样本数超过 sample_limit | 增大 sample_limit 或过滤低价值指标 || storage capacity exceeded | 磁盘空间不足 | 调整保留策略或扩容磁盘 || label name too long | 标签名超过限制 | 在 metric_relabel_configs 中重命名或丢弃过长标签 |日常运维检查清单将以下检查项纳入日常巡检,可以在问题影响业务前提前发现:监控 Prometheus 自身:用另一个 Prometheus 实例或 Thanos Sidecar 监控主实例的 up 指标和资源使用率配置变更审计:每次修改配置前用 promtool check 验证,修改后确认 Target 状态正常磁盘和内存容量规划:设置 process_resident_memory_bytes 和磁盘使用率的告警阈值在 80%告警可用性验证:定期发送测试告警(amtool alert add)确认通知链路畅通数据完整性校验:每周执行一次 promtool tsdb analyze,关注异常增长或衰减的序列数备份验证:备份不仅要做,还要定期从备份恢复到测试环境,确认数据可用
服务端阅读 05月27日 17:39

PromQL 常用函数怎么选?一文讲透用法与避坑

PromQL 是 Prometheus 的核心查询语言,掌握常用函数是写出高效监控告警规则的基础。本文按实际使用场景分类梳理 PromQL 函数,并标注每种函数的适用指标类型和常见陷阱。变化率与增量函数变化率类函数是 PromQL 中使用频率最高的函数,但也是最容易用错的一类。rate(v range-vector):计算 Counter 类型指标在时间窗口内的平均每秒增长率。适合绘制趋势图和配置告警规则,结果会被平滑处理。例如 rate(http_requests_total[5m]) 计算过去 5 分钟内 HTTP 请求的每秒平均增长率。irate(v range-vector):计算 Counter 的瞬时增长率,仅使用时间窗口内最后两个数据点。适合绘制细粒度波动图,但不适合告警(抖动太大)。例如 irate(http_requests_total[5m])。increase(v range-vector):计算 Counter 在时间窗口内的总增量,等价于 rate() * 窗口秒数。返回值是整数趋势但实际可能是小数(外推导致)。例如 increase(http_requests_total[1h]) 表示过去 1 小时新增了多少请求。delta(v range-vector):计算 Gauge 类型指标的差值,仅用于 Gauge。例如 delta(cpu_temp_celsius[1h]) 表示温度变化量。关键区别:rate 和 irate 只能用于 Counter,delta 用于 Gauge。increase 虽然概念上返回整数,但由于外推机制,结果可能非整数。如果需要精确整数,用 floor(increase(...))。时间窗口选择:窗口太短会导致数据稀疏时出现 NaN,太长会掩盖波动。一般建议窗口至少是采集间隔的 4 倍。聚合函数聚合函数用于将多个时间序列合并为更少的结果序列,是构建大盘面板的基础。sum():求和。最常用的聚合,例如 sum(rate(http_requests_total[5m])) 计算总 QPS。avg():求平均值。注意平均值容易受极端值影响,监控延迟场景更推荐用分位数。max() / min():最大值和最小值。适合找出峰值或谷值。count():计数。统计时间序列的数量,例如 count(up == 0) 统计宕机实例数。count_values():按值分组计数。例如 count_values("status", http_requests_total) 统计各状态码出现次数。topk(k, …):返回值最大的 k 个时间序列。适合找出负载最高的实例,例如 topk(5, rate(http_requests_total[5m]))。bottomk(k, …):返回值最小的 k 个时间序列。quantile(φ, …):计算分位数。例如 quantile(0.95, rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])) 估算 P95 延迟。分组聚合:使用 by 子句按标签分组,例如 sum(rate(http_requests_total[5m])) by (method) 按请求方法分组求和。使用 without 排除指定标签,sum(...) without (instance) 按 instance 维度聚合。时间窗口聚合函数(overtime 系列)这类函数对时间窗口内每个时间序列的数据点做聚合,与上面聚合函数的区别是:聚合函数跨序列合并,overtime 系列在单个序列的时间维度上聚合。avgovertime(x[5m]):5 分钟内平均值,适合平滑 Gauge 指标。maxovertime(x[5m]):5 分钟内最大值,常用于找出峰值。minovertime(x[5m]):5 分钟内最小值。sumovertime(x[5m]):5 分钟内求和。countovertime(x[5m]):5 分钟内数据点数量,可用于检测数据缺失。quantileovertime(0.9, x[5m]):5 分钟内分位数计算。stddevovertime(x[5m]):5 分钟内标准差,衡量波动程度。stdvarovertime(x[5m]):5 分钟内方差。典型用法:max_over_time(node_load15[1h]) 找出过去 1 小时最大负载。数学与取整函数abs(v):绝对值。ceil(v):向上取整,例如 ceil(cpu_usage) 将 72.3 变为 73。floor(v):向下取整。round(v, nearest):四舍五入到最近的整数或指定精度。round(3.14159, 0.01) 结果为 3.14。sqrt(v):平方根。exp(v):指数函数 e^v。ln(v) / log2(v) / log10(v):自然对数、以 2 为底和以 10 为底的对数。clamp(v, min, max):将值限制在指定范围内。clamp(cpu_percent, 0, 100) 确保百分比不超出 0-100。clampmax(v, max) / clampmin(v, min):单侧限制。预测与统计函数predict_linear(v range-vector, t):基于 2 小时(默认)的线性回归预测 t 秒后的值。常用于磁盘空间预警:predict_linear(node_filesystem_avail_bytes[1h], 3600*24) < 0 预测 24 小时后磁盘是否耗尽。deriv(v range-vector):计算 Gauge 指标的瞬时导数(变化率),适合 Gauge 趋势分析。holt_winters(v range-vector, sf, tf):基于 Holt-Winters 双指数平滑进行平滑和预测。sf 是平滑因子(0-1),tf 是趋势因子(0-1)。适合有周期性波动的指标。标签操作函数labelreplace(v, dstlabel, replacement, src_label, regex):基于正则从源标签提取值写入目标标签。例如 label_replace(up, "host", "$1", "instance", "(.*):.*") 从 instance 标签提取主机名写入 host 标签。labeljoin(v, dstlabel, separator, srclabel1, srclabel2, …):将多个源标签拼接后写入目标标签。例如 label_join(up, "endpoint", "-", "job", "instance") 将 job 和 instance 用短横线拼接。其他实用函数changes(v range-vector):计算时间窗口内值变化的次数。适合检测配置变更或重启次数:changes(process_start_time_seconds[1d]) 检测一天内重启次数。absent(v):如果传入的向量没有数据点则返回 1,有数据则返回空。常用于检测指标消失:absent(up{job="myapp"}) 在 myapp 完全无数据时触发告警。time():返回当前 Unix 时间戳。常用于相对时间计算。timestamp(v):返回向量中每个样本的时间戳。sort(v) / sort_desc(v):升序/降序排列。histogram_quantile(φ, v):从直方图桶中计算分位数,是延迟监控的核心函数。例如 histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) 计算 P99 延迟。注意 by (le) 不可省略,le 是桶边界标签。实战示例计算 QPS 并按服务分组:sum(rate(http_requests_total[5m])) by (service)计算内存使用率百分比:sum(container_memory_usage_bytes) by (container) / sum(container_spec_memory_limit_bytes) by (container) * 100计算 P95 请求延迟:histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, method))预测 24 小时后磁盘是否耗尽:predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[1h], 3600*24) < 0检测服务重启:changes(process_start_time_seconds[1d]) > 0检测指标缺失:absent(up{job="critical-service"})常见陷阱对 Gauge 用 rate:rate/irate/increase 只适用于 Counter(只增不减的累计值)。Gauge 用 delta 或 deriv。窗口过短:采集间隔 15 秒却用 [30s] 窗口,容易得到 NaN。窗口建议至少 4 倍采集间隔。increase 结果非整数:由于外推机制,increase 可能返回小数。需要精确整数时加 floor()。histogram_quantile 忘记 by(le):le 标签是分位数计算必需的,省略会导致结果错误。topk 不稳定:topk 结果随数据波动变化大,不适合直接用于告警。absent 的触发条件:absent 返回 1 表示无数据,告警规则应写 absent(...) == 1 而非 absent(...) > 0。