Loki Operator 发布流程全解:从 bundle 生成到 OperatorHub 上架
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
本指南系统讲解 Grafana Loki Operator(位于 operator/ 目录)的社区版本发布机制:如何通过make bundle-all生成 OLM bundle、如何借助 release-please 自动化版本号提升与 CHANGELOG 生成、如何将镜像发布到制品仓库,以及如何自动向两个 OperatorHub 社区仓库提交上架 PR。读完本文,你将完整掌握 Loki Operator 从一次代码合并到在 Kubernetes 生态中可被 OLM 安装的端到端发布链路,并理解每一环背后的工作流设计与配置细节。
发布流程总体设计
发布一个 Loki Operator 社区版本,需要依次完成以下四件事(对应 release.md 中 "Design" 一节的描述):
- 提升 Loki Operator 版本号并生成 bundle 清单:执行
make bundle-all; - 更新 CHANGELOG.md到新版本;
- 创建 release tag 与 GitHub release;
- 向 [k8s-operatorhub/community-operators] 与 [redhat-openshift-ecosystem/community-operators-prod] 两个仓库各开一个 PR,提交新版本 bundle 的内容。
其中第 2、3 步由 GitHub 官方维护的release-pleaseaction 自动化完成;第 4 步则由一个在 release tag 创建时触发的 workflow 自动执行。第 1 步(bundle 生成)目前仍是人工操作,是整条链路中唯一没有自动化的环节。
值得强调的是,Loki Operator 与 Loki 主项目共享同一个仓库,但Operator 的 release-please 流程与 Loki 自身的发布流程是相互独立的:Operator 的配置单独存放在 operator/release-please-config.json,且 workflow 只监听operator/**路径的变更。
第 1 步:用 make bundle-all 生成 bundle 清单
bundle 是什么
bundle 是 OLM(Operator Lifecycle Manager)生态中 Operator 的标准打包格式,包含 ClusterServiceVersion(CSV)清单、CustomResourceDefinition(CRD)以及元数据注解。Loki Operator 针对不同发行渠道维护了三个 bundle 变体(见 operator/Makefile 中的VARIANT变量):
| 变体 | 镜像仓库 | 通道 | 适用场景 |
|---|---|---|---|
community | docker.io/grafana | alpha | 通用社区分发(默认) |
community-openshift | docker.io/grafana | alpha | 带 OpenShift 兼容注解的社区包 |
openshift | quay.io/openshift-logging | stable | OpenShift 官方渠道(随 OpenShift Logging 发行) |
bundle-all目标会依次生成以上三个变体(operator/Makefile):
.PHONY: bundle-all bundle-all: ## Generate both bundles. $(MAKE) bundle $(MAKE) bundle VARIANT=community-openshift $(MAKE) bundle VARIANT=openshiftbundle 生成的具体动作
单个bundle目标(operator/Makefile)做三件事:
.PHONY: bundle bundle: manifests $(KUSTOMIZE) $(OPERATOR_SDK) ## Generate variant bundle manifests and metadata, then validate generated files. cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) cd $(BUNDLE_DIR) && cp ../../PROJECT . && $(KUSTOMIZE) build ../../$(MANIFESTS_DIR) | $(OPERATOR_SDK) generate bundle $(BUNDLE_BUILD_GEN_FLAGS) && rm PROJECT $(OPERATOR_SDK) bundle validate $(BUNDLE_DIR)- 通过
kustomize edit set image controller=$(IMG)把 CSV 中的控制器镜像地址替换为当前版本镜像; - 用
operator-sdk generate bundle从config/manifests/<variant>生成 bundle 清单,输出到./bundle/<variant>目录(如 operator/bundle/community/); - 用
operator-sdk bundle validate校验生成的 bundle 是否符合 OLM 规范。
版本号由VERSION变量控制(当前仓库默认值为0.11.0,见 operator/Makefile)。发布前需要先在 PR 中把该版本号提升为目标版本,例如升级到0.11.1或0.12.0,同时保证提交信息规范(见下文 "Releasing" 一节),这也是原文档强调 "be careful with the commit message" 的原因。
第 2、3 步:release-please 自动化版本发布
release-please 的工作原理
release-please 通过解析 git 历史中的Conventional Commit提交信息来驱动整个发布流程:
- 扫描到可发布单元(releasable unit,即以
feat、fix、deps为前缀的提交)后,自动创建 release PR(其中包含版本号提升与 CHANGELOG 生成); - release PR 被合并后,release-please 自动创建 GitHub release;
- 随后继续等待下一个可发布单元,再开启下一轮 release PR。
Operator 的 release-please workflow 位于 .github/workflows/operator-release-please.yml,其触发条件与关键参数如下:
on: push: paths: - 'operator/**' branches: - main即只有当operator/**路径下有合并到main的提交时才会运行;使用 GitHub App(loki-gh-app)签发的 token 调用googleapis/release-please-action(v5.0.0),并指定:
path: operator:只关注operator子目录;config-file: operator/release-please-config.json:使用独立的 manifest 配置。
operator/release-please-config.json 详解
operator/release-please-config.json 是理解整个发布策略的关键,其完整内容如下:
{ "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": true, "include-component-in-tag": true, "draft": true, "tag-separator": "/", "packages": { "operator": { "component": "operator", "release-type": "go", "pull-request-title-pattern": "chore(${component}): Community release ${version}", "changelog-path": "CHANGELOG.md" } } }结合原文档的说明,各配置项的含义如下:
include-component-in-tag+tag-separator: "/":release tag 采用operator/vX.Y.Z格式(组件名 + 斜杠 + 版本号)。这个 tag 前缀也是后续 OperatorHub 发布 workflow 的触发过滤器;release-type: "go":按 Go 模块语义处理版本提升,作用于 operator/ 目录;pull-request-title-pattern:release PR 的标题固定为chore(operator): Community release <version>格式——注意,这个标题格式正是下一节 "防止未更新 manifests 就合并" 检查工作流的匹配依据;changelog-path: "CHANGELOG.md":生成的变更日志写入 operator/CHANGELOG.md。从该文件可以看到 release-please 的实际输出风格:每个版本一节,包含⚠ BREAKING CHANGES、Features、Bug Fixes分组,且 security/deps 类提交也会被归类记录。
为什么使用 bump-minor-pre-major 与 bump-patch-for-minor-pre-major
由于 Operator 目前仍处于v1.0.0之前的阶段(pre-major),配置开启了bump-minor-pre-major与bump-patch-for-minor-pre-major,效果是:
- 合并
feat、fix、deps提交 → 只提升patch版本(如v0.10.1→v0.10.2); - 合并
feat!、fix!(带感叹号的破坏性变更提交)→ 提升minor版本(如v0.10.x→v0.11.0)。
由此,在未引入破坏性变更的前提下,当前仅监听main分支的 release-please 可以支撑两种发布场景:
- Case 1(patch 发布):从
v0.Y.x的 diff 发布v0.Y.x+1。该场景在破坏性特性合并进main之前一直有效; - Case 2(minor 发布):从
v0.Y.x的 diff 发布新版本v0.Y+1.0。
为什么启用 draft
Operator 与 Loki 主项目共享同一个 GitHub 仓库,直接由 release-please 创建的 release 会被标记为仓库的latest,导致 "Loki 最新版本" 被误显示为 Operator 的版本。release-please 本身没有提供关闭latest标记的选项,因此配置了"draft": true,让 release-please 只创建草稿 release,再由后续步骤发布:
.github/workflows/operator-release-please.yml 中的publishReleasejob 在镜像构建完成后执行:
gh release edit "$RELEASE_NAME" --draft=false --latest=false即把草稿转为正式 release,同时明确不设为 latest,从而与 Loki 主项目的发布记录区分开。
publishImages:构建并推送 Operator 镜像
当 release-please 判定需要发布(release_created输出为 true)时,publishImagesjob 通过复用工作流 .github/workflows/operator-reusable-image-build.yml 构建并推送 Operator 镜像,传入参数包括:
dockerfile: operator/Dockerfile(见 operator/Dockerfile);registry: us-docker.pkg.dev、organization: grafanalabs-global/dockerhub-loki-prod-mirror、image_name: loki-operator;tag由 release-please 输出的major.minor.patch三部分拼接而成。
镜像最终推送到 Google Artifact Registry(GAR)的地址为us-docker.pkg.dev/grafanalabs-global/dockerhub-loki-prod-mirror/loki-operator:<version>,之后由gar-image-mirror服务将其镜像同步到 Docker Hub 的docker.io/grafana/loki-operator。
从复用工作流源码可以看到构建细节(.github/workflows/operator-reusable-image-build.yml):
- 使用 QEMU + Docker Buildx 进行多架构构建,
platforms: "linux/amd64,linux/arm64,linux/arm"; - 构建上下文按镜像名推断:
loki-operator-bundle使用operator/bundle/openshift,其余(含loki-operator)使用operator目录; - 该复用工作流同时支持两套仓库体系:登录 GAR(
us-docker.pkg.dev)或从 Vault 拉取凭据登录quay.io(OpenShift 渠道使用)。
第 4 步:向 OperatorHub 社区仓库发布
触发机制
发布工作流 .github/workflows/operator-publish-operator-hub.yml 监听 GitHubrelease 发布事件,并校验 tag 前缀:
on: release: types: [published] jobs: operator-hub-prod-release: if: startsWith(github.event.release.tag_name, 'operator/') ...当 tag 以operator/开头(即 release-please 创建的operator/vX.Y.Z)时,会并发调用两次复用工作流 .github/workflows/operator-reusable-hub-release.yml,分别面向:
redhat-openshift-ecosystem/community-operators-prod(OpenShift 生产渠道);k8s-operatorhub/community-operators(通用社区渠道)。
复用工作流做了哪些事
.github/workflows/operator-reusable-hub-release.yml 依次完成以下动作:
签发 GitHub App token:使用
loki-operator-hub-publisher这个专用 GitHub App,保证后续以受信任的 bot 身份操作;提取版本号:从 tag
operator/vX.Y.Z中通过${TAG:10}切片去掉operator/v前缀,得到纯版本号写入环境变量;同步 fork:用
gh repo sync grafanabot/$INPUTS_REPO --source <org>/<repo> --force先把 grafanabot 名下的 fork 同步到最新,避免拉取完整上游仓库(浅克隆会导致 push 报 "shallow update not allowed");检出两个仓库:分别 checkout grafanabot fork 的 operatorhub 仓库(工作目录)与
grafana/loki主仓库(tmp/目录);复制 bundle 清单:将新版本目录创建到 operatorhub 仓库的
operators/loki-operator/<version>/下,内容来自./tmp/operator/bundle/community${OCP_DIR}/*:mkdir operators/loki-operator/${VERSION} cp -R ./tmp/operator/bundle/community${OCP_DIR}/* operators/loki-operator/${VERSION} rm -f "operators/loki-operator/${VERSION}/bundle.Dockerfile"注意这里同时做了两件重要的适配:一是按目标渠道选择
community或community-openshift变体;二是删除 bundle.Dockerfile(OperatorHub 社区目录不需要它);添加 OpenShift 支持版本注解(仅 OpenShift 仓库):使用
fjogeleit/yaml-update-action在operators/loki-operator/<version>/metadata/annotations.yaml中写入annotations['com.redhat.openshift.versions'],当前值为v4.12。这正是原文档所说 "Adding the ocp supported version annotation to the metadata.yaml" 的实现;创建 PR:以 grafanabot 身份(CLA 已批准)新建分支
update-loki-operator-to-<version>,提交并gh pr create --repo <org>/<repo> --base main,PR 标题为Update the loki-operator to <version>。
防止未更新 manifests 就合并 release-please PR
由于第 1 步(make bundle-all生成 bundle)尚未自动化、与 release-please 相互独立,存在"release-please PR 先合并、bundle 后补"的时序风险。为此仓库设置了专门的守卫工作流 .github/workflows/operator-check-prepare-release-commit.yml。
该工作流只针对 release-please PR 运行(通过两个条件限定):
if: | github.event.pull_request.head.ref == 'release-please--branches--main--components--operator' && contains(github.event.pull_request.title, 'chore( operator): Community release')其逻辑是:先从 PR 标题chore( operator): Community release <semver>中提取目标版本号,再用gh search commits在main分支搜索提交信息为chore(operator): Prepare community release v<semver>的提交;若找不到,则以退出码 1 使检查失败,阻止 PR 合并。也就是说,人工提交的"版本提升 + bundle 生成"准备提交必须先于 release-please PR 存在。原文档也指出:一旦第 1 步实现自动化,这个守卫工作流就可以移除。
Releasing:一次完整发布的操作清单
综合以上设计,一次真实的 Loki Operator 社区发布按以下步骤执行(对应原文档 "Releasing" 一节):
- 创建版本提升 PR:先手动提交一次包含版本号提升与 bundle 生成的变更,提交信息务必为
chore(operator): Prepare community release v<version>(例如 v0.6.1 的准备工作),并合并到main; - 在 release-please PR 上重新触发
operator-publish-operator-hub检查:确保守卫工作流通过; - 合并 release-please PR:合并后 release-please 自动创建 release(草稿态),标题形如
chore(operator): Community release v<version>; - 镜像发布:
publishImagesjob 构建多架构镜像并推送到 GAR,随后由gar-image-mirror同步到 Docker Hub 的docker.io/grafana/loki-operator; - OperatorHub PR 自动创建:
operator-publish-operator-hubworkflow 检测到operator/前缀的正式 release 后,自动向k8s-operatorhub/community-operators与redhat-openshift-ecosystem/community-operators-prod两个仓库各开一个包含新版本 bundle 的 PR。
全链路小结
把整条发布流水线串起来看,Loki Operator 的发布由四类 GitHub Actions 工作流协同完成:
| 工作流 | 触发时机 | 职责 |
|---|---|---|
| operator-release-please.yml | operator/**合并到main | 生成 CHANGELOG、创建草稿 release、调度镜像构建与正式发布 |
| operator-reusable-image-build.yml | 被 release workflow 调用 | 多架构构建并推送 Operator 镜像到 GAR / quay.io |
| operator-check-prepare-release-commit.yml | release-please PR 上 | 校验 "Prepare community release" 准备提交已存在 |
| operator-publish-operator-hub.yml | tag 以operator/开头的 release 发布 | 触发向两个 OperatorHub 社区仓库的上架 PR |
其中唯一的人工环节是第 1 步:更新 operator/Makefile 中的VERSION并执行make bundle-all重新生成 operator/bundle/ 下的清单,随后以约定的提交信息合入main。其余版本号计算、CHANGELOG 生成、release 创建、镜像发布与 OperatorHub 上架全部由自动化流水线接管,既保证了与 Loki 主项目发布记录的清晰隔离(operator/tag 前缀 + draft +--latest=false),也保证了每次上架内容的规范性与可追溯性。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考