背景描述

搭建victoriametrics cluster集群后,使用VMServiceScrape方式对 kube-state-metrics进行指标采集是发现个有趣的问题。
这里先放一下的采集配置文件:
VMServiceScrape 配置文件如下:

apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
  annotations:
  labels:
    app.kubernetes.io/component: exporter
    app.kubernetes.io/name: kube-state-metrics
    app.kubernetes.io/part-of: kube-prometheus
    app.kubernetes.io/version: 2.13.0
  name: kube-state-metrics
  namespace: kube-system
spec:
  endpoints:
  - interval: 30s
    metricRelabelConfigs:
    - action: drop
      regex:
      - kube_endpoint_address_not_ready|kube_endpoint_address_available
      sourceLabels:
      - __name__
    port: http-metrics
    relabelConfigs:
    - action: labeldrop
      regex:
      - (pod|service|endpoint|namespace)
    scheme: http
    scrapeTimeout: 30s
  jobLabel: app.kubernetes.io/name
  selector:
    matchLabels:
      app.kubernetes.io/component: exporter
      app.kubernetes.io/name: kube-state-metrics

该配置文件生成的vmagent配置如下

- job_name: serviceScrape/kube-system/kube-state-metrics/0
  scrape_interval: 30s
  scrape_timeout: 30s
  scheme: http
  relabel_configs:
  - action: keep
    source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_component]
    regex: exporter
  - action: keep
    source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]
    regex: kube-state-metrics
  - action: keep
    source_labels: [__meta_kubernetes_endpoint_port_name]
    regex: http-metrics
  - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
    separator: ;
    target_label: node
    regex: Node;(.*)
    replacement: ${1}
  - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
    separator: ;
    target_label: pod
    regex: Pod;(.*)
    replacement: ${1}
  - source_labels: [__meta_kubernetes_pod_name]
    target_label: pod
  - source_labels: [__meta_kubernetes_pod_container_name]
    target_label: container
  - source_labels: [__meta_kubernetes_namespace]
    target_label: namespace
  - source_labels: [__meta_kubernetes_service_name]
    target_label: service
  - source_labels: [__meta_kubernetes_service_name]
    target_label: job
    replacement: ${1}
  - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]
    target_label: job
    regex: (.+)
    replacement: ${1}
  - target_label: endpoint
    replacement: http-metrics
  - action: labeldrop
    regex: (pod|service|endpoint|namespace)
  metric_relabel_configs:
  - action: drop
    source_labels: [__name__]
    regex:
    - kube_endpoint_address_not_ready
    - kube_endpoint_address_available
  kubernetes_sd_configs:
  - role: endpoints
    namespaces:
      own_namespace: false
      names:
      - kube-system

在通过target-relabel-debug 页面分析数据采集处理过程中发现如下配置:

  - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
    separator: ;
    target_label: pod
    regex: Pod;(.*)
    replacement: ${1}
  - source_labels: [__meta_kubernetes_pod_name]
    target_label: pod

这里对Pod进行了2次操作分别如下:

两次操作

第一次操作
- source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]
  separator: ;
  target_label: pod
  regex: Pod;(.*)
  replacement: ${1}
  • 目标:从 Endpoint 对象中提取 Pod 名称,仅当目标类型是 Pod 时生效。
  • 适用场景:通过 Kubernetes Endpoint 发现的 Pod(如 Service 后端的 Pod)。
第二次操作
- source_labels: [__meta_kubernetes_pod_name]
  target_label: pod
  • 目标:直接从 Pod 元数据中获取名称,覆盖或补充第一次操作的结果。
  • 适用场景:直接发现的 Pod(如通过 role: pod 的服务发现)或第一次操作未匹配的情况。

为什么需要两次操作?

1. 覆盖不同的服务发现场景

Kubernetes 服务发现有多种方式:

  • 通过 Endpoint 发现(如 role: endpoints):
    元数据包含 __meta_kubernetes_endpoint_address_target_kind__meta_kubernetes_endpoint_address_target_name,但不保证所有 Endpoint 都指向 Pod(可能是 Node 或 ExternalName)。

  • 直接发现 Pod(如 role: pod):
    元数据包含 __meta_kubernetes_pod_name,但没有上述 Endpoint 相关标签。

两次操作结合,可以确保无论使用哪种服务发现方式,都能获取到 Pod 名称。

2. 处理特殊情况
  • Endpoint 指向非 Pod 目标
    若 Endpoint 指向 Node 或其他资源,第一次操作会失败(正则不匹配),此时第二次操作可从 Pod 自身的元数据中获取名称。

  • 标签优先级控制
    第二次操作会覆盖第一次的结果,确保使用 Pod 自身的精确名称而非 Endpoint 关联的名称(两者通常相同,但在特殊配置下可能不同)。

示例场景

场景1:通过 Service 发现 Pod
  1. 服务发现返回:
    • __meta_kubernetes_endpoint_address_target_kind=Pod
    • __meta_kubernetes_endpoint_address_target_name=nginx-123
    • __meta_kubernetes_pod_name=nginx-123
  2. 第一次操作:提取 pod=nginx-123
  3. 第二次操作:覆盖 pod=nginx-123(值相同,无实际影响)。
场景2:直接发现 Pod
  1. 服务发现返回:
    • __meta_kubernetes_endpoint_address_target_kind=Node(假设)
    • __meta_kubernetes_endpoint_address_target_name=node-1
    • __meta_kubernetes_pod_name=nginx-123
  2. 第一次操作:正则不匹配,pod 标签未设置。
  3. 第二次操作:设置 pod=nginx-123

最佳实践建议

  1. 保持操作顺序
    先通过 Endpoint 元数据提取(第一次操作),再通过 Pod 自身元数据补充(第二次操作)。

  2. 添加防御性标签
    可在两次操作后添加默认值,确保标签始终存在:

    - target_label: pod
      replacement: unknown  # 若前面操作都失败,设置为 unknown
    
  3. 结合其他标签
    通常还会同时提取 namespaceservice 等标签,形成完整的标识体系:

    - source_labels: [__meta_kubernetes_namespace]
      target_label: namespace
    - source_labels: [__meta_kubernetes_service_name]
      target_label: service
    

这种看似冗余的配置实际上是一种 健壮性设计,确保在复杂的 Kubernetes 环境中,无论 Pod 以何种方式被发现,都能正确获取和设置关键标签。
在 relabeling 配置中,连续对同一个标签(如 pod)进行操作 是一种常见的 防御性编程策略,用于确保在各种场景下都能正确获取标签值。

那么问题来了,关于node的匹配为什么只有一次呢?

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐