- 使用自签名证书与容器容器镜像仓库
- Docker 登录尝试失败:’token signed by untrusted key’
- 使用 AWS S3 与极狐 GitLab 容器镜像仓库推送大图像时出现错误
- 支持旧版 Docker 客户端
- Docker 连接错误
- 图像推送错误
- 启用容器镜像仓库调试服务器
- 标签名称为空
- 高级故障排除
- 缺少
gitlab-registry.key
阻止容器库删除 - 容器镜像仓库服务监听 IPv6 地址而不是 IPv4
在调查具体问题之前,请尝试以下故障排除步骤:
-
验证您的 Docker 客户端和极狐 GitLab 服务器上的系统时钟是否同步(例如,通过 NTP)。
-
对于 S3 支持的容器镜像仓库,验证您的 IAM 权限和 S3 凭证(包括区域)是否正确。
-
检查容器镜像仓库日志中的错误(例如,
/var/log/gitlab/registry/current
)以及极狐 GitLab 生产日志(例如,/var/log/gitlab/gitlab-rails/production.log
)。 -
查看容器容器镜像仓库的 NGINX 配置文件(例如,
/var/opt/gitlab/nginx/conf/gitlab-registry.conf
)以确认哪个端口接收请求。 -
验证请求是否正确转发到容器容器镜像仓库:
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)” >}}
-
编辑
/etc/gitlab/gitlab.rb
:registry['storage'] = { 's3' => { 'accesskey' => 'AKIAKIAKI', 'secretkey' => 'secret123', 'bucket' => 'gitlab-registry-bucket-AKIAKIAKI', 'chunksize' => 25000000 } }
-
保存文件并重新配置极狐 GitLab以使更改生效。
{{< /tab >}}
{{< tab title=”Self-compiled (source)” >}}
-
编辑
config/gitlab.yml
:storage: s3: accesskey: 'AKIAKIAKI' secretkey: 'secret123' bucket: 'gitlab-registry-bucket-AKIAKIAKI' chunksize: 25000000
-
保存文件并重新启动极狐 GitLab以使更改生效。
{{< /tab >}}
{{< /tabs >}}
支持旧版 Docker 客户端
极狐 GitLab 附带的 Docker 容器容器镜像仓库默认禁用 schema1 清单。如果您仍在使用旧版 Docker 客户端(1.9 或更早版本),可能会在推送图像时遇到错误。
您可以添加配置选项以支持向后兼容性。
{{< tabs >}}
{{< tab title=”Linux package (Omnibus)” >}}
-
编辑
/etc/gitlab/gitlab.rb
:registry['compatibility_schema1_enabled'] = true
-
保存文件并重新配置极狐 GitLab以使更改生效。
{{< /tab >}}
{{< tab title=”Self-compiled (source)” >}}
-
编辑您在部署容器镜像仓库时创建的 YAML 配置文件。添加以下片段:
compatibility: schema1: enabled: true
-
重启容器镜像仓库以使更改生效。
{{< /tab >}}
{{< /tabs >}}
Docker 连接错误
当群组、项目或分支名称中存在特殊字符时,可能会发生 Docker 连接错误。特殊字符可能包括:
- 前置下划线
- 后置连字符/短划线
- 双连字符/短划线
为解决此问题,您可以更改群组路径,更改项目路径或更改分支名称。另一个选项是创建一个推送规则来防止整个实例出现此错误。
图像推送错误
即使 docker login
成功,您可能在推送 Docker 图像时会陷入重试循环。
当 NGINX 未正确将标头转发到容器镜像仓库时,通常在 SSL 被第三方反向代理卸载的自定义设置中,会出现此问题。
要解决此问题,请更新您的 NGINX 配置以启用容器镜像仓库中的相对 URL:
{{< tabs >}}
{{< tab title=”Linux package (Omnibus)” >}}
-
编辑
/etc/gitlab/gitlab.rb
:registry['env'] = { "REGISTRY_HTTP_RELATIVEURLS" => true }
-
保存文件并重新配置极狐 GitLab以使更改生效。
{{< /tab >}}
{{< tab title=”Self-compiled (source)” >}}
-
编辑您在部署容器镜像仓库时创建的 YAML 配置文件。添加以下片段:
http: relativeurls: true
-
保存文件并重新启动极狐 GitLab以使更改生效。
{{< /tab >}}
{{< tab title=”Docker Compose” >}}
-
编辑您的
docker-compose.yaml
文件:GITLAB_OMNIBUS_CONFIG: | registry['env'] = { "REGISTRY_HTTP_RELATIVEURLS" => true }
-
如果问题仍然存在,请确保两个 URL 使用 HTTPS:
GITLAB_OMNIBUS_CONFIG: | external_url 'https://git.example.com' registry_external_url 'https://git.example.com:5050'
-
保存文件并重启容器:
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:
- 安装 mitmproxy。
- 运行
mitmproxy --port 9000
生成其证书。按下 CTRL-C 退出。 -
从
~/.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
在这种情况下,请遵循以下步骤:
-
在您的
gitlab.rb
中临时启用实例范围的容器容器镜像仓库设置:gitlab_rails['registry_enabled'] = true
- 保存文件并重新配置极狐 GitLab以使更改生效。
- 再次尝试删除。
如果仍然无法通过常用方法删除库,可以使用 极狐 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"