ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

kube-state-metrics自定义标签暴露指南:allowlist与relabel配置

kube-state-metrics自定义标签暴露指南:allowlist与relabel配置 1. 问题现象Node 上明明有 rack、dc 标签Prometheus 指标里却查不到1.1 一次失败的按机柜聚合有段时间我在做告警治理想把节点故障告警按物理机柜维度聚合这样值班同学一眼就能看出是哪一排机柜的网络或电源出了问题。集群里的 Node 对象上很早就打了rack和dc标签Kubernetes 本身管理这些标签没有任何问题。于是在 Prometheus 里我写了一条很常规的查询count by (rack) (kube_node_info)结果让我很意外rack这个维度根本不存在查询结果里都是空字符串或者直接没有这个标签。当时第一反应是采集配置出了问题检查了 Prometheus 的 service monitor、抓取任务、relabel 规则都没毛病。最后才把矛头指向 Prometheus 生态里负责暴露 Kubernetes 对象状态的组件——kube-state-metrics。它默认并不会把我们在 Kubernetes 对象上自定义的标签直接放到指标里。这就是这篇文章要解决的典型场景怎么让 kube-state-metrics 把额外的资源标签注入到指标中。1.2 kube-state-metrics 默认不带你自定义标签的设计逻辑先明确一个容易混淆的概念。我们常说的 Prometheus 监控体系里有两个职责完全不同的抓取目标node-exporter 负责暴露节点的机器指标比如 CPU、内存、磁盘kube-state-metrics下文简称 KSM负责监听 Kubernetes API Server把 Deployment、Pod、Node、Namespace 等对象的状态翻译成指标。KSM 的核心工作是对象状态 → 指标的转换。比如kube_node_info这个指标它表示 Node 对象的基础信息指标上默认带的是 Node 的名称、UID、内核版本、容器运行时版本等。而你在 Node metadata 里打的rack、dc这类自定义标签属于业务自定义元数据KSM 认为不是每个集群都需要暴露所以默认情况下不会把它们作为指标标签输出。这不是 KSM 偷懒而是有意为之。Kubernetes 对象上的 label 可以非常灵活团队可以随手打created-by、owner、cost-center、feature-flag等各种键值。如果 KSM 把所有 label 都直接变成指标标签series 数量会随着 label 的取值组合迅速膨胀。Prometheus 的存储和查询对高基数非常敏感一个取值不断变化的 label 足以把一个单机实例的查询拖垮。KSM 于是选择了一种更稳妥的策略默认只暴露 name、namespace、uid 等跟对象身份强相关的固定标签自定义标签必须通过白名单显式开启。1.3 KSM 的标签来源内置字段与白名单提取KSM 里的指标标签主要来自三类来源对象内置字段metadata.name、metadata.namespace、metadata.uid这类对象本身就有的信息直接作为指标的基础标签。allowlist 白名单提取通过启动参数把metadata.labels里指定的 key 提取出来附加到对应资源的指标上。模板生成标签某些资源类型会额外生成与对象属性相关的标签比如 Deployment 指标上的deployment标签、Namespace 指标上的namespace标签。这里说的加入额外资源标签指的就是第二类用启动参数把metadata.labels中我们关心的 key 加入指标。需要特别提醒的是KSM 只负责暴露 label不做 Prometheus relabel 的操作。你要加的标签必须已经在 Kubernetes 对象的 labels 上存在KSM 才有东西可提取。如果 Node 上根本没打rack标签那不管 KSM 参数怎么配指标里都不会凭空出现这个标签。2. 参数选型--metric-labels-allowlist、--labels-allowlist 到底用哪个2.1 版本分水岭v2.0 前后的参数变化在 KSM 的 v2.0 版本之前暴露自定义标签的参数叫--labels-allowlist用法类似--labels-allowlistpods[team,env],nodes[rack,dc]但到了 v2.0官方把参数改成了--metric-labels-allowlist语义更明确只影响指标 labels 的提取和 annotations 的处理区分开。与此同时--metric-annotations-allowlist也被加入用于暴露对象的 annotations。如果你还在用 v2.0 之前的版本写--labels-allowlist没问题如果你已经升级到 v2.0建议使用新参数名。旧参数在新版本里未必会报错但不同小版本的表现不完全一致有的会打 warning有的可能被忽略。最稳妥的方式是部署前先执行kube-state-metrics --help看当前版本到底支持哪个参数。我见过不少升级后配置静默失效的案例集群从 v1.x 升到 v2.x部署 manifest 没跟着改结果自定义标签全没了告警规则和 Grafana 面板一片红。这个排查过程很折腾因为 Prometheus 抓取是正常的指标都在就是少了几个 label。所以升级 KSM 后除了看 Pod 启动日志第一件事就是确认参数名跟上了版本。2.2 白名单语法格式和资源作用域参数语法格式是resource[label1,label2,...]多个资源类型之间用逗号分隔。例如--metric-labels-allowlistnodes[rack,dc],pods[team,environment,app.kubernetes.io/name],namespaces[team]这段配置的含义是Node 对象上的rack、dc标签会出现在 KSM 的 Node 相关指标上Pod 对象上的team、environment、app.kubernetes.io/name标签会出现在 Pod 相关指标上Namespace 对象上的team标签会出现在 Namespace 相关指标上。需要注意两点。第一app.kubernetes.io/name这种带斜杠的 label key 在指标里不会原样保留。Prometheus 的 label name 只允许[a-zA-Z_][a-zA-Z0-9_]*所以 KSM 会把app.kubernetes.io/name转成app_kubernetes_io_name。后面第五章我会再展开讲。第二资源类型前缀是可选的。如果只想对所有资源类型统一开启某个标签可以用空资源名加通配符的写法但这有基数风险建议谨慎。KSM 支持这种白名单的资源类型比较多我列几个实际常用的资源类型典型用途nodes机柜、机房、地域、硬件规格pods业务团队、环境、应用名namespaces负责人、成本中心、环境deployments发布平台、Git 仓库路径services网关、域名、流量入口分类persistentvolumeclaims存储类型、备份策略statefulsets / daemonsets中间件集群、节点组件分类2.3 用 [*] 通配前先把基数账算清楚--metric-labels-allowlist[*]或--metric-labels-allowlistpods[*]表示把所有自定义 label 都暴露出来。从功能上看这确实省事不用一个个列 label 名字了但在生产环境我很不建议这么干。基数的膨胀往往不是一次性爆炸而是慢慢腐蚀。举个例子假如集群里有 2000 个 Pod其中一部分带有app.kubernetes.io/revision这种每次发布都会变化的 label当你允许 Pod 的所有 label 暴露后kube_pod_container_status_restarts_total这类指标就会按照每个 Pod × 每个修订版本生成多个 series。Prometheus 对这种组合爆炸的容忍度很低一旦触发高基数告警查询性能会明显下降存储占用也会快速上升。我建议的做法是白名单只放真正在查询、告警、Grafana 面板里用得到的 label。新增 label 前先问一句这个维度有没有对应的聚合场景如果答不上来就不加。3. 实操为 Node、Pod、Namespace 添加额外资源标签3.1 直接修改 KSM Deployment以 YAML 为例如果你的 kube-state-metrics 是直接用 manifest 部署的修改方式很简单编辑 Deployment在容器的 args 里加上--metric-labels-allowlist。先确认当前 deploymentkubectl -n monitoring get deploy kube-state-metrics -o yaml找到spec.template.spec.containers[0].args加入参数。例如spec: template: spec: containers: - name: kube-state-metrics args: - --port8080 - --metric-labels-allowlistnodes[rack,dc] - --metric-labels-allowlistpods[team,environment,app.kubernetes.io/name] - --metric-labels-allowlistnamespaces[team]这里我用了多个--metric-labels-allowlist参数每个参数只针对一个资源类型可读性更好。实际使用中也可以合并到一个参数内用逗号分隔不同资源类型效果一样。改完后 applyKSM Pod 会滚动重启。要确认参数确实生效可以看一眼运行中的 Pod 启动命令kubectl -n monitoring get pod -l app.kubernetes.io/namekube-state-metrics -o jsonpath{.items[0].spec.containers[0].args}3.2 通过 kube-prometheus-stack 的 values.yaml 配置如果 KSM 是通过 Helm 安装的我优先推荐在 values.yaml 里配置而不是直接改 Deployment。因为直接改 Deployment 在下次helm upgrade时会被覆盖配置就丢了。在 kube-prometheus-stack 里配置通常长这样kube-state-metrics: metricLabelsAllowlist: - nodes[rack,dc] - pods[team,environment,app.kubernetes.io/name] - namespaces[team]这里有个小坑不同版本的 kube-prometheus-stack / prometheus-community chart字段名不完全一样。有的版本用metricLabelsAllowlist旧一点的可能用labelsAllowlist还有一些版本倾向于直接传extraArgs。最靠谱的方法是先看当前 chart 的默认 valueshelm show values prometheus-community/kube-prometheus-stack | grep -B2 -A5 -i allowlist确认字段名后再写避免 chart 渲染时把你的配置悄悄忽略掉。如果你用的是独立的 kube-state-metrics chartvalues 里的写法可能更接近kube-state-metrics: extraArgs: - --metric-labels-allowlistnodes[rack,dc] - --metric-labels-allowlistpods[team,environment]3.3 验证标签是否生效Prometheus UI 与命令行双管齐下参数加完后别急着去 Grafana 看面板先在 Prometheus UI 的 Graph 页面验证一下。以 Node 为例执行这条查询kube_node_info结果里应该能直接看到rack、dc两个标签。如果看到rack或者干脆没有这个标签可能的原因有三个Node 对象上本身没有打这个 labelKSM 参数里的资源类型写错了比如给 nodes 写的白名单是rack但参数被写到了--metric-labels-allowlistnamespaces[rack]KSM 版本参数名不兼容参数没被解析成功。还有一种验证方式直接看 KSM 暴露的 metrics 端点搜索某个具体指标curl -s http://ksm-service-address:8080/metrics | grep ^kube_node_info | head -5这能看到最新样本的完整 label set非常直观。3.4 在 Grafana 里按新标签聚合的效果配置生效后我们再把文章开头那条查询拉出来count by (rack) (kube_node_info)现在结果就正常了每个机柜标识对应一个节点数量。后续做故障聚合、成本分摊、容量规划都方便了。把这套标签和告警规则结合的例子也很常见count by (rack) (kube_node_status_condition{conditionReady,statustrue} 0)这条规则可以统计每个机柜里不健康的节点数量配合告警路由就能把问题定位精确到机柜而不是让值班同学在一堆 IP 里猜。4. 不重启 KSM 的备选方案用 metric_relabel_configs 从 kube_pod_labels 提取标签4.1 为什么很多老集群选 relabel 而不是 allowlist除了 KSM 白名单还有一个老办法在 Prometheus 的抓取配置里用metric_relabel_configs对 KSM 暴露的label_系列标签做提取。KSM 默认会生成几个专门的指标来展示对象的 label比如kube_pod_labels、kube_node_labels、kube_namespace_labels。这些指标本身带着label_key格式的标签例如kube_node_labels{nodenode-01, label_rackrack-a1, label_dcdc-sh}很多老集群不愿意动 KSM 部署或者 KSM 版本太旧不支持 allowlist就会在 Prometheus 抓取时把label_前缀的标签改写成普通标签。这样做的好处是不用重启 KSM纯 Prometheus 配置层面就能解决。4.2 labelmap 正则提取label_前缀标签在 Prometheus 的scrape_configs里针对 kube-state-metrics 这个 job 加一段metric_relabel_configsscrape_configs: - job_name: kube-state-metrics honor_timestamps: true static_configs: - targets: [kube-state-metrics.monitoring:8080] metric_relabel_configs: - action: labelmap regex: label_(.) replacement: $1labelmap动作的含义是凡是标签名匹配正则的都用 replacement 重新生成一个标签。label_rack会变成racklabel_dc会变成dc。全部在采集阶段完成KSM 不需要做任何改动。这个方案在生产上确实是可行的我见过不少大集群就是这么干的。但要注意副作用该 job 下所有以label_开头的标签都会被重命名如果有些 label 本身就以label_开头可能出现覆盖或重复。而且 relabel 后的标签和原始label_xxx同时存在可能让指标体积变大。4.3 两种方案的边界什么时候该用哪种allowlist 和 relabel 不是互斥的选择哪种要看具体场景。对比维度KSM allowlistmetric_relabel_configs配置位置KSM Deployment 或 Helm valuesPrometheus 抓取配置生效方式KSM Pod 重启Prometheus 热加载影响范围只影响 allowlist 指定的资源指标整个 job 下所有指标灵活性高可精确指定资源类型和 label较低按标签名前缀批量处理维护成本需要升级/重启 KSM只改配置但不直观我的建议是如果你的 KSM 版本支持--metric-labels-allowlist优先用它因为它能精确控制哪些资源类型暴露哪些 label语义清晰出问题的概率低。只有当你没法轻易重启 KSM、或者集群有特殊的全局标签提取需求时再考虑metric_relabel_configs。5. 几个容易翻车的联动问题HPA、跨集群采集与 label 名改写5.1 HPA 配合 KSM 指标时自定义标签要注意什么很多团队会用 Prometheus Adapter 把 KSM 的指标暴露给 HorizontalPodAutoscaler 使用比如根据kube_pod_container_status_restarts_total或kube_deployment_status_replicas做扩缩容。这时候你可能会担心新加的自定义标签会不会影响 HPA直接回答如果指标标签集里加入了新 labelHPA 本身不会被撑爆但 Prometheus Adapter 的metricsQuery和seriesQuery配置里可能会因为查询条件变了导致匹配不到指标。举个例子Adapter 默认按namespace和pod做资源关联这不会受自定义标签影响但如果你的 Adapter 配置里用labelSelector过滤了某些对象而过滤条件依赖的 label 又被 allowlist 改变了命名比如斜杠变下划线就会匹配不上。另外如果你在 HPA 里使用的是针对某个 Deployment 副本数的 custom metric指标中新增的自定义标签不会破坏现有的匹配关系因为 HPA 关联的是被评估对象的 name而不是某个业务标签。真正的风险点在于你想按新标签拆分告警或扩容策略但 Prometheus Adapter 的metricsQuery没有把新标签保留在返回结果里。这个不是 KSM 的问题是 Adapter 层的聚合逻辑问题需要检查metricsQuery中是否有group by或聚合操作把自定义 label 丢掉了。5.2 跨集群采集时的标签冲突与区分如果你的公司有多个 Kubernetes 集群监控数据统一汇总到一套 Prometheus 或 Thanos / VictoriaMetrics 里那么 Node 名称、Pod 名称、甚至 Namespace 名称都可能是重复的。此时如果只依赖 KSM 的自定义标签做区分远远不够因为跨集群的指标合流时不同集群的同类对象会互相覆盖。我比较推荐的做法是在采集阶段用relabel_configs给每个集群打一个全局静态标签比如clusterscrape_configs: - job_name: kube-state-metrics static_configs: - targets: [cluster-a-ksm:8080] labels: cluster: cluster-a这个cluster标签和 KSM allowlist 提取的标签是不同来源的一个来自抓取任务配置一个来自对象元数据。两者配合使用才能回答哪个集群的哪个节点、在哪个机柜这样的问题。5.3 特殊字符去哪了allowlist 对 label key 的规范化规则Kubernetes label key 允许出现的字符比 Prometheus label name 宽松得多。app.kubernetes.io/name、example.com/team、my-label这些在 Kubernetes 里都是合法的但 Prometheus 的 label name 只允许[a-zA-Z_][a-zA-Z0-9_]*斜杠和短横线都不能出现。KSM 在做 label 提取时会对非法的字符做替换斜杠、点、短横线一律替换成下划线。所以你写在 allowlist 里的 key 是app.kubernetes.io/name最后在指标上看到的标签名大概率是app_kubernetes_io_name。这个改写在kube_pod_labels指标的label_系列里同样存在比如kube_pod_labels{label_app_kubernetes_io_nameorder-service}所以配置告警规则或 Grafana 时不要想当然地写成原始的 label key先去 Prometheus UI 里搜一下实际标签名再写表达式。这个坑很多人踩过包括我自己一次告警规则里写错标签名结果静默了半个月才发现。6. 生产环境落地建议五条让我躲过坑的经验6.1 白名单字段必须走评审流程给 KSM 加一个 label 看起来只是改一行参数但它影响的是整条 Prometheus 数据链路的基数。我建议在团队内部定一个简单规矩所有新增到 allowlist 的标签必须说明使用场景和预期基数。没有聚合场景的标签不允许加。这样可以从源头控制 series 数增长。6.2 把标签命名规范写进团队约定Kubernetes 对象的 label key 最好尽量使用不带斜杠的后缀部分作为 Prometheus 里的最终标签名或者反过来提前约定所有要暴露到监控系统的 label key 都使用[a-zA-Z0-9_]以内的字符。比如用rack不用example.com/rack。这样可以减少 KSM 转换带来的标签名歧义也方便告警模板和面板变量复用。6.3 用告警和 recording rule 监控 series 增长我对每个 Prometheus 实例都会配置一条针对 series 数量的观察型查询sum(scrape_series_added) by (job)同时关注kube_state_metrics自身的tsdb_head_series和prometheus_tsdb_head_series。一旦发现新增 label 后 series 数异常上升能第一时间感知而不是等到查询变慢才去排查。6.4 需要剔除指标时保留一副切除工具allowlist 只负责加如果某个 label 或指标不需要了你得会减。KSM 暴露的指标本身全量很大有些公司会用--metric-denylist一类参数排除不需要的高基数指标。保留这些参数配置在出现基数问题时能快速止血。不要只盯着增加标签这一个维度删掉无用的内置标签同样是控制资源消耗的手段。6.5 文档化你的 allowlist 配置最后一条看起来像废话但实际能救很多人。把每个集群的 KSM 参数记录下来包括资源类型、暴露的 label、用途、负责人。下次谁要改配置先看文档再改代码然后验证。我的经验是监控系统的绝大多数线上事故都来自无人知道这条配置为什么存在的隐性维护债。如果你正在经历Kubernetes 对象标签在 Prometheus 里不可见的问题照着前面的配置加一下 allowlist再验证一遍标签是否出现在目标指标上基本就能解决。配置通常只要几分钟但省下来的排查时间可能是好几天。
返回列表