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

它填补了“不是标准包格式,但仍需版本化托管”的空白。

GitLab 三类制品仓库:存储模型 · API · 权限 Package Registry 存储模型 项目 → 包名 → 版本 → 文件 格式:npm / Maven / PyPI / NuGet 版本不可变,支持依赖解析 API 访问 GET /api/v4/projects/:id/packages PUT /api/v4/projects/:id/packages/npm/ 包管理器原生协议直连 权限体系 项目可见性 + 成员角色 Reporter 可读,Developer 可写 支持 Deploy Token / CI_JOB_TOKEN 适用场景 库/框架发布、依赖分发 多语言包管理、版本锁定 Container Registry 存储模型 仓库名 : 标签 / @摘要 OCI Distribution 规范 manifest + layer,按 digest 去重 API 访问 docker login registry.example.com docker push / pull GET /v2/<name>/manifests/<ref> 权限体系 项目成员角色 + Deploy Token CI_JOB_TOKEN 自动鉴权 支持只读/读写 Token 粒度 适用场景 容器镜像、K8s 部署 CI/CD 镜像流水线、多架构 Generic Packages 存储模型 包名 / 版本 / 文件名 不解析包元数据 任意文件,版本化托管 API 访问 PUT /api/v4/projects/:id/packages/ generic/:name/:ver/:file GET 同路径下载 权限体系 项目成员角色 Deploy Token / CI_JOB_TOKEN 与 Package Registry 共用权限 适用场景 二进制、安装包、配置文件 非标准格式的版本化交付

二、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 决定,适合本地开发或脚本调用,但不应硬编码在仓库中。
Deploy Token 的 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 二进制和部署包。三者不是替代关系,而是按制品形态分工。

一个容易踩的坑:不要把大体积二进制塞进 Package Registry 的 npm 包中。npm 包应保持轻量,大文件走 Generic Packages 或对象存储,否则每次 npm install 都会拉取冗余内容,拖慢依赖解析。