Prometheus 和 ServiceMonitor 介绍
7月 7, 2026
在 Kubernetes 中接入 Prometheus 时,经常会遇到一个概念:ServiceMonitor。
它看起来像是一个“监控服务”的东西,但名字很容易让人误会。ServiceMonitor 不是一个 Pod,也不是一个会运行的监控进程。它本质上是 Prometheus Operator 提供的一个 Kubernetes 自定义资源,用来声明“Prometheus 应该去抓哪些 Service 的 metrics,以及怎么抓”。
这篇文章把 Prometheus、Prometheus Operator、ServiceMonitor、Service、Pod 之间的关系整理一下。
Prometheus 是什么#
Prometheus 是一个开源监控系统,核心能力包括:
- 通过 HTTP 主动拉取指标,也就是 pull model
- 按时间序列存储指标数据
- 使用 PromQL 查询指标
- 根据规则计算告警
- 和 Alertmanager、Grafana 等组件配合使用
一个被 Prometheus 监控的应用,通常会暴露一个 HTTP 接口,例如:
GET /metrics这个接口返回 Prometheus 能识别的文本格式指标,例如:
http_requests_total{method="GET",path="/api/users",status="200"} 1024
process_cpu_seconds_total 12.34Prometheus 定期访问这些地址,把返回的指标样本保存到自己的时间序列数据库中。
在传统环境中,Prometheus 的配置可能长这样:
scrape_configs:
- job_name: my-app
static_configs:
- targets:
- 10.0.1.12:8080
- 10.0.1.13:8080这表示 Prometheus 会定期抓取 10.0.1.12:8080 和 10.0.1.13:8080。
Kubernetes 中的问题#
在 Kubernetes 里,直接维护静态 targets 列表并不舒服。原因很简单:
- Pod 会重建,Pod IP 会变化
- Service 后面的 Endpoints 会动态变化
- 应用通常分布在不同 namespace
- 同一个集群中可能有多个团队、多个服务、多个抓取规则
- 监控配置最好也能通过 Kubernetes YAML 管理
如果不用 Prometheus Operator,也可以使用 Prometheus 原生的 Kubernetes service discovery,例如:
scrape_configs:
- job_name: kubernetes-endpoints
kubernetes_sd_configs:
- role: endpoints然后再通过 relabel_configs 根据 namespace、service label、annotation、端口名等信息过滤目标。
这种方式很灵活,但配置会比较复杂。Prometheus Operator 的出现,就是为了把这些 Prometheus 配置变成 Kubernetes 原生对象来管理。
Prometheus Operator 是什么#
Prometheus Operator 是运行在 Kubernetes 集群里的一个控制器。它引入了一组 CRD,也就是自定义资源,例如:
PrometheusAlertmanagerServiceMonitorPodMonitorProbePrometheusRuleScrapeConfig
这些对象都是 Kubernetes API 对象。你通过 YAML 创建它们,Operator 监听这些对象的变化,然后生成 Prometheus 真正需要的配置。
可以把它理解成:
Kubernetes YAML
|
v
Prometheus Operator
|
v
Prometheus 原生配置
|
v
Prometheus 抓取指标其中 Prometheus 对象描述“我要运行一个怎样的 Prometheus 集群”,Operator 会根据它创建对应的 StatefulSet、Service、Secret 等资源。
而 ServiceMonitor 对象描述“这个 Prometheus 应该怎样抓取一组 Service”。
ServiceMonitor 是什么#
ServiceMonitor 是 Prometheus Operator 提供的 CRD,完整 API kind 是:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor它的作用是定义一组 Service 的抓取规则,包括:
- 通过 label selector 选择哪些 Service
- 从哪个 namespace 发现 Service
- 抓取哪个端口
- 使用哪个 path,例如
/metrics - 使用什么抓取间隔
- 是否使用 TLS、认证、Bearer Token
- 是否做 target relabel 或 metric relabel
严格来说,Prometheus 最终抓取的是由 Kubernetes Service 关联出来的 Endpoints 或 EndpointSlice。只是大多数场景下,我们是通过 Service 的 labels 和命名端口来表达这组目标,所以可以把 ServiceMonitor 理解成“选择 Service 并描述抓取方式”的规则。
ServiceMonitor 自己不会抓指标,真正抓指标的仍然是 Prometheus。
所以这几个概念可以这样区分:
Pod 真正运行应用,应用暴露 /metrics
Service 给一组 Pod 提供稳定入口
ServiceMonitor 一份抓取规则,告诉 Prometheus 怎么发现和抓取 Service
Prometheus 真正执行抓取、存储和查询的进程
Operator 把 ServiceMonitor 翻译成 Prometheus 配置ServiceMonitor 是 Pod 吗#
不是。
创建一个 ServiceMonitor 不会启动新的 Pod,不会启动新的容器,也不会单独运行一个采集进程。
它只是一个 Kubernetes API 对象,和 Deployment、Service、ConfigMap 一样,通过 Kubernetes API Server 创建、读取和更新。底层通常会持久化在 Kubernetes 的 etcd 里。
不过要注意,etcd 里存的是 ServiceMonitor 这个配置对象,不是指标数据。
指标数据的流向是:
应用 /metrics
|
v
Prometheus scrape
|
v
Prometheus TSDB / remote write也就是说:
ServiceMonitor对象存储在 Kubernetes API 中,底层通常是 etcd- Prometheus 抓到的 metrics 不存 etcd
- metrics 存在 Prometheus 自己的 TSDB 中,或者通过 remote write 写到外部存储
ServiceMonitor 的工作流程#
一个典型流程如下:
1. 应用 Pod 暴露 /metrics
2. Service 通过 selector 选中这些 Pod
3. Service 有一个命名端口,例如 metrics
4. ServiceMonitor 通过 label selector 选中 Service
5. Prometheus 对象通过 serviceMonitorSelector 选中 ServiceMonitor
6. Prometheus Operator 生成 Prometheus scrape_config
7. Prometheus reload 配置并开始抓取
8. 在 Prometheus UI 的 Status -> Targets 中能看到 target这里有两层 selector 很重要:
第一层是 ServiceMonitor.spec.selector,它决定这个 ServiceMonitor 会匹配哪些 Service。
第二层是 Prometheus.spec.serviceMonitorSelector,它决定某个 Prometheus 实例会使用哪些 ServiceMonitor。
很多时候 ServiceMonitor 已经创建成功,但 Prometheus Targets 里看不到,问题就出在这两层 selector 之一。
一个完整例子#
假设有一个应用暴露在 default namespace,应用的 metrics 地址是:
http://my-app.default.svc:8080/metrics先准备一个 Service:
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: default
labels:
app: my-app
spec:
selector:
app: my-app
ports:
- name: metrics
port: 8080
targetPort: 8080然后创建 ServiceMonitor:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
namespaceSelector:
matchNames:
- default
selector:
matchLabels:
app: my-app
endpoints:
- port: metrics
path: /metrics
interval: 30s这个 YAML 表达的意思是:
ServiceMonitor自己创建在monitoringnamespace- 它去
defaultnamespace 里找 Service - 它只找带有
app: my-applabel 的 Service - 它抓取 Service 中名为
metrics的端口 - 它访问
/metrics - 它每 30 秒抓一次
特别注意:
endpoints:
- port: metrics这里的 metrics 是 Service ports 里的端口名:
ports:
- name: metrics
port: 8080这里不是容器端口号,也不是 Service 名字。端口名写错是 ServiceMonitor 最常见的问题之一。
Prometheus 如何选中 ServiceMonitor#
创建了 ServiceMonitor 还不够,Prometheus 对象也要选中它。
例如 Prometheus 配置中可能有:
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: kube-prometheus-stack-prometheus
namespace: monitoring
spec:
serviceMonitorSelector:
matchLabels:
release: kube-prometheus-stack
serviceMonitorNamespaceSelector: {}这表示:
- 只选择带有
release: kube-prometheus-stacklabel 的 ServiceMonitor - 从所有 namespace 中查找 ServiceMonitor
如果 serviceMonitorNamespaceSelector 没有放开,那 Prometheus 可能只会看自己所在 namespace 的 ServiceMonitor。
因此排查时要同时看:
kubectl get prometheus -A
kubectl get servicemonitor -A
kubectl describe prometheus <prometheus-name> -n <namespace>
kubectl describe servicemonitor <servicemonitor-name> -n <namespace>如果是通过 Helm 安装的 kube-prometheus-stack,经常需要给 ServiceMonitor 加上类似这样的 label:
metadata:
labels:
release: kube-prometheus-stack具体 label 取决于你的 Helm release name 和 Chart 配置。
一个 ServiceMonitor 可以监控多个 Service 吗#
可以。
ServiceMonitor 的 selector 是 label selector,不是固定绑定某一个 Service。因此只要多个 Service 有相同 label,并且它们的抓取方式一致,就可以由一个 ServiceMonitor 管理。
例如:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: backend-apps
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
namespaceSelector:
matchNames:
- default
- payment
- user
selector:
matchLabels:
metrics: enabled
endpoints:
- port: metrics
path: /metrics
interval: 30s只要这些 Service 都有:
metadata:
labels:
metrics: enabled并且都有名为 metrics 的端口,它们就能被同一个 ServiceMonitor 选中。
什么时候需要多个 ServiceMonitor#
是否拆成多个 ServiceMonitor,取决于抓取规则是否一致,而不是取决于 Service 数量。
如果这些 Service 满足以下条件,可以放在一个 ServiceMonitor 中:
- 使用相同端口名
- 使用相同 metrics path
- 使用相同协议
- 使用相同认证方式
- 使用相同抓取间隔
- 可以使用同一组 label selector 表达
如果存在明显差异,通常拆开更清晰:
- A 服务用
/metrics,B 服务用/actuator/prometheus - A 服务端口名是
metrics,B 服务端口名是http-metrics - A 服务 15 秒抓一次,B 服务 60 秒抓一次
- A 服务需要 TLS,B 服务不需要
- A 服务属于平台团队,B 服务属于业务团队
- A 服务在生产 namespace,B 服务在测试 namespace
所以结论是:
ServiceMonitor 是抓取规则,不是运行单元。
一个 ServiceMonitor 可以覆盖一组规则相同的 Service。
多个 ServiceMonitor 通常是为了拆分不同的抓取规则。namespaceSelector 和 serviceMonitorNamespaceSelector 的区别#
这两个字段很容易混。
ServiceMonitor.spec.namespaceSelector 表示:
这个 ServiceMonitor 去哪些 namespace 里找 Service例如:
spec:
namespaceSelector:
matchNames:
- default表示这个 ServiceMonitor 去 default namespace 找 Service。
Prometheus.spec.serviceMonitorNamespaceSelector 表示:
这个 Prometheus 去哪些 namespace 里找 ServiceMonitor例如:
spec:
serviceMonitorNamespaceSelector: {}表示这个 Prometheus 可以从所有 namespace 选择 ServiceMonitor。
两者不是一回事。
可以用一张表记住:
| 字段 | 写在哪里 | 作用 |
|---|---|---|
ServiceMonitor.spec.selector | ServiceMonitor | 选择哪些 Service |
ServiceMonitor.spec.namespaceSelector | ServiceMonitor | 去哪些 namespace 找 Service |
Prometheus.spec.serviceMonitorSelector | Prometheus | 选择哪些 ServiceMonitor |
Prometheus.spec.serviceMonitorNamespaceSelector | Prometheus | 去哪些 namespace 找 ServiceMonitor |
没有 ServiceMonitor 怎么办#
如果不用 Prometheus Operator,也就没有 ServiceMonitor 这层抽象。此时就需要直接维护 Prometheus 原生配置,也就是 scrape_configs。
最简单的是静态 targets:
scrape_configs:
- job_name: my-app
static_configs:
- targets:
- my-app.default.svc.cluster.local:8080这种方式能用,但在 Kubernetes 里不够灵活。
更 Kubernetes 原生一点的是使用 Prometheus 的 Kubernetes service discovery:
scrape_configs:
- job_name: kubernetes-services
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_metrics]
action: keep
regex: enabled
- source_labels: [__meta_kubernetes_endpoint_port_name]
action: keep
regex: metrics这样 Prometheus 会从 Kubernetes API 发现 Endpoints,再根据 label 和端口名筛选。
如果已经使用 Prometheus Operator,但有些抓取规则无法用 ServiceMonitor 表达,还可以考虑:
PodMonitor:直接按 Pod 发现目标Probe:配合 blackbox exporter 做 HTTP/TCP/ICMP 探测ScrapeConfig:更接近 Prometheus 原生scrape_config的 CRDadditionalScrapeConfigs:把原生 Prometheus scrape 配置追加进去
一般来说,Kubernetes 集群内常规应用优先用 ServiceMonitor;没有 Service 或者想直接抓 Pod 时用 PodMonitor;外部目标或复杂规则再考虑 ScrapeConfig 或 additionalScrapeConfigs。
常见排查清单#
1. CRD 是否存在#
kubectl get crd servicemonitors.monitoring.coreos.com如果没有这个 CRD,说明 Prometheus Operator 相关 CRD 没有安装。
2. ServiceMonitor 是否创建成功#
kubectl get servicemonitor -A
kubectl describe servicemonitor my-app -n monitoring创建成功只代表 Kubernetes 接收了这个对象,不代表 Prometheus 已经使用它。
3. Prometheus 是否选中了 ServiceMonitor#
查看 Prometheus 的 selector:
kubectl get prometheus -A
kubectl describe prometheus <name> -n <namespace>重点检查:
serviceMonitorSelector:
serviceMonitorNamespaceSelector:ServiceMonitor 的 labels 必须能匹配 serviceMonitorSelector。
ServiceMonitor 所在 namespace 必须能被 serviceMonitorNamespaceSelector 选中。
4. ServiceMonitor 是否选中了 Service#
看 Service 的 labels:
kubectl get svc my-app -n default --show-labels确认它能匹配:
spec:
selector:
matchLabels:
app: my-app5. Service 是否真的有 Endpoints#
kubectl get endpoints my-app -n default
kubectl get endpointslice -n default -l kubernetes.io/service-name=my-app如果没有 endpoints,通常是 Service 的 selector 没有匹配到 Pod。
6. 端口名是否一致#
ServiceMonitor 中:
endpoints:
- port: metricsService 中:
ports:
- name: metrics这两个必须对上。
7. metrics path 是否正确#
手动访问一下:
kubectl port-forward svc/my-app 8080:8080 -n default
curl http://localhost:8080/metrics如果应用实际暴露的是 /actuator/prometheus,那 ServiceMonitor 也要写:
path: /actuator/prometheus8. 去 Prometheus UI 看 Targets#
进入 Prometheus UI:
Status -> Targets
Status -> Service Discovery如果 target 被发现但状态是 DOWN,通常是网络、路径、认证、TLS 或应用自身问题。
如果 target 完全没有出现,通常是 selector、namespaceSelector 或 CRD 配置问题。
小结#
ServiceMonitor 可以理解成 Prometheus Operator 世界里的“Service 抓取规则”。
它不是 Pod,不会运行进程。它作为 Kubernetes 自定义资源存储在 Kubernetes API 中,底层通常持久化到 etcd。真正运行和抓取指标的是 Prometheus,真正把 ServiceMonitor 翻译成 Prometheus 配置的是 Prometheus Operator。
创建一个新的 ServiceMonitor,本质上就是创建一份 YAML:
我在哪些 namespace 找 Service
我用什么 label 选择 Service
我抓哪个命名端口
我访问哪个 path
我多久抓一次如果没有 ServiceMonitor,就需要回到 Prometheus 原生的 scrape_configs。可以手写静态 targets,也可以使用 Kubernetes service discovery,只是配置会更底层、更复杂。
在日常 Kubernetes 监控中,可以记住这个链路:
Pod 暴露 /metrics
|
Service 暴露命名端口
|
ServiceMonitor 选择 Service
|
Prometheus 选择 ServiceMonitor
|
Operator 生成 scrape_config
|
Prometheus 抓取并存储指标理解这条链路后,大部分 ServiceMonitor 相关问题都能顺着 selector、namespace、port、path 一层层定位出来。