victoriaMetrics无法获取抓取target的问题

简介: victoriaMetrics无法获取抓取target的问题

问题描述

最近在新环境中部署了一个服务,其暴露的指标路径为:10299/metrics,配置文件如下(名称字段有修改):

apiVersion: v1
items:
- apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
labels:
app_id: audit
name: audit
namespace: default
spec:
endpoints:
- path: /metrics
targetPort: 10299
namespaceSelector:
matchNames:
- default
selector:
matchLabels:
app_id: audit

但在vmagent上查看其状态如下,vmagent无法发现该target:

一般排查方式

  1. 确保服务本身没问题,可以通过${podIp}:10299/metrics访问到指标
  2. 确保vmservicescrape-->service-->enpoints链路是通的,即配置的selector字段能够正确匹配到对应的资源
  3. 确保vmservicescrape格式正确。注:vmservicescrape资源格式不正确可能会导致vmagent无法加载配置,可以通过第5点检测到
  4. 确保vmagent中允许发现该命名空间中的target
  5. 在vmagent的UI界面执行reload,查看vmagent的日志是否有相关错误提示

经过排查发现上述方式均无法解决问题,更奇怪的是在vmagent的api/v1/targets中无法找到该target,说明vmagent压根没有发现该服务,即vmservicescrape配置没有生效。在vmagent中查看上述vmservicescrape生成的配置文件如下(其拼接了静态配置),可以看到它使用了kubernetes_sd_configs的方式来发现target:

- job_name: serviceScrape/default/audit/0
metrics_path: /metrics
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app_id]
regex: audit
action: keep
- source_labels: [__meta_kubernetes_pod_container_port_number]
regex: "10299"
action: keep
- 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}
- target_label: endpoint
replacement: "8080"
kubernetes_sd_configs:
- role: endpoints
namespaces:
own_namespace: false
names:
- default

代码分析

既然配置没有问题,那只能通过victoriametrics的kubernetes_sd_configs的运作方式看下到底是哪里出问题了。在victoriametrics的源码可以看到其拼接的target url如下:

scrapeURL := fmt.Sprintf("%s://%s%s%s%s", schemeRelabeled, addressRelabeled, metricsPathRelabeled, optionalQuestion, paramsStr)

其中:

  • schemeRelabeled:默认是http
  • metricsPathRelabeled:即生成的配置文件的metrics_path字段
  • optionalQuestionparamsStr没有配置,可以忽略

最主要的字段就是addressRelabeled,它来自一个名为"__address__"的标签

func mergeLabels(swc *scrapeWorkConfig, target string, extraLabels, metaLabels map[string]string) []prompbmarshal.Label {
  ...
  m["job"] = swc.jobName
  m["__address__"] = target
  m["__scheme__"] = swc.scheme
  m["__metrics_path__"] = swc.metricsPath
  m["__scrape_interval__"] = swc.scrapeInterval.String()
  m["__scrape_timeout__"] = swc.scrapeTimeout.String()
  ...
}

继续跟踪代码,可以看到该标签是通过sc.KubernetesSDConfigs[i].MustStart获取到的,从KubernetesSDConfigs的名称上看,它就是负责处理kubernetes_sd_configs机制的:

func (sc *ScrapeConfig) mustStart(baseDir string) {
  swosFunc := func(metaLabels map[string]string) interface{} {
    target := metaLabels["__address__"]
    sw, err := sc.swc.getScrapeWork(target, nil, metaLabels)
    if err != nil {
      logger.Errorf("cannot create kubernetes_sd_config target %q for job_name %q: %s", target, sc.swc.jobName, err)
      return nil
    }
    return sw
  }
  for i := range sc.KubernetesSDConfigs {
    sc.KubernetesSDConfigs[i].MustStart(baseDir, swosFunc)
  }
}

继续往下看,看看这个"__address__"字段到底是什么,函数调用如下:

MustStart--> cfg.aw.mustStart --> aw.gw.startWatchersForRole --> uw.reloadScrapeWorksForAPIWatchersLocked --> o.getTargetLabels

最后一个函数getTargetLabels是个接口方法

type object interface {
  key() string
  // getTargetLabels must be called under gw.mu lock.
  getTargetLabels(gw *groupWatcher) []map[string]string
}

getTargetLabels的实现如下,这就是kubernetes_sd_configs的各个role的具体实现。上述服务用到的是kubernetes_sd_configsrole为endpoints

实现如下:

func (eps *Endpoints) getTargetLabels(gw *groupWatcher) []map[string]string {
  var svc *Service
  if o := gw.getObjectByRoleLocked("service", eps.Metadata.Namespace, eps.Metadata.Name); o != nil {
    svc = o.(*Service)
  }
  podPortsSeen := make(map[*Pod][]int)
  var ms []map[string]string
  for _, ess := range eps.Subsets {
    for _, epp := range ess.Ports {
      ms = appendEndpointLabelsForAddresses(ms, gw, podPortsSeen, eps, ess.Addresses, epp, svc, "true")
      ms = appendEndpointLabelsForAddresses(ms, gw, podPortsSeen, eps, ess.NotReadyAddresses, epp, svc, "false")
    }
  }
  // See https://kubernetes.io/docs/reference/labels-annotations-taints/#endpoints-kubernetes-io-over-capacity
  // and https://github.com/kubernetes/kubernetes/pull/99975
  switch eps.Metadata.Annotations.GetByName("endpoints.kubernetes.io/over-capacity") {
  case "truncated":
    logger.Warnf(`the number of targets for "role: endpoints" %q exceeds 1000 and has been truncated; please use "role: endpointslice" instead`, eps.Metadata.key())
  case "warning":
    logger.Warnf(`the number of targets for "role: endpoints" %q exceeds 1000 and will be truncated in the next k8s releases; please use "role: endpointslice" instead`, eps.Metadata.key())
  }
  // Append labels for skipped ports on seen pods.
  portSeen := func(port int, ports []int) bool {
    for _, p := range ports {
      if p == port {
        return true
      }
    }
    return false
  }
  for p, ports := range podPortsSeen {
    for _, c := range p.Spec.Containers {
      for _, cp := range c.Ports {
        if portSeen(cp.ContainerPort, ports) {
          continue
        }
        addr := discoveryutils.JoinHostPort(p.Status.PodIP, cp.ContainerPort)
        m := map[string]string{
          "__address__": addr,
        }
        p.appendCommonLabels(m)
        p.appendContainerLabels(m, c, &cp)
        if svc != nil {
          svc.appendCommonLabels(m)
        }
        ms = append(ms, m)
      }
    }
  }
  return ms
}

可以看到,"__address__"其实就是拼接了p.Status.PodIPcp.ContainerPort,而p则代表一个kubernetes的pod数据结构,因此要求:

  1. pod状态是running的,且能够正确分配到PodIP
  2. p.Spec.Containers[].ports[].ContainerPort中配置了暴露metrics target的端口

问题解决

鉴于上述分析,查看了一下环境中的deployment,发现该deployment只配置了8080端口,并没有配置暴露指标的端口10299。问题解决。

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app_id: audit
name: audit
namespace: default
spec:
...
template:
metadata:
...
spec:
containers:
- env:
- name: APP_ID
value: audit
ports:
- containerPort: 8080
protocol: TCP
...

总结

kubernetes_sd_configs方式其实就是通过listwatch的方式获取对应role的配置,然后拼接出target的__address__,此外它还会暴露一些额外的指标,如:

  • __meta_kubernetes_endpoint_hostname: Hostname of the endpoint.
  • __meta_kubernetes_endpoint_node_name: Name of the node hosting the endpoint.
  • __meta_kubernetes_endpoint_ready: Set to true or false for the endpoint's ready state.
  • __meta_kubernetes_endpoint_port_name: Name of the endpoint port.
  • __meta_kubernetes_endpoint_port_protocol: Protocol of the endpoint port.
  • __meta_kubernetes_endpoint_address_target_kind: Kind of the endpoint address target.
  • __meta_kubernetes_endpoint_address_target_name: Name of the endpoint address target.
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
存储 前端开发 索引
【面试题】数组去重的五种方法(必会)
【面试题】数组去重的五种方法(必会)
635 1
|
Dubbo Cloud Native Java
ZooKeeper 避坑实践:由于jute.maxbuffer 设置问题导致的集群不可用
微服务引擎 MSE 面向业界主流开源微服务项目, 提供注册配置中心和分布式协调(原生支持 Nacos/ZooKeeper/Eureka )、云原生网关(原生支持Higress/Nginx/Envoy,遵循Ingress标准)、微服务治理(原生支持 Spring Cloud/Dubbo/Sentinel,遵循 OpenSergo 服务治理规范)能力。
ZooKeeper 避坑实践:由于jute.maxbuffer 设置问题导致的集群不可用
|
3月前
|
人工智能 Ubuntu 网络安全
OpenClaw阿里云/本地部署喂饭级教程与自主工作流搭建,让 AI 从被动工具变身超级员工
90%的用户都把OpenClaw用成了“高级版ChatGPT”——问一句答一句,沦为被动等待指令的工具。但真正的高手,能让OpenClaw变身24小时不休息的超级员工:自动整理报告、推进项目进度、甚至提前预判需求。核心差距不在于技术或设备,而在于部署后的5个关键设置。
879 5
|
4月前
|
人工智能 弹性计算 机器人
Clawdbot部署安装、接入钉钉全流程,跟着教程一步步操作,新手也能成功部署
OpenClaw(Clawdbot)是现代化RPA平台,支持零代码自动化桌面任务。本教程详解其在阿里云计算巢的一键部署及钉钉机器人接入全流程,含应用创建、卡片配置、权限申请与发布,新手30分钟即可打造专属AI员工。
|
6月前
|
人工智能 数据可视化 BI
n8n 与 Coze 怎么选?低代码集成与AI智能体的自动化路线对比
本文对比了n8n和Coze两款自动化工具。n8n开源灵活,适合技术人员处理复杂系统集成和自定义逻辑;Coze无代码易上手,便于业务人员快速搭建审批等标准流程。实际应用中,可根据团队角色和流程复杂度选择,或混合使用以形成完整的自动化闭环。
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
安全 关系型数据库 MySQL
mysql 安装插件 validate_password
mysql 安装插件 validate_password
1245 0
|
Kubernetes 调度 容器
【赵渝强老师】K8s的Job控制器多工作队列的并行方式
Kubernetes Job 是一次性任务控制器,用于控制 Pod 中的容器执行特定任务。本文介绍了 Job 控制器的工作原理、运行方式及多工作队列并行执行的示例。示例中创建了 5 个作业,以 3 个队列并行执行,整个过程需 2 分钟。文中还提供了详细的 YAML 文件配置和执行命令。
419 0
|
Prometheus 监控 Cloud Native
性能监控之 node_exporter+Prometheus+Grafana 实现主机监控
【8月更文挑战第3天】性能监控之 node_exporter+Prometheus+Grafana 实现主机监控
2758 0
|
存储 前端开发 数据挖掘
什么是埋点测试?
点其实就是在程序中的某个位置加一个标记,当用户触发到某个行为的时候,就采集一下数据,然后将数据上报到某个位置进行存储,埋点的最终目的是收集到相关的数据,用于给运营人员提供数据支撑等。
什么是埋点测试?