GitLab 进阶:内置 Registry 发包与 CI/CD 自动发布
GitLab 内置制品库概述
GitLab 内置了 Package Registry(制品库),无需额外搭建私有仓库,开箱即用。支持的包管理格式:
| 类型 | 用途 | 适用场景 |
|---|---|---|
| npm | Node.js 包管理 | 前端组件库、工具函数共享 |
| Maven | Java 包管理 | Java 项目依赖管理 |
| PyPI | Python 包管理 | Python 模块发布 |
| NuGet | .NET 包管理 | .NET 类库分发 |
| Composer | PHP 包管理 | PHP 扩展包 |
| Container Registry | Docker 镜像 | 容器化应用镜像管理 |
| Generic Packages | 通用文件包 | 二进制文件、压缩包分发 |
本文聚焦两个核心场景:npm 包的发布与 Container Registry 的 Docker 镜像推送,均配合 CI/CD 实现自动化。
第一部分:NPM 包发布与 CI/CD 自动化
1. 创建项目
在 GitLab 上创建一个空项目作为 npm 包仓库:
# 克隆项目
git clone http://your-gitlab-domain/root/pkg-demo.git
cd pkg-demo
# 初始化 npm 项目
npm init -y
bash
2. 配置 package.json
{
"name": "@root/pkg-demo",
"version": "1.0.0",
"description": "Demo of GitLab NPM Registry",
"main": "index.js",
"author": "root <root@example.com>",
"license": "MIT"
}
json
关键点:
name必须以@scope开头,scope 通常是 GitLab 的用户名或组名- scope 决定了包在 Registry 中的命名空间
- 对应到 npm 官方文档中的 scoped packages 概念
3. 创建入口文件
// index.js
console.log('Hello World from @root/pkg-demo');
javascript
4. 配置 .npmrc 文件
.npmrc 文件告诉 npm 从哪里下载包、推送到哪里、使用什么认证方式。
手动发布模式
// .npmrc
@root:registry=http://your-gitlab-domain/api/v4/packages/npm/
//your-gitlab-domain/api/v4/packages/npm/:_authToken=<your-npm-token>
//your-gitlab-domain/api/v4/projects/<project-id>/packages/npm/:_authToken=<your-npm-token>
ini
需要替换的参数:
| 参数 | 说明 | 获取方式 |
|---|---|---|
@root | scope 名称 | GitLab 用户名或组名 |
your-gitlab-domain | GitLab 域名 | 自建 GitLab 的访问地址 |
<project-id> | 项目 ID | 项目 Settings > General 中查看 |
<your-npm-token> | 认证令牌 | 创建部署令牌(见下文) |
5. 创建部署令牌
- 进入项目 -> Settings > Repository > Deploy tokens
- 创建令牌:
- Name:
pkg-publish - Scopes:勾选
read_package_registry+write_package_registry - 点击 Create deploy token
- Name:
- 复制生成的 token(只显示一次)
安全建议:本地开发只需
read_package_registry权限,写权限交给 CI/CD。
6. 手动发布
# 发布
npm publish
# 验证输出
# npm notice Publishing to http://your-gitlab-domain/api/v4/projects/6/packages/npm/
# + @root/pkg-demo@1.0.0
bash
7. 安装验证
在其他项目中安装发布的包:
# 初始化新项目
mkdir test-install && cd test-install
npm init -y
# 复制 .npmrc 文件(使用只读 token)
# 安装包
npm install @root/pkg-demo@1.0.0
bash
发布成功后,可在 GitLab 项目 -> Packages & Registries > Package Registry 中查看已发布的版本。
8. CI/CD 自动发布 npm 包
手动发布既不安全又繁琐。通过 CI/CD 流水线,可以实现代码推送后自动发布。
切换 .npmrc 配置
将 .npmrc 改为使用 CI/CD 内置变量:
// .npmrc(CI/CD 版本)
@root:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/
${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
ini
关键变量说明:
| 变量 | 来源 | 说明 |
|---|---|---|
$CI_API_V4_URL | 自动注入 | GitLab API v4 地址 |
$CI_PROJECT_ID | 自动注入 | 当前项目 ID |
$CI_JOB_TOKEN | 自动注入 | Job 临时令牌,自动认证 |
这三个变量由 CI/CD 流水线自动注入,无需手动配置。
配置 .gitlab-ci.yml
# .gitlab-ci.yml
stages:
- deploy
publish:
stage: deploy
image: node:18-alpine
script:
# 配置 .npmrc(使用 CI 变量)
- |
{
echo "@root:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
echo "${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}"
} | tee .npmrc
# 安装依赖(如有)
- npm ci --cache .npm --prefer-offline || true
# 发布包
- npm publish
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
rules:
# 仅在默认分支触发
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
yaml
推送触发流水线
# 修改版本号
# 编辑 package.json,将 version 从 1.0.0 改为 1.0.1
git add .
git commit -m "chore: bump version to 1.0.1"
git push origin main
bash
推送后,GitLab 自动触发流水线,执行 npm publish 发布新版本。
验证结果
- 在 Build > Pipelines 查看流水线执行状态
- 在 Packages & Registries > Package Registry 查看新版本
- 流水线日志中应显示
+ @root/pkg-demo@1.0.1发布成功
注意:如果发布失败提示版本已存在,需要在本地修改
package.json中的version字段后重新推送。同时注意合并线上通过流水线编辑器创建的.gitlab-ci.yml文件(使用git pull --rebase后再 push)。
第二部分:Container Registry 与 Docker 镜像发布
GitLab 内置的 Container Registry 可以直接存储和管理 Docker 镜像,无需额外部署 Harbor 或 Docker Registry。
Container Registry 核心概念
| 概念 | 说明 |
|---|---|
| Registry 地址 | registry.example.com/group/project |
| 镜像命名规则 | registry.example.com/group/project/image:tag |
| 访问控制 | 与项目权限一致(私有/公开) |
| 存储 | GitLab 服务器本地或对象存储 |
1. 手动构建与推送 Docker 镜像
# 登录 GitLab Container Registry
docker login registry.example.com
# 构建镜像
docker build -t registry.example.com/root/my-app:v1.0.0 .
# 推送镜像
docker push registry.example.com/root/my-app:v1.0.0
bash
2. CI/CD 自动构建与推送镜像
GitLab CI/CD 提供了一系列预定义变量,用于自动认证和推送镜像:
| 变量 | 说明 |
|---|---|
$CI_REGISTRY | Container Registry 地址(如 registry.example.com) |
$CI_REGISTRY_USER | CI/CD 作业的用户名 |
$CI_REGISTRY_PASSWORD | CI/CD 作业的密码(临时令牌) |
$CI_REGISTRY_IMAGE | 当前项目的完整镜像地址 |
基础版:Docker-in-Docker(DinD)构建推送
# .gitlab-ci.yml - Docker 镜像构建推送
docker-build:
image: docker:24.0.5-cli
stage: build
services:
- docker:24.0.5-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
yaml
进阶版:多阶段流水线(构建-测试-发布-部署)
# .gitlab-ci.yml - 完整的 Docker 镜像 CI/CD 流水线
default:
image: docker:24.0.5-cli
services:
- docker:24.0.5-dind
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
stages:
- build
- test
- release
- deploy
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_DRIVER: overlay2
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
# 阶段1:构建镜像
build:
stage: build
script:
- docker build --pull -t $CONTAINER_TEST_IMAGE .
- docker push $CONTAINER_TEST_IMAGE
# 阶段2:测试(并行执行两个测试任务)
test1:
stage: test
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker run $CONTAINER_TEST_IMAGE /script/to/run/tests
test2:
stage: test
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test
# 阶段3:打 tag 发布
release-image:
stage: release
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
rules:
- if: $CI_COMMIT_BRANCH == "main"
# 阶段4:部署
deploy:
stage: deploy
script:
- ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
environment: production
yaml
流水线说明:
- build 阶段:使用
docker build --pull确保拉取最新的基础镜像,构建后推送到 Registry - test 阶段:从 Registry 拉取刚构建的镜像,运行测试(支持并行多个测试任务)
- release 阶段:测试通过后,将镜像标记为
latest并推送 - deploy 阶段:通过部署脚本将镜像部署到生产环境
3. Dockerfile 示例(前端项目)
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
dockerfile
4. 不使用特权模式的替代方案
DinD 需要 privileged 模式,在某些安全要求严格的环境中不可用。GitLab 提供了替代方案:
Buildah(无需特权模式)
build:
stage: build
image: quay.io/buildah/stable
variables:
STORAGE_DRIVER: vfs
BUILDAH_FORMAT: docker
FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/app"
before_script:
- echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
- buildah build -t $FQ_IMAGE_NAME
- buildah push $FQ_IMAGE_NAME
yaml
构建方式对比
| 方式 | 需要特权模式 | 安全性 | 速度 | 推荐场景 |
|---|---|---|---|---|
| Docker-in-Docker (DinD) | 是 | 中等 | 快 | 通用场景 |
| Docker Socket Binding | 否 | 较低 | 最快 | 自建 Runner |
| Buildah | 否 | 高 | 中等 | 安全要求高的环境 |
| BuildKit | 可选 | 高 | 快 | 现代 Docker 构建 |
版本发布工作流总览
NPM 包发布流程
开发者修改代码
|
v
修改 package.json 版本号
|
v
git commit & push to main
|
v
GitLab CI/CD 触发 Pipeline
|
v
自动执行 npm publish
|
v
Package Registry 更新
text
Docker 镜像发布流程
开发者修改代码 + Dockerfile
|
v
git commit & push to main
|
v
CI/CD Pipeline 触发
|
v
Stage 1: docker build --> 推送到 Registry
|
v
Stage 2: docker pull + 运行测试
|
v
Stage 3: 测试通过 --> 打 latest tag --> 推送
|
v
Stage 4: 部署脚本执行 --> 部署到生产环境
text
安全最佳实践
| 场景 | Token 类型 | 权限 | 存放位置 |
|---|---|---|---|
| 本地开发安装 npm 包 | 部署令牌(只读) | read_package_registry | 本地 .npmrc |
| CI/CD 发布 npm 包 | $CI_JOB_TOKEN | 临时令牌,自动获取 | 流水线内置 |
| CI/CD 推送 Docker 镜像 | $CI_REGISTRY_PASSWORD | 临时令牌,自动获取 | 流水线内置 |
| 外部项目使用 | 部署令牌(只读) | read_package_registry | CI/CD Variables |
核心原则:发布操作完全交给 CI/CD,本地只需只读令牌来安装依赖或拉取镜像。
令牌权限与过期设置
在创建部署令牌时,可以设置:
- 过期日期:限制令牌使用期限,过期后自动失效
- 用户权限:仅允许特定用户在指定日期前下载
- 版本控制:新版本发布后,旧令牌可被回收
- 私有仓库:可只让部分用户在指定日期之前下载,之后升级版本需使用新令牌
CI/CD 变量安全
- 在 Settings > CI/CD > Variables 中配置敏感信息
- 启用 Masked 选项隐藏日志中的变量值
- 启用 Protected 选项限制变量仅在受保护分支上可用
- 定期轮换令牌
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
403 Forbidden npm 发布失败 | Token 权限不足 | 确认包含 write_package_registry |
404 Not Found npm 下载失败 | scope 或 URL 配置错误 | 检查 .npmrc 中的 scope 和 registry 地址 |
| npm 版本已存在 | 重复发布同版本 | 每次发布前更新 version |
CI_JOB_TOKEN 认证失败 | Pipeline 配置错误 | 检查 .npmrc 模板中变量替换语法 |
Docker 镜像推送 denied | 未登录 Registry | 检查 docker login 命令和 $CI_REGISTRY_PASSWORD |
DinD 连接失败 no such host | 未设置 service alias | 为 docker:dind 服务添加 alias: docker |
| 镜像构建缓慢 | 无缓存 | 启用 Docker layer caching 或使用 --cache-from |
| 令牌泄露 | 不安全存储 | 使用 Masked 变量,定期轮换 |
总结
- GitLab 内置 Package Registry 和 Container Registry,无需额外搭建私有仓库
- npm 包发布:配置 scope -> 配置 .npmrc -> 创建令牌 -> npm publish
- Docker 镜像发布:配置 Dockerfile -> docker build -> docker push -> 自动部署
- CI/CD 自动发布使用内置临时令牌(
$CI_JOB_TOKEN/$CI_REGISTRY_PASSWORD),无需手动配置认证 - 遵循最小权限原则:本地只读,CI/CD 负责发布
- 推荐使用多阶段流水线(build -> test -> release -> deploy)确保镜像质量
↑