Skip to content

Prometheus + Grafana 面试题集(10 题) ​

生成日期:2026-06-06 21:00 难度参考:⭐ 基础 · ⭐⭐ 进阶 · ⭐⭐⭐ 深入


题目 1:Prometheus 整体架构 ​

难度:⭐⭐

题目:简述 Prometheus 的整体架构,说明 Server、Exporter、Pushgateway、Alertmanager 四个核心组件各自的作用以及数据采集的基本流程。

标准答案:

Prometheus 采用拉(Pull)模型为主、推(Push)为辅的架构设计:

  • Prometheus Server:核心组件,负责时序数据的采集、存储、查询和告警评估。包含三个子模块:Retrieval(采集管理器)、TSDB(时序数据库)、HTTP API(查询接口)。定期从配置的目标(Targets)拉取指标数据。
  • Exporter:适配器组件,负责采集第三方系统或应用的指标并暴露为 Prometheus 可抓取的 HTTP /metrics 端点。常见的有 node_exporter(主机)、blackbox_exporter(网络探测)、mysqld_exporter 等。
  • Pushgateway:用于临时性/批处理任务的指标推送入口。适合短生命周期 Job(如 CI/CD 构建任务)——任务结束时推送指标到 Pushgateway,再由 Prometheus 拉取。
  • Alertmanager:告警管理组件,接收 Prometheus Server 发出的告警,负责告警的去重、分组、抑制、静默,并路由到不同的接收器(Email、Slack、PagerDuty、Webhook 等)。

数据采集流程:

  1. Prometheus Server 通过配置文件或 Service Discovery 发现采集目标
  2. Server 按 scrape_interval 定时向目标的 /metrics HTTP 端点发起 GET 请求
  3. 目标返回包含指标名、标签、时间戳和样本值的文本数据(Prometheus Exposition Format)
  4. Server 解析并存储到本地 TSDB

延伸思考:Pull 模型相比 Push 模型的优势是什么?什么场景下必须使用 Pushgateway 而不是 Pull?Pushgateway 可能带来的单点故障和指标陈旧风险如何解决?


题目 2:Prometheus TSDB 存储引擎 ​

难度:⭐⭐⭐

题目:Prometheus 的 TSDB(时序数据库)底层是如何设计的?请解释 V2 存储引擎的核心数据结构(包括 Block、Chunk、Index、WAL)以及数据写入和查询的基本流程。

标准答案:

Prometheus V2 TSDB(自 2.0 起)采用自定义的块(Block)存储结构,核心思想是拆分-合并、按时间分区:

核心数据结构 ​

  • Block:每个 Block 默认存储 2 小时的时序数据,包含三个核心文件:chunks/(存储实际的样本数据)、index/(倒排索引,支持按标签快速查找 Series)、meta.json(元数据,含最小最大时间戳)。Block 是不可变的(immutable),一旦写入完成即为只读。
  • Chunk:Block 内部将样本压缩成 Chunk(默认每个 Chunk ~512 个样本)。使用常见的压缩算法:Gorilla(Facebook 的浮点数压缩算法,每点从 16 字节压缩到 ~1.37 字节)+ XOR + Delta-of-Delta + Zigzag。大幅减少磁盘 I/O 和存储成本。
  • WAL(Write-Ahead Log):Head Block(当前活跃区块)的数据先写入 WAL 确保崩溃恢复能力,每 --storage.tsdb.wal-segment-size 切分一个 Segment。写入 Head 的数据定期刷新到内存中的 Chunk。
  • Index:倒排索引结构,支持 {__name__="http_requests_total", method="GET", ...} 这类带标签过滤的高效查找。使用 Postings(符号表)+ 跳跃表(跳跃表缩短查找路径)。

写入流程 ​

  1. 数据到达 → 写入 WAL(顺序追加,高性能)
  2. 同步写入内存中的 Head Chunk
  3. Head 累积到 2 小时 → 冻结为只读 Block 并持久化到磁盘
  4. 后台 Compactor 将多个小 Block 合并成大 Block(可配置的保留时间:默认 --storage.tsdb.retention.time=15d)
  5. 过期的 Block 被删除

查询流程 ​

  1. 查询时间范围确定 → 找出覆盖该时间范围的 Block 列表
  2. 每个 Block 内用倒排索引定位匹配的 Series
  3. 从 Chunk 中解压样本数据
  4. 跨 Block 的查询结果合并(Merge)

延伸思考:为什么 Block 设计成 2 小时而非更大/更小?Compaction 的层级策略是什么?哪些场景下会触发大量的 Chunk 分裂与合并,导致性能抖动?


题目 3:PromQL 核心语法 ​

难度:⭐⭐

题目:请解释以下 PromQL 查询的含义:rate(http_requests_total{status="5xx"}[5m]) > 0.1。另外,分别说明 Counter、Gauge、Histogram、Summary 四种指标类型的特点及及适用场景。

标准答案:

查询解析 ​

promql
rate(http_requests_total{status="5xx"}[5m]) > 0.1
  • http_requests_total:Counter 类型指标,表示 HTTP 请求总数的累计值
  • {status="5xx"}:标签选择器,过滤出状态码为 5xx 的序列
  • [5m]:范围选择器,指定取最近 5 分钟的数据
  • rate(...):计算每秒平均增长率(Counter 专用函数,自动处理 Counter 重置问题)
  • > 0.1:过滤出超过 0.1 QPS 的结果

整体含义:过去 5 分钟内,5xx 错误请求的每秒速率超过 0.1 的 Series。

四种指标类型 ​

类型特点典型值适用场景
Counter只增不减(重启时重置为 0)http_requests_total 1024请求数、错误数、任务完成数
Gauge可增可减,反映当前值memory_usage_bytes 8.1e+09CPU 使用率、内存、温度、连接数
Histogram分桶计数,可计算分位数_bucket{le="0.5"} 10请求延迟(P50/P99)、响应大小
Summary客户端预计算分位数_quantile{quantile="0.99"} 0.3已知分位数需求的延迟追踪

延伸思考:为什么处理 Counter 时推荐用 rate() 而非 irate()?Histogram 计算 P99 延迟时,桶边界设置不合理会导致什么偏差?Summary 的 Quantile 有什么局限性(不可聚合)?


题目 4:Exporter 开发 ​

难度:⭐⭐

题目:现需要为内部的自研中间件监控编写一个 Prometheus Exporter,请简述开发一个自定义 Exporter 的标准步骤,包括 Prometheus 客户端库的使用方式、Metric 注册和暴露的基本代码骨架(以 Go 或 Python 为例)。

标准答案:

标准步骤 ​

  1. 选择客户端库:官方推荐 Go(prometheus/client_golang),也支持 Python(prometheus_client)、Java、Rust 等
  2. 定义自定义指标:选择指标类型(Counter/Gauge/Histogram/Summary)+ 标签设计
  3. 实现 Collector:实现 Describe(告知 Prometheus 有哪些指标)和 Collect(实际采集逻辑)方法
  4. 注册到 DefaultRegisterer:通过 prometheus.MustRegister() 注册
  5. 暴露 HTTP 端点:在 /metrics 路径上注册 promhttp.Handler()
  6. 集成指标采集:在业务代码的关键位置更新指标值

Go 代码骨架 ​

go
package main

import (
    "net/http"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    // 定义指标:myapp_processed_ops_total(带 labels)
    processedOps = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "myapp_processed_ops_total",
            Help: "Total number of processed operations.",
        },
        []string{"status", "type"},
    )

    currentReq = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Name: "myapp_requests_in_flight",
            Help: "Current number of in-flight requests.",
        },
    )
)

func init() {
    prometheus.MustRegister(processedOps, currentReq)
}

func main() {
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8080", nil)
}

Python 代码骨架 ​

python
from prometheus_client import start_http_server, Counter, Gauge, Summary
import time

# 定义指标
REQUESTS = Counter('myapp_requests_total', 'Total requests', ['method'])
IN_FLIGHT = Gauge('myapp_requests_in_flight', 'Requests in flight')
REQUEST_TIME = Summary('myapp_request_duration_seconds', 'Request latency')

@REQUEST_TIME.time()
def process_request(method):
    IN_FLIGHT.inc()
    time.sleep(0.1)
    REQUESTS.labels(method=method).inc()
    IN_FLIGHT.dec()

if __name__ == '__main__':
    start_http_server(8000)
    while True:
        process_request('GET')
        time.sleep(1)

延伸思考:Exporter 应该使用 Gauge 还是 Counter 来报告 MongoDB 的当前连接数?为什么生产环境中不推荐在 Collect() 方法里做重型网络调用?Exporter 性能优化(如缓存采集结果)的最佳实践有哪些?


题目 5:Service Discovery 机制 ​

难度:⭐⭐

题目:在 Kubernetes 环境下,Prometheus 如何自动发现需要监控的目标?请详述 Kubernetes SD 的工作机制以及 relabel_configs 在过滤和目标修改中的作用。

标准答案:

Kubernetes SD 工作机制 ​

  1. 自动发现:Prometheus 通过 Kubernetes API 监听资源变化(Pods、Services、Endpoints、Nodes 等)。kubernetes_sd_configs 基于角色(role)配置发现范围:

    • role: pod → 发现所有 Pod,基于 Pod IP 和注解(annotations)生成抓取目标
    • role: endpoints → 发现 Endpoint/Service
    • role: node → 发现集群 Node
    • role: service → 发现 Service
    • role: ingress → 发现 Ingress
  2. 注解约定:Pod 上加 prometheus.io/scrape: "true"、prometheus.io/port: "9090"、prometheus.io/path: "/metrics",Prometheus 据此生成抓取目标。

relabel_configs ​

relabel_configs 在抓取流程的目标发现之后、实际抓取之前执行,用于:

  • 过滤目标:action: keep / action: drop,只保留/丢弃匹配特定标签的 Target
  • 修改标签:action: replace / action: hashmod / action: labelmap
  • 创建新标签:基于已有元数据标签(__meta_kubernetes_*)提取信息,创建用户可见的 __address__、instance、job 等

核心机制:Prometheus 根据 SD 返回的目标列表,对每个 Target 的标签经过 relabel_configs 处理后,移除所有 __ 前缀的内部标签,最终得到用户可见的标签集。

示例:只保留带 scrape=true 注解的 Pod ​

yaml
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: (.+)

延伸思考:大量 Pod 频繁滚动更新时,SD 频繁更新 Target 对 Prometheus 性能有什么影响?如何通过 scrape_interval 和 relabel_configs 的 keep 策略来降低 Prometheus Server 负载?Consul SD 和 Kubernetes SD 各有什么优缺点?


题目 6:告警规则与告警路由 ​

难度:⭐⭐⭐

题目:Prometheus 的告警从产生到发送的全链路是怎样的?请分别解释告警规则(Alerting Rules)中 for 参数的设计意图,以及 Alertmanager 中的路由(Route)和接收器(Receiver)的匹配逻辑。

标准答案:

告警全链路 ​

Prometheus(规则评估)→ Alertmanager(分组/抑制/静默)→ 接收器
                          ↑
                    告警推送(HTTP POST /api/v2/alerts)
  1. 规则评估:Prometheus Server 定期评估 rules 中的告警表达式
  2. Pending → Firing:表达式匹配的持续时间 >= for 参数,告警从 Pending 转为 Firing
  3. 推送:Firing 告警通过 HTTP POST 推送到配置的 Alertmanager 实例
  4. 路由匹配:Alertmanager 基于 route 树匹配告警标签
  5. 分组/抑制/静默:Alertmanager 对告警进行分组去重、抑制(如果更高优先级的告警已触发则抑制低优先级的)、静默(根据静默规则跳过通知)
  6. 通知分发:路由到对应的 receiver(Email、Slack、PagerDuty 等)

for 参数的设计意图 ​

yaml
groups:
  - name: node_rules
    rules:
      - alert: HighCPUUsage
        expr: 100 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100 > 80
        for: 5m   # 关键参数
  • for: 5m:触发条件必须持续满足 5 分钟才转为 Firing
  • 设计目的:过滤瞬态抖动,避免 PagerDuty 等方式被短时尖峰触发告警风暴。例如:CPU 飙升 1 秒即恢复,不应触发告警通知
  • 持续时间不到 for 的告警停留在 Pending 状态,不推送到 Alertmanager

Route 与 Receiver ​

Alertmanager 的 route 是树形结构,告警从上到下根据标签匹配选择第一条匹配的路由:

yaml
route:
  receiver: 'default'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      routes:
        - match:
            team: billing
          receiver: 'pagerduty-billing'
    - match_re:
        severity: warning
      receiver: 'slack-warning'
  • 分组键:group_by: ['alertname', 'cluster'],相同分组键的告警合并为一条通知
  • group_wait:组内第一个告警到发送通知前的等待时间(收集更多组内告警)
  • group_interval:同一组告警的发送间隔
  • repeat_interval:告警持续未恢复时,重复发送的最小间隔

延伸思考:for 如果设得太长(如 30 分钟)会带来什么风险?如何设计 Alertmanager 的 group_by 策略才能在不遗漏重要告警的同时减少告警疲劳?PagerDuty 和 Slack 两种接收器在告警生命周期中的定位差异是什么?


题目 7:Alertmanager 静默、抑制与分组 ​

难度:⭐⭐

题目:Alertmanager 的静默(Silence)、抑制(Inhibition)、分组(Grouping)三种机制分别解决什么场景问题?请用具体例子说明。

标准答案:

分组(Grouping)——减少通知数量 ​

  • 机制:将相同分组键(如 alertname, cluster)的告警合并到一条通知中。
  • 解决的问题:大规模服务故障时大量类似告警涌入,导致接收方被通知轰炸。例如某集群 50 个节点同时宕机,如果每个节点发一条通知等同 DoS 式报警。
  • 例子:group_by: ['alertname', 'cluster'],集群 50 个节点同时宕机 → 只发一条通知 "50 个实例离线",附集群标签。

抑制(Inhibition)——避免冗余告警的级联 ​

  • 机制:如果更高优先级的告警已触发,则抑制依赖该触发条件的低优先级告警。
  • 解决的问题:基础设施故障引发级联告警。例如主机宕机 → 该主机上所有服务不可达 → 同时弹出 "主机宕机"+"Nginx 不可达"+"PHP 进程挂了"等多条告警。
  • 例子:
    yaml
    inhibit_rules:
      - source_match:        # 源告警:severity=critical
          severity: critical
        target_match:        # 目标告警:severity=warning
          severity: warning
        equal: ['instance'] # 当 instance 相同时,抑制
    主机宕机(critical)→ 抑制该主机的所有 warning 级告警。

静默(Silence)——主动忽略已知事件 ​

  • 机制:基于标签匹配器在指定时间范围内静默告警,不发送通知。本质上是一个管理员手动创建或 API 创建的一条规则(类似"免打扰")。
  • 解决的问题:计划内维护窗口,已知不影响业务的告警无需通知。
  • 例子:阿里云 ECS 弹性扩缩容期间,创建静默规则 `alertname="NodeDown", instance=~".spot-instance.",持续 2 小时,避免运维人员反复收到 Spot 实例被回收的错误告警。

延伸思考:抑制(Inhibition)配置不当可能导致什么问题?静默过期后忘记清理会带来什么隐患?三种机制的优先级关系是怎样的(分组→抑制→静默)?


题目 8:Grafana 面板设计与变量模板 ​

难度:⭐⭐

题目:在 Grafana 中,如何使用 Dashboard Variables(变量)实现多环境、多实例的动态面板?请说明 Query 类型变量、自定义变量和 Interval 变量的定义方式,以及变量在 PromQL 查询中的引用语法。

标准答案:

Dashboard Variables 概述 ​

Grafana 变量允许用户通过下拉菜单或输入框动态改变面板的查询范围和内容,避免为每个环境、每台机器创建重复面板。

常见变量类型 ​

1. Query 类型变量(最常用) ​

从数据源查询返回的值作为选项列表:

Name: env
Type: Query
Data Source: Prometheus
Query: label_values(env)   # 返回所有 env 标签值
Name: instance
Type: Query
Query: label_values(up{env="$env"}, instance)  # 依赖上级变量

效果:选择 env=prod → instance 下拉只显示 prod 环境的实例。

2. 自定义变量 ​

手动枚举值列表,适合固定值集:

Name: severity
Type: Custom
Values: critical,warning,info

3. Interval 变量(时间间隔) ​

Name: interval
Type: Interval
Values: 1m,5m,15m,1h

在 PromQL 中使用变量 ​

promql
# 使用 $instance 变量
rate(http_requests_total{instance="$instance", env="$env"}[$interval])

# 使用自定义变量
max by(instance) (node_memory_MemAvailable_bytes{env="$env"}) / 1024 / 1024

设计最佳实践 ​

  • 变量依赖链:env → region → instance → metric,层级关联
  • 标签化命名:变量名与 Prometheus 标签名对齐(如 instance、env、job)
  • 默认值:all 值设为 .* 配合 $__all 正则
  • 隐藏变量:不需要用户选择的中间变量设为 Hidden

延伸思考:Grafana 中 $__rate_interval 与 $__interval 的区别是什么?如何利用 Repeat(重复面板)功能配合变量实现实时扩缩容的实例监控面板?模板变量加载慢时如何优化(缓存、限制查询结果数量)?


题目 9:监控体系设计(RED/USE 方法) ​

难度:⭐⭐⭐

题目:请解释监控领域的 RED 方法和 USE 方法,说明它们各自适用的场景(服务 vs 资源),并分别给出 PromQL 查询示例。

标准答案:

USE 方法 —— 面向资源(Resource) ​

由 Brendan Gregg(《性能之巅》作者)提出,适用于基础设施资源(CPU、内存、磁盘、网络)。

  • Utilization(利用率):资源处于繁忙状态的时间百分比
  • Saturation(饱和度):资源的额外排队或等待程度
  • Errors(错误):资源层面的错误计数

典型 PromQL(以 node_exporter 为例):

promql
# Utilization - CPU 使用率
100 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100

# Saturation - CPU 平均负载 (Load Average)
node_load1{instance="web-01"}

# Errors - 网卡错误包
rate(node_network_errors_total{device="eth0"}[5m])

RED 方法 —— 面向服务(Service) ​

由 Tom Wilkie(WeaveWorks/Grafana Labs)提出,适用于应用和服务,源自 Google SRE 的四大黄金信号演进。

  • Rate(速率):每秒请求数
  • Errors(错误):失败请求的比例
  • Duration(持续时间):请求延迟分布

典型 PromQL(以自定义应用指标为例):

promql
# Rate - 请求速率 QPS
sum by(service) (rate(http_requests_total[5m]))

# Errors - 错误率
sum by(service) (rate(http_requests_total{status=~"5.."}[5m])))
  / on(service) sum by(service) (rate(http_requests_total[5m])) * 100

# Duration - P99 延迟
histogram_quantile(0.99, sum by(service, le)
  (rate(http_request_duration_seconds_bucket[5m])))

方法总结 ​

方法适用对象核心维度来源
USE资源层(CPU、内存、磁盘、网络)利用率/饱和度/错误Brendan Gregg
RED服务层(API、微服务、中间件)速率/错误/延迟Tom Wilkie

在生实践中,USE + RED 组合使用:下层资源用 USE,上层服务用 RED,形成端到端的可观测性体系。

延伸思考:除了 USE 和 RED,Google SRE 提出的四大黄金信号(延迟/流量/错误/饱和度)与 RED 的区别在哪里?在微服务架构中,如何利用 Exemplar 技术将 RED 指标与链路追踪 Trace ID 关联起来?


题目 10:可观测性三大支柱及 Grafana 生态 ​

难度:⭐⭐

题目:什么是可观测性的三大支柱(Metrics / Logs / Traces)?它们各自解决了什么问题?Grafana 生态如何将这三大支柱统一起来?请说明 Loki、Tempo、Mimir 三个项目各自定位以及 Prometheus 在其中的角色。

标准答案:

可观测性三大支柱 ​

支柱描述解决的问题代表工具
Metrics(指标)数值化、时序化的聚合数据(Counter / Gauge / Histogram)"什么正在发生、趋势如何"——资源利用率、错误率、QPS 等统计量Prometheus + Grafana Mimir
Logs(日志)离散的、事件化的文本记录(每行一条日志,含时间戳)"精确发生了什么"——错误的完整上下文、堆栈跟踪、用户请求详情Grafana Loki
Traces(链路追踪)请求在分布式系统中的传播路径(Span + Trace ID)"请求从哪里到哪里、耗时在哪里"——微服务间调用延迟瓶颈Grafana Tempo

Grafana 生态的统一方案 ​

Grafana Labs 构建了 Grafana + LGTM 栈,统一三大支柱的查询入口:

  • Grafana:统一可视化层。所有数据源在同一个 Dashboard 中混合展示,支持 $__rate_interval 跨数据源联合查询。
  • Grafana Mimir(对标 Thanos + Cortex):Prometheus 兼容的超大规模时序存储引擎,提供水平扩展、长期存储、全局视图等功能。原生兼容 PromQL。
  • Grafana Loki:日志聚合系统。轻量级(不建全文索引)——只索引元数据标签,日志内容用 Golang 正则引擎。支持 LogQL 查询,与 PromQL 语法风格一致。
  • Grafana Tempo:分布式链路追踪存储后端。与 Jaeger/Zipkin/Otlp 兼容,无需采样策略即可全量采集,与 Prometheus Exemplar 配合实现"指标→Trace"的无缝跳转。

Prometheus 的角色 ​

Prometheus 在 Grafana 生态中承担核心指标采集与本地存储的角色(short-term metrics),它依然是标准的 Pull 模型指标源。当需要长期存储或跨集群全局视图时,用 Mimir 替代或作为上游存储。Grafana 则作为统一的可视化前台,同时连接 Prometheus、Loki、Tempo。

关联架构示例 ​

             Prometheus(采集 + 告警 + 本地 TSDB)
                 │
         ┌───────┼───────────┐
         ▼       ▼           ▼
     Grafana  Mimir(长期)  Tempo(链路)
         │                   │
         ▼                   ▼
        Loki(日志)    Application(Exemplar注入Trace ID)

延伸思考:Grafana 从 v8.0 起引入的 Explore 模式如何帮助运维人员从指标(Metrics)跳转到相关日志(Logs)再跳转到链路(Traces)?在大规模集群中,如何平衡三者的存储成本(Loki 的"日志即索引"策略 vs Elasticsearch 的全文索引策略)?OpenTelemetry 在其中扮演了什么角色?


附录:延伸学习推荐

  • 官方文档:prometheus.io/docs、grafana.com/docs
  • 书籍:《Prometheus: Up & Running》、《SRE: Google 运维解密》、《性能之巅》
  • 实践项目:kube-prometheus-stack(Helm Chart)、Grafana LGTM 栈本地部署