Dependency Proxy

GitLab Dependency Proxy 是一个本地代理,可用于您经常访问的上游镜像。

在 CI/CD 的情况下,Dependency Proxy 接收请求并从镜像库返回上游镜像,作为 pull-through cache。

先决条件

  • Dependency Proxy 默认启用,但可以由管理员关闭

为群组启用或关闭 Dependency Proxy

要为群组启用或关闭 Dependency Proxy:

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

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

查看 Dependency Proxy

查看 Dependency Proxy:

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

Dependency Proxy 不适用于项目。

对 Docker 镜像使用 Dependency Proxy

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

先决条件:

使用 Dependency Proxy 进行身份验证

  • 引入于 13.7 版本,功能标志名为 dependency_proxy_for_private_groups。默认启用。
  • 功能标志 dependency_proxy_for_private_groups 移除于 15.0 版本。

由于 Dependency Proxy 将 Docker 镜像存储在与您的群组关联的空间中,因此您必须针对 Dependency Proxy 进行身份验证。

按照 使用来自私有注册表的图像的说明,而不使用 registry.example.com:5000,需使用没有端口 gitlab.example.com 的 GitLab 域名。

例如,手动登录:

docker login gitlab.example.com --username my_username --password my_password

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

  • 您的 GitLab 用户名和密码。
  • 个人访问令牌,范围设置为 read_registrywrite_registry
  • 群组部署令牌,范围设置为 read_registrywrite_registry

使用个人访问令牌或用户名和密码访问 Dependency Proxy 的用户,至少需要从中提取镜像的群组的 Guest 成员权限。

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

SAML SSO

启用 SSO enforcement 后,用户必须先通过 SSO 登录,然后才能通过 Dependency Proxy 拉取镜像。

在 CI/CD 内进行身份验证

Runners 会自动登录 Dependency Proxy。要通过 Dependency Proxy 拉取,请使用预定义变量 之一:

  • 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:用于登录 Dependency Proxy 的 CI/CD 用户。
  • CI_DEPENDENCY_PROXY_PASSWORD:用于登录 Dependency Proxy 的 CI/CD 密码。
  • CI_DEPENDENCY_PROXY_SERVER:用于登录 Dependency Proxy 的服务器。
  • CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX:用于通过 Dependency Proxy 从顶级群组拉取镜像的镜像前缀。
  • CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:用于通过 Dependency Proxy 从项目所属的直接组或子组中拉取镜像的镜像前缀。

CI_DEPENDENCY_PROXY_SERVERCI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIXCI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX 包括服务器端口。如果您明确包含 Dependency Proxy 路径,则必须包含端口,除非您已手动登录 Dependency Proxy 而不包含端口:

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

使用 Dependency Proxy 构建镜像时的示例:

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

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

services:
  - docker:19.03.12-dind

build:
  image: docker:19.03.12
  before_script:
    - docker login -u $CI_DEPENDENCY_PROXY_USER -p $CI_DEPENDENCY_PROXY_PASSWORD $CI_DEPENDENCY_PROXY_SERVER
  script:
    -  docker build -t test .

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

在 Dependency Proxy 缓存中存储 Docker 镜像

要将 Docker 镜像存储在 Dependency Proxy 存储中:

  1. 在左侧边栏中,选择 搜索或转到 并找到您的群组。
  2. 在左侧边栏中,选择 运维 > 依赖项代理
  3. 复制 依赖代理镜像前缀
  4. 使用以下命令之一。在这些示例中,镜像是 alpine:latest
  5. 您还可以通过 digest 拉取镜像,以准确指定要拉取的镜像版本。

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

      image: gitlab.example.com/groupname/dependency_proxy/containers/alpine:latest
      
    • 通过将镜像添加到 .gitlab-ci.yml 文件中,通过 digest 提取镜像:

      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。

减少存储使用

有关减少 Dependency Proxy 上的存储使用的信息,请参阅减少 Dependency Proxy 存储使用

Docker Hub 速率限制和 Dependency Proxy

2020 年 11 月,Docker 引入了来自 Docker Hub 的拉取请求的速率限制。 如果您的 GitLab CI/CD 配置 使用来自 Docker Hub 的镜像,则每次作业运行时,它都可以算作拉取请求。 为帮助解决此限制,您可以改为从 Dependency Proxy 缓存中拉取镜像。

当您拉取一个镜像时(通过使用类似 docker pull 的命令,或者在 .gitlab-ci.yml 文件中,image: foo:latest),Docker 客户端会生成一个请求集合:

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

Docker Hub 速率限制基于清单的 GET 请求数。 Dependency Proxy 会缓存给定图像的清单和 blob,因此当您再次请求时,不必联系 Docker Hub。

系统如何知道缓存的标签镜像是否过期?

如果您使用像 alpine:latest 这样的镜像标签,镜像会随着时间而改变。每次更改时,Manifest 都包含有关要请求哪些 blob 的不同信息。每次 manifest 更改时,Dependency Proxy 都不会拉取新镜像;它仅在 manifest 变得陈旧时进行检查。

Docker 不会将镜像 manifest 的 HEAD 请求计入速率限制。 您可以为 alpine:latest 发出 HEAD 请求,查看标头中返回的 digest(校验和)值,并确定 manifest 是否已更改。

Dependency Proxy 以 HEAD 请求启动所有请求。如果 manifest 已经过时,则只有这样才能拉取新镜像。

例如,如果您的流水线每五分钟拉取一次 node:latest,Dependency Proxy 会缓存整个镜像,并且仅在 node:latest 更改时才更新它。因此,不需要在 6 小时内对镜像发出 360 个请求(超过 Docker Hub 速率限制),而是只有一个拉取请求,除非 manifest 在那段时间内发生了变化。

检查您的 Docker Hub 速率限制

如果您想知道您向 Docker Hub 发出了多少请求以及还有多少请求,您可以从 runner 运行这些命令,甚至可以在 CI/CD 脚本中运行:

# Note, you must have jq installed to run this command
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

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

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

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

hub_docker_quota_check:
    stage: build
    image: alpine:latest
    tags:
        - <optional_runner_tag>
    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

故障排查

Dependency Proxy 连接失败

如果没有设置服务别名,docker:19.03.12 镜像将无法找到 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 作业向 Dependency Proxy 进行身份验证时的问题

极狐GitLab Runner 将自动向依赖代理进行身份验证。但是,底层的 Docker 引擎仍然受制于其授权解析过程。

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

您可以使用作业日志查看用于针对 Dependency Proxy 进行身份验证的身份验证机制:

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

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

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

类似于以下内容的 Docker 错误可能表明,运行构建作业的用户在指定的 Dependency Proxy 组中没有最低所需的 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)