Skip to content

应用指标接入

K8s 里的应用接入 Prometheus,通常是应用暴露 /metrics,再通过 ServiceMonitor 或 PodMonitor 让 Prometheus 自动发现。平台指标能说明 Pod、节点和对象状态,应用指标更接近业务运行本身,例如请求量、错误率、延迟、队列长度、任务积压和业务开关状态。

ServiceMonitor 的关系:Prometheus 不直接去找 Pod,而是先根据 ServiceMonitor 找 Service,再通过 Service 的 Endpoints 找到后端 Pod。这里最容易出错的是标签和端口名:selector.matchLabels 匹配的是 Service 标签,endpoints.port 写的是 Service 端口名,不是数字端口。

一、Demo 指标应用

当前环境里用一个很小的 BusyBox Pod 暴露静态指标,镜像使用节点上已经存在的 busybox:1.38.0,避免为了演示接入流程再拉一个额外镜像。Prometheus 3 对 /metrics 响应头比较严格,目标返回空 Content-Type 时会被拒绝,所以这里用 nc 手动返回 Prometheus 文本格式的响应头。

/root/monitoring/k8s-monitor-demo.yaml

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: k8s-monitor-demo
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-metrics-page
  namespace: k8s-monitor-demo
data:
  metrics: |
    # HELP demo_requests_total Demo request counter for ServiceMonitor test.
    # TYPE demo_requests_total counter
    demo_requests_total{app="demo-metrics",code="200"} 1024
    # HELP demo_queue_depth Demo queue depth for Grafana panel test.
    # TYPE demo_queue_depth gauge
    demo_queue_depth{app="demo-metrics"} 7
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-metrics
  namespace: k8s-monitor-demo
  labels:
    app: demo-metrics
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo-metrics
  template:
    metadata:
      labels:
        app: demo-metrics
    spec:
      containers:
        - name: demo
          image: busybox:1.38.0
          imagePullPolicy: IfNotPresent
          command:
            - sh
            - -c
            - |
              # Prometheus 3 会校验 scrape 响应格式,这里显式返回 text/plain 头
              while true; do
                {
                  printf 'HTTP/1.1 200 OK\r\n'
                  printf 'Content-Type: text/plain; version=0.0.4; charset=utf-8\r\n'
                  printf 'Connection: close\r\n\r\n'
                  cat /metrics-data/metrics
                } | nc -l -p 8080
              done
          ports:
            - name: metrics
              containerPort: 8080
          volumeMounts:
            - name: metrics-page
              mountPath: /metrics-data/metrics
              subPath: metrics
          resources:
            requests:
              cpu: 5m
              memory: 16Mi
            limits:
              memory: 64Mi
      volumes:
        - name: metrics-page
          configMap:
            name: demo-metrics-page
---
apiVersion: v1
kind: Service
metadata:
  name: demo-metrics
  namespace: k8s-monitor-demo
  labels:
    app: demo-metrics
spec:
  selector:
    app: demo-metrics
  ports:
    - name: metrics
      port: 8080
      targetPort: metrics

部署后先看 Pod、Service 和 Endpoints:

bash
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

kubectl apply -f /root/monitoring/k8s-monitor-demo.yaml
kubectl -n k8s-monitor-demo rollout status deploy/demo-metrics --timeout=180s
kubectl -n k8s-monitor-demo get pod,svc,endpoints -o wide

本次结果里,Service 已经指向后端 Pod:

text
NAME                                READY   STATUS    IP
pod/demo-metrics-8546f59879-ssgm2   1/1     Running   10.42.1.40

NAME                   TYPE        CLUSTER-IP     PORT(S)
service/demo-metrics   ClusterIP   10.43.80.226   8080/TCP

NAME                     ENDPOINTS
endpoints/demo-metrics   10.42.1.40:8080

先在 Pod 内确认 /metrics 响应格式。这里看响应头比只看内容更有意义:

bash
kubectl -n k8s-monitor-demo exec deploy/demo-metrics -- sh -c \
  'printf "GET /metrics HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc 127.0.0.1 8080 | sed -n "1,12p"'

关键输出:

text
HTTP/1.1 200 OK
Content-Type: text/plain; version=0.0.4; charset=utf-8

# HELP demo_requests_total Demo request counter for ServiceMonitor test.
# TYPE demo_requests_total counter
demo_requests_total{app="demo-metrics",code="200"} 1024

遇到过一次 target 已经被发现但状态是 DOWN,错误是 non-compliant scrape target sending blank Content-Type。这种情况不是 ServiceMonitor 标签问题,而是应用 /metrics 响应头不符合 Prometheus 3 的要求。

二、ServiceMonitor

kube-prometheus-stack 通过 Prometheus Operator 管理 Prometheus,ServiceMonitor 是 Operator 提供的 CRD。当前 Prometheus values 里已经放开 selector:

yaml
prometheus:
  prometheusSpec:
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false

这表示新增的 ServiceMonitor 不需要额外打 Helm release 标签,也可以被当前 Prometheus 选择到。

在同一个 YAML 里追加 ServiceMonitor:

yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: demo-metrics
  namespace: monitoring
  labels:
    app: demo-metrics
spec:
  namespaceSelector:
    matchNames:
      - k8s-monitor-demo
  selector:
    matchLabels:
      app: demo-metrics
  endpoints:
    - port: metrics
      path: /metrics
      interval: 60s

字段关系:

字段当前值说明
metadata.namespacemonitoringServiceMonitor 对象放在监控命名空间
namespaceSelector.matchNamesk8s-monitor-demo去哪个 namespace 找 Service
selector.matchLabelsapp: demo-metrics匹配 Service 上的标签
endpoints.portmetricsService 端口名,必须和 Service 里的 ports.name 一致
endpoints.path/metrics应用暴露指标的路径

应用 ServiceMonitor:

bash
kubectl apply -f /root/monitoring/k8s-monitor-demo.yaml
kubectl -n monitoring get servicemonitor demo-metrics -o yaml

三、Prometheus 查看

操作路径:

Status -> Target health (中文:状态 -> 目标健康)

打开 http://192.168.10.11:30900/targets,过滤或查找 serviceMonitor/monitoring/demo-metrics/0。页面里需要同时看到三件事:scrape pool 名称、目标 labels、State 是 UP

Prometheus 中 demo target 正常

操作路径:

Query -> Table (中文:查询 -> 表格)

查询 demo 指标:

promql
demo_requests_total

Prometheus 查询 demo 指标

命令行也可以查同样的数据:

bash
curl -sG http://127.0.0.1:30900/api/v1/query \
  --data-urlencode 'query=demo_requests_total'

up 查询适合确认抓取状态:

promql
up{namespace="k8s-monitor-demo", service="demo-metrics"}

本次结果里 up1demo_requests_total1024demo_queue_depth7

四、排错入口

应用指标接入失败时,按对象关系往回查,比只看 Prometheus 页面更清楚。

现象检查入口常见原因
Prometheus Targets 没有 demokubectl -n monitoring get servicemonitor demo-metrics -o yamlServiceMonitor 没创建、namespace 不对、Prometheus selector 没选中
target 出现但是 DOWNPrometheus Status -> Target healthLast error/metrics 路径错误、响应头不合规、Pod 端口不通
target 没有 endpointkubectl -n k8s-monitor-demo get endpoints demo-metrics -o wideService selector 没匹配 Pod 标签
ServiceMonitor 存在但不抓取对照 Service 的 labelsports.nameselector.matchLabelsendpoints.port 写错
指标值查不到Prometheus Query 和 Pod 内 nc/curl应用没有输出该指标,或者指标名写错

镜像拉取失败也会影响接入。本次环境统一使用 docker.1ms.run 作为 docker.io mirror;之前多 mirror 配置里有源返回 429 Too Many Requests,Pod 会卡在 ImagePullBackOff。这类问题看 kubectl describe pod 的 Events 比看 Prometheus 更直接。

五、标签设计

应用指标常见标签:

标签说明
namespace所属 namespace,K8s 场景里通常由 Prometheus 采集链路自动补上
podPod 名,适合排查单个副本问题
serviceService 名,适合按服务聚合
app应用名,适合跨 Pod 聚合
env环境,例如 prod、staging、test
methodHTTP 方法
codeHTTP 状态码
route路由模板,例如 /api/users/:id

user_idrequest_idtrace_id 不适合作为普通指标标签。它们变化太多,会让时间序列数量膨胀。TraceID 更适合放在日志和链路追踪里;指标和 Trace 的关联通常用 exemplar。