Help us learn about your current experience with the documentation. Take the survey.

容器镜像的依赖代理

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated

GitLab 容器镜像依赖代理是一个本地代理,可用于您频繁访问的上游镜像。

在 CI/CD 场景中,依赖代理接收请求并从镜像仓库返回上游镜像,充当拉取缓存。

前置条件

要使用容器镜像依赖代理,必须在 GitLab 实例中启用它。默认情况下是启用的,但管理员可以禁用它

支持的镜像和包

以下镜像和包受支持。

镜像/包 GitLab 版本
Docker 14.0+

有关计划添加的功能列表,请查看方向页面

为组启用或禁用依赖代理

为组启用或禁用依赖代理:

  1. 在左侧边栏,选择搜索或跳转至并找到您的组。
  2. 选择设置 > 包和注册表
  3. 展开依赖代理部分。
  4. 要启用代理,打开启用代理开关。要禁用它,关闭该开关。

此设置仅影响组的依赖代理。只有管理员可以为整个 GitLab 实例启用或禁用依赖代理

查看容器镜像依赖代理

要查看容器镜像依赖代理:

  1. 在左侧边栏,选择搜索或跳转至并找到您的组。
  2. 选择运维 > 依赖代理

依赖代理对项目不可用。

使用依赖代理处理 Docker 镜像

您可以将 GitLab 用作 Docker 镜像的来源。

前置条件:

使用容器镜像依赖代理进行身份验证

由于容器镜像依赖代理将 Docker 镜像存储在与您的组关联的空间中,您必须对其进行身份验证。

遵循使用私有注册表镜像的说明, 但不要使用 registry.example.com:5000,而是使用不带端口的 GitLab 域 gitlab.example.com

在使用容器镜像依赖代理进行身份验证时,管理员模式不适用。如果您是启用了管理员模式的管理员,并且创建了一个没有 admin_mode 范围的个人访问令牌,即使管理员模式已启用,该令牌仍然有效。

例如,手动登录:

echo "$CONTAINER_REGISTRY_PASSWORD" | docker login gitlab.example.com --username my_username --password-stdin

您可以使用以下方式进行身份验证:

令牌应将范围设置为以下之一:

  • api:授予完整的 API 访问权限。
  • read_registry:授予对容器注册表的只读访问权限。
  • write_registry:授予对容器注册表的读写访问权限。
  • read_virtual_registry:通过依赖代理授予对容器镜像的只读(拉取)访问权限。
  • write_virtual_registry:通过依赖代理授予对容器镜像的读取(拉取)、写入(推送)和删除访问权限。

在使用容器镜像依赖代理进行身份验证时:

  • 具有 read_virtual_registry 范围的令牌还必须包含 read_registry 范围。
  • 具有 write_virtual_registry 范围的令牌还必须包含 write_registry 范围。

使用个人访问令牌或用户名和密码访问容器镜像依赖代理的用户必须至少拥有他们拉取镜像的组的 Guest 角色。

容器镜像依赖代理遵循 Docker v2 令牌身份验证流程, 向客户端颁发用于拉取的 JWT。身份验证后颁发的 JWT 会在一段时间后过期。当令牌过期时,大多数 Docker 客户端会存储您的凭据并自动请求新令牌,无需进一步操作。

令牌过期时间是可配置的设置。 在 GitLab.com 上,过期时间为 15 分钟。

SAML SSO

当启用SSO 强制时, 用户必须通过 SSO 登录后才能通过容器镜像依赖代理拉取镜像。

SSO 强制也会影响自动合并。 如果在自动合并触发之前 SSO 会话过期,合并管道将无法通过依赖代理拉取镜像。

在 CI/CD 中进行身份验证

Runner 会自动登录到容器镜像依赖代理。要通过依赖代理拉取镜像,请使用以下预定义变量之一:

  • CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX 通过顶级组拉取。
  • CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX 通过子组或项目所在的直接组拉取。

示例拉取最新的 alpine 镜像:

# .gitlab-ci.yml
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/alpine:latest

您还可以使用其他额外的预定义 CI/CD 变量:

  • CI_DEPENDENCY_PROXY_USER:用于登录依赖代理的 CI/CD 用户。
  • CI_DEPENDENCY_PROXY_PASSWORD:用于登录依赖代理的 CI/CD 密码
  • CI_DEPENDENCY_PROXY_SERVER:用于登录依赖代理的服务器。
  • CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX:通过顶级组通过依赖代理拉取镜像的前缀。
  • CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:通过项目所属的直接组或子组通过依赖代理拉取镜像的前缀。

CI_DEPENDENCY_PROXY_SERVERCI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIXCI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX 包含服务器端口。如果您明确包含依赖代理路径,则必须包含端口,除非您手动登录依赖代理时未包含端口:

docker pull gitlab.example.com:443/my-group/dependency_proxy/containers/alpine:latest

使用依赖代理构建镜像时的示例:

# Dockerfile
FROM gitlab.example.com:443/my-group/dependency_proxy/containers/alpine:latest
# .gitlab-ci.yml
image: docker:20.10.16

variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

services:
  - docker:20.10.16-dind

build:
  image: docker:20.10.16
  before_script:
    - echo "$CI_DEPENDENCY_PROXY_PASSWORD" | docker login $CI_DEPENDENCY_PROXY_SERVER -u $CI_DEPENDENCY_PROXY_USER --password-stdin
  script:
    - docker build -t test .

您还可以使用自定义 CI/CD 变量来存储和访问您的个人访问令牌或部署令牌。

使用 Docker Hub 进行身份验证

默认情况下,依赖代理在从 Docker Hub 拉取镜像时不使用凭据。您可以使用 Docker Hub 凭据或令牌配置 Docker Hub 身份验证。

要与 Docker Hub 身份验证,您可以使用:

配置凭据

为组的依赖代理设置 Docker Hub 凭据:

  1. 在左侧边栏,选择搜索或跳转至并找到您的组。

  2. 选择设置 > 包和注册表

  3. 展开依赖代理部分。

  4. 打开启用代理开关。

  5. Docker Hub 身份验证下,输入您的凭据:

    • 身份是您的用户名(用于密码或个人访问令牌)或组织名称(用于组织访问令牌)。
    • 密钥是您的密码、个人访问令牌或组织访问令牌。

    您必须完成两个字段或都留空。如果两个字段都留空,对 Docker Hub 的请求将保持未身份验证状态。

使用 GraphQL API 配置凭据

使用 GraphQL API 在依赖代理设置中设置 Docker Hub 凭据:

  1. 转到 GraphiQL:

  2. 在 GraphiQL 中,输入此变更:

    mutation {
      updateDependencyProxySettings(input: {
        enabled: true,
          identity: "<identity>",
          secret: "<secret>",
          groupPath: "<group path>"
      }) {
        dependencyProxySetting {
         enabled
         identity
        }
        errors
      }
    }

    其中:

    • <identity> 是您的用户名(用于密码或个人访问令牌)或组织名称(用于组织访问令牌)。
    • <secret> 是您的密码、个人访问令牌或组织访问令牌。
    • <group path> 是依赖代理所在组的路径。
  3. 选择运行

  4. 检查结果面板中的任何错误。

验证您的凭据

在与依赖代理身份验证后,通过拉取 Docker 镜像验证您的 Docker Hub 凭据:

docker pull gitlab.example.com/groupname/dependency_proxy/containers/alpine:latest

如果身份验证成功,您将在您的 Docker Hub 使用情况仪表板 中看到活动。

将 Docker 镜像存储在依赖代理缓存中

要将 Docker 镜像存储在依赖代理存储中:

  1. 在左侧边栏,选择搜索或跳转至并找到您的组。

  2. 选择运维 > 依赖代理

  3. 复制依赖代理镜像前缀

  4. 使用以下命令之一。在这些示例中,镜像为 alpine:latest

  5. 您也可以通过摘要拉取镜像,以指定要拉取的确切镜像版本。

    • 通过将镜像添加到您的 .gitlab-ci.yml 文件中按标签拉取镜像:

      image: gitlab.example.com/groupname/dependency_proxy/containers/alpine:latest
    • 通过将镜像添加到您的 .gitlab-ci.yml 文件中按摘要拉取镜像:

      image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/alpine@sha256:c9375e662992791e3f39e919b26f510e5254b42792519c180aad254e6b38f4dc
    • 手动拉取 Docker 镜像:

      docker pull gitlab.example.com/groupname/dependency_proxy/containers/alpine:latest
    • 将 URL 添加到 Dockerfile

      FROM gitlab.example.com/groupname/dependency_proxy/containers/alpine:latest

GitLab 从 Docker Hub 拉取 Docker 镜像并将 blob 缓存到 GitLab 服务器上。下次拉取相同镜像时,GitLab 从 Docker Hub 获取镜像的最新信息,但从 GitLab 服务器提供现有的 blob。

减少存储使用

有关减少容器镜像依赖代理存储使用的信息,请参阅减少依赖代理存储使用

Docker Hub 速率限制和依赖代理

观看如何使用依赖代理帮助避免 Docker Hub 速率限制

Docker Hub 强制执行拉取速率限制。 如果您的 GitLab CI/CD 配置使用来自 Docker Hub 的镜像, 每次作业运行时,它可能被视为一次拉取。 为了帮助绕过此限制,您可以从依赖代理缓存中拉取镜像。

当您拉取镜像(使用 docker pull 命令或在 .gitlab-ci.yml 文件中使用 image: foo:latest)时,Docker 客户端会发出一系列请求:

  1. 请求镜像清单。清单包含有关如何构建镜像的信息。
  2. 使用清单,Docker 客户端请求一系列层,也称为 blob,一次一个。

Docker Hub 速率限制基于清单的 GET 请求数量。依赖代理缓存给定镜像的清单和 blob, 因此当您再次请求它时,无需联系 Docker Hub。

GitLab 如何知道缓存的标记镜像是否过时?

如果您使用的是 alpine:latest 这样的镜像标签,镜像会随时间变化。 每次变化时,清单包含要请求的不同 blob 信息。依赖代理不会在清单每次变化时拉取新镜像; 它只在清单变得过时时才检查。

Docker 不将镜像清单的 HEAD 请求计入速率限制。 您可以对 alpine:latest 发出 HEAD 请求,查看返回的摘要(校验和)值, 并确定清单是否已更改。

依赖代理以 HEAD 请求开始所有请求。只有当清单变得过时时,才会拉取新镜像。

例如,如果您的管道每五分钟拉取一次 node:latest, 依赖代理会缓存整个镜像,并且仅在 node:latest 更新时才更新它。 因此,在六小时内不是有 360 次镜像请求(这超过了 Docker Hub 速率限制), 而只有一次拉取,除非在此期间清单发生了变化。

检查您的 Docker Hub 速率限制

如果您好奇自己向 Docker Hub 发出了多少请求以及还剩多少, 您可以从您的 runner 中运行这些命令,甚至在 CI/CD 脚本中运行:

# 注意,您必须安装 jq 才能运行此命令
TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq --raw-output .token) && curl --head --header "Authorization: Bearer $TOKEN" "https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest" 2>&1 | grep --ignore-case RateLimit
...

输出类似于:

RateLimit-Limit: 100;w=21600
RateLimit-Remaining: 98;w=21600

此示例显示六小时内总共限制 100 次拉取,还剩 98 次拉取。

在 CI/CD 作业中检查速率限制

此示例显示了一个使用安装了 jqcurl 的镜像的 GitLab CI/CD 作业:

hub_docker_quota_check:
    stage: build
    image: alpine:latest
    tags:
        - <可选的 runner 标签>
    before_script: apk add curl jq
    script:
      - |
        TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq --raw-output .token) && curl --head --header "Authorization: Bearer $TOKEN" "https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest" 2>&1

故障排除

身份验证错误:“HTTP Basic: Access Denied”

如果您在与依赖代理身份验证时收到 HTTP Basic: Access denied 错误,请参考双因素身份验证故障排除指南

依赖代理连接失败

如果未设置服务别名,docker:20.10.16 镜像将无法找到 dind 服务,并抛出类似以下的错误:

error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host

可以通过为 Docker 服务设置服务别名来解决此问题:

services:
    - name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:18.09.7-dind
      alias: docker

从 CI/CD 作业身份验证依赖代理时出现问题

GitLab Runner 会自动身份验证到依赖代理。但是,底层的 Docker 引擎仍然受其授权解析过程的约束。

身份验证机制中的配置错误可能导致 HTTP Basic: Access denied403: Access forbidden 错误。

您可以使用作业日志查看用于身份验证依赖代理的身份验证机制:

使用来自 $DOCKER_AUTH_CONFIG 的凭据进行身份验证
使用来自 /root/.docker/config.json 的凭据进行身份验证
使用来自作业负载(GitLab 注册表)的凭据进行身份验证

确保您使用的是预期的身份验证机制。

拉取镜像时出现 “Not Found” 或 “404” 错误

此类错误可能表明运行作业的用户对依赖代理组没有至少 Guest 角色:

  • ERROR: gitlab.example.com:443/group1/dependency_proxy/containers/alpine:latest: not found
    
    failed to solve with frontend dockerfile.v0: failed to create LLB definition: gitlab.example.com:443/group1/dependency_proxy/containers/alpine:latest: not found
  • ERROR: Job failed: failed to pull image "gitlab.example.com:443/group1/dependency_proxy/containers/alpine:latest" with specified policies [always]:
    Error response from daemon: error parsing HTTP 404 response body: unexpected end of JSON input: "" (manager.go:237:1s)

有关改进类似"访问被拒绝"情况错误信息的工作,请参阅问题 354826

从依赖代理运行镜像时出现 “exec format error”

此问题已在 GitLab 16.3 中解决。 对于 16.2 或更早的 GitLab 自托管实例,您可以更新到 16.3 或使用下面记录的解决方法。

如果在 GitLab 16.2 或更早版本中尝试在基于 ARM 的 Docker 安装上使用依赖代理,会发生此错误。 依赖代理仅在拉取具有特定标签的镜像时支持 x86_64 架构。

作为解决方法,您可以指定镜像的 SHA256 强制依赖代理拉取不同的架构:

docker pull ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/library/docker:20.10.3@sha256:bc9dcf5c8e5908845acc6d34ab8824bca496d6d47d1b08af3baf4b3adb1bd8fe

在此示例中,bc9dcf5c8e5908845acc6d34ab8824bca496d6d47d1b08af3baf4b3adb1bd8fe 是基于 ARM 的镜像的 SHA256。

恢复备份后出现 “MissingFile” 错误

如果您遇到 MissingFileCannot read file 错误,可能是因为 备份存档 不包含 gitlab-rails/shared/dependency_proxy/ 的内容。

要解决此已知问题, 您可以使用 rsyncscp 或类似工具从备份源 GitLab 实例复制受影响的文件或整个 gitlab-rails/shared/dependency_proxy/ 文件夹结构。

如果不需要数据,可以使用以下命令删除数据库条目:

gitlab-psql -c "DELETE FROM dependency_proxy_blobs; DELETE FROM dependency_proxy_blob_states; DELETE FROM dependency_proxy_manifest_states; DELETE FROM dependency_proxy_manifests;"