OBSERVABILITY / 从一次 HTTP 请求开始

Grafana & Prometheus,别只会“把容器跑起来”。

沿着 Python Hello World 的数据流,理解应用如何暴露指标、Prometheus 为什么主动拉取、PromQL 如何把时间序列变成答案,以及 Grafana 到底负责哪一层。

4 张核心图3 个容器 · 自动 ProvisioningCounter / Gauge / Histogram可离线打开
01 / ARCHITECTURE

三套职责,两个方向的数据流。

业务请求进入 Python;Prometheus 不等待应用“上报”,而是周期性访问 /metrics;Grafana 再向 Prometheus 发 PromQL 查询。Grafana 不抓指标,Python 也不直接把指标推给 Grafana。

本地实验室架构用户请求 Python 服务,Prometheus 抓取指标并存储,Grafana 查询 Prometheus,浏览器查看三个服务。Docker Compose network · 容器用服务名互相发现 Browser / curl发业务请求 · 看 UIlocalhost: 8000 / 9090 / 3000 Python Hello业务端点 + /metricsCounter · Gauge · Histogram Prometheus抓取 · 存储 · PromQLTSDB volume · 7 天保留 Grafana仪表盘 · 可视化查询 Prometheus,不存原始指标 /metrics 文本快照当前累计值,不是历史数据库HTTP 请求GET /metricsPromQL关键边界:应用负责“测量”;Prometheus 负责“历史”;Grafana 负责“表达”。

Python Client

在请求路径上更新指标,并把当前值编码为 Prometheus exposition format。

Prometheus

按 scrape interval 拉取样本,追加时间戳,保存为带标签的时间序列。

Grafana

通过数据源执行 PromQL,把结果画成时间线、统计值和告警信号。

02 / SCRAPE LOOP

一次抓取,发生了什么?

配置里的 target 是容器网络地址 hello:8000,不是宿主机的 localhost:8000。每 5 秒,Prometheus 读取一次瞬时快照并写入自己的 TSDB。

① Scheduler每 5 秒触发② HTTP GEThello:8000/metrics③ Parse名称 · 标签 · 数值④ Append附加抓取时间戳⑤ TSDB时间序列历史⑥ QueryPromQL 计算窗口抓取成功同时产生 up{job="python-hello"}=1;连接或解析失败时为 0。
03 / METRIC MODEL

指标 = 名称 + 标签集合 + 样本值 + 时间戳。

标签让一项指标切成多条时间序列,但每个新标签值组合都会增加 cardinality。路径模板、状态码适合作标签;用户 ID、请求 ID 通常不适合。

hello_http_requests_total{method="GET", path="/slow", status="200"} 30指标名         标签集合             样本值唯一时间序列同一名称 + 完全相同标签集合 = 一条序列;每次抓取追加一个点 Counter只增或重置请求总数 → rate()Gauge可增可减处理中请求数Histogram落入累计 buckets延迟 → quantile()Cardinality标签组合数量避免无界标签本实验不把 /health 和 /metrics 算入业务请求指标,避免探针流量污染业务视图。

Counter 看变化率

累计值随进程重启归零。通常用 rate(counter[窗口]) 看每秒增长,而非只看总数。

Histogram 看分布

_bucket、_sum、_count 共同描述分布,可聚合后计算分位数。

标签不是日志字段

user_id 等高基数字段会制造海量序列。逐请求细节应进入日志或 trace。

04 / PROMQL

PromQL 对“序列集合”做筛选、变换与聚合。

1. Selector · 选择

hello_http_requests_total{
  status=~"5.."
}

保留指标名匹配且 status 为 5xx 的序列。

2. Range + rate · 变换

rate(
  hello_http_requests_total[1m]
)

取最近一分钟样本,估算 Counter 平均每秒增长率,并处理重置。

3. Aggregation · 聚合

sum by (path, status) (
  rate(hello_http_requests_total[1m])
)

合并其他维度,只按 path 与 status 保留结果。

4. Quantile · 分位数

histogram_quantile(0.95,
  sum by (le, path) (
    rate(hello_http_request_duration_seconds_bucket[5m])
  )
)

先聚合 bucket 速率,再估算每条路径的 P95。

05 / HANDS-ON

按这个顺序动手,不要直接盯仪表盘。

① 看原始暴露

make up 后访问 8000/metrics,找到 HELP、TYPE 和三类 hello 指标。

② 确认抓取

打开 Prometheus Targets 或执行 make targets,确认 python-hello 为 up。

③ 制造信号

执行 make traffic,明确知道成功、慢请求和 500 各产生多少次。

④ 逐层写 PromQL

先 selector,再 rate,再 sum by;观察每一步的标签和结果类型。

⑤ 看 Grafana

打开预置 Dashboard,确认面板与刚才的 PromQL 对应,而非把它当黑盒。

⑥ 故障实验

停止 hello 容器,观察 up 变 0;恢复后区分“无流量”和“不可抓取”。

为什么 Grafana 数据源是 http://prometheus:9090?

数据源请求从 Grafana 容器发出;容器里的 localhost 指 Grafana 自己。Compose DNS 会把服务名 prometheus 解析到 Prometheus 容器。

为什么访问 /metrics 看不到历史曲线?

它只是应用此刻的指标快照。Prometheus 周期性保存快照才形成历史;Grafana 查询历史后再绘图。

为什么错误率的分母用了 clamp_min?

没有流量时分母可能为 0。设置很小的正下限可避免除零;真实告警还应结合最小流量门槛。

入口只有一个:进入 prome/ 执行 make up。实验结束用 make down 保留数据;想完全重置才用 make clean。