容器镜像的依赖代理

  1. Tier: 基础版, 专业版, 旗舰版
  2. Offering: JihuLab.com, 私有化部署

极狐GitLab 的容器镜像依赖代理是一个本地代理,您可以用来访问经常使用的上游镜像。

在 CI/CD 的情况下,依赖代理接收请求并从仓库返回上游镜像,充当一个拉取缓存。

先决条件#

要使用容器镜像的依赖代理,必须为极狐GitLab 实例启用它。默认情况下是启用的,但管理员可以将其关闭

支持的镜像和软件包#

以下镜像和软件包是支持的。

镜像/软件包极狐GitLab 版本
Docker14.0+

为群组启用或关闭依赖代理#

History
    1. 在极狐GitLab 15.0 中,所需角色从开发者更改为维护者。
    2. 在极狐GitLab 17.0 中,所需角色从维护者更改为所有者。

要为群组启用或关闭依赖代理:

  1. 在左侧边栏,选择 搜索或前往 并找到您的群组。
  2. 选择 设置 > 软件包和仓库
  3. 展开 依赖代理 部分。
  4. 要启用代理,请打开 启用代理。要关闭它,请关闭切换按钮。

此设置仅影响群组的依赖代理。只有管理员可以为整个极狐GitLab 实例打开或关闭依赖代理

查看容器镜像的依赖代理#

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

  1. 在左侧边栏,选择 搜索或前往 并找到您的群组。
  2. 选择 操作 > 依赖代理

依赖代理在项目中不可用。

使用依赖代理来处理 Docker 镜像#

您可以使用极狐GitLab 作为 Docker 镜像的源。

先决条件:

  • 您的镜像必须存储在 Docker Hub 上。

为容器镜像的依赖代理进行身份验证#

History
    1. 在极狐GitLab 15.0 中,移除了功能标志 dependency_proxy_for_private_groups
    2. 在极狐GitLab 16.3 中,引入了对群组访问令牌的支持介绍。
    3. 在极狐GitLab 17.11 中引入了名为 dependency_proxy_read_write_scopes 的标志,默认情况下禁用的部署令牌范围 read_virtual_registrywrite_virtual_registry 介绍。

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

请按照使用私有仓库中的镜像的说明,但不使用 registry.example.com:5000,而是使用您的极狐GitLab 域名且不带端口 gitlab.example.com

管理员模式 在与容器镜像的依赖代理进行身份验证时不适用。如果您是启用了管理员模式的管理员,并且创建了不带 admin_mode 范围的个人访问令牌,那么即使管理员模式已启用,该令牌也可以正常工作。

例如,要手动登录:

shell
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: 授予通过依赖代理对容器镜像的读(拉取)、写(推送)和删除访问权限。

使用个人访问令牌或用户名和密码访问容器镜像依赖代理的用户必须至少具有从中拉取镜像的群组的访客角色。

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

令牌过期时间是一个可配置的设置。在 JihuLab.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 镜像:

yaml
# .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 包括服务器端口。如果您明确包含了依赖代理路径,则必须包括端口,除非您已手动登录到依赖代理而未包括端口:

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

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

plaintext
# Dockerfile FROM gitlab.example.com:443/my-group/dependency_proxy/containers/alpine:latest
yaml
1# .gitlab-ci.yml 2image: docker:20.10.16 3 4variables: 5 DOCKER_HOST: tcp://docker:2375 6 DOCKER_TLS_CERTDIR: "" 7 8services: 9 - docker:20.10.16-dind 10 11build: 12 image: docker:20.10.16 13 before_script: 14 - echo "$CI_DEPENDENCY_PROXY_PASSWORD" | docker login $CI_DEPENDENCY_PROXY_SERVER -u $CI_DEPENDENCY_PROXY_USER --password-stdin 15 script: 16 - docker build -t test .

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

使用 Docker Hub 进行身份验证#

History
    1. 在极狐GitLab 17.10 中,添加了对 Docker Hub 凭证的支持。
    2. 在极狐GitLab 17.11 中,添加了 UI 支持。

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

要使用 Docker Hub 进行身份验证,可以使用:

  • 您的 Docker Hub 用户名和密码。
    • 此方法不兼容于 强制单点登录(SSO)的 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 中,输入此变更:

    graphql
    1mutation { 2 updateDependencyProxySettings(input: { 3 enabled: true, 4 identity: "<identity>", 5 secret: "<secret>", 6 groupPath: "<group path>" 7 }) { 8 dependencyProxySetting { 9 enabled 10 identity 11 } 12 errors 13 } 14}

    其中:

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

  4. 检查结果窗格中的任何错误。

验证您的凭证#

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

shell
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 文件中添加镜像来按标签拉取镜像:

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

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

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

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

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

减少存储使用#

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

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 脚本中运行这些命令:

shell
# 注意,您必须安装 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 ...

输出如下所示:

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

此示例显示了 6 小时内 100 次拉取的总限制,还剩 98 次拉取。

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

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

yaml
1hub_docker_quota_check: 2 stage: build 3 image: alpine:latest 4 tags: 5 - <optional_runner_tag> 6 before_script: apk add curl jq 7 script: 8 - | 9 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 服务,并抛出如下错误:

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

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

yaml
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 错误。

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

plaintext
Authenticating with credentials from $DOCKER_AUTH_CONFIG
plaintext
Authenticating with credentials from /root/.docker/config.json
plaintext
Authenticating with credentials from job payload (GitLab Registry)

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

拉取镜像时的 Not Found404 错误#

此类错误可能表明运行作业的用户没有依赖代理群组的最低访客角色:

  • plaintext
    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
  • plaintext
    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)

从依赖代理运行镜像时的 exec format error#

此问题在极狐GitLab 16.3 中已解决。对于极狐GitLab 私有化部署实例为 16.2 或更早版本,您可以更新您的实例到 16.3 或使用下面记录的解决方法。

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

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

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

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

还原备份后的 MissingFile 错误#

如果您遇到 MissingFile无法读取文件 错误,可能是因为备份档案不包括 gitlab-rails/shared/dependency_proxy/ 的内容。

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

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

shell
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;"