跳到主要内容

GitLab Container Registry

GitLab Container Registry 基于 OCI/Docker Registry v2,与项目权限绑定:能读项目的人通常能 pull 镜像,push 需要 Developer 及以上角色。生产环境应配合 Tag 策略、清理规则和漏洞扫描,避免 Registry 无限膨胀。

本章示例 Registry 地址为 registry.example.com;Omnibus 默认可用 gitlab.example.com:5050 或配置后的独立域名。

1. Registry架构

GitLab Container Registry 架构与 JWT 鉴权流程

镜像数据流与鉴权流是两条不同的链路:Registry 负责 Layer 和 Manifest 的读写,GitLab Rails 负责校验用户或 Token 的项目权限并签发 JWT。

组件作用
GitLab Rails校验 JWT,映射用户/Deploy Token 权限
Registry 后端存储 layer 与 manifest
Metadata DB项目在 container_repositories 等表记录镜像元数据

单机 Omnibus 默认镜像存 /var/opt/gitlab/gitlab-rails/shared/registry;生产建议外置 S3 兼容对象存储,便于扩容和备份。

2. 开启Container Registry

Omnibus 在 /etc/gitlab/gitlab.rb 中配置:

# 使用独立域名(推荐)
registry_external_url 'https://registry.example.com'

# 或挂在 GitLab 同域路径
# registry_external_url 'https://gitlab.example.com:5050'

gitlab_rails['registry_enabled'] = true

# 外置对象存储示例
registry['storage'] = {
's3' => {
'bucket' => 'gitlab-registry',
'region' => 'ap-southeast-1',
'accesskey' => 'AKIA...',
'secretkey' => '...'
}
}

应用配置:

sudo gitlab-ctl reconfigure
sudo gitlab-ctl status registry
curl -I https://registry.example.com/v2/
# 预期 HTTP/1.1 401 Unauthorized(未带凭证时正常)

Helm Chart 部署时在 values.yaml 中设置 registry.enabled: true 并配置 global.registry 与对象存储。修改 registry_external_url 后,已有 CI 变量 $CI_REGISTRY 会变化,需同步更新 Runner 与外部系统。

3. 镜像上传与下载

项目级镜像路径:registry.example.com/<group>/<project>

# 登录(使用 Personal Access Token,scope 含 read_registry / write_registry)
docker login registry.example.com -u <username> -p '<token>'

# 构建并推送
export IMAGE=registry.example.com/mygroup/my-service
docker build -t "$IMAGE:$(git rev-parse --short HEAD)" .
docker push "$IMAGE:$(git rev-parse --short HEAD)"

# 拉取
docker pull "$IMAGE:$(git rev-parse --short HEAD)"

查看项目内镜像 Tag(UI:Deploy → Container Registry,或 API):

curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$PROJECT_ID/registry/repositories"

多架构镜像使用 buildx:

docker buildx build --platform linux/amd64,linux/arm64 \
-t "$IMAGE:1.2.0" --push .

推送前在本地 docker scan 或启用 GitLab Container Scanning,避免将有已知 CVE 的镜像推送到生产命名空间。

4. Registry认证

方式适用场景
用户 PAT开发机手工 push/pull
CI 内置变量$CI_REGISTRY_USER / $CI_REGISTRY_PASSWORD / $CI_REGISTRY_IMAGE
Deploy Token外部集群、Harbor 同步、只读拉取
Group/Project Access Token自动化机器人账号

创建 Deploy Token(UI:Settings → Repository → Deploy tokens,或 API):

curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$PROJECT_ID/deploy_tokens" \
--form "name=k8s-pull" \
--form "scopes[]=read_registry"

在 Kubernetes 创建 pull secret:

kubectl create secret docker-registry gitlab-registry \
--docker-server=registry.example.com \
--docker-username=<deploy-token-username> \
--docker-password=<deploy-token-password> \
--namespace production

CI Job 中优先用 $CI_JOB_TOKEN 作为 $CI_REGISTRY_PASSWORD(GitLab 15+ 默认行为),减少长期 PAT 泄露面。外部系统无法用 Job Token 时再用 Deploy Token,并设置过期时间。

5. 镜像清理策略

Registry 只增不减时磁盘或对象存储账单会快速上升。GitLab 提供 Container Registry cleanup policies(项目级):

Settings → Packages and registries → Cleanup policies 示例:

规则建议
Keep the most recent每个 Tag 前缀保留最新 N 个
Remove tags older than删除超过 90 天的非保护 Tag
Keep regex.*-prod$ 保留生产 Tag

API 触发清理:

curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$PROJECT_ID/registry/repositories/$REPO_ID/tags/cleanup" \
--form "name_regex=.*" \
--form "older_than=90d" \
--form "keep_n=10"

Omnibus 管理员还可运行 garbage collect(需维护窗口,停止 registry 或使用只读模式):

sudo gitlab-ctl registry-garbage-collect

清理策略不能替代发布规范:应用 semver 或 Git SHA Tag,避免 hundreds 个 latest-build-* 长期留存。受保护 Tag 不会被自动删除,发布 Tag 应在 GitLab 中设为 protected。

6. CI集成镜像仓库

标准 CI 变量无需额外配置即可 push:

build-and-push:
stage: build
image: docker:27-cli
services:
- docker:27-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- export TAG="${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
- docker build -t "$CI_REGISTRY_IMAGE:$TAG" .
- docker push "$CI_REGISTRY_IMAGE:$TAG"
- |
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
docker tag "$CI_REGISTRY_IMAGE:$TAG" "$CI_REGISTRY_IMAGE:latest"
docker push "$CI_REGISTRY_IMAGE:latest"
fi
rules:
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG

跨项目拉取基础镜像时,在 .gitlab-ci.yml 中声明依赖并使用 Job Token:

build:
image: $CI_REGISTRY/group/base-images/node:20-alpine

若基础镜像在私有项目,需在基础镜像项目 Settings → CI/CD → Token Access 中允许下游项目访问,或对 Runner 配置 Deploy Token。

与外部 Registry(Harbor、ECR)同步时,在 deploy 前增加 mirror Job,或使用 GitLab Geo/第三方 replication;无论哪种方式,都应对同步账号做只读或单向 push 权限分离,并监控同步失败告警。