03 - 架构与原理
03 - 架构与原理
3.1 整体架构
Prometheus 的架构设计遵循简洁、可靠的原则。整个系统由多个组件协同工作:
┌──────────────────────────────────────────────────┐
│ Service Discovery │
│ (Kubernetes, Consul, DNS, ...) │
└──────────┬───────────────────────────────────────┘
│ 发现目标列表
▼
┌────────────┐ ┌────────────────────────────┐ ┌─────────────────┐
│ Target A │◄───│ │───►│ Pushgateway │
│ (App) │ │ │ │ (短期任务推送) │
└────────────┘ │ │ └─────────────────┘
│ Prometheus Server │
┌────────────┐ │ │ ┌─────────────────┐
│ Target B │◄───│ ┌──────────┐ ┌────────┐ │ │ Alertmanager │
│ (Node Exp) │ │ │ Retriever│ │ TSDB │ │───►│ (告警管理) │
└────────────┘ │ └──────────┘ └────────┘ │ └─────────────────┘
│ ┌──────────┐ ┌────────┐ │
┌────────────┐ │ │ Rules │ │ HTTP │ │ ┌─────────────────┐
│ Target C │◄───│ │ Engine │ │ API │ │───►│ Grafana │
│ (Exporter) │ │ └──────────┘ └────────┘ │ │ (可视化) │
└────────────┘ └────────────────────────────┘ └─────────────────┘
核心组件
| 组件 | 功能 | 说明 |
|---|---|---|
| Prometheus Server | 核心服务 | 抓取、存储、查询 |
| Retriever/Scrape | 数据抓取 | Pull 指标数据 |
| TSDB | 时序数据库 | 本地高效存储 |
| Rules Engine | 规则引擎 | 告警规则 + 录制规则 |
| HTTP API | 查询接口 | PromQL 查询 + 元数据 |
| Service Discovery | 服务发现 | 动态发现监控目标 |
| Alertmanager | 告警管理 | 去重、分组、路由、通知 |
| Pushgateway | 推送网关 | 短期任务推送指标 |
| Exporter | 指标导出器 | 将第三方指标转换为 Prometheus 格式 |
3.2 抓取模型(Scrape Model)
抓取流程
时间轴 ─────────────────────────────────────────────────────►
Prometheus Target /metrics
────────── ──────────────
│ T=0s ──GET /metrics──► │
│ ◄── 200 OK + metrics ── │
│ 解析 + 存储 │
│ │
│ T=15s ──GET /metrics──► │
│ ◄── 200 OK + metrics ── │
│ 解析 + 存储 │
│ │
│ T=30s ──GET /metrics──► │
│ ◄── 500 Error ───────── │
│ 标记为不健康 │
抓取指标格式
Prometheus 支持两种文本格式:Text Format 和 OpenMetrics。
# 查看目标的原始指标
curl -H "Accept: application/openmetrics-text" http://localhost:9090/metrics
Text Format 示例:
# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 123456.78
node_cpu_seconds_total{cpu="0",mode="system"} 789.01
# HELP node_memory_MemTotal_bytes Memory information field MemTotal_bytes.
# TYPE node_memory_MemTotal_bytes gauge
node_memory_MemTotal_bytes 1.6777216e+10
格式规范:
| 行类型 | 格式 | 示例 |
|---|---|---|
| 注释 | # 文本 | # HELP ... / # TYPE ... |
| HELP | # HELP <name> <description> | 指标说明 |
| TYPE | # TYPE <name> <type> | 指标类型 |
| 样本 | <name>{<labels>} <value> [timestamp] | 数据点 |
服务健康状态
Prometheus 通过抓取结果判断目标健康状态:
| 指标 | 值 | 含义 |
|---|---|---|
up{job="xxx"} | 1 | 目标健康 |
up{job="xxx"} | 0 | 目标不可达 |
3.3 TSDB 存储引擎
Prometheus 内置的 TSDB(Time Series Database)是其核心存储组件,经历了从 v1 到 v2 的重大重构。
架构演进
| 版本 | 引擎 | 特点 |
|---|---|---|
| Prometheus 1.x | 自研 LevelDB | 简单但性能有限 |
| Prometheus 2.0+ | 全新 TSDB | 基于 V3 论文,性能大幅提升 |
存储架构
/var/lib/prometheus/
├── chunks_head/ # 活跃数据块(内存映射)
│ ├── 000001
│ └── 000002
├── wal/ # Write-Ahead Log(预写日志)
│ ├── checkpoint.00000001/
│ └── 00000002
├── chunks_index/ # 索引文件
├── meta.json # 块元数据
└── lock # 进程锁
数据块(Block)结构
┌─────────────────────────────────────┐
│ Block │
├─────────────────────────────────────┤
│ meta.json │
│ ├── minTime / maxTime │
│ ├── stats (series/samples count) │
│ └── compaction level │
├─────────────────────────────────────┤
│ index │
│ ├── 倒排索引(label → series) │
│ ├── 正排索引(series → chunks) │
│ └── postings(标签值 → series ID) │
├─────────────────────────────────────┤
│ chunks/ │
│ ├── 000001 │
│ ├── 000002 │
│ └── ... │
│ ├── 每个 chunk 包含压缩的时间序列 │
│ └── 使用 XOR 编码 + Snappy 压缩 │
└─────────────────────────────────────┘
写入流程
应用指标 ──► WAL(预写日志) ──► 内存 Head Block ──► 定时压缩 ──► 磁盘 Block
│ │
│ 保证数据不丢失 │ 内存中快速写入
▼ ▼
crash recovery 2小时后冻结
关键机制:
- WAL(Write-Ahead Log):先写日志再写内存,保证崩溃恢复
- Head Block:最近 2 小时的活跃数据块,驻留内存
- Compaction:定期将小块合并为大块,提升查询性能
压缩策略
| 压缩级别 | 块时间跨度 | 说明 |
|---|---|---|
| Level 0 | 2 小时 | 初始块(从 Head flush) |
| Level 1 | 10 小时 | 5 个 Level 0 合并 |
| Level 2 | 2.5 天 | 5 个 Level 1 合并 |
| Level 3 | 12.5 天 | 5 个 Level 2 合并 |
| Level 4 | 62.5 天 | 最高级别 |
编码压缩
原始数据点: (timestamp=1000, value=100.0)
下一个点: (timestamp=1001, value=100.5)
时间戳压缩: Delta-of-Delta
t1 - t0 = 1, t2 - t1 = 1, delta-of-delta = 0 → 只存 0
值压缩: XOR 编码
100.0 和 100.5 的 XOR 差异很小 → 只存变化位
3.4 服务发现(Service Discovery)
Prometheus 的核心优势之一是与服务发现机制的深度集成,无需手动维护监控目标列表。
服务发现类型
| 类型 | 适用场景 | 配置关键字 |
|---|---|---|
| 静态配置 | 固定服务器 | static_configs |
| Kubernetes | K8s 集群 | kubernetes_sd_configs |
| Consul | 微服务注册中心 | consul_sd_configs |
| DNS | 域名解析 | dns_sd_configs |
| 文件 | 自定义发现 | file_sd_configs |
| EC2 | AWS 云主机 | ec2_sd_configs |
| GCE | Google Cloud | gce_sd_configs |
| Azure | Azure VM | azure_sd_configs |
服务发现工作流
1. Prometheus 读取配置
2. 调用 SD API 获取目标列表
3. 附加元标签(__meta_*)
4. Relabel 处理标签转换
5. 生成最终抓取目标列表
6. 按照 scrape_interval 定时抓取
文件发现示例
# prometheus.yml
scrape_configs:
- job_name: 'file-sd'
file_sd_configs:
- files:
- /etc/prometheus/targets/*.json
- /etc/prometheus/targets/*.yml
refresh_interval: 5m
// /etc/prometheus/targets/webservers.json
[
{
"targets": ["web1:8080", "web2:8080"],
"labels": {
"env": "production",
"team": "backend"
}
}
]
3.5 Relabel 机制
Relabel(重新标记)是 Prometheus 最强大的功能之一,允许在抓取前动态修改标签。
Relabel 流程
原始标签 ──► relabel_configs ──► 最终标签
│
├── 保留/删除标签
├── 修改标签值
├── 基于正则过滤目标
└── 添加新标签
常用 Relabel 操作
# 1. 从地址中提取端口号作为新标签
relabel_configs:
- source_labels: [__address__]
regex: '(.+):(\d+)'
target_label: host
replacement: '${1}'
- source_labels: [__address__]
regex: '(.+):(\d+)'
target_label: port
replacement: '${2}'
# 2. 只保留特定标签值的目标
relabel_configs:
- source_labels: [__meta_kubernetes_namespace]
action: keep
regex: 'production'
# 3. 丢弃特定目标
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: 'true'
# 4. 修改指标路径
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
标签操作类型
| action | 说明 |
|---|---|
replace | 替换标签值(默认) |
keep | 保留匹配的目标 |
drop | 丢弃匹配的目标` |
labelmap | 映射所有匹配的标签 |
labeldrop | 删除匹配的标签 |
labelkeep | 只保留匹配的标签 |
hashmod | 基于哈希值分片 |
元标签
元标签以 __ 开头,在抓取完成后会被自动删除(除非显式保留):
| 元标签 | 说明 |
|---|---|
__address__ | 目标地址 |
__scheme__ | 抓取协议 |
__metrics_path__ | 指标路径 |
__param_<name> | URL 查询参数 |
__meta_* | 服务发现产生的元标签 |
3.6 查询引擎
PromQL 查询在内部经历以下处理阶段:
PromQL 查询 ──► 解析(Parser) ──► 逻辑计划 ──► 物理计划 ──► 执行 ──► 结果
│
├── AST 语法树
├── 类型检查
└── 优化
并行查询
- 多个时间序列可以并行查询
- 单个查询会拆分为多个子任务
- 结果在内存中聚合后返回
3.7 远程读写(Remote Write/Read)
当本地存储无法满足需求时,可以通过远程读写接口对接外部存储。
┌──────────────┐ Remote Write ┌──────────────────┐
│ Prometheus │ ─────────────────────► │ 远程存储 │
│ Server │ Remote Read │ (Thanos/Cortex/ │
│ │ ◄───────────────────── │ Mimir/InfluxDB) │
└──────────────┘ └──────────────────┘
# remote_write 配置示例
remote_write:
- url: "http://thanos-receive:19291/api/v1/receive"
queue_config:
max_samples_per_send: 5000
batch_send_deadline: 5s
max_shards: 200
# remote_read 配置示例
remote_read:
- url: "http://thanos-query:9090/api/v1/read"
read_recent: false
3.8 高可用架构模式
模式一:双写(简单高可用)
┌─────────────────┐
│ Load Balancer │
└────┬────────┬───┘
│ │
┌───────┘ └───────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Prometheus A │ │ Prometheus B │
│ (Primary) │ │ (Replica) │
└──────┬───────┘ └──────┬───────┘
│ │
└───────────┬───────────┘
▼
┌─────────────────┐
│ Alertmanager │
│ (自动去重) │
└─────────────────┘
模式二:Thanos Sidecar
┌──────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Prometheus │ │ Prometheus │ │
│ │ + Thanos │ │ + Thanos │ │
│ │ Sidecar │ │ Sidecar │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
└─────────┼───────────────────┼────────────┘
│ │
▼ ▼
┌──────────────────────────────────┐
│ Thanos Query (全局视图) │
└──────────────────────────────────┘
3.9 本章小结
| 组件 | 关键技术 | 特点 |
|---|---|---|
| TSDB | WAL + Block + Compaction | 高效写入、自动压缩 |
| 抓取模型 | HTTP Pull | 简单可靠 |
| 服务发现 | 多种 SD 后端 | 动态管理目标 |
| Relabel | 标签转换 | 灵活的元数据处理 |
| 远程读写 | Remote Write/Read | 可扩展存储 |
扩展阅读
- Prometheus 存储文档
- TSDB 论文 - Gorilla: A Fast, Scalable, In-Memory Time Series Database
- Prometheus TSDB 设计文档
- 服务发现配置
- Relabel 完全指南
上一章:02 - 安装与部署 下一章:04 - 指标类型