- 操作系统
- 云原生
- 容器运行时
【免费下载链接】linuxkit
A toolkit for building secure, portable and lean operating systems for containers
导读
本文以 OpenTelemetry Go 官方仓库的 RELEASING.md 为骨架,完整还原其多模块(multi-module)发布流程:包括 Semantic Convention 代码生成、Breaking Change 校验、Pre-Release 版本号变更、Git Tag 打标与 Release 创建,以及发布后的 contrib 仓库、官网文档与 Demo 仓库的联动更新。读完本文,你将掌握一套可复用的 Go 多模块仓库版本发布 SOP,并能理解Makefile、versions.yaml、CHANGELOG.md等文件在发布链路中的具体角色。
说明:本文所引用文档位于当前仓库的 vendored 依赖目录
pkg/init/vendor/go.opentelemetry.io/otel/下,属于 OpenTelemetry Go 官方源码随依赖一并 vendored 进 linuxkit 仓库的产物。文中的路径均以仓库根目录为起点,读者可直接点击进入原文继续深入。
一、为什么要有一套专门的发布流程:多模块仓库的版本困境
OpenTelemetry Go(go.opentelemetry.io/otel)不是一个单一 Go module,而是一个由数十个 module 组成的仓库。在 versions.yaml 中可以看到,仓库按module set(模块集)组织版本线:
stable-v1:主版本线v1.31.0,包含核心go.opentelemetry.io/otel、sdk、sdk/metric、trace、metric以及各类 exporter(otlpgrpc / otlphttp / stdout / zipkin)等稳定模块;experimental-metrics:v0.53.0,包含 prometheus exporter 等实验模块;experimental-logs:v0.7.0,包含log、sdk/log等日志信号模块;experimental-schema:v0.0.10,包含schema模块;excluded-modules:internal/tools这类仅供仓库内部使用的模块不参与发布。
这种结构决定了发布动作必须一次性、原子性地作用于整个模块集,而不是单个包。Makefile 中几乎所有发布相关 target(prerelease、add-tags、gorelease)都依赖$(MULTIMOD)工具(go.opentelemetry.io/build-tools/multimod,见 Makefile)和verify-mods前置校验,目的就是保证模块集内所有 module 的版本一致、依赖互相指向新版本。
因此整个发布流程被拆成五个阶段:Semantic Convention 生成 → Breaking Change 校验 → Pre-Release → Tag → Release → Post-Release,每一步都有对应的 make target 和人工校验点。
二、Semantic Convention 生成:make semconv-generate
OpenTelemetry 语义约定(Semantic Conventions)是各语言 SDK 共享的规范。每当 OpenTelemetry Semantic Conventions 发布新版本,semconv包就需要重新生成,这是版本升级的前置步骤。
2.1 操作步骤(原文完整流程)
export TAG="v1.21.0" # 改为你要生成的目标语义约定版本 export OTEL_SEMCONV_REPO="/absolute/path/to/opentelemetry/semantic-conventions" docker pull otel/semconvgen:latest make semconv-generate # 使用上面导出的 TAG 和 OTEL_SEMCONV_REPO具体三步为:
- 将 OpenTelemetry Semantic Conventions 仓库检出(checkout)到目标 release tag 的本地副本;
- 拉取最新
otel/semconvgen镜像:docker pull otel/semconvgen:latest; - 在本仓库执行
make semconv-generate ...。
执行成功后会在semconv下生成一个新的子包(按版本号命名)。提交 Pull Request 前务必人工核对生成结果是否正确。
2.2 源码级解读:Makefile 中发生了什么
在 Makefile 中,semconv-generate的实际逻辑是:
SEMCONVPKG ?= "semconv/" .PHONY: semconv-generate semconv-generate: $(SEMCONVGEN) $(SEMCONVKIT) [ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 ) [ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 ) $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=metric -f metric.go -t "$(SEMCONVPKG)/metric_template.j2" -s "$(TAG)" $(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)"可以提炼出几个关键点:
- 两个环境变量缺一不可:
TAG(语义约定版本)和OTEL_SEMCONV_REPO(语义约定仓库本地路径)。Makefile 里用显式[ "$(TAG)" ] || exit 1做了强校验,缺失任一变量会直接终止; SEMCONVGEN是构建工具:包名为go.opentelemetry.io/build-tools/semconvgen(见 Makefile),以 Docker 镜像otel/semconvgen:latest提供运行环境;- 生成分两类模板:
attribute_group模板生成attribute_group.go(trace 类型),metric模板生成metric.go,输入统一指向OTEL_SEMCONV_REPO/model/.(语义约定仓库的 model 目录,YAML 模型定义所在); SEMCONVKIT负责落盘:包名为go.opentelemetry.io/otel/internal/tools/semconvkit,把生成结果输出到semconv/<TAG>/新子目录。
在当前 vendored 副本中,可以实际看到这一机制的历史产物:semconv 下存在v1.20.0/、v1.21.0/、v1.26.0/三个按版本号命名的子包——这正是多次semconv-generate运行留下的累积结果,也印证了“每次新版本生成一个新子包”的流程描述。
三、Breaking Changes 校验:make gorelease
语义化版本承诺(semver)要求公共 API 不能被破坏性修改。OpenTelemetry Go 用gorelease工具做自动校验:
make gorelease- 该 target 在 Makefile 中定义为遍历
OTEL_GO_MOD_DIRS(即除internal/tools外的所有含go.mod的目录),对每个 module 依次执行gorelease; gorelease工具来源为golang.org/x/exp/cmd/gorelease(见 Makefile),它通过对比两个版本(当前工作区 vs 上一个 tag)的公共 API 差异,输出破坏性变更报告;- 如果发现非预期的公共 API 变化,就需要在发布前修正,而不是等到用户升级依赖时才发现兼容性问题。
实践建议:把
make gorelease纳入发布前的强制检查项,与 CI 中的 lint、测试、verify-mods同等对待。
四、contrib 仓库兼容性验证
主仓库(opentelemetry-go)的 API 变更往往会波及下游的 contrib 仓库(opentelemetry-go-contrib,即各类 instrumentation 库的集合)。RELEASING.md 明确要求:如果本次主仓库的变更可能影响 contrib 仓库,必须先按 contrib 仓库 RELEASING.md 中 "Verify OTel changes" 一节描述的步骤,验证变更与 contrib 仓库的兼容性。
这一步是发布前的一层“影响面审计”,避免主仓库发布后立即破坏大量下游 instrumentation 包的编译。
五、Pre-Release:版本号变更与 Changelog 整理
5.1 决定发布哪些模块集并更新 versions.yaml
发布的第一步是决策:本次要发布哪些 module set。决定后,在versions.yaml中更新对应 module set 的version字段(例如把stable-v1从v1.31.0改到v1.32.0),并把这个改动提交到一个新分支上。
紧接着要更新各个子模块的go.mod,让它们依赖即将发布的新版本(即后续步骤才会真正打出的 tag)。
5.2 执行make prerelease
make prerelease MODSET=<module set>其底层实现在 Makefile:
.PHONY: prerelease prerelease: verify-mods @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) prerelease -m ${MODSET}要点:
MODSET是必填环境变量,不设置会直接报错退出;MODSET的值即versions.yaml中定义的模块集名字,如stable-v1、experimental-logs;- 前置步骤
verify-mods调用$(MULTIMOD) verify,先校验各模块集配置的一致性; - 执行后
multimod会创建一个形如prerelease_<module set>_<new tag>的分支,该分支包含本次发布的所有版本变更(各子模块 go.mod 的依赖版本、versions.yaml 的版本号等)。
5.3 人工核对变更
git diff ...prerelease_<module set>_<new tag>核对点:所有模块的版本是否都已被改为<new tag>。确认无误后,将该分支合并进你的 pre-release 分支:
git merge prerelease_<module set>_<new tag>5.4 更新 Changelog
RELEASING.md 对 Changelog 的维护有明确规范:
完整性:确保本次发布的所有相关变更都已收录,且语言要能让非贡献者看懂。可用以下命令直接核对自上个 tag 以来的提交:
git --no-pager log --pretty=oneline "<last tag>..HEAD"归档 Unreleased:把所有
Unreleased下的变更移入新章节,标题遵循[<new tag>] - <date of release>格式。在 CHANGELOG.md 中可以看到实际格式,例如## [1.31.0/0.53.0/0.7.0/0.0.10] 2024-10-11——多版本线同时发布时用/分隔;位置约束:新章节必须放在
<!-- Released section -->注释之下,避免未来被自动化脚本覆盖(该注释在 CHANGELOG.md 中可见);更新底部链接:同步更新文末的链接索引。
5.5 推送并创建 Pull Request
将改动推送到上游,创建 Pull Request。PR 描述中务必附带从 Changelog 中整理出的发布说明(curated changes),方便评审者与用户直接了解本次发布内容。
六、Tag:打标与推送(最容易出错的一步)
6.1 为什么 Tag 必须与 Pre-Release 一致
PR 合并后,需要对合并 commit 打 tag。RELEASING.md 用两个***IMPORTANT***强调了打标环节的风险:
- Tag 必须与 Pre-Release 步骤中使用的 tag 完全一致。如果 Pre-Release 与打标之间修改了
versions.yaml,会导致发布状态错乱; - Go module 的 tag 一旦打错就无法移除(Go 模块代理缓存了错误的版本),错误版本一旦推送上游,会引发连锁的线上事故,且难以补救。因此推送前务必反复确认版本号正确。
6.2 打标命令
对每个要发布的 module set,在主干分支合并 PR 的那个 commit 上执行:
make add-tags MODSET=<module set> COMMIT=<commit hash>- 只有当工作区当前
HEAD不是目标 commit 时,才需要显式提供COMMIT值(Makefile 中COMMIT ?= "HEAD",见 Makefile); - 底层同样是
multimod:$(MULTIMOD) tag -m ${MODSET} -c ${COMMIT},它会为模块集内每个 module打上对应路径前缀的 tag。
6.3 推送 tag(含所有子模块)
git push upstream <new tag> git push upstream <submodules-path/new tag> ...注意:
- 推送目标是上游 remote(
github.com/open-telemetry/opentelemetry-go.git),不是自己的 fork; - 必须逐个推送所有子模块的 tag(例如
sdk/metric/v1.32.0这类带路径前缀的 tag),漏推任何一个都会导致对应 module 无法被go get解析。
七、Release:创建 GitHub Release
打标完成后,为新的<new tag>在 GitHub 上创建 Release:
- Release body 必须包含本次发布的全部 Changelog 发布说明(所有 release notes);
- 这一步通常与打标在同一天完成,保证 tag 与 Release 一一对应。
八、Post-Release:发布后的三处联动更新
8.1 contrib 仓库
主仓库验证通过后,需要立即为使用该版本的 contrib 仓库发布新版本,让下游 instrumentation 能同步升级到新依赖。
8.2 官网 Go 语言文档
更新 OpenTelemetry 官网中 Go 语言的 instrumentation 文档(content/en/docs/languages/go目录):
- 将文档中引用的各包版本号升级为刚发布的最新版本;
- 确保文档中所有代码示例仍能编译、内容准确。
8.3 Demo 仓库
升级 OpenTelemetry Demo 仓库中以下 Go 服务的依赖:
accountingservicecheckoutserviceproductcatalogservice
这三处升级属于“发布收尾”动作,目的是让示例与真实 SDK 始终保持同步,避免示例代码用过时 API。
九、发布流程全景回顾与最佳实践
把整个流程串起来,一次完整发布的操作序列如下:
| 阶段 | 命令 / 操作 | 关键产物 | 验证手段 |
|---|---|---|---|
| Semconv 生成 | docker pull otel/semconvgen:latest+make semconv-generate | semconv/<TAG>/新子包 | 人工核对生成代码 |
| API 校验 | make gorelease | gorelease 兼容性报告 | 无破坏性 API 变更 |
| 影响面审计 | 按 contrib 仓库流程验证 | 兼容性结论 | contrib 仓库编译/测试 |
| Pre-Release | 改versions.yaml+make prerelease MODSET=... | prerelease_<modset>_<tag>分支 | git diff核对版本号 |
| Changelog | 归档 Unreleased、更新链接 | 新版本章节 | git log <last tag>..HEAD对照 |
| Tag | make add-tags MODSET=... COMMIT=...+git push upstream | 各 module tag | 核对 tag 与 prerelease 一致 |
| Release | GitHub 创建 Release | Release notes | body 含完整 Changelog |
| Post-Release | contrib 发布、官网文档、Demo 依赖升级 | 同步更新 | 文档可编译、Demo 可运行 |
结合仓库源码可以提炼出几条通用最佳实践,适用于任何 Go 多模块仓库:
- 用
versions.yaml集中管理模块集版本,发布决策只改一处; - 让
make prerelease这类自动化工具负责机械性的版本替换,人工只做git diff审查; - Tag 是发布流程中唯一“不可逆”的操作,Go module 代理会永久缓存错误版本,所以打标前必须三查(版本号、commit、与 prerelease 一致性);
- Changelog 用
<!-- Released section -->注释做“写保护”,避免后续自动化流程覆盖已发布的历史记录; - 发布不是终点,contrib 仓库、官网文档、官方 Demo 的联动升级才能保证整个生态步调一致。
十、本仓库中的落点:vendored 副本的意义
这份 RELEASING.md 在当前仓库中位于 pkg/init/vendor/go.opentelemetry.io/otel/RELEASING.md,是 linuxkit 的 init 组件依赖 OpenTelemetry Go SDK 时随 vendor 目录一并带入的官方文档。它所在的 otel 目录 完整保留了官方仓库的核心文件——Makefile、versions.yaml、CHANGELOG.md、VERSIONING.md 以及 semconv 下的多个版本子包——这本身就构成了验证本文所述流程的“活标本”:你可以在 vendored 副本中直接查看versions.yaml的多版本线结构、比对 CHANGELOG 的[1.31.0/0.53.0/0.7.0/0.0.10]多版本格式、浏览semconv/v1.20.0、semconv/v1.21.0、semconv/v1.26.0等历次生成产物,从而把发布流程文档与真实仓库状态一一对应起来。对于负责维护该依赖的开发者而言,理解这套发布机制也有助于判断何时需要升级 vendor 中的 OTel 版本,以及升级时应该关注哪些兼容性风险。
- 操作系统
- 云原生
- 容器运行时
【免费下载链接】linuxkit
A toolkit for building secure, portable and lean operating systems for containers
相关推荐
OpenTelemetry Go 多模块发布全流程指南:从 Semantic Convention 升级到 GPG 签名 Release
OpenTelemetry Go 多模块发布全流程指南:从 Semantic Convention 升级到 GPG 签名 Release 导读 OpenTele
后端微服务存储认证鉴权opentelemetry-go 版本发布全流程实战:Semantic Convention 升级、GPG 签名与多模块 Tag 管理
opentelemetry go 版本发布全流程实战:Semantic Convention 升级、GPG 签名与多模块 Tag 管理 本文基于 sliver
网络安全OpenTelemetry Go 多模块发布流程全解:从语义约定生成到 tag、Release 与示例验证
OpenTelemetry Go 多模块发布流程全解:从语义约定生成到 tag、Release 与示例验证 导读 本文以 OpenTelemetry Go( g
云原生CLI应用安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考