概述
在 Prometheus 的监控体系中,服务发现(Service Discovery,简称 SD)是连接"被监控目标"与"采集引擎"的桥梁。当你的基础设施从几台虚拟机扩展到数百个 Kubernetes Pod、跨可用区云主机、Consul 注册节点时,手动维护 static_configs 就变成了一场灾难——每次扩容、缩容、迁移都要改配置、重启 Prometheus,告警还会因为 target 不可达而误报。
Prometheus 原生支持十余种服务发现机制,能够在目标变更后自动感知,无需重启。我将从静态配置出发,逐步深入 file_sd、kubernetes_sd、consul_sd、dns_sd、ec2_sd 等主流方案,并详细讲解 relabel_configs 标签管理这一核心能力,最后给出多集群监控的落地实践。
一、为什么需要服务发现
1.1 静态配置的局限
先看一个最简单的静态配置:
scrape_configs:
- job_name: 'node'
static_configs:
- targets:
- '192.168.1.10:9100'
- '192.168.1.11:9100'
- '192.168.1.12:9100'
labels:
env: 'production'
region: 'beijing'
这在服务器数量固定时没什么问题。但考虑以下场景:
| 场景 | 静态配置的痛点 |
|---|---|
| Kubernetes Pod 扩缩容 | Pod IP 每次重建都变,手动改配置不现实 |
| 云厂商 Auto Scaling | 弹性伸缩后新实例无法被监控,存在盲区 |
| 蓝绿部署 / 金丝雀发布 | 新版本实例需要自动加入监控 |
| 多机房迁移 | IP 段变更,需要批量修改配置 |
| 容器化微服务 | 实例数量随时变化,生命周期短 |
1.2 服务发现的核心价值
┌─────────────┐ ┌───────────────────┐ ┌──────────────┐
│ 服务注册中心 │ ← 感知 → │ Prometheus SD │ ← 采集 → │ Target 实例 │
│ (Consul/K8s) │ │ (自动更新 target) │ │ (Exporter) │
└─────────────┘ └───────────────────┘ └──────────────┘
│
▼
┌───────────────────┐
│ relabel_configs │
│ (标签过滤/改写) │
└───────────────────┘
服务发现机制让 Prometheus 从"被动配置"变为"主动感知":
- 自动发现:新实例启动后自动加入监控,无需人工干预
- 自动剔除:实例下线后自动从 target 列表中移除
- 标签丰富:从服务注册中心获取元数据,自动打标签
- 动态过滤:通过 relabel 灵活控制采集范围
二、file_sd:基于文件的服务发现
file_sd 是最简单也最灵活的服务发现方式。Prometheus 定期读取指定文件(JSON 或 YAML),文件内容的变更会自动生效。
2.1 配置示例
scrape_configs:
- job_name: 'file-sd-nodes'
file_sd_configs:
- files:
- '/etc/prometheus/targets/nodes/*.yml'
- '/etc/prometheus/targets/databases/*.json'
refresh_interval: 30s
目标文件格式(YAML):
# /etc/prometheus/targets/nodes/web-servers.yml
- targets:
- 'web-01.example.com:9100'
- 'web-02.example.com:9100'
labels:
env: 'production'
role: 'web'
region: 'beijing'
- targets:
- 'web-03.example.com:9100'
labels:
env: 'staging'
role: 'web'
region: 'shanghai'
目标文件格式(JSON):
[
{
"targets": ["db-01.example.com:9100", "db-02.example.com:9100"],
"labels": {
"env": "production",
"role": "database",
"team": "dba"
}
}
]
2.2 file_sd 的适用场景
file_sd 本质上是一个"外部程序写入文件、Prometheus 读取文件"的解耦模式。它的优势在于:
- 与现有系统集成简单:CMDB、资产管理脚本只需输出 JSON/YAML 文件
- 版本控制友好:目标文件可以纳入 Git 管理
- 无额外依赖:不需要部署 Consul 等注册中心
常见用法是配合脚本或 CI/CD 管道定期更新目标文件:
#!/bin/bash
# sync-from-cmdb.sh — 从 CMDB 同步监控目标
# 由 cron 每 5 分钟执行一次
CMDB_API="http://cmdb.internal/api/v1/hosts"
OUTPUT_DIR="/etc/prometheus/targets/nodes"
# 从 CMDB 拉取主机列表
curl -s "$CMDB_API?env=production" | \
jq '[.[] | select(.status == "active") | {
targets: [.hostname + ":9100"],
labels: {
env: .env,
role: .role,
region: .region,
instance_id: .instance_id
}
}]' > "$OUTPUT_DIR/production.yml"
echo "[$(date)] Synced $(jq 'map(.targets) | flatten | length' $OUTPUT_DIR/production.yml) targets"
注意:file_sd 文件变更后,Prometheus 会在
refresh_interval内感知。如果文件写入过程中被读到不完整内容,Prometheus 会忽略并保留上一次有效配置,不会因为文件写入中断而导致监控丢失。
三、kubernetes_sd:Kubernetes 服务发现
kubernetes_sd 是云原生环境下最常用的服务发现方式。Prometheus 可以直接从 Kubernetes API Server 获取需要监控的资源列表。
3.1 角色(Role)类型
kubernetes_sd 支持 7 种 role,每种 role 发现不同的 Kubernetes 资源:
| Role | 发现对象 | 典型用途 |
|---|---|---|
node | 集群节点 | 监控节点资源(node-exporter) |
pod | 所有 Pod | 监控应用自定义 metrics |
service | Service | 按服务发现目标 |
endpoints | Endpoints | 监控 Service 后端 Pod |
ingress | Ingress 路由 | 按入口路由发现 |
eplices | EndpointSlice | 同 endpoints,K8s 1.21+ 推荐 |
container | 容器 | 按容器发现 |
3.2 监控节点(node role)
scrape_configs:
- job_name: 'k8s-nodes'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics
这里用 relabel_configs 将 metrics_path 改为通过 API Server 代理访问节点 metrics。labelmap 动作将 K8s 节点标签(如 node-role.kubernetes.io/worker)映射为 Prometheus 标签。
3.3 监控 Pod(pod role)
scrape_configs:
- job_name: 'k8s-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# 只采集配置了 prometheus.io/scrape 注解的 Pod
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
# 使用注解中指定的端口
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
# 使用注解中指定的 path
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# 保留 namespace 标签
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
# 映射所有 Pod 标签
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
这种模式通过 Pod 注解(annotations)控制是否被采集:
# Pod 添加注解后自动被 Prometheus 发现
apiVersion: v1
kind: Pod
metadata:
name: my-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
3.4 监控 Endpoints(endpoints role)
endpoints role 是发现 Service 后端实例的推荐方式,尤其适合监控 K8s Service 对应的应用指标:
scrape_configs:
- job_name: 'k8s-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
# 只采集有 prometheus.io/scrape 注解的 Service
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)
replacement: ${1}
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: service
3.5 ServiceMonitor:更优雅的 K8s 监控声明
在 Kubernetes 生态中,Prometheus Operator 引入了 ServiceMonitor CRD,用声明式方式管理 scrape 配置,比手写 relabel 规则优雅得多:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app-monitor
namespace: monitoring
labels:
release: prometheus # 匹配 Prometheus Operator 的选择器
spec:
selector:
matchLabels:
app: my-app # 选择带有 app=my-app 标签的 Service
namespaceSelector:
matchNames:
- production
- staging
endpoints:
- port: metrics
path: /metrics
interval: 15s
scrapeTimeout: 10s
ServiceMonitor 的优势:
- 声明式:配置即代码,可纳入 GitOps
- 按命名空间隔离:不同团队管理各自的 ServiceMonitor
- 自动发现:创建 Service 资源即可被监控,无需修改 Prometheus 配置
四、consul_sd:Consul 服务发现
Consul 是 HashiCorp 出品的服务注册与发现工具,在非 Kubernetes 环境(虚拟机、裸金属)中广泛使用。
4.1 Consul SD 配置
scrape_configs:
- job_name: 'consul-services'
consul_sd_configs:
- server: 'consul:8500'
services:
- 'web'
- 'api'
- 'worker'
tags:
- 'production'
refresh_interval: 30s
relabel_configs:
# 从 Consul 元数据中提取标签
- source_labels: [__meta_consul_service]
target_label: service
- source_labels: [__meta_consul_node]
target_label: node
- source_labels: [__meta_consul_service_id]
target_label: service_id
- source_labels: [__meta_consul_datacenter]
target_label: datacenter
# 提取服务标签
- source_labels: [__meta_consul_tags]
target_label: env
regex: '.*,production,.*'
replacement: 'production'
# 过滤:只采集标记了 metrics 的服务
- source_labels: [__meta_consul_service_metadata_metrics]
action: keep
regex: .+
4.2 Consul 元数据
Consul SD 提供丰富的 __meta_ 标签可供 relabel 使用:
| 元数据标签 | 说明 |
|---|---|
__meta_consul_address | 服务地址 |
__meta_consul_dc | 数据中心 |
__meta_consul_service | 服务名 |
__meta_consul_service_id | 服务实例 ID |
__meta_consul_service_address | 服务地址 |
__meta_consul_service_port | 服务端口 |
__meta_consul_tags | 服务标签(逗号分隔) |
__meta_consul_service_metadata_<key> | 自定义元数据 |
4.3 服务注册到 Consul
应用启动时将自己注册到 Consul:
{
"ID": "web-01",
"Name": "web",
"Tags": ["production", "metrics"],
"Address": "192.168.1.10",
"Port": 9100,
"Meta": {
"metrics": "true",
"team": "platform"
},
"Check": {
"HTTP": "http://192.168.1.10:9100/metrics",
"Interval": "10s"
}
}
通过 Meta 字段附加自定义元数据,Prometheus 可通过 __meta_consul_service_metadata_<key> 访问。
五、dns_sd:DNS 服务发现
dns_sd 通过 DNS 查询发现目标,适合使用 SRV 记录或 A 记录管理服务的场景。
scrape_configs:
- job_name: 'dns-sd'
dns_sd_configs:
- names:
- '_metrics._tcp.service.consul' # SRV 记录
- 'api.service.production.consul'
type: A
port: 9100
refresh_interval: 30s
- names:
- '_prometheus._tcp.example.com' # SRV 记录
type: SRV
dns_sd 的优势在于无需额外组件,只要基础设施支持 DNS 即可。缺点是 DNS 记录信息有限,无法像 Consul 那样携带丰富的元数据。
生产建议:dns_sd 最适合作为 Consul 的后端——Consul 自动管理 DNS 记录,Prometheus 通过 dns_sd 查询,实现服务发现的解耦。
六、ec2_sd:AWS EC2 服务发现
如果你的基础设施部署在 AWS 上,ec2_sd 可以直接从 EC2 API 发现实例:
scrape_configs:
- job_name: 'ec2-nodes'
ec2_sd_configs:
- region: us-east-1
access_key: AKIAIOSFODNN7EXAMPLE
secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
port: 9100
refresh_interval: 60s
filters:
- name: tag:Monitoring
values: [enabled]
relabel_configs:
- source_labels: [__meta_ec2_availability_zone]
target_label: az
- source_labels: [__meta_ec2_instance_id]
target_label: instance_id
- source_labels: [__meta_ec2_private_ip]
target_label: private_ip
# 提取实例标签
- action: labelmap
regex: __meta_ec2_tag_(.+)
# 只采集标记 Monitoring=enabled 的实例
- source_labels: [__meta_ec2_tag_Monitoring]
action: keep
regex: enabled
类似地,Azure 用 azure_sd_configs,GCP 用 gce_sd_configs,OpenStack 用 openstack_sd_configs。
七、relabel_configs:标签管理的核心
relabel_configs 是 Prometheus 服务发现中最强大的机制,它在 target 被采集之前对标签进行过滤、改写、映射。理解 relabel 是掌握 Prometheus SD 的关键。
7.1 relabel 执行时机
服务发现 → 产生原始 target (含 __meta_ 标签)
│
▼
┌─────────────────────┐
│ relabel_configs │ ← 采集前,控制是否采集、改写地址/路径
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ 采集 metrics │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ metric_relabel_configs │ ← 采集后,控制是否存储、改写 metric 标签
└─────────────────────┘
relabel_configs:在采集之前执行,可以控制是否采集目标、修改采集地址、路径、Schememetric_relabel_configs:在采集之后、存储之前执行,可以丢弃不需要的 metric 或改写 metric 标签
7.2 relabel 动作(Action)
| Action | 作用 | 典型场景 |
|---|---|---|
replace | 替换或新增标签值 | 改写 __address__、添加自定义标签 |
keep | 保留匹配的 target,丢弃不匹配的 | 只采集特定 namespace 的 Pod |
drop | 丢弃匹配的 target | 排除特定环境的目标 |
labelmap | 将一批标签映射为新标签 | 映射 K8s/Consul 的 __meta_ 标签 |
labelkeep | 保留匹配的标签 | 清理冗余标签 |
labeldrop | 丢弃匹配的标签 | 移除高基数标签 |
lowercase | 标签值转小写 | 统一标签格式 |
uppercase | 标签值转大写 | 统一标签格式 |
hashmod | 标签值取模 | 多 Prometheus 分片采集 |
7.3 实战示例
只采集生产环境的 Pod:
relabel_configs:
- source_labels: [__meta_kubernetes_namespace]
action: keep
regex: (production|production-.+)
根据标签分片(双副本 Prometheus 各采集一半 target):
relabel_configs:
- source_labels: [__address__]
modulus: 2 # 总共 2 个分片
target_label: __tmp_hash
action: hashmod
- source_labels: [__tmp_hash]
regex: 0 # 当前 Prometheus 只采集 hash=0 的 target
action: keep
丢弃不需要的高基数 metric:
metric_relabel_configs:
- source_labels: [__name__]
regex: 'go_(gc|memstats)_.+'
action: drop
重命名 metric 标签:
metric_relabel_configs:
- source_labels: [__name__]
regex: 'http_requests_total'
target_label: __name__
replacement: 'http_requests_total'
action: replace
八、标签管理好的实践
标签是 Prometheus 时间序列的维度标识,良好的标签设计直接影响查询效率和存储开销。
8.1 标签设计原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 低基数 | 标签值数量有限 | env="prod" ✓,user_id="12345" ✗ |
| 有业务意义 | 标签用于聚合和过滤 | service="payment" ✓,ip="10.0.1.5" 通常无意义 |
| 统一命名 | 团队约定标签命名规范 | env、service、team、severity |
| 控制数量 | 每条时间序列标签不宜超过 10 个 | 过多标签影响查询性能 |
8.2 标签命名规范
# 推荐的标签层次
env → environment 标识 (production/staging/dev)
service → 微服务名称
instance → 实例标识(Prometheus 自动添加)
team → 负责团队
severity → 告警级别 (critical/warning/info)
8.3 避免高基数标签
# 危险:user_id 作为标签,每个用户一条时间序列
metric_relabel_configs:
- source_labels: [user_id]
target_label: user_id # ✗ 灾难性做法
# 正确:移除高基数标签,仅保留聚合数据
metric_relabel_configs:
- action: labeldrop
regex: 'user_id|session_id|request_id'
存储成本提醒:Prometheus 中每条时间序列的存储开销约 1-3 KB。如果有 10 万用户,仅
user_id标签就会产生 10 万条时间序列,严重拖慢查询和写入性能。
九、多集群监控方案
在多 Kubernetes 集群或多数据中心环境下,服务发现需要跨越集群边界。
9.1 方案一:Prometheus + Thanos Sidecar(推荐)
┌─── Cluster A (K8s) ────────────────────┐
│ Prometheus-A ─── Thanos Sidecar ──────┼──┐
│ (kubernetes_sd: 本集群) │ │
└────────────────────────────────────────┘ │
│ Thanos Store
┌─── Cluster B (K8s) ────────────────────┐ │ (全局查询)
│ Prometheus-B ─── Thanos Sidecar ──────┼──┘
│ (kubernetes_sd: 本集群) │
└────────────────────────────────────────┘
每个集群部署独立的 Prometheus,使用 kubernetes_sd 监控本集群。Thanos Sidecar 将数据上传到对象存储,Thanos Query 提供全局查询入口。
9.2 方案二:联邦集群
# 全局 Prometheus 配置
scrape_configs:
- job_name: 'federate'
scrape_interval: 30s
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="node"}'
- '{job="kubernetes"}'
- '{__name__=~"up|prometheus_.*"}'
static_configs:
- targets:
- 'prometheus-cluster-a:9090'
- 'prometheus-cluster-b:9090'
relabel_configs:
- source_labels: [__address__]
regex: 'prometheus-(.+):9090'
target_label: cluster
replacement: '${1}'
联邦方案的优势是简单,但会增加子 Prometheus 的查询负载,适合小规模集群。大规模场景推荐 Thanos 或 VictoriaMetrics。
9.3 方案三:远程写入
# 各集群 Prometheus 配置远程写入
remote_write:
- url: 'https://mimir-central.example.com/api/v1/push'
headers:
X-Scope-OrgID: 'tenant-a'
write_relabel_configs:
# 只上传关键指标,减少带宽
- source_labels: [__name__]
regex: 'up|node_.+|container_.+|http_requests_total'
action: keep
各集群 Prometheus 通过 remote_write 将数据集中推送到中心存储(Mimir/Thanos Receive/VictoriaMetrics),实现集中监控。
十、多方案对比
| 维度 | file_sd | kubernetes_sd | consul_sd | dns_sd | ec2_sd |
|---|---|---|---|---|---|
| 适用环境 | 通用 | Kubernetes | 虚拟机/混合 | DNS 基础设施 | AWS EC2 |
| 元数据丰富度 | 低(手写) | 高(K8s 标签/注解) | 高(Consul tags/meta) | 低 | 中(EC2 tags) |
| 自动发现 | 半自动(需脚本) | 全自动 | 全自动 | 半自动 | 全自动 |
| 额外依赖 | 无 | K8s API Server | Consul Server | DNS Server | AWS API |
| 运维复杂度 | 低 | 低(K8s 原生) | 中 | 低 | 低 |
| 典型场景 | CMDB 集成 | 云原生监控 | 微服务注册发现 | 简单 DNS 服务发现 | AWS 基础设施监控 |
十一、常见问题与排查
11.1 Target 显示为 down
# 检查 target 状态
curl http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | select(.health != "up")'
# 查看 target 的 lastError
curl -s http://localhost:9090/api/v1/targets | \
jq '.data.activeTargets[] | select(.health != "up") | {scrapeUrl, lastError, labels}'
常见原因:
- 网络不通:Prometheus 与 target 之间网络策略限制
- 证书问题:https 采集时证书不匹配
- 认证失败:bearer token 过期或权限不足
- relabel 误配:relabel 将
__address__改成了错误的地址
11.2 Target 列表为空
# 查看 service discovery 发现的原始 target
curl -s http://localhost:9090/api/v1/targets?state=any | jq '.data.droppedTargets'
如果 droppedTargets 有数据但 activeTargets 为空,说明 relabel_configs 的 keep/drop 规则过滤掉了所有 target。检查 keep 正则是否正确。
11.3 标签缺失或错误
# 查看 target 的所有标签
curl -s http://localhost:9090/api/v1/targets | \
jq '.data.activeTargets[0].discoveredLabels'
discoveredLabels 包含所有 __meta_ 前缀的原始标签,确认服务发现是否返回了预期的元数据。
总结
服务发现是 Prometheus 监控体系的"神经末梢",决定了监控覆盖率和自动化程度。核心要点:
- 选择合适的 SD 方案:Kubernetes 环境用 kubernetes_sd,非 K8s 环境用 consul_sd 或 file_sd,云厂商用原生 SD(ec2/gce/azure)
- 掌握 relabel_configs:它是标签管理的核心,决定了哪些 target 被采集、标签如何映射、数据如何分片
- 设计合理的标签体系:低基数、有业务意义、命名统一,避免高基数标签拖垮 Prometheus
- 多集群用 Thanos/VM:联邦方案适合小规模,大规模场景优先 Thanos 或 VictoriaMetrics 远程写入
- 善用 ServiceMonitor:在 K8s 环境下,Prometheus Operator + ServiceMonitor 是声明式管理监控的标配
服务发现没有"推荐方案",只有"最适合你环境的方案"。理解每种 SD 机制的工作原理和适用场景,才能做出正确的选型决策。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Prometheus 官方文档 — Configuration — Prometheus 官方,参考了Prometheus 官方文档 — Configuration相关内容