1. 制品治理全景
在 GitLab 中,“制品”并不是一个单一概念,而是覆盖了包、容器镜像、通用文件等多种形态的集合。它们共同承担一个使命:把一次流水线中产生的可交付成果,稳定地保存下来,并让后续的部署、回滚、审计都能找到确定的版本。理解制品治理,首先要理解 GitLab 提供的三类主要制品载体,以及它们各自适合承载什么。
第一类是包(Package),通过 GitLab Package Registry 管理。它面向语言生态的依赖分发,支持 npm、Maven、PyPI、NuGet、Composer、Go Proxy 等格式。包的核心特征是“按坐标寻址”:例如 Maven 包由 groupId、artifactId、version 三者共同确定,npm 包由包名和版本确定。第二类是容器镜像(Container Image),通过 GitLab Container Registry 管理,以仓库名加 tag 或 digest 定位。镜像适合承载完整运行环境,是部署到 Kubernetes 或容器平台的主要交付物。第三类是通用包(Generic Package),同样走 Package Registry,但不绑定特定语言格式,可以上传任意文件,例如二进制、压缩包、配置文件、安装脚本。它常被用来存放不适合纳入语言包体系的构建产物。
三类制品并非互斥,而是经常在同一条流水线中协同出现。一个典型的前端项目可能同时产出 npm 包、Docker 镜像和一份部署用的通用压缩包。治理的目标不是限制使用哪一种,而是让每一种制品都有明确的版本、来源和生命周期。
版本语义是制品治理的骨架。GitLab 本身不强制你使用某种版本规则,但工程实践中普遍采用语义化版本(SemVer):MAJOR.MINOR.PATCH。MAJOR 表示不兼容的变更,MINOR 表示向后兼容的功能新增,PATCH 表示向后兼容的缺陷修复。预发布版本用连字符追加标识,例如 2.1.0-rc.1;构建元数据用加号追加,例如 2.1.0+build.42。在 CI 中,版本号通常来自 Git tag,例如 tag v2.1.0 去掉前缀后得到 2.1.0。如果流水线运行在分支上而非 tag 上,则应生成快照版本,例如 2.1.0-SNAPSHOT.20240612.abc1234,避免与正式版本冲突。
发布生命周期把制品从“能构建”推进到“可交付”。一个清晰的阶段划分是:开发版、预发布版、正式版。开发版对应主干或特性分支的每次提交,允许频繁覆盖;预发布版对应 release 分支或 rc tag,用于集成验证;正式版对应正式 tag,一旦发布即不可变。GitLab 的 Package Registry 和 Container Registry 都支持对已发布版本设置保护,防止被同名覆盖。容器镜像尤其要注意:同一个 tag 被重新推送会改变其指向的 digest,因此正式发布应使用不可变 tag 或直接引用 digest。
治理目标可以归纳为四点。第一,可追溯:每个制品都能回答“它由哪次提交、哪条流水线、哪个 Job 产生”。GitLab 在包和镜像的元数据中记录了 pipeline ID 和 commit SHA,但前提是你在构建时正确传递了这些信息。第二,不可变:正式版本发布后不允许被覆盖,保证下游引用稳定。第三,可清理:通过保留策略控制存储成本,例如只保留最近 10 个正式版本和最近 30 天的开发版本。第四,可控制:通过项目或群组权限控制谁可以发布、谁可以下载、谁可以删除。
下面给出一个可直接参考的版本与归档策略示例,适用于中等规模项目:
| 制品类型 | 版本来源 | 发布阶段 | 保留策略 | 不可变 |
|---|---|---|---|---|
| npm 包 | package.json + tag | 开发 / 正式 | 正式版永久,开发版 30 天 | 正式版是 |
| Maven 包 | pom.xml + tag | 开发 / 预发布 / 正式 | 正式版永久,快照 14 天 | 正式版是 |
| 容器镜像 | tag + commit SHA | 开发 / 预发布 / 正式 | 正式版永久,开发版 7 天 | 正式版是 |
| 通用包 | 自定义版本文件 | 正式 | 按项目需要 | 是 |
在流水线中落地这套策略,关键动作有三个。其一,在构建 Job 中显式写入版本和来源信息。例如使用 CI_COMMIT_TAG 或 CI_COMMIT_SHORT_SHA 拼接版本号,并在上传时附带 CI_PIPELINE_ID。其二,区分“上传”和“发布”两个动作:上传只是把文件放进 Registry,发布则是打上正式版本标记并触发下游。其三,配置清理规则。GitLab 支持在项目设置中为 Package Registry 和 Container Registry 配置保留策略,也可以使用 API 定期清理。对于容器镜像,还可以在 CI 中调用 gitlab-ci-token 删除旧 tag。
2.1.0 发布 2.1.1,而不是覆盖 2.1.0。覆盖会破坏所有已经引用该版本的部署和缓存。
最后需要强调,制品治理不是发布之后才考虑的事,而是从第一次构建就应该纳入流水线设计。构建时写入来源、上传时区分阶段、发布时锁定版本、归档后执行清理,这四个动作贯穿制品的一生。只有把这条链路打通,后续的部署、回滚和审计才有可靠的基础。