首页
/ Kubernetes 指标稳定性治理实战:hack/tools/instrumentation 工具链完全指南

Kubernetes 指标稳定性治理实战:hack/tools/instrumentation 工具链完全指南

2026-09-06 19:02:05作者:尤辰城Agatha

导读

本文聚焦 Kubernetes 主仓库中负责指标(metrics)工程治理的工具链目录 hack/tools/instrumentation,深入讲解其官方文档 README.md 定义的四大能力:稳定指标(STABLE 级指标)的回归测试、指标命名规范的自动化校验、指标到"组件/端点"的映射机制,以及面向 k8s/website 的指标文档自动生成流程。读完本文,你将掌握 Kubernetes 核心组件(kube-apiserver、kubelet 等)如何保证对外暴露的 Prometheus 指标在跨版本演进中不破坏契约,理解 golden list(黄金清单)测试模式在大型代码库中的工程实践,并能独立完成"新增一个指标目录"的接入与文档同步操作。

文中所有命令均以 Kubernetes 仓库根目录为当前目录执行,引用的文件路径均以仓库根目录为起点。

一、模块全景:instrumentation 目录里有什么

Kubernetes 的主仓库在 pkg/cmd/staging/src/k8s.io/ 下散布着大量 metrics.NewCountermetrics.NewHistogram 之类的指标声明。为了让"哪些指标算稳定、指标暴露在哪个端点、文档是否与代码一致"这件事可被持续验证,社区将一套静态分析工具收敛到了 hack/tools/instrumentation

文件/目录 职责
main.go 指标提取主程序:对指定目录/文件做 Go AST 静态分析,抽取稳定指标并输出 YAML
endpoint-mappings.yaml 定义"源码路径 → 组件 + 端点"的映射规则
endpoint_mapping.go 映射规则的加载与匹配实现
stability-utils.sh 被仓库根 hack/ 下各校验/更新脚本共用的函数库
test-verify.sh / test-update.sh 针对 testdata 假指标的验证与更新入口
testdata/ 存放 golden list 与用于演练的假指标代码
documentation/ 从指标清单渲染出 Markdown 指标参考文档的生成器
metric-sort/ 指标排序辅助工具

该工具本身是一个独立 Go 模块(见 go.mod),它通过 k8s.io/component-base/metrics 包提供的数据结构来完成指标定义解析。文档性质上,本目录属于开发工具链而非运行时组件,因此所有结论都以仓库内脚本与 Go 源码为依据。

二、稳定指标回归测试:Golden List 机制

2.1 为什么需要"稳定指标清单"

Kubernetes 将指标按 API 契约强度分为三个稳定级别(该语义同时体现在 documentation/main.go 的模板描述中):

  • STABLE(稳定):受严格 API 契约约束,整个生命周期内不允许增删 label,改名/删除均属于破坏性变更;
  • BETA:契约较宽松,生命周期内不允许移除 label,但允许新增 label
  • ALPHA:无任何保证,后续版本可能整体删除或破坏既有仪表盘/告警。

由于稳定的对外指标相当于 Kubernetes 的"公共 API 面",仓库用一份受检入的 golden list 对其实施硬性控制。README.md 明确指出:

如果你新增或删除一个稳定指标,这个测试会失败,你需要更新 testdata/ 中存储的 golden list;对该文件的修改必须由 sig-instrumentation 评审。

这份 golden list 就是 testdata/stable-metrics-list.yaml,其内容是按"稳定级别 + FQName"排序的指标 YAML 序列,例如:

- name: automatic_reload_last_timestamp_seconds
  subsystem: authentication_config_controller
  namespace: apiserver
  help: Timestamp of the last automatic reload of authentication configuration split
    by status and apiserver identity.
  type: Gauge
  stabilityLevel: BETA
  labels:
  - apiserver_id_hash
  - status

2.2 触发与修复闭环

CI 中通过 hack/verify-generated-stable-metrics.sh 跑校验:它构建工具后,用 git ls-files 收集受版本控制的 Go 文件,实时静态分析抽取出的指标清单与 golden list 做 diff,不一致即失败(实现见 stability-utils.shkube::validate::stablemetrics)。

当你在开发分支上有意新增/移除稳定指标时,按官方文档的流程更新清单:

./hack/update-generated-stable-metrics.sh

该脚本(见 hack/update-generated-stable-metrics.sh)实际调用 kube::update::stablemetrics:重新分析全部源码并把结果写回 testdata/stable-metrics-list.yaml。随后把变更后的文件随 PR 提交,交由 sig-instrumentation 评审即可。

2.3 校验范围:什么代码会被纳入分析

分析范围并非整个仓库,而是刻意排除与"对外契约"无关的目录。从 stability-utils.shfind_files_to_check 可以看到排除规则:vendor/、所有 testdata/third_party/hack/test/*_test.go 以及 hack/tools/instrumentation 自身;换言之,只有随二进制发布、影响线上指标面的生产代码才参与契约校验。

三、用假指标演练稳定性框架:test-verify 与 test-update

直接在生产指标上做试验会污染 golden list。为此工具链提供了独立的测试夹具

官方文档给出的演练方法是:往夹具文件里添加你想要试验的指标,然后验证:

./hack/tools/instrumentation/test-verify.sh

该脚本会 diff test-stable-metrics-list.yaml,若你的假指标写法未被分析器正确识别就会失败。确认行为符合预期后,再更新夹具对应的 golden list:

./hack/tools/instrumentation/test-update.sh

这也解释了文档反复强调的"先 test-verify、再 test-update"的节奏——两者在 stability-utils.sh 中分别对应 kube::validate::test::stablemetricskube::update::test::stablemetrics,唯一区别是 diff 结果是否写回 golden 文件。

四、指标命名规范校验:Prometheus 命名约定

仓库要求所有指标(不限稳定级别)都符合 Prometheus 命名规范。运行:

./hack/verify-metrics-naming.sh

其背后(见 hack/verify-metrics-naming.sh)会执行:

"${GOBIN}/instrumentation" -allstabilityclasses -lint pkg staging

即一次性扫描 pkgstaging 两个目录、覆盖全部稳定级别,并开启 -lint 模式。在 main.go 的实现中,lint 阶段会把每个指标拼成一段 # HELP/# TYPE 的 Prometheus 文本,交给 prometheus/client_golangpromlint 校验;TimingRatioHistogram 会被当作 histogram 检查。项目维护了一份允许的例外清单,若发现例外未使用,工具还会报"Unused exceptions found"并提示清理 staging/src/k8s.io/component-base/metrics/testutil/promlint.go 中的例外条目——避免例外越积越多。

命令退出码为 0 且输出 All metrics passed linter (excluding exceptions) 即代表通过。

五、组件端点映射:endpoint-mappings.yaml 全解析

生成指标文档时,需要知道每个指标由哪个组件在哪个端点暴露。指标源码文件路径与组件/端点的关联关系存放在 endpoint-mappings.yaml 中,README 对其 Schema 给出了精确定义。

5.1 Components:按前缀把文件归入组件

指标通过静态分析发现,每发现一个指标就会根据 YAML 中 coreComponents 下的路径前缀列表尝试匹配其源文件路径。核心组件包括:

  • kube-apiservercmd/kube-apiserver/pkg/controlplane/pkg/registry/pkg/serviceaccount/plugin/pkg/admission/plugin/pkg/auth/ 以及 staging/src/k8s.io/apiserver/apiextensions-apiserver/kube-aggregator/pod-security-admission/
  • kube-controller-managercmd/kube-controller-manager/pkg/controller/staging/src/k8s.io/controller-manager/
  • kube-schedulercmd/kube-scheduler/pkg/scheduler/
  • kube-proxycmd/kube-proxy/pkg/proxy/
  • kubeletcmd/kubelet/pkg/kubelet/pkg/credentialprovider/pkg/volume/
  • cloud-controller-managerstaging/src/k8s.io/cloud-provider/

规则要点(README 强调):

  • 每个组件可以拥有多条路径
  • 每个组件只定义一次
  • 匹配是路径前缀级别的(对应 endpoint_mapping.goinferComponentsstrings.HasPrefix)。
coreComponents:
  kube-apiserver:
    - "cmd/kube-apiserver/"
    - "pkg/controlplane/"
  kube-controller-manager:
    - "cmd/kube-controller-manager/"
    - "pkg/controller/"
  ...

5.2 sharedPaths:共享基础设施指标

所有"核心"组件都默认继承公共指标(如 kubernetes_healthcheck)。这些公共指标的源码目录被列在 sharedPaths 下:

sharedPaths:
  - "staging/src/k8s.io/component-base/"

当文件路径命中 sharedPaths 时(endpoint_mapping.goinferComponentEndpoints),该指标会被归属到全部 coreComponents 上。这与 coreComponents 的前缀匹配是两条独立的推断路径,互不叠加。

5.3 Endpoints:默认端点与覆盖规则

默认的指标端点被假定为 /metrics。需要覆盖的路径通过 endpointMappings 声明,当前仓库里的三条例子正好体现了典型场景:

endpointMappings:
  - pathContains: "staging/src/k8s.io/component-base/metrics/prometheus/slis"
    endpoint: /metrics/slis
  - pathContains: "pkg/kubelet/metrics/collectors/resource_metrics.go"
    endpoint: /metrics/resource
  - pathContains: "pkg/kubelet/prober/"
    endpoint: /metrics/probes

# 兜底:未命中任何规则时的默认端点
defaultEndpoint: /metrics

覆盖规则按 YAML 文件中的定义顺序自上而下求值,第一个命中生效(对应 inferEndpointEndpointMappings 的遍历)。文件末尾的 defaultEndpoint 为兜底值,若未在 YAML 中显式提供,endpoint_mapping.goloadEndpointMappingConfig 会自动补成 /metrics

5.4 组件端点信息如何进入产物

分析器为每个指标生成结构化的 componentEndpoints 字段(模型定义见 internal/metric/metric.go,含 ComponentEndpoint 两个键)。以文档清单 documentation-list.yaml 为例:

- name: aggregation_count_total
  subsystem: aggregator_discovery
  type: Counter
  stabilityLevel: ALPHA
  componentEndpoints:
  - component: kube-apiserver
    endpoint: /metrics

六、注册新指标目录:读懂那条 WARNING

当指标出现在尚未映射的目录时,生成文档期间会看到警告(README 原文示例):

WARNING: found 1 metric(s) in "pkg/mycomponent/metrics/metrics.go" but could not infer component endpoints. Consider updating endpoint-mappings.yaml.

这条警告与 main.go 中的逻辑一一对应:当文件不在 sharedPaths、也不匹配任何 coreComponents 前缀,却又确实提取到了指标时,inferComponentEndpoints 返回空,工具便打印该警告。README 给出的处理方式非常明确:

  • pkg/mycomponent/metrics/ 这一目录路径加入某个相关组件(Schema > Components 中的 coreComponents 前缀列表)即可消除警告,并让该目录下的指标获得正确的组件归属与默认端点 /metrics
  • 若该目录的指标实际暴露在非默认端点,再按 5.3 节为特定路径补充 endpointMappings 覆盖项。

需要说明的是,是否出现警告与文档是否生成成功无关:端点推断为空只影响文档中组件/端点信息的完整性,因此在 CI 中它扮演的是"提醒开发者补全映射"的角色。

七、从代码看整体流水线:静态分析原理

把散落在各验证脚本里的调用拼起来,可以得到一条清晰的处理流水线(主逻辑位于 main.go):

  1. 入参-allstabilityclasses(是否覆盖全部稳定级别)、-endpoint-mappings(映射文件路径)、-lint(开启命名校验);位置参数为 <DIR or FILE or '-'>- 表示从 stdin 逐行读取文件路径;
  2. 遍历filepath.Walk 跳过 vendor*_test.go,其余 .go 文件进入解析;
  3. AST 分析:用 go/parser 解析文件,仅处理导入了 k8s.io/component-base/metrics 的文件;通过包名解析得到本地 import 别名(getLocalNameOfImportedPackage),拒绝点号导入;
  4. 收集声明:找出全局变量声明,并把被导入包中的常量(如桶配置、defObjectives)以 别名.常量名 形式并入符号表(importedGlobalVariableDeclaration),从而能解出 Buckets: defObjectives 这类间接引用,甚至处理按 GOOS 区分的跨平台文件;
  5. 去重与排序:按 BuildFQName()(namespace/subsystem/name 拼接)去重,未显式标注稳定级别的补 ALPHA,最终按稳定级别与 FQName 排序(sort.Sort(metric.ByFQName(...)));
  6. 输出:YAML 序列化后打印到 stdout,由各调用脚本 diff 或写盘。

这一设计解释了为何"更新 golden list"必须借助专用脚本:手工编辑极易与排序/序列化规则不一致,而脚本复用同一套分析器,保证 golden 文件始终与工具输出字节级一致。

八、指标文档自动生成与网站同步

8.1 两步生成流程

指标"官方文档"由代码驱动,避免文档漂移,仓库根 hack/ 下对应 update-metrics-documentation-list.sh(更新清单)与 hack/tools/instrumentation/update-documentation-metrics.sh(渲染页面)。README 给出的完整命令序列:

# 更新文档化的指标清单(携带全量稳定级别与端点映射信息)
./hack/tools/instrumentation/update-documentation-metrics.sh

该脚本会以 -allstabilityclasses -endpoint-mappings=... 重新分析并写回 documentation-list.yaml;随后生成面向网站发布的指标参考页面:

# 为 k8s/website 更新文档化的指标清单
./hack/tools/instrumentation/update-documentation.sh

渲染工作由 documentation/main.go 完成:它读取 documentation-list.yaml,按稳定级别(ALPHA/BETA/STABLE)分组后套入 Go 模板,输出带 front-matter、标注 auto_generated: true 与版本(通过 --major/--minor 传入)的 Markdown——本仓库检入的样例 documentation.md 头部即注明 Metrics (v1.37) 与自动生成日期。

8.2 拷贝到网站仓库

README 特别说明:生成的 documentation.md 不应默认检入当前仓库,而是先通过环境变量指定网站仓库根目录:

export WEBSITE_ROOT=<path to website root>

然后在 k8s/k8s 仓库根目录执行拷贝:

cp ./hack/tools/instrumentation/documentation/documentation.md $WEBSITE_ROOT/content/en/docs/reference/instrumentation/metrics.md

8.3 对应的持续校验

与更新配套的校验脚本同样位于仓库根 hack/verify-metrics-documentation-list.sh 对文档清单做 diff,防止有人绕过更新流程手工改动。至此形成闭环:代码定义指标 → 静态分析 → golden list / 文档清单 diff(CI)→ 文档生成 → 网站发布,任何一环与源码不一致都会在提交时暴露。

九、实践要点速查

  1. 只动稳定指标:改完代码先跑本地验证(等价于 CI 里的 hack/verify-generated-stable-metrics.sh),失败后执行 ./hack/update-generated-stable-metrics.sh 刷新 golden list,PR 请 sig-instrumentation 评审。
  2. 试验分析器行为:在 testdata/pkg/kubelet/metrics/metrics.go 加假指标,用 ./hack/tools/instrumentation/test-verify.sh 验证、./hack/tools/instrumentation/test-update.sh 固化结果,绝不影响生产 golden list。
  3. 新增指标目录:留意文档生成/校验时的 WARNING,把新目录加入 endpoint-mappings.yamlcoreComponents;端点特殊时补充 endpointMappings(按顺序、先命中先生效)。
  4. 命名合规:提交前运行 ./hack/verify-metrics-naming.sh,它基于 promlint 覆盖全部稳定级别的指标命名检查。
  5. 文档同步update-documentation-metrics.shupdate-documentation.sh 只在发布前运行,产物通过 WEBSITE_ROOT 拷贝到网站仓库,不在本仓库保留。
  6. 只读约束:本仓库为只读源码视图,上述命令仅供你在自己的 Kubernetes 开发分支上执行,用于查看与验证机制本身时可参考本仓库中的既有 golden 文件与脚本实现。

通过这套工具链,Kubernetes 把"指标即契约"落到了每次提交都可见、可回滚、可评审的工程机制上——这正是大型基础设施项目在规模扩张中依然能对 Prometheus 生态保持稳定兼容的关键一环。

登录后查看全文
热门项目推荐
相关项目推荐