- Published on
OTel 的 Kubernetes 属性变成 stable 了 — 在 k8sattributes 默认值翻转之前要做的事
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言 — stable 晋升悄悄地过去了
- 长达四个月的晋升时间线
- 实际会破坏的 7 处
- 两个特性开关,以及一张真值表
- RFC 的四阶段推进 — 默认值到底什么时候翻转
- 现在该做的事 — 趁窗口还开着
- 诚实的权衡 — 双重发布不是免费的
- 到底谁会受影响 — 谁不会
- 结语
- 参考资料
引言 — stable 晋升悄悄地过去了
2026 年 6 月 12 日,semconv v1.42.0 发布,OpenTelemetry 的一批 Kubernetes 属性晋升为 stable。变更日志里的这行写道 — "Promote a selection of k8s and container registry attributes to stable"。
然后,什么都没发生。
确切地说,是被设计成什么都不会发生。如果你正在运行 Collector 的 k8sattributes 处理器,此时此刻,你仍然在输出旧模式(以下称 v0)。以最新版本 collector-contrib v0.156.0(2026-07-07)为准,相关的特性开关依然是 alpha,而 alpha 阶段的默认值就是 v0。
这是一个宽限期,不是豁免。按照 Collector RFC 制定的推进路线,一旦开关升到 beta,默认行为就会翻转为仅 v1。而且这个变更的性质很讨厌 — 它不会报错,仪表盘只是变空而已。
本文将梳理:实际会改变什么、何时翻转,以及在现在这个开着的窗口期里该做什么。最后,我还会说明这其实是一个比听起来窄得多的问题。
长达四个月的晋升时间线
先按时间顺序把事实摆出来。这些全部都能在 semconv 仓库的发布记录和已合并 PR 中核实。
2026-02-19 semconv v1.40.0 k8s.pod.name 等 -> stability: beta
2026-03-02 PR #3491 开启 "Promote a selection of k8s/container attributes to RC"
2026-03-09 PR #3491 合并 beta -> release_candidate
2026-03-16 官方博客公告 "Kubernetes attributes promoted to release candidate"
2026-04-28 semconv v1.41.0 42 个属性以 release_candidate 状态进入该版本
2026-05-11 semconv v1.41.1 仍是 release_candidate
2026-06-12 semconv v1.42.0 -> stable (变更日志 #3382)
2026-07-03 semconv v1.43.0 保持 stable
2026-07-07 contrib v0.156.0 特性开关依然是 alpha (自 v0.145.0 起)
PR #3491 的正文里把意图写得很明确 — 「这是变成 stable 之前的最后一次中间阶段晋升,也是征求反馈的最后一次机会」。3 月 16 日的官方博客(Christos Markou/Elastic,David Ashpole/Google)也强调「停留在 release_candidate 期间正是给反馈的时候」。
那扇窗口在 6 月 12 日关闭了。这些属性现在是 stable。
这里值得留意的是最后两行之间的落差。semconv 在 6 月 12 日宣布了 stable,可 7 月 7 日的 Collector 版本里,开关依然是 alpha。后面会看到,RFC 里写着「在 semconv 把该约定标记为 stable 的同一个 Collector 版本里,把开关升到 beta」。也就是说,现在的进度落后于 RFC 画出的蓝图。至于为什么落后、到底会在哪个版本翻转,我能核实到的资料里没有 — 所以这里不会编日期。但方向已经定了,留给你的,是这两个时刻之间的这段时间。
实际会破坏的 7 处
v0 与 v1 之间的破坏性变更,在 k8sattributes 处理器的 README 里明确列了整整 7 个。
container.image.tag -> container.image.tags
k8s.pod.labels.KEY -> k8s.pod.label.KEY
k8s.pod.annotations.KEY -> k8s.pod.annotation.KEY
k8s.node.labels.KEY -> k8s.node.label.KEY
k8s.node.annotations.KEY -> k8s.node.annotation.KEY
k8s.namespace.labels.KEY -> k8s.namespace.label.KEY
k8s.namespace.annotations.KEY -> k8s.namespace.annotation.KEY
其中六个,说到底不过是复数变成单数。labels 变成 label,annotations 变成 annotation。落到实际的属性名上是这样的。
v0: k8s.pod.labels.app = "checkout"
v1: k8s.pod.label.app = "checkout"
只差一个字母。而这一点正是危险所在。如果名字整个变了,查询要么报错要么明显崩掉;可是拿一个已经不存在的属性去做过滤的查询,通常只会悄悄地返回空结果。告警不会触发(因为没有符合条件的时间序列),仪表盘面板显示「No data」,也没有人在凌晨被叫醒。等到几天后才会想起来:「话说这张图从什么时候开始是空的?」
第七个性质不一样。从 container.image.tag 变成 container.image.tags,不只是改名,类型也变了。根据处理器文档,前者是 Any Str,后者是 Any Slice。从单个字符串变成数组,所以就算改了名字,像 tag = "v1.2.3" 这样的等值过滤对数组也不会按你想要的方式生效。这不是靠字符串替换就能结束的变更,而是要重写查询本身。
两个特性开关,以及一张真值表
Collector 刻意没有采用 SDK 那边的 OTEL_SEMCONV_STABILITY_OPT_IN 环境变量方式。借用 RFC 的说法,那种方式「感觉不够 Collector 原生」,尤其是不支持在升级后回滚到之前的行为。取而代之的,是一对成双的特性开关。两个都从 v0.145.0 起就存在,目前都是 alpha。
processor.k8sattributes.EmitV1K8sConventions— 打开后会发出新的(stable)模式。processor.k8sattributes.DontEmitV0K8sConventions— 打开后会关闭旧的(legacy)模式。
关键在于这两者是相互独立的。于是就有了四种组合,RFC 把真值表定义成这样。
| EmitV1 | DontEmitV0 | 结果行为 |
|---|---|---|
| 关 | 关 | 只发 v0(当前默认值) |
| 关 | 开 | 启动时报错 — 因为不会有任何遥测数据发出 |
| 开 | 关 | v0 与 v1 两者都发(双重发布) |
| 开 | 开 | 只发 v1 |
第三行是本文的重点。不需要两个都开,只打开 EmitV1,两种模式就会同时发出。也就是说,在迁移期间,旧仪表盘和新仪表盘可以同时活着。
第二行也值得留意。「新的关掉、旧的也关掉」这种组合,不是被静静忽略,而是启动失败。这比因为配置失误让遥测数据悄无声息地消失,要好得多的设计。
开关是在启动 Collector 时用命令行标志打开的。
# 双重发布:同时发出 v0 和 v1(用于迁移期间)
otelcol-contrib --config=config.yaml \
--feature-gates=+processor.k8sattributes.EmitV1K8sConventions
# 切换完成:只发出 v1
otelcol-contrib --config=config.yaml \
--feature-gates=+processor.k8sattributes.EmitV1K8sConventions,+processor.k8sattributes.DontEmitV0K8sConventions
RFC 的四阶段推进 — 默认值到底什么时候翻转
semconv-feature-gates RFC 把这一对开关要走的阶段钉死成这样。两个开关始终以相同的速度一起移动。
- alpha — 两个开关默认都关。默认行为只发 v0。用户如果想要,可以自行选择开启双重发布,或者选择只用 v1。RFC 规定这个阶段的组件必须在启动时记录一条警告日志("A warning message must be logged by the component at startup indicating the upcoming change")。
- beta — 一旦 semconv 把该约定标记为 stable 的版本发布,开关就会在同一个 Collector 版本里升级到 beta。此时,默认行为翻转为仅 v1。 现在反过来,想用 v0 就得主动选择退出(opt-out)。
- stable — 从 beta 起再过 4 个次要版本,就升到 stable 阶段。从这时起,只能使用 v1。
- 移除 — 再过 4 个次要版本,开关本身就会消失。
我们现在处在第 1 阶段,semconv 那一侧的条件(v1.42.0 晋升为 stable)已经满足了。所以第 2 阶段是一件已经排定的事。如上所述,确切的版本号还没有得到证实,所以我不会妄下断言。但有两个事实可以作为规划的依据 — 翻转会在升级 Collector 的那一刻到来,而 beta 之后再过8 个次要版本,回退的手段本身就会消失。
如果你在自动升级 Collector(把 operator 的镜像标签留在 latest,或者没有固定渲染后的 chart 版本),那么这次翻转就不会是你挑的那一天,而是任何一天都可能发生。
现在该做的事 — 趁窗口还开着
顺序很重要。这正是 RFC 特意留出双重发布这个阶段的原因。
1. 先确认自己是否暴露在受影响范围内。 在 Collector 配置里查看 k8sattributes 处理器的 extract 块。有没有 labels 或 annotations 子项?metadata 列表里有没有 container.image.tag?如果两者都没有 — 就像后面会解释的那样 — 那么这项变更和你无关。
2. 打开双重发布。 只打开 EmitV1K8sConventions,让 v0 和 v1 同时发出去。从这时起,后端里两个名字都会存在,旧仪表盘可以继续活着,同时你可以拿新名字试着查询。而且回退也很容易。
3. 迁移查询和仪表盘。 从复数变单数的那六个,基本上是机械式的替换。container.image.tag 因为前面提到的类型变更,需要手动逐个检查。
4. 关掉 v0。 确认新查询能出数据之后,再打开 DontEmitV0K8sConventions。这一刻的行为,就是以后的默认行为,相当于提前把未来过了一遍。
5. 之后再升级 Collector。 如果已经只跑 v1 了,就算开关翻转到 beta,你也不会有任何事发生。这就是目标。
关键在于把第 2 步和第 5 步分开。如果什么都不做就直接升级,模式切换和版本升级就会撞在同一天同一时刻,等某处变空的时候,你根本分不清是这两者中的哪一个造成的。
诚实的权衡 — 双重发布不是免费的
我不想把双重发布这个阶段说成「反正开着就是安全的」。它是有代价的。
资源属性会变成两份。 如果原本提取 5 个标签,现在就会附带 10 个。这会随着每一个 span、metric、log 的资源一起发出去,所以在网络和存储两方面都会产生成本。具体会增加百分之多少,完全取决于你抽取了多少个标签,所以这里我不会凭空编造数字 — 但在自己的流水线上实测一下并不难。
如果是指标,基数(cardinality)那一侧更疼。 在把资源属性纳入时间序列标识的后端里,同一个值以两个名字同时存在,可能会影响时间序列的数量。不同后端处理资源属性的方式不一样,没法一概而论,所以在全面打开双重发布之前,更安全的做法是先在一个小的命名空间里试着打开,看一下实测数值。
所以双重发布不是终点,而是一座桥。 RFC 的推进路线本身就是这么设定的 — 双重发布这个组合,一到 stable 阶段就会消失。既然打开了,就得看到它的结局。「先开着,以后再说」是代价最高的选择。
到底谁会受影响 — 谁不会
这一节或许是全文最重要的部分。到目前为止的内容听起来像是「K8s 语义约定要坏了」,但实际会破坏的范围,比传言窄得多。
你每天在用的属性不会变。 k8s.pod.name、k8s.namespace.name、k8s.deployment.name、k8s.node.name、k8s.container.name、k8s.pod.uid — 名字全都原封不动。它们都不在破坏性变更的清单里。大多数仪表盘都建立在这些属性之上,那些仪表盘什么都不会经历。
会变的只有标签/注解提取,以及 container.image.tag。 而且就连标签提取也是有条件的 — 根据处理器的 README,复数形式的名字是没有指定 tag_name 时使用的默认格式。也就是说,如果你在 extract 配置里一直给每个标签明确指定了 tag_name、起了自己的名字,那个名字就是你的,与这次改名无关。受影响的是那些依赖默认格式的配置。
# 指定了 tag_name 的情况 — 属性名是 'l2'。与这次变更无关。
extract:
labels:
- tag_name: l2
key: label2
from: pod
# 没有 tag_name 的情况 — 会遵循默认格式,因此受影响。
# v0: k8s.pod.labels.label2 -> v1: k8s.pod.label.label2
extract:
labels:
- key: label2
from: pod
而且,这次变成 stable 的也只是「一部分」。 解析一下 semconv v1.43.0 的 k8s 属性注册表就会发现,89 个属性里 stable 的只有 42 个,剩下 45 个还是 development,另外 2 个是 experimental。3 月那篇博客的标题本身也是 "a selection of"。所以不能把这理解成「K8s 约定现在全都稳定了」 — 相当一部分与工作负载相关的属性,依然处在可能变化的位置上。这次稳定下来的 42 个,是 k8sattributes 和 resourcedetection 这类以稳定化为目标的 Collector 组件实际在用的那些。
总结一下就是这样。
现在就该动手的情况
- 在
k8sattributes的extract里没有指定tag_name就在提取标签/注解。 - 有用
container.image.tag做过滤或分组的查询。 - Collector 是自动跟随升级的,所以升级的时间点不是你自己挑的。
不用操心的情况
- 只用到
k8s.pod.name/k8s.namespace.name/k8s.deployment.name这种程度 — 大多数人属于这一类。 - 提取标签时一直明确指定了
tag_name。 - Collector 版本是固定的,升级是刻意规划的 — 如果是这样,本文就是一张贴在下次升级工单上的便签。
结语
这件事的教训不是「OTel 又改名了」。恰恰相反。semconv 花了四个月,一步步走过 beta → release_candidate → stable,每一步都发了公告,甚至还附上了最后一次征求反馈的呼吁。Collector 那边设计了真值表清晰的一对开关、双重发布的窗口,以及横跨 8 个次要版本的宽限期。为了防止悄无声息的失败,能准备的几乎都准备了。
尽管如此,这类变更真正伤到人的那个点,始终是同一个 — 因为默认值翻转的那一刻,和你按下升级的那一刻,是同一刻。现在,正处在这两个时刻之间还留有时间这种并不常见的状态里。semconv 已经宣布了 stable,而 Collector 的默认值还没翻转。这道缝隙,正好是打开双重发布、迁移查询、确认无误后再往前走的好地方。
而且我要再说一遍:如果你检查之后发现 extract 块里根本没有标签 — 那就到此为止了。这是本文最常见的结局,只需要 5 分钟就能确认。
参考资料
- Kubernetes attributes promoted to release candidate in OTel Semantic Conventions (2026-03-16 官方博客)
- semantic-conventions PR #3491 — k8s/container 属性晋升为 RC (2026-03-09 合并)
- semantic-conventions 发布记录 — v1.42.0 晋升为 stable (2026-06-12)
- Collector RFC — Semantic conventions migrations in the Collector (真值表与四阶段推进)
- k8sattributes 处理器 README — Semantic Conventions Compatibility (7 种破坏性变更)
- k8sattributes 处理器 documentation.md — 特性开关阶段与属性类型
- Collector Feature Gates 文档
- OpenTelemetry Collector 流水线设计 (相关文章)