Skip to content

Split View: OTel의 Kubernetes 속성이 stable이 됐다 — k8sattributes 기본값이 뒤집히기 전에 할 일

|

OTel의 Kubernetes 속성이 stable이 됐다 — k8sattributes 기본값이 뒤집히기 전에 할 일

들어가며 — 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 전용으로 뒤집힙니다. 그리고 그 변경의 성질이 고약합니다 — 에러가 나지 않습니다. 대시보드가 그냥 빕니다.

이 글은 실제로 무엇이 바뀌는지, 언제 뒤집히는지, 지금 열려 있는 창구에서 무엇을 해야 하는지를 정리합니다. 그리고 마지막에, 이게 생각보다 훨씬 좁은 문제라는 이야기도 하겠습니다.

4개월간의 승급 타임라인

먼저 사실관계를 시간순으로 놓겠습니다. 전부 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 사이의 breaking change는 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

여섯 개는 복수형에서 단수형으로 바뀌는 것이 전부입니다. labelslabel이 되고, annotationsannotation이 됩니다. 실제 속성 이름으로 보면 이렇습니다.

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가 진리표를 이렇게 정의합니다.

EmitV1DontEmitV0결과 동작
꺼짐꺼짐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의 4단계 롤아웃 — 기본값은 언제 뒤집히나

semconv-feature-gates RFC는 게이트 한 쌍이 밟아 갈 단계를 이렇게 못박아 놨습니다. 두 게이트는 항상 같은 속도로 함께 움직입니다.

  1. alpha — 둘 다 기본 꺼짐. 기본 동작은 v0만 발행. 사용자는 원하면 이중 발행이나 v1 전용으로 옵트인할 수 있습니다. RFC는 이 단계에서 컴포넌트가 기동 시 경고 로그를 남겨야 한다고 규정합니다("A warning message must be logged by the component at startup indicating the upcoming change").
  2. beta — semconv가 해당 컨벤션을 stable로 표시하는 릴리스가 나오면, 같은 Collector 릴리스에서 게이트가 beta로 승급합니다. 이때 기본 동작이 v1 전용으로 뒤집힙니다. 이제는 반대로 v0을 쓰려면 옵트아웃해야 합니다.
  3. stable — beta에서 마이너 릴리스 4개가 지나면 stable 단계로 올라갑니다. 이 시점부터는 v1만 쓸 수 있습니다.
  4. 제거 — 거기서 마이너 릴리스 4개가 더 지나면 게이트 자체가 사라집니다.

지금 우리는 1단계에 있고, semconv 쪽 조건(v1.42.0의 stable 승급)은 이미 충족됐습니다. 그러니 2단계는 예정된 사건입니다. 위에서 말했듯 정확한 릴리스 번호는 확인되지 않으니 단정하지 않겠습니다. 하지만 계획을 세울 때 기준으로 삼을 사실은 두 가지입니다 — 뒤집힘은 Collector를 업그레이드하는 순간 찾아오고, beta 이후 마이너 릴리스 8개가 지나면 되돌릴 수단 자체가 없어집니다.

Collector를 자동 업그레이드하고 있다면(오퍼레이터의 이미지 태그를 latest로 뒀거나, 렌더링된 차트 버전을 고정하지 않았다면) 이 뒤집힘은 당신이 고른 날이 아니라 아무 날에나 옵니다.

지금 할 일 — 창구가 열려 있는 동안

순서가 중요합니다. RFC가 이중 발행 단계를 굳이 만들어 둔 이유가 이것입니다.

1. 내가 노출돼 있는지부터 확인합니다. Collector 설정에서 k8sattributes 프로세서의 extract 블록을 봅니다. labelsannotations 하위 항목이 있습니까? metadata 목록에 container.image.tag가 있습니까? 둘 다 아니라면 — 뒤에서 설명하듯 — 당신은 이 변경과 무관합니다.

2. 이중 발행을 켭니다. EmitV1K8sConventions만 켜서 v0과 v1이 동시에 나가게 합니다. 이 시점부터 백엔드에는 두 이름이 모두 존재하므로, 옛 대시보드는 계속 살아 있고 새 이름으로 쿼리를 시험해 볼 수 있습니다. 되돌리기도 쉽습니다.

3. 쿼리와 대시보드를 옮깁니다. 복수형에서 단수형으로 가는 여섯 개는 기계적 치환에 가깝습니다. container.image.tag는 앞서 말한 타입 변경 때문에 손으로 봐야 합니다.

4. v0을 끕니다. 새 쿼리가 데이터를 내는 걸 확인한 뒤에 DontEmitV0K8sConventions를 켭니다. 이 시점의 동작이 곧 앞으로의 기본값이므로, 미래를 미리 겪어 보는 셈입니다.

5. 그러고 나서 Collector를 올립니다. 이미 v1 전용으로 돌고 있다면 게이트가 beta로 뒤집혀도 당신에게는 아무 일도 일어나지 않습니다. 그게 목표입니다.

핵심은 2단계와 5단계를 분리하는 것입니다. 아무것도 안 하고 있다가 업그레이드를 하면 스키마 전환과 버전 업그레이드가 한날한시에 겹치고, 무언가 비었을 때 원인이 둘 중 무엇인지 알 수 없게 됩니다.

정직한 트레이드오프 — 이중 발행은 공짜가 아니다

이중 발행 단계를 "그냥 켜두면 안전한 것"처럼 말하고 싶지 않습니다. 대가가 있습니다.

리소스 속성이 두 벌이 됩니다. 레이블 5개를 추출하고 있었다면 이제 10개가 붙습니다. 이건 모든 스팬·메트릭·로그의 리소스에 실려 나가므로 네트워크와 저장 양쪽에 비용이 붙습니다. 정확히 몇 퍼센트가 늘어나는지는 당신이 몇 개의 레이블을 뽑고 있는지에 전적으로 달려 있어서, 여기서 숫자를 지어내지 않겠습니다 — 다만 자기 파이프라인에서 재 보는 건 어렵지 않습니다.

메트릭이라면 카디널리티 쪽이 더 아픕니다. 리소스 속성이 시계열 아이덴티티에 들어가는 백엔드에서는 같은 값을 담은 속성이 두 이름으로 존재하는 게 시계열 수에 영향을 줄 수 있습니다. 백엔드마다 리소스 속성을 다루는 방식이 달라서 일률적으로 말할 수 없으니, 이중 발행을 켜기 전에 작은 네임스페이스 하나에서 먼저 켜 보고 계측값을 보는 편이 안전합니다.

그래서 이중 발행은 종착지가 아니라 다리입니다. RFC의 롤아웃 자체가 그걸 전제합니다 — 이중 발행 조합은 stable 단계가 되면 사라집니다. 켰으면 끝을 봐야 합니다. "일단 켜두고 나중에"가 가장 비싼 선택입니다.

누가 실제로 영향받나 — 그리고 안 받나

여기가 이 글에서 제일 중요한 절일지도 모르겠습니다. 지금까지의 이야기는 "K8s 시맨틱 컨벤션이 깨진다"처럼 들리지만, 깨지는 범위는 소문보다 훨씬 좁습니다.

당신이 매일 쓰는 속성들은 안 바뀝니다. k8s.pod.name, k8s.namespace.name, k8s.deployment.name, k8s.node.name, k8s.container.name, k8s.pod.uid — 전부 이름 그대로입니다. breaking change 목록에 없습니다. 대부분의 대시보드는 이 속성들 위에 서 있고, 그런 대시보드는 아무 일도 겪지 않습니다.

바뀌는 건 레이블/어노테이션 추출과 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개는 k8sattributesresourcedetection 같은, 안정화를 목표로 한 Collector 컴포넌트가 실제로 쓰던 것들입니다.

정리하면 이렇습니다.

지금 움직여야 하는 경우

  • k8sattributesextract에서 tag_name 없이 레이블/어노테이션을 뽑고 있다.
  • container.image.tag로 필터하거나 그룹핑하는 쿼리가 있다.
  • Collector를 자동으로 따라 올리고 있어서 업그레이드 시점을 당신이 고르지 않는다.

신경 안 써도 되는 경우

  • k8s.pod.name / k8s.namespace.name / k8s.deployment.name 정도만 쓴다 — 대다수가 여기 해당합니다.
  • 레이블 추출에 항상 tag_name을 명시해 왔다.
  • Collector 버전을 고정해 두고 업그레이드를 의도적으로 계획한다 — 그렇다면 이 글은 다음 업그레이드 티켓에 붙일 메모입니다.

마치며

이 이야기의 교훈은 "OTel이 또 이름을 바꿨다"가 아닙니다. 오히려 반대에 가깝습니다. semconv는 beta → release_candidate → stable을 4개월에 걸쳐 밟았고, 그때마다 공지했고, 마지막 호출까지 붙였습니다. Collector 쪽은 진리표가 명확한 게이트 한 쌍과 이중 발행 창구, 그리고 8개 마이너 릴리스에 걸친 유예를 설계해 뒀습니다. 조용한 실패를 막기 위해 갖출 수 있는 건 거의 다 갖춘 셈입니다.

그럼에도 이런 변경이 사람을 다치게 하는 지점은 언제나 같습니다 — 기본값이 뒤집히는 순간과 당신이 업그레이드를 누르는 순간이 같기 때문입니다. 지금은 그 두 순간 사이에 시간이 남아 있는, 흔치 않은 상태입니다. semconv는 이미 stable을 선언했고 Collector 기본값은 아직 안 뒤집혔습니다. 이 틈이 이중 발행을 켜고, 쿼리를 옮기고, 확인하고 넘어가기에 정확히 좋은 자리입니다.

그리고 다시 말하지만, 확인해 봤더니 extract 블록에 레이블이 없더라 — 라면 그걸로 끝입니다. 그게 이 글의 가장 흔한 결말이고, 5분이면 확인됩니다.

참고 자료

OTel's Kubernetes Attributes Are Stable Now — What to Do Before k8sattributes Flips Its Default

Introduction — The Stable Promotion Passed Quietly

On June 12, 2026, semconv v1.42.0 shipped, and a batch of OpenTelemetry's Kubernetes attributes got promoted to stable. The changelog line reads — "Promote a selection of k8s and container registry attributes to stable".

And nothing happened.

More precisely, it is designed so that nothing happens. If you're running the Collector's k8sattributes processor, right now, at this very moment, you're still emitting the old schema (hereafter v0). As of the latest release, collector-contrib v0.156.0 (2026-07-07), the relevant feature gate is still alpha, and at alpha the default is v0.

This is a grace period, not an exemption. Per the rollout the Collector's RFC lays out, the moment the gate advances to beta, the default behavior flips to v1-only. And the nature of that change is nasty — it doesn't error. Dashboards just go empty.

This post lays out what actually changes, when it flips, and what to do in the window that's open right now. And at the end, I'll also make the case that this is a much narrower problem than it sounds.

The Four-Month Promotion Timeline

Let's lay out the facts in chronological order first. All of it is verifiable from the semconv repo's releases and merged PRs.

2026-02-19  semconv v1.40.0   k8s.pod.name, etc. -> stability: beta
2026-03-02  PR #3491 opened   "Promote a selection of k8s/container attributes to RC"
2026-03-09  PR #3491 merged   beta -> release_candidate
2026-03-16  Official blog post "Kubernetes attributes promoted to release candidate"
2026-04-28  semconv v1.41.0   42 attributes ship as release_candidate
2026-05-11  semconv v1.41.1   still release_candidate
2026-06-12  semconv v1.42.0   -> stable  (changelog #3382)
2026-07-03  semconv v1.43.0   stays stable
2026-07-07  contrib v0.156.0  feature gate still alpha (since v0.145.0)

The body of PR #3491 states the intent clearly — that this is "the last intermediate-stage promotion before going stable, and a final call for feedback." The March 16 official blog post (Christos Markou/Elastic, David Ashpole/Google) likewise nailed down that "the time to give feedback is while it stays at release_candidate."

That window closed on June 12. The attributes are stable now.

The thing worth noting here is the gap between the last two lines. semconv declared stable on June 12, but the gate is still alpha in the July 7 Collector release. As we'll see below, the RFC states that "the gate is promoted to beta in the same Collector release as the one where semconv marks the convention stable." In other words, we're currently running behind the picture the RFC drew. I don't have material that lets me confirm why it's late, or exactly which release will flip it — so I won't make up a date. But the direction is set, and what you've been given is the time in between.

The 7 Things That Actually Break

The breaking changes between v0 and v1 are specified exactly, all 7 of them, in the k8sattributes processor README.

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

Six of them amount to nothing more than plural becoming singular. labels becomes label, annotations becomes annotation. In terms of the actual attribute names, it looks like this.

v0:  k8s.pod.labels.app       = "checkout"
v1:  k8s.pod.label.app        = "checkout"

One letter. And that's exactly what makes it dangerous. When a name changes completely, a query errors out or breaks visibly, but a query that filters on an attribute that no longer exists usually just quietly returns an empty result. Alerts don't fire (there's no time series matching the condition), dashboard panels show "No data," and nobody gets paged at 3am. It comes back days later as "wait, since when has this graph been empty?"

The seventh one is a different animal. Going from container.image.tag to container.image.tags isn't just a name change — the type changes too. Per the processor docs, the former is Any Str, the latter Any Slice. It's going from a single string to an array, so even after you fix the name, an equality filter like tag = "v1.2.3" won't behave the way you want against an array. This isn't a change you can finish with a string substitution — you have to rewrite the query itself.

Two Feature Gates, and a Truth Table

The Collector deliberately does not use the SDK-side OTEL_SEMCONV_STABILITY_OPT_IN environment-variable approach. To borrow the RFC's phrasing, that approach "doesn't feel Collector-native," and in particular it doesn't support rolling back to the previous behavior after an upgrade. Instead it uses a paired set of two feature gates. Both have existed since v0.145.0, and both are currently alpha.

  • processor.k8sattributes.EmitV1K8sConventions — when on, emits the new (stable) schema.
  • processor.k8sattributes.DontEmitV0K8sConventions — when on, turns off the old (legacy) schema.

The key is that the two are independent. That gives four combinations, and the RFC defines the truth table like this.

EmitV1DontEmitV0Resulting Behavior
offoffv0 only (current default)
offonerror at startup — because no telemetry would go out at all
onoffemits both v0 and v1 (dual emission)
ononv1 only

The third row is the point of this post. You don't need both on — turning on just EmitV1 makes both schemas go out at the same time. That means the old dashboard and the new dashboard can stay alive side by side during the migration period.

The second row is worth knowing too. The combination of "turn off the new one, turn off the old one" isn't silently ignored — it's a startup failure. That's a much better design than having telemetry silently vanish because of a config mistake.

Gates are turned on with a flag at Collector startup.

# Dual emission: emit v0 and v1 at the same time (for the migration period)
otelcol-contrib --config=config.yaml \
  --feature-gates=+processor.k8sattributes.EmitV1K8sConventions

# Fully cut over: emit v1 only
otelcol-contrib --config=config.yaml \
  --feature-gates=+processor.k8sattributes.EmitV1K8sConventions,+processor.k8sattributes.DontEmitV0K8sConventions

The RFC's 4-Stage Rollout — When Does the Default Actually Flip

The semconv-feature-gates RFC nails down the stages the gate pair will walk through like this. The two gates always move together, at the same pace.

  1. alpha — both off by default. Default behavior is v0 only. Users can opt in to dual emission or v1-only if they want. The RFC requires that at this stage, the component must log a warning at startup ("A warning message must be logged by the component at startup indicating the upcoming change").
  2. beta — once the release ships where semconv marks the convention stable, the gate is promoted to beta in that same Collector release. At this point, the default behavior flips to v1-only. Now, conversely, you have to opt out if you want v0.
  3. stable — after 4 minor releases at beta, it moves up to the stable stage. From this point on, only v1 is usable.
  4. removal — after 4 more minor releases from there, the gate itself disappears.

We're at stage 1 right now, and the semconv-side condition (v1.42.0's promotion to stable) has already been met. So stage 2 is a scheduled event. As I said above, the exact release number isn't confirmed, so I won't state one. But there are two facts you can plan around — the flip arrives the moment you upgrade the Collector, and after 8 minor releases past beta, the means to revert disappears entirely.

If you're auto-upgrading the Collector (you left the operator's image tag on latest, or you never pinned the rendered chart version), this flip arrives not on a day you chose, but on any day at all.

What to Do Now — While the Window Is Open

Order matters. This is exactly why the RFC bothered to build in a dual-emission stage.

1. First, check whether you're exposed. Look at the extract block of the k8sattributes processor in your Collector config. Is there a labels or annotations sub-item? Is container.image.tag in the metadata list? If neither — as explained below — this change doesn't apply to you.

2. Turn on dual emission. Turn on only EmitV1K8sConventions so v0 and v1 go out at the same time. From this point on, both names exist in the backend, so the old dashboard keeps working while you try out queries against the new name. It's also easy to revert.

3. Migrate your queries and dashboards. The six that go from plural to singular are close to a mechanical substitution. container.image.tag needs a manual look, because of the type change mentioned earlier.

4. Turn off v0. After confirming the new queries return data, turn on DontEmitV0K8sConventions. The behavior at this point is exactly what the future default will be, so you're essentially living the future early.

5. Only then, upgrade the Collector. If you're already running v1-only, nothing happens to you even when the gate flips to beta. That's the goal.

The key is decoupling step 2 from step 5. If you do nothing and then upgrade, the schema switch and the version upgrade collide on the same day at the same hour, and when something goes empty, you can't tell which of the two caused it.

The Honest Tradeoff — Dual Emission Isn't Free

I don't want to talk about the dual-emission stage as if "just leaving it on is safe." There's a cost.

Resource attributes double up. If you were extracting 5 labels, now 10 get attached. This rides along on the resource of every span, metric, and log, so it costs you on both network and storage. Exactly how many percent it grows by depends entirely on how many labels you're pulling, so I won't invent a number here — but measuring it on your own pipeline isn't hard.

For metrics, cardinality is the part that hurts more. On backends where resource attributes go into time-series identity, having an attribute with the same value exist under two names can affect the number of time series. How each backend handles resource attributes differs, so I can't make a blanket statement — it's safer to turn it on in one small namespace first before enabling dual emission everywhere, and look at the measured numbers.

So dual emission is a bridge, not a destination. The RFC's own rollout presupposes exactly that — the dual-emission combination disappears once the stable stage arrives. If you turn it on, you have to see it through to the end. "Turn it on and deal with it later" is the most expensive choice.

Who Is Actually Affected — And Who Isn't

This might be the single most important section in this post. Everything so far sounds like "K8s semantic conventions are breaking," but the blast radius is much narrower than the rumor.

The attributes you use every day don't change. k8s.pod.name, k8s.namespace.name, k8s.deployment.name, k8s.node.name, k8s.container.name, k8s.pod.uid — every one of them keeps its name. None of them are on the breaking-change list. Most dashboards are built on top of these attributes, and those dashboards feel nothing.

The only things that change are label/annotation extraction and container.image.tag. And even label extraction comes with a condition — per the processor README, the plural name is the default format used when you don't specify a tag_name. In other words, if your extract config has always given each label its own name via an explicit tag_name, that name is yours, and it's unaffected by this renaming. What's affected is configuration that was relying on the default format.

# When tag_name is specified — the attribute name is 'l2'. Unaffected by this change.
extract:
  labels:
    - tag_name: l2
      key: label2
      from: pod

# When tag_name is absent — it follows the default format, so it's affected.
# v0: k8s.pod.labels.label2   ->   v1: k8s.pod.label.label2
extract:
  labels:
    - key: label2
      from: pod

And what became stable is also just a "selection." If you parse the k8s registry of semconv v1.43.0, of 89 attributes, 42 are stable, the remaining 45 are still development, and 2 are experimental. The March blog post's own title said "a selection of." So you shouldn't read this as "K8s conventions are all settled now" — a good chunk of the workload-related ones are still sitting in a spot where they can change. The 42 that stabilized this time are the ones that Collector components aiming for stabilization — k8sattributes and resourcedetection, for example — were actually using.

To sum up:

When you need to move now

  • You're pulling labels/annotations in k8sattributes's extract without a tag_name.
  • You have queries that filter or group by container.image.tag.
  • You auto-follow Collector upgrades, so you don't get to pick the upgrade moment.

When you don't need to care

  • You use only things like k8s.pod.name / k8s.namespace.name / k8s.deployment.name — this covers the majority.
  • You've always specified tag_name explicitly for label extraction.
  • You pin the Collector version and plan upgrades deliberately — if so, this post is a note to attach to your next upgrade ticket.

Closing

The lesson of this story isn't "OTel changed names again." It's closer to the opposite. semconv walked beta → release_candidate → stable over four months, announced it at every step, and even attached a final call for feedback. On the Collector side, they designed a gate pair with a clear truth table, a dual-emission window, and a grace period spanning 8 minor releases. They've put in place pretty much everything you could, to prevent a quiet failure.

And yet, the point where a change like this actually hurts people is always the same — because the moment the default flips and the moment you hit upgrade are the same moment. Right now, we're in the unusual state where there's still time left between those two moments. semconv has already declared stable, and the Collector default hasn't flipped yet. This gap is exactly the right spot to turn on dual emission, migrate your queries, verify, and move on.

And again — if you check and it turns out there's no label in your extract block, that's the end of it. That's the most common ending to this post, and it takes five minutes to confirm.

References