Dependency Proxy

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

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

先决条件

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

为群组启用或关闭 Dependency Proxy

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

  1. 转到您组的 设置 > 软件包和镜像库
  2. 展开 依赖代理 部分。
  3. 要启用代理,请打开 启用代理。要关闭它,请关闭切换开关。

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

查看 Dependency Proxy

查看 Dependency Proxy:

  • 转到您组的 软件包和镜像库 > 依赖代理

Dependency Proxy 不适用于项目。

对 Docker 镜像使用 Dependency Proxy

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

先决条件:

使用 Dependency Proxy 进行身份验证

由于 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 成员权限。

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_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. 复制 Dependency Proxy URL
  3. 使用以下命令之一。在这些示例中,镜像是 alpine:latest
  4. 您还可以通过 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。

减少存储使用

Blob 永远保存在 GitLab 服务器上,并且对可以存储的数据量没有硬性限制。

使用 API 清除缓存

要回收不再需要的镜像 blob 使用的磁盘空间,请使用 清除整个缓存。

如果清除缓存,则流水线下次运行时必须从 Docker Hub 拉取镜像或标签。

清理策略

从极狐GitLab 中启用清理策略

引入于 14.6 版本

您可以从用户界面为依赖代理启用自动生存时间 (TTL) 策略。为此,请到您的群组的 设置 > 软件包与镜像库 > 依赖代理,并启用设置,在 90 天后自动从缓存中清除项目。

使用 GraphQL 启用清理策略

引入于 14.4 版本。

清理策略是一项计划作业,可用于清除不再使用的缓存镜像,从而释放额外的存储空间。这些策略使用生存时间 (TTL) 逻辑:

  • 天数已配置。
  • 删除在配置的天数内,没有拉取的缓存的 Dependency Proxy 文件。

使用 GraphQL API 来启用和配置清理策略:

mutation {
  updateDependencyProxyImageTtlGroupPolicy(input:
    {
      groupPath: "<your-full-group-path>",
      enabled: true,
      ttl: 90
    }
  ) {
    dependencyProxyImageTtlPolicy {
      enabled
      ttl
    }
    errors
  }
}

最初启用该策略时,默认 TTL 设置为 90 天。启用后,陈旧的 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