3. 集群指标采集与ServiceMonitor实战

在OpenShift中,Prometheus Operator把“监控谁、怎么抓、抓哪些指标”抽象成了Kubernetes自定义资源(CRD)。运维人员不再手工编辑prometheus.yml,而是声明式地创建ServiceMonitor、PodMonitor和Probe三类对象,由Operator监听这些对象的变化,自动生成并热加载Prometheus的抓取配置。理解这三类资源的适用边界,是让业务自定义指标稳定进入集群监控的第一步。

三类采集资源的分工

ServiceMonitor面向Service,通过Service的标签选择器找到目标Service,再依据Service的端口名或targetPort定位后端Pod的指标端口,适合“一个Service对应一组同构Pod”的常规业务。PodMonitor直接选择Pod,不经过Service,适合没有Service、或者需要按Pod粒度单独抓取的场景,例如Job、DaemonSet或临时调试Pod。Probe则用于黑盒探测,它不抓取/metrics,而是让Prometheus主动访问HTTP、TCP或ICMP端点,判断可用性与响应延迟,常用于外部域名、Ingress入口或第三方依赖的健康检查。

三者的选择可以归纳为一条判断链:需要抓取应用暴露的指标文本,且目标有Service,用ServiceMonitor;有指标但无Service,用PodMonitor;不抓指标、只探测可达性与响应,用Probe。下面这张图展示了三类资源从声明到Prometheus实际抓取配置的转换关系。

ServiceMonitor / PodMonitor / Probe 到抓取配置的转换 ServiceMonitor selector: app=order-api endpoints: port=metrics path=/metrics interval=30s 经 Service 定位后端 Pod PodMonitor selector: app=batch-job podMetricsEndpoints: port=9100 path=/metrics interval=60s 直接选择 Pod,无需 Service Probe targets: ingress/staticConfig prober: http / tcp / icmp module: http_2xx 黑盒探测,不抓取指标文本 Prometheus Operator 监听 CRD 变化 校验 selector 与 namespaceSelector → 生成 scrape_config → 写入 Secret 并热加载 Prometheus 实例 按生成的 job 周期性抓取 /metrics 或执行探测,写入 TSDB 业务 Pod :8080/metrics 自定义指标文本 Ingress / 外部端点 探测结果 up / probe_duration

为业务应用暴露自定义指标

要让ServiceMonitor真正抓到数据,业务应用必须先暴露符合Prometheus文本格式的指标。以Go应用为例,引入prometheus/client_golang后注册一个计数器和一个直方图:

httpRequests := prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "order_http_requests_total",
        Help: "Total number of HTTP requests.",
    },
    []string{"method", "path", "status"},
)
prometheus.MustRegister(httpRequests)

httpDuration := prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "order_http_request_duration_seconds",
        Help:    "HTTP request latency.",
        Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5},
    },
    []string{"method", "path"},
)
prometheus.MustRegister(httpDuration)

http.Handle("/metrics", promhttp.Handler())

指标命名遵循“应用前缀_单位_类型后缀”的约定,例如order_http_requests_total中的_total表示计数器,order_http_request_duration_seconds中的_seconds表示单位。暴露路径通常为/metrics,端口可以是业务端口,也可以单独开一个管理端口。部署后先用curl验证:

oc exec -it deploy/order-api -- curl -s localhost:8080/metrics | grep order_http
# HELP order_http_requests_total Total number of HTTP requests.
# TYPE order_http_requests_total counter
order_http_requests_total{method="GET",path="/orders",status="200"} 128

只有确认指标文本可被访问,后续的ServiceMonitor才有意义。如果这里返回404或空内容,应先排查应用侧,而不是怀疑Operator。

编写ServiceMonitor并接入集群监控

假设order-api的Service带有标签app=order-api,端口名为metrics,指向容器8080端口。ServiceMonitor的编写要点是:selector.matchLabels必须与Service标签一致;namespaceSelector决定在哪些命名空间查找Service;endpoints.port填写Service中定义的端口名,而不是数字端口。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: order-api-monitor
  namespace: app-prod
  labels:
    release: cluster-monitoring
spec:
  namespaceSelector:
    matchNames:
      - app-prod
  selector:
    matchLabels:
      app: order-api
  endpoints:
    - port: metrics
      path: /metrics
      interval: 30s
      scrapeTimeout: 10s
      honorLabels: true

其中release标签非常关键。OpenShift的Prometheus实例通常配置了serviceMonitorSelector,只选择带有特定标签的ServiceMonitor。如果创建的ServiceMonitor缺少该标签,Prometheus不会加载它,表现为Targets页面看不到目标。可通过以下命令确认Prometheus的选择器:

oc -n openshift-monitoring get prometheus k8s \
  -o jsonpath='{.spec.serviceMonitorSelector}{"\n"}'
# 输出示例:{"matchLabels":{"release":"cluster-monitoring"}}

应用资源后,在OpenShift控制台的Observe → Targets中应能看到order-api-monitor对应的Endpoint,状态为UP。若为DOWN,常见原因是端口名不匹配、NetworkPolicy阻断了Prometheus到Pod的流量,或Service的selector没有选中任何Pod。可以先用oc get endpoints确认Service后端是否为空。

PodMonitor与Probe的典型用法

对于没有Service的批处理任务,PodMonitor直接按Pod标签选择。注意PodMonitor使用podMetricsEndpoints字段,且端口既可以是容器端口名,也可以是数字端口:

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: batch-job-monitor
  namespace: app-prod
  labels:
    release: cluster-monitoring
spec:
  selector:
    matchLabels:
      app: batch-job
  podMetricsEndpoints:
    - port: "9100"
      path: /metrics
      interval: 60s

Probe用于黑盒探测,需要先在Prometheus中配置blackbox exporter作为prober,再通过Probe资源指定目标。下面示例探测一个Ingress域名,判断HTTP状态码是否为2xx:

apiVersion: monitoring.coreos.com/v1
kind: Probe
metadata:
  name: order-ingress-probe
  namespace: app-prod
  labels:
    release: cluster-monitoring
spec:
  jobName: order-ingress
  interval: 30s
  module: http_2xx
  prober:
    url: blackbox-exporter.monitoring.svc:9115
  targets:
    staticConfig:
      static:
        - https://order.example.com/healthz

Probe产生的指标包括probe_success(1表示成功,0表示失败)和probe_duration_seconds(探测耗时)。这两个指标适合配置告警,例如probe_success == 0持续2分钟即触发入口不可用告警。

验证与排障要点

接入完成后,建议按以下顺序验证:第一,用curl确认应用/metrics可访问;第二,用oc get servicemonitor确认资源已创建且标签正确;第三,在Targets页面确认目标状态;第四,在Prometheus查询框输入自定义指标名,确认能查到时间序列。若Targets中完全没有出现该job,优先检查ServiceMonitor的release标签与Prometheus的serviceMonitorSelector是否匹配;若目标出现但状态为DOWN,则检查端口名、路径、NetworkPolicy和证书。把这三类资源的标签选择关系理清,业务自定义指标就能稳定进入集群监控体系。