- 后端
- 可观测性
- 链路追踪
【免费下载链接】tempo
Grafana Tempo is a high volume, minimal dependency distributed tracing backend.
本仓库(Grafana Tempo)作为分布式追踪后端,其 Go 依赖
go.opentelemetry.io/otel(当前锁定 v1.44.0)在 vendor 目录中完整保留了上游官方的RELEASING.md发布流程文档。本文将这份文档作为主体,围绕语义约定(Semantic Conventions)升级、破坏性变更校验、预发布、打 tag、签名工件与发布后收尾等关键环节,逐条展开讲解,并结合仓库内的Makefile、versions.yaml、CHANGELOG.md、verify_released_changelog.sh以及 semconv 子包等真实文件,帮助读者理解一个大型多模块 Go 项目是如何规范、可复现地完成一次版本发布的。
<输出文章>
深入解析 OpenTelemetry Go 多模块发布流程:基于 Grafana Tempo 仓库中 RELEASING.md 的完整拆解
在 Grafana Tempo 的 Go 依赖树中,go.opentelemetry.io/otel(当前版本 v1.44.0)作为分布式追踪与遥测的核心库被大量使用,其 vendor 目录下完整保留了上游官方的发布流程文档 RELEASING.md。这份文档不仅是 OTel Go 维护者的操作手册,更是一个"多模块仓库 + 语义化版本 + 自动化工具链"发布工程的绝佳范本。本文以该文档为骨架,逐阶段拆解发布流程,并结合仓库内真实的 Makefile、versions.yaml、CHANGELOG 与 semconv 子包等源码证据,说明每一步"为什么这么做、底层由什么驱动",让读者既能照做一次发布,也能理解背后的工程机制。
一、发布前奏:创建 Version Release 跟踪 Issue
任何一次发布都始于一个明确的跟踪载体。文档要求先在 GitHub 上创建Version Releaseissue,用于串起从语义约定升级到最终发布、收尾的所有待办项。这一步的价值在于:
- 让发布过程可审计、可追踪,每个阶段的状态都能在 issue 的 todo list 中体现;
- 让社区成员(尤其是非维护者)能够看到当前发布进度,方便协同与反馈;
- 为后续 "Close the milestone" 阶段提供对照依据,确保该版本所有已合并 PR 都被记录。
从仓库实际状态看,CHANGELOG.md 顶部即为[Unreleased]区块与<!-- Released section -->注释标记,最新已发布版本为[1.44.0/0.66.0/0.20.0/0.0.17] 2026-05-27,这说明该仓库采取"一次多模块联合发版"的节奏,与文档描述的流程完全一致。
二、语义约定(Semantic Conventions)升级
OpenTelemetry 的语义约定(semconv)定义了 span 属性、指标名称等遥测数据的标准命名。每次上游open-telemetry/semantic-conventions发布新版本,OTel Go 都需要重新生成对应的semconv/vX.Y.Z子包。这是整个发布流程中技术含量最高、最容易出错的一步。
2.1 通过 semconv-generate 生成新版本子包
文档给出的操作方式是基于环境变量TAG驱动 Makefile 目标:
export TAG="v1.30.0" # 换成你要生成的目标版本 make semconv-generate # 使用导出的 TAG在仓库的 Makefile 中可以找到semconv-generate的真实实现,它揭示了底层机制远比表面命令复杂:
- 前置校验:
[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 ),强制要求必须显式设置TAG,防止误生成。 - 目录准备:
mkdir -p $(PWD)/semconv/${TAG},为生成的子包创建目标目录;同时mkdir -p ~/.weaver用于缓存 weaver 工具下载的 semconv 仓库。 - weaver 容器生成:通过
docker run --rm启动$(WEAVER_IMAGE)(从dependencies.Dockerfile中解析出来的 weaver 镜像),执行registry generate --registry=https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/$(TAG).zip[model] --templates=... --param tag=$(TAG) go /home/weaver/target,即从指定 tag 的语义约定模型归档中渲染出 Go 代码。 - 收尾处理:
$(SEMCONVKIT) -semconv "semconv/" -tag "$(TAG)"调用 semconv 生成工具(同样来自go.opentelemetry.io/build-tools)做最终的包整理。
生成完成后,应检查semconv目录下是否出现了对应版本子包。仓库当前实际存在的版本包括 semconv/v1.18.0、v1.21.0、v1.25.0、v1.26.0、v1.34.0、v1.37.0、v1.38.0、v1.40.0、semconv/v1.41.0 等,这些正是历次发布累积下来的结果,也印证了"每个语义约定大版本对应一个独立子包"的版本策略——子包版本号与仓库主模块版本号互相独立。
生成后必须同步更新 CHANGELOG.md,文档给出了标准的条目模板:
- The `go.opentelemetry.io/otel/semconv/<NEW VERSION>` package. The package contains semantic conventions from the `<NEW VERSION>` version of the OpenTelemetry Semantic Conventions. See the migration documentation for information on how to upgrade from `go.opentelemetry.io/otel/semconv/<PREVIOUS VERSION>`. (#PR_NUMBER)仓库中实际的 CHANGELOG 条目与此完全吻合,例如 v1.41.0 的条目:
- Add `go.opentelemetry.io/otel/semconv/v1.41.0` package. The package contains semantic conventions from the `v1.41.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](https://link.gitcode.com/i/8ebaea2e8a7c31099d2317b907dcaf26) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.40.0`. (#8324)每个新版本子包还包含一份自动生成的 MIGRATION.md,以 v1.40.0 → v1.41.0 为例,它列出被移除的声明(如DeploymentEnvironmentName),并说明哪些属于官方弃用、哪些因缺乏用途被移除——这份迁移指南就是文档中"Ensure things look correct before submitting a pull request"所指的检查要点。
2.2 更新代码库中的 semconv 导入
生成新子包后,必须把全仓库对旧版本的引用切到新版本。文档给出的典型 diff 如下:
// 修改前 semconv "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" // 修改后 semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"在 Grafana Tempo 自身代码中同样能看到这种多版本 semconv 共存的情况:例如 modules/generator/processor/servicegraphs/config.go 同时导入了semconv "go.opentelemetry.io/otel/semconv/v1.25.0"与semconvnew "go.opentelemetry.io/otel/semconv/v1.34.0",通过别名区分新旧属性——这正是文档"update all semconv imports throughout the codebase"在真实下游项目中的典型形态。替换完成后运行make全量编译与测试,确认无破坏。
2.3 属性变更的处理原则
semconv 版本升级往往伴随着属性重命名、属性合并甚至属性值语义改变。文档给出的处理原则是:
- 优先迁移到替代旧属性的新属性,保持与最新语义约定一致;
- 但对历史兼容场景,仍可依据
OTEL_SEMCONV_STABILITY_OPT_IN环境变量决定是否继续输出 legacy 属性; - 复杂迁移的跟踪与执行可参考上游 issue #7806 的案例(例如属性从
http.*迁移到url.*这类大规模改名)。
2.4 联动更新 opentelemetry-go-contrib 的 lint 配置
由于 contrib 仓库(opentelemetry-go-contrib)依赖主仓库的 semconv 版本,文档要求同步更新其 .golangci.yml 中的 lint 规则,强制 contrib 使用最新 semconv 版本,避免两个仓库之间出现属性引用漂移。
三、破坏性变更校验:make gorelease
Go 的语义化版本承诺意味着公共 API 不能被无意修改。文档要求发布前运行make gorelease,它底层调用golang.org/x/exp/cmd/gorelease工具,逐模块对比当前代码与上次发布 tag 的公共 API 差异。
从 Makefile 的实现看,该目标遍历$(OTEL_GO_MOD_DIRS)中所有子模块目录并逐一执行 gorelease:
gorelease: $(OTEL_GO_MOD_DIRS:%=gorelease/%) gorelease/%: DIR=$* gorelease/%:| $(GORELEASE) @echo "gorelease in $(DIR):" \ && cd $(DIR) \ && $(GORELEASE) \ || echo ""gorelease 会基于 go.mod 中的模块版本推断本次应该发布的主版本号,检查是否存在不兼容的 API 变化,并输出相应的版本号建议。它是发布前的"公共 API 体检",任何意外导出的符号删除、签名变更都会在这里暴露。文档同时提示,gorelease 自身的问题可以反馈到 golang.org issue #26420。
四、验证对 contrib 仓库的兼容性
OTel 主仓库的变更往往会被opentelemetry-go-contrib消费。文档要求在发布前按照 contrib 仓库 RELEASING.md 中 "Verify OTel changes" 一节的操作,验证主仓库改动与 contrib 的兼容性。这属于发布前的交叉验证环节:主仓库是上游,contrib 是最大的下游消费者,两者版本必须同步演进,否则会导致下游用户升级主库后 contrib 无法编译。
五、Pre-Release:确定模块集并准备发布分支
5.1 模块集(Module Set)与 versions.yaml
OTel Go 仓库包含数十个独立发布的 Go module(如go.opentelemetry.io/otel、otel/metric、otel/sdk、各 exporters 子包等),它们被组织成多个"模块集",每个模块集共享一个版本号。这一映射关系定义在仓库根目录的 versions.yaml 中,当前仓库中实际存在四个模块集:
| 模块集 | 版本(当前仓库) | 包含模块 |
|---|---|---|
stable-v1 | v1.44.0 | go.opentelemetry.io/otel、otel/metric、otel/sdk、otel/trace、otel/exporters/otlp/*、otel/exporters/stdout/*、otel/exporters/zipkin、otel/bridge/*等 |
experimental-metrics | v0.66.0 | otel/exporters/prometheus、otel/metric/x |
experimental-logs | v0.20.0 | otel/log、otel/log/logtest、otel/sdk/log、otel/exporters/otlp/otlplog/*、otel/exporters/stdout/stdoutlog |
experimental-schema | v0.0.17 | otel/schema |
同时versions.yaml通过excluded-modules排除go.opentelemetry.io/otel/internal/tools等内部模块,并通过version-refs将部分 exporter 的版本与内部internal/version.go文件挂钩,确保版本号在生成代码中保持一致。当前仓库版本 v1.44.0/0.66.0/0.20.0/0.0.17 恰好对应 CHANGELOG 中最新发布条目,四者一次发布,完全符合文档描述的模块集机制。
5.2 prerelease make 目标
发布的第一步是在versions.yaml中更新目标模块集的新版本号并提交到新分支,然后运行:
make prerelease MODSET=<module set>Makefile 的实现显示,prerelease先执行verify-mods(即multimod verify),再调用$(MULTIMOD) prerelease -m ${MODSET}:
prerelease: verify-mods @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) prerelease -m ${MODSET}其中MULTIMOD是go.opentelemetry.io/build-tools/multimod工具,它是整个多模块版本管理的核心引擎。执行后会自动创建名为prerelease_<module set>_<new tag>的分支,并把模块集中所有模块的 go.mod 版本统一改为新 tag。
随后用git diff ...prerelease_<module set>_<new tag>核对所有模块版本是否都已正确更新,确认无误后合并进预发布分支:
git merge prerelease_<module set>_<new tag>5.3 更新 CHANGELOG
文档要求把预发布分支中的 CHANGELOG.md 整理为正式发布形态:
- 用
git --no-pager log --pretty=oneline "<last tag>..HEAD"检查上次 tag 以来的全部提交,确保没有遗漏变更; - 将所有
Unreleased变更移入新章节,标题遵循[<new tag>] - <date of release>格式,例如仓库中的## [1.44.0/0.66.0/0.20.0/0.0.17] 2026-05-27; - 新章节必须放在
<!-- Released section -->注释之下,以便被发布脚本保护、防止未来被误改; - 更新文末的所有版本链接。
这里值得展开说明"Released section"注释的作用:仓库根目录的 verify_released_changelog.sh 脚本专门用于保证已发布章节不被篡改——它会从目标分支检出上一版本的 CHANGELOG,用awk '/^<!-- Released section -->/ {flag=1} /^<!-- Released section ended -->/ {flag=0}'分别提取新旧文件的已发布区块并 diff,一旦发现差异立即以非零状态退出(false)。这与 CHANGELOG.md 中<!-- Released section -->与<!-- Released section ended -->标记的实际位置一一对应。也就是说:发布一旦完成,历史章节即被"锁死",任何后续 PR 都不得回改已发布内容——这是保证发布记录不可变审计链的工程手段。
- 推送并创建 Pull Request,PR 描述中必须附带整理好的 Changelog 内容。
六、Tag:为合并提交打上版本标签
PR 合并到主干后进入打 tag 阶段。文档用两个加粗的 IMPORTANT 强调了其严肃性:
- 必须使用与 Pre-Release 阶段完全相同的 tag,否则系统会处于 broken 状态;只要在两步之间不修改
versions.yaml,版本就不会错位。 - Go module 的 tag 一旦打错就无法删除(见 golang issue #34189),错误的 tag 会引发下游用户的紧急事故(见 opentelemetry-go issue #331),因此推送前必须反复确认版本号。
操作命令:
make add-tags MODSET=<module set> COMMIT=<commit hash>Makefile 的实现为:
COMMIT ?= "HEAD" add-tags: verify-mods @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}COMMIT默认为当前HEAD,仅当本地 HEAD 不是合并提交时才需要显式传值。打 tag 完成后,需要把主模块与所有子模块的 tag 全部推送到上游(注意是官方仓库github.com/open-telemetry/opentelemetry-go.git,不是自己的 fork):
git push upstream <new tag> git push upstream <submodules-path/new tag> ...这正是versions.yaml中每个子模块(如otel/exporters/otlp/otlptrace/otlptracegrpc)都需要独立 tag 的原因——Go module proxy 要求每个 module 的版本对应一个独立的 tag 路径。
七、签名发布工件(Sign artifacts)
为符合 CNCF 最佳实践,发布工件必须用 GPG 签名。文档给出的步骤是:
- 从 GitHub tags 页面下载新 tag 对应的
.tar.gz与.zip两份归档; - 签名前可用 attest-sh 脚本核验归档内容;
- 用
gpg --list-secret-keys --keyid-format=long获取密钥 ID(即sec rsa4096/之后的 16 位字符串); - 设置环境变量并分别签名:
export VERSION="<version>" # 例如 v1.32.0 export KEY_ID="<your-gpg-key-id>" gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.tar.gz gpg --local-user $KEY_ID --armor --detach-sign opentelemetry-go-$VERSION.zip- 验证签名:
gpg --verify opentelemetry-go-$VERSION.tar.gz.asc opentelemetry-go-$VERSION.tar.gz gpg --verify opentelemetry-go-$VERSION.zip.asc opentelemetry-go-$VERSION.zip签名的作用是让下游用户和供应链安全工具能够验证归档文件的完整性与发布者身份,这是 CNCF 项目中软件供应链安全(SLSA/签名校验)实践的一部分。
八、发布 GitHub Release
在 GitHub 上为<new tag>创建 Release,正文必须包含该版本的完整 Changelog 内容。文档特别强调:
GitHub Releases 一旦创建即不可变,必须在创建时就把已签名的四个工件(
.tar.gz、.tar.gz.asc、.zip、.zip.asc)一并上传,之后无法补充或修改。
这一约束把"签名"与"上传"两个动作绑定在同一时刻,杜绝了"先发不带签名的包、事后补签"的供应链风险。
九、Post-Release 收尾
发布不是终点,文档列出了四项收尾工作:
9.1 Contrib 仓库联动发布
确认主仓库发布无误后,按 contrib 仓库的 RELEASING.md 流程发布基于该版本的上游 contrib 版本,保证贡献库的用户也能拿到新能力。
9.2 官方文档更新
更新 OpenTelemetry 官网content/en/docs/languages/go下的 Go instrumentation 文档,重点是把文档中引用的包版本 bump 到刚发布的版本,并确保所有代码示例仍然可编译、准确。
9.3 关闭 Milestone
把所有本次发布修复的 issue 和合并的 PR 归入对应 milestone,然后关闭 milestone。文档给出了两个辅助搜索:
- 查找未归入 milestone 的已关闭 issue:使用
is:issue no:milestone is:closed ... linked:pr查询; - 查找未归入 milestone 的已合并 PR:使用
is:pr no:milestone is:merged查询。
这保证了版本变更的可追溯性——每个修复都能追溯到它属于哪个发布版本。
9.4 关闭 Version Release Issue
当 Version Release issue 中的 todo list 全部完成(从语义约定升级、预发布、打 tag、签名、发布到 milestone 收尾),关闭该 issue,一次完整的发布流程即告结束。
十、发布流程全景与 Tempo 仓库的对应关系
将以上阶段串联起来,一次 OTel Go 发布的生命周期为:
创建 Version Release issue → semconv 升级(semconv-generate + 更新导入 + 更新 contrib lint) → make gorelease 校验公共 API → 验证 contrib 兼容性 → Pre-Release(versions.yaml + make prerelease + CHANGELOG) → Tag(make add-tags + 推送所有子模块 tag) → GPG 签名归档 → 创建 GitHub Release(上传签名工件) → Post-Release(contrib 发布 + 官网文档 + 关闭 milestone + 关闭 issue)对于 Grafana Tempo 而言,其 go.mod 中锁定go.opentelemetry.io/otel v1.44.0、go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0、otlptracehttp v1.44.0等版本,全部落在stable-v1模块集的统一版本下;同时 Tempo 自身的 metrics-generator(如 servicegraphs 处理器)同时使用 v1.25.0 与 v1.34.0 两代 semconv 子包——这既是本文所述 semconv 子包独立版本策略的直接体现,也说明下游项目可以按需引用多个语义约定版本。理解这套发布流程,不仅有助于 OTel Go 生态的贡献者参与发布,也能帮助所有 Go 多模块项目的维护者设计自己的版本发布工程。
需要说明的是:本文中涉及的 OTel 版本号、模块集划分与 semconv 子包列表,均以当前 Tempo 仓库 vendor 目录中锁定的内容为准;若以其他版本为发布目标,请以当时仓库实际的versions.yaml、CHANGELOG.md与 Makefile 为准。发布操作请在 OTel Go 官方仓库(非 Tempo 仓库)的维护流程中进行,本仓库仅作为文档与依赖的只读样例。
- 后端
- 可观测性
- 链路追踪
【免费下载链接】tempo
Grafana Tempo is a high volume, minimal dependency distributed tracing backend.
相关推荐
OpenTelemetry Go SDK 版本发布全流程指南:从语义约定升级、多模块打标签到 GPG 签名与收尾
OpenTelemetry Go SDK 版本发布全流程指南:从语义约定升级、多模块打标签到 GPG 签名与收尾 本指南以 OpenTelemetry Go S
云原生CLI镜像仓库OpenTelemetry Go 多模块版本发布流程深度指南:从 semconv 升级、模块集打 tag 到 GPG 签名
OpenTelemetry Go 多模块版本发布流程深度指南:从 semconv 升级、模块集打 tag 到 GPG 签名 本文以当前仓库 vendored 的
云原生容器编排集群管理微服务OpenTelemetry Go SDK 发布流程实战指南:从语义约定升级到 GPG 签名发布
OpenTelemetry Go SDK 发布流程实战指南:从语义约定升级到 GPG 签名发布 本指南以本仓库中随依赖一并 vendored 的 vendor/
后端任务调度工作流自动化微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考