在调查具体问题之前,请尝试以下故障排除步骤:

  1. 验证您的 Docker 客户端和极狐 GitLab 服务器上的系统时钟是否同步(例如,通过 NTP)。

  2. 对于 S3 支持的容器镜像仓库,验证您的 IAM 权限和 S3 凭证(包括区域)是否正确。

  3. 检查容器镜像仓库日志中的错误(例如,/var/log/gitlab/registry/current)以及极狐 GitLab 生产日志(例如,/var/log/gitlab/gitlab-rails/production.log)。

  4. 查看容器容器镜像仓库的 NGINX 配置文件(例如,/var/opt/gitlab/nginx/conf/gitlab-registry.conf)以确认哪个端口接收请求。

  5. 验证请求是否正确转发到容器容器镜像仓库:

    curl --verbose --noproxy "*" https://<hostname>:<port>/v2/_catalog
    

    响应应包括一个包含 service="container_registry"Www-Authenticate: Bearer 行。例如:

    < HTTP/1.1 401 Unauthorized
    < Server: nginx
    < Date: Fri, 07 Mar 2025 08:24:43 GMT
    < Content-Type: application/json
    < Content-Length: 162
    < Connection: keep-alive
    < Docker-Distribution-Api-Version: registry/2.0
    < Www-Authenticate: Bearer realm="https://<hostname>/jwt/auth",service="container_registry",scope="registry:catalog:*"
    < X-Content-Type-Options: nosniff
    <
    {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":
    [{"Type":"registry","Class":"","Name":"catalog","ProjectPath":"","Action":"*"}]}]}
    * Connection #0 to host <hostname> left intact
    

使用自签名证书与容器容器镜像仓库

如果您在容器容器镜像仓库中使用自签名证书,可能会在 CI 任务期间遇到以下问题:

Error response from daemon: Get registry.example.com/v1/users/: x509: certificate signed by unknown authority

运行命令的 Docker 守护进程期望由认可的 CA 签署的证书,因此出现上述错误。

虽然极狐 GitLab 不支持直接使用自签名证书与容器容器镜像仓库,但可以通过指示 Docker 守护进程信任自签名证书,挂载 Docker 守护进程,并在极狐 GitLab Runner 的 config.toml 文件中设置 privileged = false 来实现。设置 privileged = true 会优先于 Docker 守护进程:

  [runners.docker]
    image = "ruby:2.6"
    privileged = false
    volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]

Docker 登录尝试失败:’token signed by untrusted key’

容器镜像仓库依赖极狐 GitLab 验证凭证 如果容器镜像仓库无法验证有效的登录尝试,您将收到以下错误消息:

# docker login gitlab.company.com:4567
Username: user
Password:
Error response from daemon: login attempt to https://gitlab.company.com:4567/v2/ failed with status: 401 Unauthorized

并且更具体的错误将显示在 /var/log/gitlab/registry/current 日志文件中:

level=info msg="token signed by untrusted key with ID: "TOKE:NL6Q:7PW6:EXAM:PLET:OKEN:BG27:RCIB:D2S3:EXAM:PLET:OKEN""
level=warning msg="error authorizing context: invalid token" go.version=go1.12.7 http.request.host="gitlab.company.com:4567" http.request.id=74613829-2655-4f96-8991-1c9fe33869b8 http.request.method=GET http.request.remoteaddr=10.72.11.20 http.request.uri="/v2/" http.request.useragent="docker/19.03.2 go/go1.12.8 git-commit/6a30dfc kernel/3.10.0-693.2.2.el7.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.2 \(linux\))"

极狐 GitLab 使用证书密钥对的内容加密容器镜像仓库的身份验证令牌。此消息表示这些内容不一致。

检查正在使用的文件:

  • grep -A6 'auth:' /var/opt/gitlab/registry/config.yml

    ## Container registry certificate
       auth:
         token:
           realm: https://gitlab.my.net/jwt/auth
           service: container_registry
           issuer: omnibus-gitlab-issuer
      -->  rootcertbundle: /var/opt/gitlab/registry/gitlab-registry.crt
           autoredirect: false
    
  • grep -A9 'Container Registry' /var/opt/gitlab/gitlab-rails/etc/gitlab.yml

    ## Container registry key
       registry:
         enabled: true
         host: gitlab.company.com
         port: 4567
         api_url: http://127.0.0.1:5000 # internal address to the registry, is used by GitLab to directly communicate with API
         path: /var/opt/gitlab/gitlab-rails/shared/registry
    -->  key: /var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key
         issuer: omnibus-gitlab-issuer
         notification_secret:
    

这些 openssl 命令的输出应匹配,证明证书密钥对是匹配的:

/opt/gitlab/embedded/bin/openssl x509 -noout -modulus -in /var/opt/gitlab/registry/gitlab-registry.crt | /opt/gitlab/embedded/bin/openssl sha256
/opt/gitlab/embedded/bin/openssl rsa -noout -modulus -in /var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key | /opt/gitlab/embedded/bin/openssl sha256

如果证书的两个部分不一致,请删除文件并运行 gitlab-ctl reconfigure 以重新生成密钥对。密钥对会使用 /etc/gitlab/gitlab-secrets.json 中的现有值重新创建(如果存在)。要生成新的密钥对,请删除 /etc/gitlab/gitlab-secrets.json 中的 registry 部分,然后运行 gitlab-ctl reconfigure

如果您使用自己的证书覆盖了自动生成的自签名密钥对,并确保其内容一致,可以删除 /etc/gitlab/gitlab-secrets.json 中的 registry 部分,然后运行 gitlab-ctl reconfigure

使用 AWS S3 与极狐 GitLab 容器镜像仓库推送大图像时出现错误

使用 AWS S3 与极狐 GitLab 容器镜像仓库时,推送大图像时可能会出现错误。查看容器镜像仓库日志中的以下错误:

level=error msg="response completed with error" err.code=unknown err.detail="unexpected EOF" err.message="unknown error"

要解决此错误,请在容器镜像仓库配置中指定一个 chunksize 值。建议使用 25000000(25 MB)到 50000000(50 MB)之间的值。

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

  1. 编辑 /etc/gitlab/gitlab.rb

    registry['storage'] = {
      's3' => {
        'accesskey' => 'AKIAKIAKI',
        'secretkey' => 'secret123',
        'bucket'    => 'gitlab-registry-bucket-AKIAKIAKI',
        'chunksize' => 25000000
      }
    }
    
  2. 保存文件并重新配置极狐 GitLab以使更改生效。

{{< /tab >}}

{{< tab title=”Self-compiled (source)” >}}

  1. 编辑 config/gitlab.yml

    storage:
      s3:
        accesskey: 'AKIAKIAKI'
        secretkey: 'secret123'
        bucket: 'gitlab-registry-bucket-AKIAKIAKI'
        chunksize: 25000000
    
  2. 保存文件并重新启动极狐 GitLab以使更改生效。

{{< /tab >}}

{{< /tabs >}}

支持旧版 Docker 客户端

极狐 GitLab 附带的 Docker 容器容器镜像仓库默认禁用 schema1 清单。如果您仍在使用旧版 Docker 客户端(1.9 或更早版本),可能会在推送图像时遇到错误。

您可以添加配置选项以支持向后兼容性。

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

  1. 编辑 /etc/gitlab/gitlab.rb

    registry['compatibility_schema1_enabled'] = true
    
  2. 保存文件并重新配置极狐 GitLab以使更改生效。

{{< /tab >}}

{{< tab title=”Self-compiled (source)” >}}

  1. 编辑您在部署容器镜像仓库时创建的 YAML 配置文件。添加以下片段:

    compatibility:
        schema1:
            enabled: true
    
  2. 重启容器镜像仓库以使更改生效。

{{< /tab >}}

{{< /tabs >}}

Docker 连接错误

当群组、项目或分支名称中存在特殊字符时,可能会发生 Docker 连接错误。特殊字符可能包括:

  • 前置下划线
  • 后置连字符/短划线
  • 双连字符/短划线

为解决此问题,您可以更改群组路径更改项目路径或更改分支名称。另一个选项是创建一个推送规则来防止整个实例出现此错误。

图像推送错误

即使 docker login 成功,您可能在推送 Docker 图像时会陷入重试循环。

当 NGINX 未正确将标头转发到容器镜像仓库时,通常在 SSL 被第三方反向代理卸载的自定义设置中,会出现此问题。

要解决此问题,请更新您的 NGINX 配置以启用容器镜像仓库中的相对 URL:

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

  1. 编辑 /etc/gitlab/gitlab.rb

    registry['env'] = {
      "REGISTRY_HTTP_RELATIVEURLS" => true
    }
    
  2. 保存文件并重新配置极狐 GitLab以使更改生效。

{{< /tab >}}

{{< tab title=”Self-compiled (source)” >}}

  1. 编辑您在部署容器镜像仓库时创建的 YAML 配置文件。添加以下片段:

    http:
        relativeurls: true
    
  2. 保存文件并重新启动极狐 GitLab以使更改生效。

{{< /tab >}}

{{< tab title=”Docker Compose” >}}

  1. 编辑您的 docker-compose.yaml 文件:

    GITLAB_OMNIBUS_CONFIG: |
      registry['env'] = {
        "REGISTRY_HTTP_RELATIVEURLS" => true
      }
    
  2. 如果问题仍然存在,请确保两个 URL 使用 HTTPS:

    GITLAB_OMNIBUS_CONFIG: |
      external_url 'https://git.example.com'
      registry_external_url 'https://git.example.com:5050'
    
  3. 保存文件并重启容器:

    sudo docker restart gitlab
    

{{< /tab >}}

{{< /tabs >}}

启用容器镜像仓库调试服务器

您可以使用容器容器镜像仓库调试服务器诊断问题。调试端点可以监控指标和健康状况,还可以进行分析。

{{< alert type=”warning” >}}

敏感信息可能从调试端点可用。必须在生产环境中锁定对调试端点的访问。

{{< /alert >}}

可以通过在 gitlab.rb 配置中设置容器镜像仓库调试地址来启用可选的调试服务器。

registry['debug_addr'] = "localhost:5001"

添加设置后,重新配置极狐 GitLab以应用更改。

使用 curl 请求来自调试服务器的调试输出:

curl "localhost:5001/debug/health"
curl "localhost:5001/debug/vars"

启用容器镜像仓库 Prometheus 指标

如果启用了调试服务器,您还可以启用 Prometheus 指标。此端点提供几乎所有容器镜像仓库操作的详细遥测数据。

registry['debug'] = {
  'prometheus' => {
    'enabled' => true,
    'path' => '/metrics'
  }
}

使用 curl 请求来自 Prometheus 的调试输出:

curl "localhost:5001/debug/metrics"

标签名称为空

如果使用 AWS DataSync 将容器镜像仓库数据复制到或在 S3 存储桶之间复制,目标存储桶中的每个容器库的根路径会创建一个空元数据对象。这会导致容器镜像仓库将这些文件解释为在极狐 GitLab UI 和 API 中显示没有名称的标签。

要解决此问题,您可以执行以下两种操作之一:

  • 使用 AWS CLI rm 命令从每个受影响的库的根中删除空对象。特别注意尾部 /,确保使用 --recursive 选项:

    aws s3 rm s3://<bucket>/docker/registry/v2/repositories/<path to repository>/
    
  • 使用 AWS CLI sync 命令将容器镜像仓库数据复制到新存储桶并将容器镜像仓库配置为使用它。这会留下空对象。

高级故障排除

我们使用一个具体示例来说明如何诊断 S3 设置的问题。

调查清理策略

如果您不确定为什么清理策略未删除或删除了标签,请通过在 Rails 控制台 中运行以下脚本逐行执行策略。这可以帮助诊断策略的问题。

repo = ContainerRepository.find(<repository_id>)
policy = repo.project.container_expiration_policy

tags = repo.tags
tags.map(&:name)

tags.reject!(&:latest?)
tags.map(&:name)

regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{policy.name_regex}\\z")
regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{policy.name_regex_keep}\\z")

tags.select! { |tag| regex_delete.match?(tag.name) && !regex_retain.match?(tag.name) }

tags.map(&:name)

now = DateTime.current
tags.sort_by! { |tag| tag.created_at || now }.reverse! # Lengthy operation

tags = tags.drop(policy.keep_n)
tags.map(&:name)

older_than_timestamp = ChronicDuration.parse(policy.older_than).seconds.ago

tags.select! { |tag| tag.created_at && tag.created_at < older_than_timestamp }

tags.map(&:name)
  • 该脚本构建要删除的标签列表 (tags)。
  • tags.map(&:name) 打印要删除标签的列表。这可能是一个耗时的操作。
  • 在每次过滤后,检查 tags 列表以查看它是否包含要销毁的预期标签。

推送过程中出现意外的 403 错误

用户尝试启用 S3 支持的容器镜像仓库。docker login 步骤进行顺利。然而,推送图像时输出显示:

The push refers to a repository [s3-testing.myregistry.com:5050/root/docker-test/docker-image]
dc5e59c14160: Pushing [==================================================>] 14.85 kB
03c20c1a019a: Pushing [==================================================>] 2.048 kB
a08f14ef632e: Pushing [==================================================>] 2.048 kB
228950524c88: Pushing 2.048 kB
6a8ecde4cc03: Pushing [==>                                                ] 9.901 MB/205.7 MB
5f70bf18a086: Pushing 1.024 kB
737f40e80b7f: Waiting
82b57dbc5385: Waiting
19429b698a22: Waiting
9436069b92a3: Waiting
error parsing HTTP 403 response body: unexpected end of JSON input: ""

此错误模糊不清,因为不清楚 403 错误是来自极狐 GitLab Rails 应用程序、Docker 容器镜像仓库还是其他原因。在这种情况下,因为我们知道登录成功,我们可能需要查看客户端和容器镜像仓库之间的通信。

Docker 客户端和容器镜像仓库之间的 REST API 在 Docker 文档中描述。通常情况下,您可以使用 Wireshark 或 tcpdump 捕获流量并查看问题出在哪里。然而,由于 Docker 客户端和服务器之间的所有通信都是通过 HTTPS 进行的,即使您知道私钥,也很难快速解密流量。我们可以做什么呢?

一种方法是通过设置不安全的容器镜像仓库来禁用 HTTPS。这可能会引入安全漏洞,仅建议用于本地测试。如果您有生产系统并且不能或不想这样做,还有另一种方法:使用 mitmproxy,即中间人代理。

mitmproxy

mitmproxy 允许您在客户端和服务器之间放置一个代理以检查所有流量。一个问题是您的系统需要信任 mitmproxy 的 SSL 证书才能使其工作。

以下安装说明假设您正在运行 Ubuntu:

  1. 安装 mitmproxy。
  2. 运行 mitmproxy --port 9000 生成其证书。按下 CTRL-C 退出。
  3. ~/.mitmproxy 安装证书到您的系统:

    sudo cp ~/.mitmproxy/mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy-ca-cert.crt
    sudo update-ca-certificates
    

如果成功,输出应指示添加了证书:

Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.

要验证证书是否正确安装,运行:

mitmproxy --port 9000

此命令在端口 9000 上运行 mitmproxy。在另一个窗口中运行:

curl --proxy "http://localhost:9000" "https://httpbin.org/status/200"

如果一切设置正确,信息将在 mitmproxy 窗口中显示,并且 curl 命令不会生成错误。

使用代理运行 Docker 守护进程

要使 Docker 通过代理连接,您必须使用正确的环境变量启动 Docker 守护进程。最简单的方法是关闭 Docker(例如 sudo initctl stop docker),然后手动运行 Docker。以 root 身份运行:

export HTTP_PROXY="http://localhost:9000"
export HTTPS_PROXY="http://localhost:9000"
docker daemon --debug

此命令启动 Docker 守护进程并通过 mitmproxy 代理所有连接。

运行 Docker 客户端

现在我们有 mitmproxy 和 Docker 在运行,我们可以尝试登录并推送容器图像。您可能需要以 root 身份运行。例如:

docker login example.s3.amazonaws.com:5050
docker push example.s3.amazonaws.com:5050/root/docker-test/docker-image

在上面的示例中,我们在 mitmproxy 窗口中看到以下跟踪:

PUT https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/uploads/(UUID)/(QUERYSTRING)
    ← 201 text/plain [no content] 661ms
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
    ← 307 application/octet-stream [no content] 93ms
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
    ← 307 application/octet-stream [no content] 101ms
HEAD https://example.s3.amazonaws.com:4567/v2/root/docker-test/blobs/sha256:(SHA)
    ← 307 application/octet-stream [no content] 87ms
HEAD https://amazonaws.example.com/docker/registry/vs/blobs/sha256/dd/(UUID)/data(QUERYSTRING)
    ← 403 application/xml [no content] 80ms
HEAD https://amazonaws.example.com/docker/registry/vs/blobs/sha256/dd/(UUID)/data(QUERYSTRING)
    ← 403 application/xml [no content] 62ms

此输出显示:

  • 初始 PUT 请求通过,状态码为 201
  • 201 将客户端重定向到 Amazon S3 存储桶。
  • 对 AWS 存储桶的 HEAD 请求报告了 403 Unauthorized

这意味着什么?这强烈表明 S3 用户没有正确的权限来执行 HEAD 请求。解决方案:再次检查 IAM 权限。设置正确的权限后,错误消失。

缺少 gitlab-registry.key 阻止容器库删除

如果禁用了极狐 GitLab 实例的容器容器镜像仓库,并尝试删除有容器库的项目,将发生以下错误:

Errno::ENOENT: No such file or directory @ rb_sysopen - /var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key

在这种情况下,请遵循以下步骤:

  1. 在您的 gitlab.rb 中临时启用实例范围的容器容器镜像仓库设置:

    gitlab_rails['registry_enabled'] = true
    
  2. 保存文件并重新配置极狐 GitLab以使更改生效。
  3. 再次尝试删除。

如果仍然无法通过常用方法删除库,可以使用 极狐 GitLab Rails 控制台 强制删除项目:

# Path to the project you'd like to remove
prj = Project.find_by_full_path(<project_path>)

# The following will delete the project's container registry, so be sure to double-check the path beforehand!
if prj.has_container_registry_tags?
  prj.container_repositories.each { |p| p.destroy }
end

容器镜像仓库服务监听 IPv6 地址而不是 IPv4

如果 localhost 主机名在您的极狐 GitLab 服务器上解析为 IPv6 回环地址 (::1),而极狐 GitLab 期望容器镜像仓库服务可在 IPv4 回环地址 (127.0.0.1) 上使用,您可能会看到以下错误:

request: "GET /v2/ HTTP/1.1", upstream: "http://[::1]:5000/v2/", host: "registry.example.com:5005"
[error] 1201#0: *13442797 connect() failed (111: Connection refused) while connecting to upstream, client: x.x.x.x, server: registry.example.com, request: "GET /v2/<path> HTTP/1.1", upstream: "http://[::1]:5000/v2/<path>", host: "registry.example.com:5005"

要解决此错误,请在 /etc/gitlab/gitlab.rb 中将 registry['registry_http_addr'] 更改为 IPv4 地址。例如:

registry['registry_http_addr'] = "127.0.0.1:5000"