2. GitLab制品仓库核心机制
GitLab 的制品仓库并不是一个单一系统,而是由多个面向不同制品形态的存储子系统组成。理解它们的关键,不是记住“能上传什么”,而是看清三件事:制品以什么形态落盘、通过什么 API 访问、由哪一层权限体系控制。这三者共同决定了某个制品类型是否适合你的发布场景。
本节围绕 Package Registry、Container Registry 和 Generic Packages 展开,先建立统一的存储模型认知,再分别剖析 API 访问方式与权限体系,最后给出适用场景的对比判断。
一、统一视角:三类仓库的存储模型差异
GitLab 的制品最终都落在对象存储或本地磁盘上,但“逻辑寻址方式”完全不同。
Package Registry 是面向包管理器的仓库。它按 项目 → 包名 → 版本 → 文件 的层级组织,支持 npm、Maven、PyPI、NuGet、Composer、Go Proxy 等多种格式。每个包格式有自己的元数据规范,例如 Maven 需要 groupId/artifactId/version,npm 需要 package.json 中的 name 与 version。Package Registry 的存储路径通常形如:
@scope/package-name/-/package-name-1.2.3.tgz
com/example/demo/1.2.3/demo-1.2.3.jar
它的核心特征是:版本不可变。同一个包名+版本一旦发布,默认不允许覆盖,这为依赖解析提供了确定性。
Container Registry 遵循 OCI Distribution 规范,存储的是镜像的 manifest 与 layer。逻辑寻址是 仓库名:标签 或 仓库名@摘要。例如:
registry.example.com/group/project/app:v1.2.3
registry.example.com/group/project/app@sha256:abc123...
镜像由多层 layer 组成,层内容按 digest 去重存储。标签是可变的,可以指向不同 digest;而 digest 是不可变的,适合做发布锁定。Container Registry 的存储模型天然支持增量推送与拉取,这是它与 Package Registry 最大的工程差异。
Generic Packages 是 Package Registry 的一个特殊格式,用于存放任意文件,如二进制、压缩包、配置文件、安装包。它不解析包元数据,只按 包名/版本/文件名 存储。访问路径形如:
/api/v4/projects/:id/packages/generic/:package_name/:version/:file_name
它填补了“不是标准包格式,但仍需版本化托管”的空白。
二、API 访问方式:从“包管理器原生”到“REST 直连”
三类仓库的 API 访问方式差异很大,这直接影响 CI/CD 中的使用方式。
Package Registry 的访问分为两层。第一层是包管理器原生协议,例如 npm 客户端直接配置 .npmrc 指向 GitLab 的 npm 端点,Maven 在 settings.xml 中配置 repository。第二层是 GitLab 提供的 REST API,用于列出、删除、查询包。例如列出某项目下所有包:
GET /api/v4/projects/123/packages
Header: PRIVATE-TOKEN: <your_token>
上传 npm 包时,实际是 npm publish 走原生协议,GitLab 在服务端解析并落库。这意味着包格式的合法性由包管理器协议保证,GitLab 不做额外校验。
Container Registry 的访问完全遵循 Docker Registry HTTP API V2。登录使用:
docker login registry.example.com -u <username> -p <token>
推送与拉取使用标准 docker push / docker pull。在 CI 中,GitLab 预置了 CI_REGISTRY、CI_REGISTRY_USER、CI_REGISTRY_PASSWORD 等变量,配合 CI_JOB_TOKEN 即可免密登录。需要注意的是,Container Registry 的鉴权走的是 /jwt/auth 端点换取 Bearer Token,而不是直接使用 PRIVATE-TOKEN。
Generic Packages 的访问最直接,只有 REST API 一种方式。上传示例:
curl --header "PRIVATE-TOKEN: <token>" \
--upload-file ./app-release.tar.gz \
"https://gitlab.example.com/api/v4/projects/123/packages/generic/app-release/1.2.3/app-release.tar.gz"
下载时使用相同路径的 GET 请求。它的优势是不依赖任何包管理器客户端,在任意有 curl 或 HTTP 库的环境中都能使用。
三、权限体系:项目角色、Token 与可见性
三类仓库共享 GitLab 的项目权限模型,但细节上有差异。
基础权限来自项目成员角色:Guest、Reporter、Developer、Maintainer、Owner。对于 Package Registry 和 Generic Packages,Reporter 及以上可以读取,Developer 及以上可以写入。对于 Container Registry,权限同样映射到项目角色,但推送镜像通常需要 Developer 及以上。
在 CI/CD 场景中,更常用的是 Token 体系:
- CI_JOB_TOKEN:流水线作业自动注入,权限限定在当前项目,适合项目内构建与推送。
- Deploy Token:为部署场景创建,可设置只读或读写,可跨项目使用,适合生产环境拉取镜像或下载制品。
- Personal Access Token:绑定个人账号,权限范围由 scope 决定,适合本地开发或脚本调用,但不应硬编码在仓库中。
read_package_registry 与 write_package_registry 是独立开关。如果只勾选只读,推送会被拒绝并返回 403。生产环境拉取镜像时,建议使用只读 Deploy Token,避免泄露写入权限。
此外,项目可见性会影响匿名访问:公开项目的 Package Registry 和 Generic Packages 在开启相应设置后允许匿名读取,但 Container Registry 默认仍需要认证。这一点在多团队共享基础镜像时需要特别注意。
四、适用场景对比与选择依据
选择哪类仓库,核心看三个问题:制品是否遵循标准包格式、是否需要层去重与增量传输、消费方使用什么工具链。
| 维度 | Package Registry | Container Registry | Generic Packages |
|---|---|---|---|
| 制品形态 | 标准包(npm/Maven/PyPI 等) | OCI 镜像 | 任意文件 |
| 版本模型 | 包名+版本,不可变 | 标签可变,digest 不可变 | 包名+版本+文件名 |
| 访问协议 | 包管理器原生 + REST | Docker Registry V2 | REST API |
| 增量传输 | 不支持 | 支持 layer 去重 | 不支持 |
| 典型消费方 | npm/mvn/pip 等客户端 | docker/k8s/containerd | curl/脚本/安装程序 |
| 发布锁定 | 版本号 | digest 最佳 | 版本号+文件名 |
实践中的常见组合是:用 Container Registry 托管服务镜像,用 Package Registry 托管内部库,用 Generic Packages 托管 CLI 二进制和部署包。三者不是替代关系,而是按制品形态分工。
npm install 都会拉取冗余内容,拖慢依赖解析。