排查 Gitaly 问题

请参考以下信息进行 Gitaly 的问题排查。有关 Gitaly 集群(Praefect)的排查信息,请参阅 排查 Gitaly 集群

以下部分提供了 Gitaly 错误的可能解决方案。

另请参阅 Gitaly 超时 设置,以及我们关于 解析 gitaly/current 文件 的建议。

当使用独立 Gitaly 服务器时检查版本

使用独立 Gitaly 服务器时,必须确保它们与极狐GitLab保持相同版本以确保完全兼容:

  1. 在左侧边栏底部选择 管理员
  2. 选择 概览 > Gitaly 服务器
  3. 确认所有 Gitaly 服务器显示已更新。

查找存储资源详情

您可以在 Rails 控制台 中运行以下命令,以确定 Gitaly 存储上的可用和已用空间:

Gitlab::GitalyClient::ServerService.new("default").storage_disk_statistics
# 对于 Gitaly 集群
Gitlab::GitalyClient::ServerService.new("<storage name>").disk_statistics

使用 gitaly-debug

gitaly-debug 命令为 Gitaly 和 Git 性能提供“生产调试”工具。它旨在帮助生产工程师和支持工程师调查 Gitaly 性能问题。

要查看 gitaly-debug 的帮助页面以获取支持的子命令列表,请运行:

gitaly-debug -h

当需要 Git 进行排查时使用 gitaly git

使用 gitaly git 执行 Git 命令,使用与 Gitaly 相同的 Git 执行环境进行调试或测试。gitaly git 是确保版本兼容性的首选方法。

gitaly git 将所有参数传递给底层的 Git 调用,并支持 Git 支持的所有输入形式。要使用 gitaly git,请运行:

sudo -u git -- /opt/gitlab/embedded/bin/gitaly git <git-command>

例如,要在软件包实例中通过 Gitaly 在仓库的工作目录中运行 git ls-tree

sudo -u git -- /opt/gitlab/embedded/bin/gitaly git ls-tree --name-status HEAD

提交、推送和克隆返回 401

remote: GitLab: 401 Unauthorized

您需要同步 gitlab-secrets.json 文件与您的极狐GitLab 应用程序节点。

仓库页面上的 500 和 fetching folder content 错误

Fetching folder content,以及在某些情况下 500 错误,表明极狐GitLab 与 Gitaly 之间的连接问题。请查阅 客户端 gRPC 日志 以获取详细信息。

客户端 gRPC 日志

Gitaly 使用 gRPC RPC 框架。Ruby gRPC 客户端有自己的日志文件,当您看到 Gitaly 错误时,它可能包含有用的信息。您可以通过 GRPC_LOG_LEVEL 环境变量控制 gRPC 客户端的日志级别。默认级别是 WARN

您可以运行 gRPC 跟踪:

sudo GRPC_TRACE=all GRPC_VERBOSITY=DEBUG gitlab-rake gitlab:gitaly:check

如果此命令因 failed to connect to all addresses 错误而失败,请检查是否存在 SSL 或 TLS 问题:

/opt/gitlab/embedded/bin/openssl s_client -connect <gitaly-ipaddress>:<port> -verify_return_error

检查 Verify return code 字段是否表明 已知的 Linux 软件包安装配置问题

如果 openssl 成功但 gitlab-rake gitlab:gitaly:check 失败,请检查 Gitaly 的 证书要求

服务器端 gRPC 日志

gRPC 跟踪也可以通过 GODEBUG=http2debug 环境变量在 Gitaly 本身中启用。要在 Linux 软件包安装中设置此项:

  1. 将以下内容添加到您的 gitlab.rb 文件中:

    gitaly['env'] = {
      "GODEBUG=http2debug" => "2"
    }
    
  2. 重新配置极狐GitLab。

将 Git 进程与 RPC 关联

有时您需要找出哪个 Gitaly RPC 创建了特定的 Git 进程。

一种方法是使用 DEBUG 日志记录。然而,这需要提前启用,并且生成的日志非常详细。

一种轻量级的关联方法是通过检查 Git 进程的环境(使用其 PID)并查看 CORRELATION_ID 变量:

PID=<Git process ID>
sudo cat /proc/$PID/environ | tr '\0' '\n' | grep ^CORRELATION_ID=

对于 git cat-file 进程,此方法不可靠,因为 Gitaly 在内部对这些进程进行池化并跨 RPC 重新使用。

仓库更改失败并显示 401 Unauthorized 错误

如果您在自己的服务器上运行 Gitaly 并注意到以下情况:

  • 用户可以成功克隆和通过 SSH 和 HTTPS 提取仓库。
  • 用户无法推送到仓库,或在尝试在 Web UI 中进行更改时收到 401 Unauthorized 消息。

Gitaly 可能无法通过 Gitaly 客户端进行身份验证,因为它具有 错误的密钥文件

确认以下所有内容为真:

  • 当任何用户对此 Gitaly 服务器上的任何仓库执行 git push 时,它失败并显示 401 Unauthorized 错误:

    remote: GitLab: 401 Unauthorized
    To <REMOTE_URL>
    ! [remote rejected] branch-name -> branch-name (pre-receive hook declined)
    error: failed to push some refs to '<REMOTE_URL>'
    
  • 当任何用户使用极狐GitLab UI 从仓库添加或修改文件时,它立即失败并显示红色 401 Unauthorized 横幅。
  • 创建一个新项目并 使用 README 初始化 成功创建项目但未创建 README。
  • 在 Gitaly 客户端上 尾随日志 并重现错误时,您在访问 /api/v4/internal/allowed 端点时收到 401 错误:

    # api_json.log
    {
      "time": "2019-07-18T00:30:14.967Z",
      "severity": "INFO",
      "duration": 0.57,
      "db": 0,
      "view": 0.57,
      "status": 401,
      "method": "POST",
      "path": "\/api\/v4\/internal\/allowed",
      "params": [
        {
          "key": "action",
          "value": "git-receive-pack"
        },
        {
          "key": "changes",
          "value": "REDACTED"
        },
        {
          "key": "gl_repository",
          "value": "REDACTED"
        },
        {
          "key": "project",
          "value": "\/path\/to\/project.git"
        },
        {
          "key": "protocol",
          "value": "web"
        },
        {
          "key": "env",
          "value": "{\"GIT_ALTERNATE_OBJECT_DIRECTORIES\":[],\"GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE\":[],\"GIT_OBJECT_DIRECTORY\":null,\"GIT_OBJECT_DIRECTORY_RELATIVE\":null}"
        },
        {
          "key": "user_id",
          "value": "2"
        },
        {
          "key": "secret_token",
          "value": "[FILTERED]"
        }
      ],
      "host": "gitlab.example.com",
      "ip": "REDACTED",
      "ua": "Ruby",
      "route": "\/api\/:version\/internal\/allowed",
      "queue_duration": 4.24,
      "gitaly_calls": 0,
      "gitaly_duration": 0,
      "correlation_id": "XPUZqTukaP3"
    }
    
    # nginx_access.log
    [IP] - - [18/Jul/2019:00:30:14 +0000] "POST /api/v4/internal/allowed HTTP/1.1" 401 30 "" "Ruby"
    

要解决此问题,请确认 Gitaly 服务器上的 gitlab-secrets.json 文件 与 Gitaly 客户端上的文件匹配。如果不匹配,请更新 Gitaly 服务器上的密钥文件以匹配 Gitaly 客户端,然后 重新配置

如果您已确认所有 Gitaly 服务器和客户端上的 gitlab-secrets.json 文件相同,则应用程序可能正在从其他文件中获取此密钥。您的 Gitaly 服务器的 config.toml file 指示正在使用的密钥文件。如果该设置丢失,极狐GitLab 默认使用 /opt/gitlab/embedded/service/gitlab-rails/.gitlab_shell_secret 下的 .gitlab_shell_secret

仓库推送失败并显示 401 UnauthorizedJWT::VerificationError

尝试 git push 时,您可能会看到:

  • 401 Unauthorized 错误。
  • 服务器日志中的以下内容:

    {
      ...
      "exception.class":"JWT::VerificationError",
      "exception.message":"Signature verification raised",
      ...
    }
    

这种错误组合发生在极狐GitLab服务器已升级到极狐GitLab 15.5 或更高版本但 Gitaly 尚未升级时。

从极狐GitLab 15.5 开始,极狐GitLab 使用 JWT 令牌而不是共享密钥与极狐GitLab Shell 进行身份验证。您应按照 升级外部 Gitaly 的建议 并在极狐GitLab服务器之前升级 Gitaly。

仓库推送失败并显示 deny updating a hidden ref 错误

Gitaly 有极狐GitLab内部的只读引用,用户不允许更新。如果尝试使用 git push --mirror 更新内部引用,Git 返回拒绝错误 deny updating a hidden ref

以下引用是只读的:

  • refs/environments/
  • refs/keep-around/
  • refs/merge-requests/
  • refs/pipelines/

要仅镜像推送分支和标签,并避免尝试镜像推送受保护的引用,请运行:

git push origin +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*

管理员希望推送的其他命名空间也可以通过附加模式包括在内。

命令行工具无法连接到 Gitaly

如果:

  • 您无法通过命令行工具连接到 Gitaly 服务器。
  • 某些操作导致出现 14: Connect Failed 错误消息。

请验证您可以通过 TCP 连接到 Gitaly:

sudo gitlab-rake gitlab:tcp_check[GITALY_SERVER_IP,GITALY_LISTEN_PORT]

如果 TCP 连接:

  • 失败,请检查您的网络设置和防火墙规则。
  • 成功,您的网络和防火墙规则正确。

如果您在命令行环境中使用代理服务器(如 Bash),这些可能会干扰您的 gRPC 流量。

如果您使用 Bash 或兼容的命令行环境,请运行以下命令以确定是否配置了代理服务器:

echo $http_proxy
echo $https_proxy

如果这些变量中的任何一个有值,您的 Gitaly CLI 连接可能会被路由到一个无法连接到 Gitaly 的代理。

要移除代理设置,请运行以下命令(取决于哪个变量有值):

unset http_proxy
unset https_proxy

访问仓库时 Gitaly 或 Praefect 日志中出现权限被拒绝错误

您可能会在 Gitaly 和 Praefect 日志中看到以下内容:

{
  ...
  "error":"rpc error: code = PermissionDenied desc = permission denied: token has expired",
  "grpc.code":"PermissionDenied",
  "grpc.meta.client_name":"gitlab-web",
  "grpc.request.fullMethod":"/gitaly.ServerService/ServerInfo",
  "level":"warning",
  "msg":"finished unary call with code PermissionDenied",
  ...
}

日志中的这些信息是 gRPC 调用的 错误响应代码

如果出现此错误,即使 Gitaly 身份验证令牌设置正确,可能是 Gitaly 服务器正在经历 时钟漂移。发送给 Gitaly 的身份验证令牌包含一个时间戳。为了被认为有效,Gitaly 要求时间戳与 Gitaly 服务器时间在 60 秒内。

确保 Gitaly 客户端和服务器同步,并使用网络时间协议(NTP)时间服务器保持同步。

重新配置后 Gitaly 不在新地址上监听

更新 gitaly['configuration'][:listen_addr]gitaly['configuration'][:prometheus_listen_addr] 值时,Gitaly 可能在 sudo gitlab-ctl reconfigure 后继续在旧地址上监听。

出现这种情况时,运行 sudo gitlab-ctl restart 解决问题。

健康检查警告

可以忽略 /var/log/gitlab/praefect/current 中的以下警告。

"error":"full method name not found: /grpc.health.v1.Health/Check",
"msg":"error when looking up method info"

文件未找到错误

可以忽略 /var/log/gitlab/gitaly/current 中的以下错误。它们是由极狐GitLab Rails 应用程序检查仓库中不存在的特定文件引起的。

"error":"not found: .gitlab/route-map.yml"
"error":"not found: Dockerfile"
"error":"not found: .gitlab-ci.yml"

启用 Dynatrace 时 Git 推送很慢

Dynatrace 可能导致 sudo -u git -- /opt/gitlab/embedded/bin/gitaly-hooks 引用事务钩子启动和关闭需要几秒钟。gitaly-hooks 在用户推送时执行两次,导致显著延迟。

如果启用 Dynatrace 时 Git 推送过慢,请禁用 Dynatrace。

gitaly check401 状态码失败

如果 Gitaly 无法访问极狐GitLab内部 API,gitaly check 可能会因 401 状态码失败。

解决此问题的一种方法是确保在 gitlab.rb 中配置的极狐GitLab内部 API URL 的条目正确 gitlab_rails['internal_api_url']

使用 Gitaly TLS 时新合并请求的更改(差异)未加载

启用 Gitaly TLS 后,新合并请求的更改(差异)未生成,您在极狐GitLab 中看到以下消息:

Building your merge request... This page will update when the build is complete

Gitaly 必须能够连接到自身才能完成某些操作。如果 Gitaly 证书不被 Gitaly 服务器信任,则无法生成合并请求差异。

如果 Gitaly 无法连接到自身,您会在 Gitaly 日志 中看到如下消息:

{
   "level":"warning",
   "msg":"[core] [Channel #16 SubChannel #17] grpc: addrConn.createTransport failed to connect to {Addr: \"ext-gitaly.example.com:9999\", ServerName: \"ext-gitaly.example.com:9999\", }. Err: connection error: desc = \"transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority\"",
   "pid":820,
   "system":"system",
   "time":"2023-11-06T05:40:04.169Z"
}
{
   "level":"info",
   "msg":"[core] [Server #3] grpc: Server.Serve failed to create ServerTransport: connection error: desc = \"ServerHandshake(\\\"x.x.x.x:x\\\") failed: wrapped server handshake: remote error: tls: bad certificate\"",
   "pid":820,
   "system":"system",
   "time":"2023-11-06T05:40:04.169Z"
}

要解决此问题,请确保您已将 Gitaly 证书添加到 Gitaly 服务器上的 /etc/gitlab/trusted-certs 文件夹,并:

  1. 重新配置极狐GitLab,使证书被符号链接
  2. 手动重启 Gitaly sudo gitlab-ctl restart gitaly 以使证书被 Gitaly 进程加载。

Gitaly 无法分叉存储在 noexec 文件系统上的进程

noexec 选项应用于挂载点(例如 /var)会导致 Gitaly 抛出与分叉进程相关的 permission denied 错误。例如:

fork/exec /var/opt/gitlab/gitaly/run/gitaly-2057/gitaly-git2go: permission denied

要解决此问题,请从文件系统挂载中移除 noexec 选项。另一种选择是更改 Gitaly 的运行时目录:

  1. /etc/gitlab/gitlab.rb 中添加 gitaly['runtime_dir'] = '<PATH_WITH_EXEC_PERM>' 并指定没有 noexec 设置的位置。
  2. 运行 sudo gitlab-ctl reconfigure

提交签名失败并显示 invalid argumentinvalid data

如果提交签名失败并出现以下错误:

  • invalid argument: signing key is encrypted
  • invalid data: tag byte does not have MSB set

此错误发生是因为 Gitaly 提交签名是无头的,与特定用户无关。GPG 签名密钥必须在没有密码的情况下创建,或者必须在导出前移除密码。

Gitaly 日志在 info 消息中显示错误

由于在极狐GitLab 16.3 中 引入的错误,额外的条目被写入 Gitaly 日志。这些日志条目包含 "level":"info"msg 字符串似乎包含错误。

例如:

{"level":"info","msg":"[core] [Server #3] grpc: Server.Serve failed to create ServerTransport: connection error: desc = \"ServerHandshake(\\\"x.x.x.x:x\\\") failed: wrapped server handshake: EOF\"","pid":6145,"system":"system","time":"2023-12-14T21:20:39.999Z"}

此日志条目的原因是底层 gRPC 库有时输出详细的运输日志。这些日志条目似乎是错误,但一般情况下可以安全忽略。

此错误已在极狐GitLab 16.4.5、16.5.5 和 16.6.0 中修复,防止这些类型的消息被写入 Gitaly 日志。

性能分析 Gitaly

Gitaly 在 Prometheus 监听端口上公开了几个 Go 内置性能分析工具。例如,如果 Prometheus 在极狐GitLab服务器的端口 9236 上监听:

  • 获取正在运行的 goroutines 及其回溯:

    curl --output goroutines.txt "http://<gitaly_server>:9236/debug/pprof/goroutine?debug=2"
    
  • 运行 30 秒的 CPU 分析:

    curl --output cpu.bin "http://<gitaly_server>:9236/debug/pprof/profile"
    
  • 分析堆内存使用情况:

    curl --output heap.bin "http://<gitaly_server>:9236/debug/pprof/heap"
    
  • 记录 5 秒的执行跟踪。这会影响 Gitaly 的性能:

    curl --output trace.bin "http://<gitaly_server>:9236/debug/pprof/trace?seconds=5"
    

在安装了 go 的主机上,可以在浏览器中查看 CPU 分析和堆分析:

go tool pprof -http=:8001 cpu.bin
go tool pprof -http=:8001 heap.bin

可以通过运行查看执行跟踪:

go tool trace heap.bin

分析 Git 操作

  • 在极狐GitLab 16.9 中引入,带有名为 log_git_traces 的标记。默认情况下禁用。
在私有化部署极狐GitLab中,默认情况下此功能不可用。要使其可用,管理员可以 启用功能标记 名为 log_git_traces。在 GitLab.com 上,此功能可用但只能由 GitLab.com 管理员配置。在 GitLab Dedicated 上,此功能不可用。

您可以通过向 Gitaly 日志发送有关 Git 操作的附加信息来分析 Gitaly 执行的 Git 操作。有了这些信息,用户在性能优化、调试和一般遥测收集方面有更多的洞察力。有关更多信息,请参阅 Git Trace2 API 参考

为了防止系统过载,附加信息日志记录受到速率限制。如果超过速率限制,跟踪会被跳过。然而,在速率恢复到健康状态后,跟踪会自动再次处理。速率限制确保系统保持稳定并避免因过度跟踪处理而产生任何不利影响。

极狐GitLab恢复后仓库显示为空

使用 fapolicyd 增强安全性时,极狐GitLab可以报告从极狐GitLab备份文件恢复成功但:

  • 仓库显示为空。
  • 创建新文件导致类似的错误:

    13:commit: commit: starting process [/var/opt/gitlab/gitaly/run/gitaly-5428/gitaly-git2go -log-format json -log-level -correlation-id
    01GP1383JV6JD6MQJBH2E1RT03 -enabled-feature-flags -disabled-feature-flags commit]: fork/exec /var/opt/gitlab/gitaly/run/gitaly-5428/gitaly-git2go: operation not permitted.
    
  • Gitaly 日志可能包含类似的错误:

     "error": "exit status 128, stderr: \"fatal: cannot exec '/var/opt/gitlab/gitaly/run/gitaly-5428/hooks-1277154941.d/reference-transaction':
    
      Operation not permitted\\nfatal: cannot exec '/var/opt/gitlab/gitaly/run/gitaly-5428/hooks-1277154941.d/reference-transaction': Operation
      not permitted\\nfatal: ref updates aborted by hook\\n\"",
     "grpc.code": "Internal",
     "grpc.meta.deadline_type": "none",
     "grpc.meta.method_type": "client_stream",
     "grpc.method": "FetchBundle",
     "grpc.request.fullMethod": "/gitaly.RepositoryService/FetchBundle",
    ...
    

您可以使用调试模式来帮助确定 fapolicyd 是否根据当前规则拒绝执行。

如果发现 fapolicyd 拒绝执行,请考虑以下事项:

  1. 在您的 fapolicyd 配置中允许 /var/opt/gitlab/gitaly 中的所有可执行文件:

    allow perm=any all : ftype=application/x-executable dir=/var/opt/gitlab/gitaly/
    
  2. 重启服务:

    sudo systemctl restart fapolicyd
    
    sudo gitlab-ctl restart gitaly
    

在启用 fapolicyd 的 RHEL 实例上推送时 Pre-receive hook declined 错误

当推送到启用 fapolicyd 的基于 RHEL 的实例时,您可能会收到 Pre-receive hook declined 错误。此错误可能是由于 fapolicyd 阻止 Gitaly 二进制文件的执行而发生。要解决此问题,请执行以下操作:

  • 禁用 fapolicyd
  • 创建一个 fapolicyd 规则,以允许启用 fapolicyd 时执行 Gitaly 二进制文件。

要创建允许 Gitaly 二进制文件执行的规则:

  1. /etc/fapolicyd/rules.d/89-gitlab.rules 创建一个文件。
  2. 在文件中输入以下内容:

    allow perm=any all : ftype=application/x-executable dir=/var/opt/gitlab/gitaly/
    
  3. 重启服务:

    systemctl restart fapolicyd
    

服务重启后,新规则生效。

删除具有重复路径的存储后更新仓库

  • 在极狐GitLab 17.1 中引入的 Rake 任务 gitlab:gitaly:update_removed_storage_projects

在极狐GitLab 17.0 中,支持配置具有重复路径的存储被移除。这可能意味着您必须从 gitaly 配置中删除重复的存储配置。

caution 仅在旧存储和新存储在同一 Gitaly 服务器上共享相同磁盘路径时使用此 Rake 任务。在任何其他情况下使用此 Rake 任务会导致仓库不可用。使用 项目仓库存储移动 API 在所有其他情况下在存储之间传输项目。

从 Gitaly 配置中删除使用与其他存储相同路径的存储时,必须将与旧存储关联的项目重新分配给新存储。

例如,您可能有类似以下的配置:

gitaly['configuration'] = {
  storage: [
    {
       name: 'default',
       path: '/var/opt/gitlab/git-data/repositories',
    },
    {
       name: 'duplicate-path',
       path: '/var/opt/gitlab/git-data/repositories',
    },
  ],
}

如果要从配置中删除 duplicate-path,您可以运行以下 Rake 任务,将分配给它的任何项目关联到 default

::Tabs

:::TabTitle Linux 软件包安装

sudo gitlab-rake "gitlab:gitaly:update_removed_storage_projects[duplicate-path, default]"

:::TabTitle 自编译安装

sudo -u git -H bundle exec rake "gitlab:gitaly:update_removed_storage_projects[duplicate-path, default]" RAILS_ENV=production

::EndTabs