Loki Operator 镜像构建与推送策略全解析:从 Dockerfile 到 CI/CD 工作流
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
本文围绕 Grafana Loki Operator 的镜像构建与推送机制展开,系统讲解 operator 镜像(loki-operator)、OpenShift 捆绑包镜像(loki-operator-bundle)以及存储容量计算工具镜像(storage-size-calculator)在 DockerHub 与 Quay.io 两大注册中心的构建、打标签与发布策略。读者阅读后可完整掌握 Loki Operator 的持续集成镜像流水线、release-please 驱动的版本化发布流程、多平台(amd64/arm64/arm)交叉构建原理,以及如何在本地复现这些镜像的构建过程。
镜像构建与推送总览
Loki Operator 仓库采用「一套构建逻辑、多注册中心分发」的策略:所有镜像构建逻辑收敛到复用的构建工作流中,再由各 CI 工作流按场景(日常开发 vs 正式发布)调用。整个体系由三部分组成:
- 持续集成构建(latest 标签):推送
main分支(仅operator/**路径变更)或手动触发时,构建并推送所有 operator 相关镜像,标签为latest; - 正式发布构建(版本号标签):release-please 创建新 Release 时,构建并推送带语义化版本号(
{major}.{minor}.{patch})的镜像; - 复用构建工作流:集中处理多平台构建(
linux/amd64、linux/arm64、linux/arm)与基于注册中心的认证逻辑,供其他工作流workflow_call调用。
对应的工作流文件与镜像 Dockerfile 均位于仓库根目录,可对照查阅:operator-images.yaml、operator-release-please.yml、operator-reusable-image-build.yml,以及 operator/Dockerfile、operator/calculator.Dockerfile、operator/bundle/openshift/bundle.Dockerfile。
构建与推送策略
DockerHub(docker.io)
官方文档声明,loki-operator镜像同时面向 DockerHub 分发。在仓库的实际工作流中,DockerHub 的推送经由 Google Artifact Registry(us-docker.pkg.dev)下的grafanalabs-global/dockerhub-loki-prod-mirror仓库完成,即先将镜像推送到 GAR 的镜像仓库,再同步/镜像至 DockerHub 命名空间:
| 镜像 | 触发时机 | 组织 | 标签 | 工作流文件 |
|---|---|---|---|---|
loki-operator | 推送 main 分支 | grafana(经grafanalabs-global/dockerhub-loki-prod-mirror中转) | latest | operator-images.yaml |
loki-operator | 创建 Release | 同上 | {major}.{minor}.{patch} | operator-release-please.yml |
Quay.io
Quay.io 是 OpenShift 生态的分发主阵地,面向openshift-logging组织推送三款镜像,其中 operator 主镜像与 bundle 捆绑包镜像配合,可被 OpenShift OperatorHub / OLM 直接消费:
| 镜像 | 触发时机 | 组织 | 标签 | 工作流文件 |
|---|---|---|---|---|
loki-operator | 推送 main 分支 | openshift-logging | latest | operator-images.yaml |
loki-operator-bundle | 推送 main 分支 | openshift-logging | latest | operator-images.yaml |
storage-size-calculator | 推送 main 分支 | openshift-logging | latest | operator-images.yaml |
值得注意的是,实际工作流在 Quay.io 侧还额外发布了第 4 个镜像passthrough-gateway(基于 operator/passthrough-gateway.Dockerfile,对应 operator/cmd/passthrough-gateway 入口),用于 OpenShift 上的多租户网关场景,属于对文档表格的运行时扩展。
工作流文件逐层拆解
operator-images.yaml:持续集成构建
operator-images.yaml 是 daily CI 的入口,触发条件为:
push到main分支,且变更路径命中operator/**(即只有 operator 相关代码变动才触发镜像构建,避免无关提交浪费构建资源);workflow_dispatch手动触发,便于运维人员随时重新构建。
该工作流在同一文件内声明了 5 个并行 job,全部通过uses: ./.github/workflows/operator-reusable-image-build.yml复用统一构建逻辑,仅通过with传入不同的构建参数:
| Job | Dockerfile | Registry | 组织/仓库 | 镜像名 | 标签 |
|---|---|---|---|---|---|
publish-grafana-operator | operator/Dockerfile | us-docker.pkg.dev | grafanalabs-global/dockerhub-loki-prod-mirror | loki-operator | latest |
publish-openshift-operator | operator/Dockerfile | quay.io | openshift-logging | loki-operator | latest |
publish-openshift-bundle | operator/bundle/openshift/bundle.Dockerfile | quay.io | openshift-logging | loki-operator-bundle | latest |
publish-openshift-size-calculator | operator/calculator.Dockerfile | quay.io | openshift-logging | storage-size-calculator | latest |
publish-openshift-passthrough-gateway | operator/passthrough-gateway.Dockerfile | quay.io | openshift-logging | passthrough-gateway | latest |
每个 job 都显式声明id-token: write权限,这是 OIDC 无密钥认证(Workload Identity Federation)的前提——推送到 GAR 时不再依赖静态账号密码。
operator-reusable-image-build.yml:集中式构建核心
operator-reusable-image-build.yml 是整套体系的「心脏」,采用workflow_call可复用事件,对外暴露 5 个必填输入:
image_name:镜像名称;dockerfile:构建所用的 Dockerfile 路径;tag:镜像标签;registry:目标注册中心(us-docker.pkg.dev或quay.io);organization:注册中心下的组织/项目名。
其执行步骤完整呈现了现代多平台镜像 CI 的标准姿势:
- checkout 代码:使用
actions/checkout(v7.0.1)拉取仓库,并设置persist-credentials: false防止令牌泄漏; - 配置 QEMU:通过
docker/setup-qemu-action注册模拟器,使构建机能够为linux/arm64、linux/arm等非宿主架构执行跨平台编译; - 配置 Buildx:通过
docker/setup-buildx-action启用 BuildKit 多平台构建能力; - 认证(按注册中心分流):
- 目标为 GAR(
us-docker.pkg.dev)时,调用grafana/shared-workflows/actions/login-to-gar完成 OIDC 登录; - 目标为 Quay.io 时,先调用
get-vault-secrets从 Vault 读取openshift-credentials(用户名/密码),再通过docker/login-action(v4.6.0,带logout: true)登录;
- 目标为 GAR(
- 构建并推送:调用
docker/build-push-action(v7.3.0),关键参数如下:
- name: Build and push uses: docker/build-push-action@v7.3.0 with: # 根据镜像名推断构建上下文: # bundle 镜像使用 operator/bundle/openshift,其余使用 operator context: ${{ inputs.image_name == 'loki-operator-bundle' && 'operator/bundle/openshift' || 'operator' }} file: ${{ inputs.dockerfile }} platforms: "linux/amd64,linux/arm64,linux/arm" push: true tags: ${{ inputs.registry }}/${{ inputs.organization }}/${{ inputs.image_name }}:${{ inputs.tag }}工作流中的注释特别说明了 context 的推断逻辑:采用条件表达式而非直接暴露context输入,是为了规避 zizmor(GitHub Actions 静态安全扫描工具)对「输入可能展开为攻击者可控代码」的高危告警——这是 CI 供应链安全加固的典型实践。
operator-release-please.yml:版本化发布流水线
operator-release-please.yml 定义了正式发布流程,触发条件为推送main分支且变更operator/**。其流程分为三个串联阶段:
- releasePlease job:使用
googleapis/release-please-action(v5.0.0)分析 Conventional Commits,自动推进版本号并创建 Release。其行为由 operator/release-please-config.json 控制,核心配置包括:bump-minor-pre-major: true(0.x 阶段升 minor 而非 major)、include-component-in-tag: true、tag-separator: "/"(形成operator/vX.Y.Z形式的 tag)、PR 标题模板chore(operator): Community release ${version}以及changelog-path: CHANGELOG.md。该 job 还会输出operator--release_created、operator--tag_name、operator--major/minor/patch等关键结果供下游消费。认证使用 GitHub App(loki-gh-app)生成的临时令牌,避免使用机器人账号密码; - publishImages job:
if: ${{ needs.releasePlease.outputs.release_created }}守卫——仅当 release 真正创建后才执行。复用operator-reusable-image-build.yml构建loki-operator,但标签不再是latest,而是拼接为{major}.{minor}.{patch}的正式版本号,推送到us-docker.pkg.dev/grafanalabs-global/dockerhub-loki-prod-mirror; - publishRelease job:依赖前两个 job 成功,通过
gh release edit将 Release 从草稿状态(release-please 默认draft: true)转为正式发布,并标记为非最新(--latest=false),确保版本的发布状态与镜像推送结果保持一致。
该设计实现了「版本号生成 → 镜像构建推送 → Release 正式发布」的原子化闭环:镜像构建失败则 Release 不会转正。
镜像清单与 Dockerfile 深入解读
loki-operator:主 Operator 二进制镜像
构建文件为 operator/Dockerfile,采用经典的两阶段构建:
# 阶段一:编译 FROM golang:1.26.6@sha256:0d1d3a794be25f809dd2cb3160d8c73276c4056a9f8242a138e908ddeee7b6b6 as builder WORKDIR /workspace # 先复制 go.mod/go.sum 与 api 模块,缓存依赖层 COPY api/ api/ COPY go.mod go.mod COPY go.sum go.sum RUN go mod download # 再复制源码,避免源码变更导致依赖层失效 COPY cmd/loki-operator/main.go cmd/loki-operator/main.go COPY internal/ internal/ RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -mod=readonly -o manager cmd/loki-operator/main.go # 阶段二:精简运行镜像 FROM gcr.io/distroless/static:nonroot WORKDIR / COPY --from=builder /workspace/manager . USER 65532:65532 ENTRYPOINT ["/manager"]几个值得注意的工程细节:
- 依赖缓存:先
COPY go.mod go.sum并执行go mod download,再复制源码,利用 Docker 层缓存避免每次提交都重新下载依赖; - 静态编译:
CGO_ENABLED=0 GOOS=linux产出纯静态二进制,可安全运行于不含 glibc 的 distroless 基础镜像; - 最小攻击面:最终镜像基于
gcr.io/distroless/static:nonroot,无 shell、无包管理器,并以非 root 用户(UID 65532)运行; - 入口:ENTRYPOINT 直指编译产物
/manager,对应 operator 主程序入口 operator/cmd/loki-operator/main.go。
storage-size-calculator:容量计算工具镜像
构建文件为 operator/calculator.Dockerfile,结构与主镜像完全对称:在golang:1.26.6builder 阶段编译 operator/cmd/size-calculator/main.go,产物为size-calculator;运行阶段同样基于 distroless 并以非 root 运行,ENTRYPOINT ["/size-calculator"]。该工具用于计算 LokiStack 各组件(如查询、写入、存储)的资源与容量需求,与 operator 内的 size-calculator 逻辑呼应。
loki-operator-bundle:OpenShift OLM 捆绑包镜像
构建文件为 operator/bundle/openshift/bundle.Dockerfile,它与前两者完全不同——不包含任何二进制,而是 OLM(Operator Lifecycle Manager)规范下的元数据镜像:
FROM scratch # Core bundle labels. LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=loki-operator LABEL operators.operatorframework.io.bundle.channels.v1=stable LABEL operators.operatorframework.io.bundle.channel.default.v1=stable LABEL operators.operatorframework.io.metrics.builder=operator-sdk-unknown LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 # Labels for testing. LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ # Copy files to locations specified by labels. COPY ./manifests /manifests/ COPY ./metadata /metadata/ COPY ./tests/scorecard /tests/scorecard/基于scratch的捆绑包镜像通过一组operators.operatorframework.io.*标签声明其 OLM 元数据布局,并复制三个关键目录:
manifests/:包含 ClusterServiceVersion(loki-operator.clusterserviceversion.yaml)、CRD(loki.grafana.com_lokistacks.yaml、loki.grafana.com_alertingrules.yaml、loki.grafana.com_recordingrules.yaml、loki.grafana.com_rulerconfigs.yaml)、RBAC 与 ServiceMonitor 等清单(见 operator/bundle/openshift/manifests);metadata/:存放annotations.yaml,供 OLM 索引解析 bundle 元数据(见 operator/bundle/openshift/metadata);tests/scorecard/:scorecard 测试配置,用于在发布前对 operator 进行 smoke test。
OLM 依据channels.v1=stable与channel.default.v1=stable将loki-operator暴露在stable频道,OpenShift 集群用户可通过 OperatorHub 直接订阅安装。
本地复现镜像构建
在仓库根目录下,可以通过 Makefile 提供的 target 在本地验证 operator 镜像构建流程。Makefile 中定义:
# Loki Operator loki-operator-image: ## build the operator docker image $(OCI_BUILD) -t $(OPERATOR_IMAGE) -f operator/Dockerfile ./operator其中OPERATOR_IMAGE := $(IMAGE_PREFIX)/loki-operator:$(IMAGE_TAG),执行make loki-operator-image即会以operator/为构建上下文、operator/Dockerfile 为构建文件产出镜像。这与 CI 中context: operator、file: operator/Dockerfile的调用完全一致,本地产物与线上流水线行为可对齐验证。
若需完全复刻 CI 的多平台构建,可参照 operator-reusable-image-build.yml 中的 Buildx 配置,在本地启用 buildx 后指定--platform linux/amd64,linux/arm64,linux/arm进行交叉构建。
小结
Loki Operator 的镜像交付体系可以归纳为「一个可复用构建工作流 + 两条发布通道 + 三类镜像」:
- 一个核心:operator-reusable-image-build.yml 统一承担多平台构建、注册中心认证与推送,通过
workflow_call被 CI 与发布流水线共享,最大程度消除构建逻辑分叉; - 两条通道:operator-images.yaml 负责日常
latest构建,operator-release-please.yml 负责 release-please 驱动的版本化发布,并通过「Release 草稿转正依赖镜像构建成功」的依赖关系保证发布原子性; - 三类镜像:operator 主程序镜像、bundle 捆绑包元数据镜像(供 OLM 消费)、工具类镜像(容量计算),分别由三份职责清晰的 Dockerfile 产出,且主程序与工具镜像均以 distroless 非 root 形态交付。
对需要自建或改造 Operator 镜像 CI 的团队而言,这套策略在触发粒度(operator/**路径过滤)、跨平台构建、OIDC/密钥分级认证、安全加固(zizmor 告警规避、最小基础镜像)以及发布闭环(release-please 与镜像联动)等方面都提供了可直接借鉴的模板。
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考