极狐GitLab Gitaly 集群故障排除

在故障排除 Gitaly 集群 (Praefect) 时,请参考以下信息。有关故障排除 Gitaly 的信息,请参阅 极狐GitLab Gitaly 故障排除

检查集群健康状况

check Praefect 子命令运行一系列检查以确定 Gitaly 集群的健康状况。

gitlab-ctl praefect check

如果使用 Praefect chart 部署了 Praefect,请直接运行二进制文件。

/usr/local/bin/praefect check

以下部分描述了运行的检查。

Praefect 迁移

由于数据库迁移必须是最新的才能使 Praefect 正常工作,因此检查 Praefect 迁移是否是最新的。

如果此检查失败:

  1. 查看数据库中的 schema_migrations 表以查看哪些迁移已运行。
  2. 运行 praefect sql-migrate 以使迁移保持最新。

节点连接和磁盘访问

检查 Praefect 是否可以访问其所有 Gitaly 节点,以及每个 Gitaly 节点是否具有对其所有存储的读写访问权限。

如果此检查失败:

  1. 确认网络地址和令牌设置正确:
    • 在 Praefect 配置中。
    • 在每个 Gitaly 节点的配置中。
  2. 在 Gitaly 节点上,检查 gitaly 进程是否以 git 用户身份运行。可能存在权限问题,导致 Gitaly 无法访问其存储目录。
  3. 确认连接 Praefect 到 Gitaly 节点的网络没有问题。

数据库读写访问

检查 Praefect 是否可以从数据库读取和写入数据库。

如果此检查失败:

  1. 查看 Praefect 数据库是否处于恢复模式。在恢复模式下,表可能是只读的。要检查,请运行:

    select pg_is_in_recovery()
    
  2. 确认 Praefect 用于连接 PostgreSQL 的用户具有数据库的读写权限。
  3. 查看数据库是否已被置于只读模式。要检查,请运行:

    show default_transaction_read_only
    

无法访问的仓库

检查有多少仓库由于缺少主分配或主节点不可用而无法访问。

如果此检查失败:

  1. 查看是否有任何 Gitaly 节点宕机。运行 praefect ping-nodes 进行检查。
  2. 检查 Praefect 数据库的负载是否过高。如果 Praefect 数据库响应缓慢,可能会导致健康检查无法持久化到数据库,导致 Praefect 认为节点不健康。

日志中的 Praefect 错误

如果收到错误,请检查 /var/log/gitlab/gitlab-rails/production.log

以下是常见错误及潜在原因:

  • 500 响应代码
    • ActionView::Template::Error (7:permission denied)
      • praefect['configuration'][:auth][:token]gitlab_rails['gitaly_token'] 在极狐GitLab 服务器上不匹配。
      • 在 Sidekiq 服务器上缺少 git_data_dirs 存储配置。
    • Unable to save project. Error: 7:permission denied
      • 极狐GitLab 服务器上的 praefect['configuration'][:virtual_storage] 中的秘密令牌与一个或多个 Gitaly 服务器上的 gitaly['auth_token'] 值不匹配。
  • 503 响应代码
    • GRPC::Unavailable (14:failed to connect to all addresses)
      • 极狐GitLab 无法访问 Praefect。
    • GRPC::Unavailable (14:all SubCons are in TransientFailure...)
      • Praefect 无法访问其一个或多个子 Gitaly 节点。尝试运行 Praefect 连接检查器以诊断。

Praefect 数据库经历高 CPU 负载

Praefect 数据库经历高 CPU 使用率的一些常见原因包括:

  • Prometheus 指标抓取运行昂贵查询。在 gitlab.rb 中设置 praefect['configuration'][:prometheus_exclude_database_from_default_metrics] = true
  • 读取分布缓存被禁用,导致用户流量高时对数据库的查询次数增加。确保启用读取分布缓存。

确定主 Gitaly 节点

要确定仓库的主节点,请使用 praefect metadata 子命令。

查看仓库元数据

Gitaly 集群维护关于存储在集群上的仓库的 元数据库。使用 praefect metadata 子命令来检查元数据以进行故障排除。

您可以通过其 Praefect 分配的仓库 ID 检索仓库的元数据:

sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id <repository-id>

当物理存储上的物理路径以 @cluster 开头时,您可以 在物理路径中找到仓库 ID

您还可以通过其虚拟存储和相对路径检索仓库的元数据:

sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage <virtual-storage> -relative-path <relative-path>

示例

要检索具有 Praefect 分配仓库 ID 为 1 的仓库的元数据:

sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id 1

要检索虚拟存储为 default 且相对路径为 @hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git 的仓库的元数据:

sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage default -relative-path @hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git

这些示例中的任意一个检索示例仓库的以下元数据:

Repository ID: 54771
Virtual Storage: "default"
Relative Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
Replica Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
Primary: "gitaly-1"
Generation: 1
Replicas:
- Storage: "gitaly-1"
  Assigned: true
  Generation: 1, fully up to date
  Healthy: true
  Valid Primary: true
  Verified At: 2021-04-01 10:04:20 +0000 UTC
- Storage: "gitaly-2"
  Assigned: true
  Generation: 0, behind by 1 changes
  Healthy: true
  Valid Primary: false
  Verified At: unverified
- Storage: "gitaly-3"
  Assigned: true
  Generation: replica not yet created
  Healthy: false
  Valid Primary: false
  Verified At: unverified

可用元数据

praefect metadata 检索的元数据包括以下表中的字段。

字段 描述
Repository ID Praefect 分配给仓库的永久唯一 ID。与极狐GitLab 用于仓库的 ID 不同。
Virtual Storage 仓库存储所在的虚拟存储的名称。
Relative Path 仓库在虚拟存储中的路径。
Replica Path 仓库的副本存储在 Gitaly 节点磁盘上的位置。
Primary 仓库的当前主节点。
Generation Praefect 用于跟踪仓库更改。每次对仓库的写入都会增加仓库的代数。
Replicas 存在或预计存在的副本列表。

对于每个副本,以下元数据可用:

Replicas 字段 描述
Storage 包含副本的 Gitaly 存储的名称。
Assigned 指示副本是否预计存在于存储中。如果 Gitaly 节点从集群中移除或存储在仓库的复制因子减少后包含额外副本,则可以为 false
Generation 副本的最新确认代数。它指示:

- 如果代数与仓库的代数匹配,则副本完全是最新的。
- 如果副本的代数小于仓库的代数,则副本已过时。
- 如果副本在存储上根本不存在,则为 replica not yet created
Healthy 指示托管此副本的 Gitaly 节点是否被 Praefect 节点的共识视为健康。
Valid Primary 指示副本是否适合充当主节点。如果仓库的主节点不是有效主节点,则在下次写入仓库时,如果有另一个副本是有效主节点,则会发生故障转移。副本是有效主节点,如果:

- 存储在健康的 Gitaly 节点上。
- 完全是最新的。
- 未受到减少复制因子的待删除作业的目标。
- 已分配。
Verified At 验证工作者 成功验证副本的最后时间。如果副本尚未验证,则在最后一次成功验证时间的位置显示 unverified。在极狐GitLab 15.0 中引入。

命令失败并显示“repository not found”

如果 -virtual-storage 的提供值不正确,命令会返回以下错误:

get metadata: rpc error: code = NotFound desc = repository not found

文档示例指定 -virtual-storage default。检查 Praefect 服务器设置 /etc/gitlab/gitlab.rb 中的 praefect['configuration'][:virtual_storage]

检查仓库是否同步

某些情况下,Praefect 数据库可能会与底层 Gitaly 节点不同步。要检查给定仓库是否在所有节点上完全同步,请在 Rails 节点上运行 gitlab:praefect:replicas Rake 任务。此 Rake 任务对所有 Gitaly 节点上的仓库进行校验和。

PRAEFECT dataloss 命令仅检查 Praefect 数据库中仓库的状态,在这种情况下无法依赖于检测同步问题。

dataloss 命令显示 @failed-geo-sync 仓库不同步

@failed-geo-sync 是 GitLab 16.1 及更早版本中在项目同步失败时由 Geo 使用的遗留路径,并已被弃用。

在 GitLab 16.2 及更高版本中,您可以安全地删除此路径。@failed-geo-sync 目录位于 Gitaly 节点上的 仓库路径 下。

关系不存在错误

默认情况下,Praefect 数据库表由 gitlab-ctl reconfigure 任务自动创建。

但是,Praefect 数据库表在初始配置时未创建,并且如果发生以下任一情况,则可能会抛出关系不存在的错误:

  • 未执行 gitlab-ctl reconfigure 命令。
  • 执行期间发生错误。

例如:

  • ERROR: relation "node_status" does not exist at character 13
  • ERROR: relation "replication_queue_lock" does not exist at character 40
  • 此错误:

    {"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"}
    

为了解决此问题,可以使用 praefect 命令的 sql-migrate 子命令进行数据库架构迁移:

$ sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate
praefect sql-migrate: OK (applied 21 migrations)

请求失败并显示“repository scoped: invalid Repository”错误

这表示 Praefect 配置 中使用的虚拟存储名称与极狐GitLab 中的 gitaly['configuration'][:storage][<index>][:name] 设置 中使用的存储名称不匹配。

通过匹配 Praefect 和极狐GitLab 配置中使用的虚拟存储名称来解决此问题。

云平台上的 Gitaly 集群性能问题

Praefect 不需要大量的 CPU 或内存,可以在小型虚拟机上运行。云服务可能会对小型 VM 可以使用的资源施加其他限制,例如磁盘 IO 和网络流量。

Praefect 节点产生大量网络流量。如果云服务对其网络带宽进行了限制,则可能会观察到以下症状:

  • Git 操作性能不佳。
  • 网络延迟高。
  • Praefect 的内存使用率高。

可能的解决方案:

  • 配置更大的 VM 以获得更大的网络流量额度。
  • 使用云服务的监控和日志记录检查 Praefect 节点是否没有耗尽流量额度。

gitlab-ctl reconfigure 失败并出现 Praefect 配置错误

如果 gitlab-ctl reconfigure 失败,您可能会看到以下错误:

STDOUT: praefect: configuration error: error reading config file: toml: cannot store TOML string into a Go int

此错误发生在 praefect['database_port']praefect['database_direct_port'] 被配置为字符串而不是整数时。

常见复制错误

以下是一些常见的复制错误及可能的解决方案。

锁定文件存在

锁定文件用于防止对同一引用进行多个更新。有时锁定文件变得陈旧,复制失败并出现错误 error: cannot lock ref

要清除陈旧的 *.lock 文件,可以在 Rails 控制台 上触发 OptimizeRepositoryRequest

p = Project.find <Project ID>
client = Gitlab::GitalyClient::RepositoryService.new(p.repository)
client.optimize_repository

如果触发 OptimizeRepositoryRequest 不起作用,请手动检查文件以确认创建日期并决定是否可以手动删除 *.lock 文件。任何超过 24 小时创建的锁定文件都可以安全删除。

Git fsck 错误

具有无效对象的 Gitaly 仓库可能会导致复制失败,并在 Gitaly 日志中出现以下错误:

  • exit status 128, stderr: "fatal: git upload-pack: not our ref"
  • "fatal: bad object 58....e0f... ssh://gitaly/internal.git did not send all necessary objects"

只要其中一个 Gitaly 节点仍然有健康的仓库副本,这些问题可以通过以下步骤解决:

  1. 从 Praefect 数据库中移除仓库
  2. 使用 Praefect track-repository 子命令 重新跟踪它。

这将使用权威 Gitaly 节点上的仓库副本覆盖所有其他 Gitaly 节点上的副本。在运行这些命令之前,请确保最近备份了仓库。

  1. 将坏仓库移出位置:

    run `mv <REPOSITORY_PATH> <REPOSITORY_PATH>.backup`
    

    例如:

    mv /var/opt/gitlab/git-data/repositories/@cluster/repositories/de/74/2335 /var/opt/gitlab/git-data/repositories/@cluster/repositories/de/74/2335.backup
    
  2. 运行 Praefect 命令以触发复制:

    # 验证您拥有正确的仓库。
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage gitaly -relative-path '<relative_path>' -db-only
    
    # 再次运行并加上 '--apply' 标志以从 Praefect 跟踪数据库中移除仓库
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage gitaly -relative-path '<relative_path>' -db-only --apply
    
    # 重新跟踪仓库,覆盖次要节点
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repository -virtual-storage gitaly -authoritative-storage '<healthy_gitaly>' -relative-path '<relative_path>' -replica-path '<replica_path>'-replicate-immediately
    

复制静默失败

如果 Praefect dataloss 显示 部分不可用的仓库,并且 accept-dataloss 命令 在没有日志中的错误的情况下未能同步仓库,这可能是由于 Praefect 数据库中的 storage_repositories 表的 repository_id 字段不匹配导致。要检查是否存在不匹配:

  1. 连接到 Praefect 数据库。
  2. 运行以下查询:

    select * from storage_repositories where relative_path = '<relative-path>';
    

    @hashed 开头的仓库路径替换 <relative-path>

备用目录不存在

极狐GitLab 使用 Git 备用机制进行去重alternates 是一个指向 @pool 仓库中的 objects 目录以获取对象的文本文件。如果此文件指向无效路径,复制可能会失败并出现以下错误之一:

  • "error":"no alternates directory exists", "warning","msg":"alternates file does not point to valid git repository"
  • "error":"unexpected alternates content:
  • remote: error: unable to normalize alternate object path

要调查此错误的原因:

  1. 使用 Rails 控制台 检查项目是否是池的一部分:

    project = Project.find_by_id(<project id>)
    project.pool_repository
    
  2. 检查池仓库路径是否存在于磁盘上,并且与 alternates 文件 内容匹配。
  3. 检查项目中的 objects 目录是否可以从 alternates 文件中访问。

执行这些检查后,请将收集的信息提交给极狐GitLab 支持。