Prometheus 和 ServiceMonitor 介绍

7月 7, 2026
Cloud, Kubernetes, DevOps, Observability, ByAI

在 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.34

Prometheus 定期访问这些地址,把返回的指标样本保存到自己的时间序列数据库中。

在传统环境中,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:808010.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,也就是自定义资源,例如:

  • Prometheus
  • Alertmanager
  • ServiceMonitor
  • PodMonitor
  • Probe
  • PrometheusRule
  • ScrapeConfig

这些对象都是 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 对象,和 DeploymentServiceConfigMap 一样,通过 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 自己创建在 monitoring namespace
  • 它去 default namespace 里找 Service
  • 它只找带有 app: my-app label 的 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-stack label 的 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 吗#

可以。

ServiceMonitorselector 是 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.selectorServiceMonitor选择哪些 Service
ServiceMonitor.spec.namespaceSelectorServiceMonitor去哪些 namespace 找 Service
Prometheus.spec.serviceMonitorSelectorPrometheus选择哪些 ServiceMonitor
Prometheus.spec.serviceMonitorNamespaceSelectorPrometheus去哪些 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 的 CRD
  • additionalScrapeConfigs:把原生 Prometheus scrape 配置追加进去

一般来说,Kubernetes 集群内常规应用优先用 ServiceMonitor;没有 Service 或者想直接抓 Pod 时用 PodMonitor;外部目标或复杂规则再考虑 ScrapeConfigadditionalScrapeConfigs

常见排查清单#

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-app

5. 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: metrics

Service 中:

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/prometheus

8. 去 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 一层层定位出来。

参考资料#

本文共 4472 字,上次修改于 Aug 13, 2026,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» 服务网络与 Service Mesh 详解

» Serverless与边缘计算:从底层硬件到架构的深度解析

» Pulumi 中的 all 和 apply 方法使用介绍

» CIDR 表示法

» pm2 使用