完整性检查 Rake 任务

极狐GitLab 提供了 Rake 任务来检查各种组件的完整性。

Repository 完整性

尽管 Git 非常有弹性并试图防止数据完整性问题,但有时也会出现问题。以下 Rake 任务旨在帮助 GitLab 管理员诊断问题存储库,以便修复它们。

有 3 件事要检查以确定完整性。

  1. Git 仓库文件系统检查(git fsck)。此步骤验证仓库中对象的连通性和有效性。
  2. 检查仓库目录中的 config.lock
  3. 检查 refs/heads 中是否有任何分支/引用锁定文件。

单独存在 config.lock 或引用锁并不一定表示存在问题。 当 Git 和 GitLab 对存储库执行操作时,通常会创建和删除锁定文件。它们用于防止数据完整性问题。但是,如果 Git 操作中断,这些锁可能无法正确清除。

以下症状可能表明仓库完整性存在问题。如果遇到这些症状,您可以使用下面描述的 Rake 任务来准确确定导致问题的仓库。

  • 尝试推送代码时收到错误 - remote: error: cannot lock ref
  • 查看 GitLab 仪表板或访问特定项目时出现 500 错误。

检查项目代码仓库

此任务循环遍历项目代码仓库并运行前面描述的完整性检查。如果项目使用池仓库,也将被检查。其它类型的 Git 存储库不检查。

  • Linux 软件包安装:

    sudo gitlab-rake gitlab:git:fsck
    
  • 自编译安装:

    sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production
    

仓库引用的校验和

通过对每个仓库的所有引用进行校验和,可以将一个 Git 仓库与另一个进行比较。如果两个仓库具有相同的引用,并且两个仓库都通过了完整性检查,那么我们可以确信两个仓库是相同的。

例如,可用于将仓库的备份与源仓库进行比较。

检查所有 GitLab 仓库

此任务遍历 GitLab 服务器上的所有存储库,并以 <PROJECT ID>,<CHECKSUM> 格式输出校验和。

  • 如果仓库不存在,项目 ID 将有一个空白校验和。
  • 如果仓库存在但为空,则输出校验和为 0000000000000000000000000000000000000000
  • 不存在的项目被跳过。

  • Linux 软件包安装:

    sudo gitlab-rake gitlab:git:checksum_projects
    
  • 自编译安装:

    sudo -u git -H bundle exec rake gitlab:git:checksum_projects RAILS_ENV=production
    

例如,如果:

  • ID#2 的项目不存在,它将被跳过。
  • ID#4 的项目没有仓库,其校验和将为空。
  • ID#5 的项目有一个空的仓库,它的校验和将为 0000000000000000000000000000000000000000

输出将如下所示:

1,cfa3f06ba235c13df0bb28e079bcea62c5848af2
3,3f3fb58a8106230e3a6c6b48adc2712fb3b6ef87
4,
5,0000000000000000000000000000000000000000
6,6c6b48adc2712fb3b6ef87cfa3f06ba235c13df0

检查特定的 GitLab 仓库

或者,可以通过使用逗号分隔的整数列表,设置环境变量 CHECKSUM_PROJECT_IDS 来对特定项目 ID 进行校验和,例如:

CHECKSUM_PROJECT_IDS="1,3" sudo gitlab-rake gitlab:git:checksum_projects

上传文件完整性

用户可以将各种类型的文件上传到 GitLab 安装。 这些完整性检查可以检测丢失的文件。此外,对于本地存储的文件,上传时会生成校验和并将其存储在数据库中,这些检查会根据当前文件验证它们。

目前,以下类型的文件支持完整性检查:

  • CI 产物
  • LFS 对象
  • 用户上传文件

  • Linux 软件包安装:

    sudo gitlab-rake gitlab:artifacts:check
    sudo gitlab-rake gitlab:ci_secure_files:check
    sudo gitlab-rake gitlab:lfs:check
    sudo gitlab-rake gitlab:uploads:check
    
  • 自编译安装:

    sudo -u git -H bundle exec rake gitlab:artifacts:check RAILS_ENV=production
    sudo -u git -H bundle exec rake gitlab:ci_secure_files:check RAILS_ENV=production
    sudo -u git -H bundle exec rake gitlab:lfs:check RAILS_ENV=production
    sudo -u git -H bundle exec rake gitlab:uploads:check RAILS_ENV=production
    

这些任务还接受一些可用于覆盖某些值的环境变量:

变量 类型 描述
BATCH integer 指定批次的大小。默认为 200。
ID_FROM integer 指定开始的 ID,包括值。
ID_TO integer 指定结束的 ID,包括值。
VERBOSE boolean 导致故障单独列出,而不是汇总。
sudo gitlab-rake gitlab:artifacts:check BATCH=100 ID_FROM=50 ID_TO=250
sudo gitlab-rake gitlab:lfs:check BATCH=100 ID_FROM=50 ID_TO=250
sudo gitlab-rake gitlab:uploads:check BATCH=100 ID_FROM=50 ID_TO=250

示例输出:

$ sudo gitlab-rake gitlab:uploads:check
Checking integrity of Uploads
- 1..1350: Failures: 0
- 1351..2743: Failures: 0
- 2745..4349: Failures: 2
- 4357..5762: Failures: 1
- 5764..7140: Failures: 2
- 7142..8651: Failures: 0
- 8653..10134: Failures: 0
- 10135..11773: Failures: 0
- 11777..13315: Failures: 0
Done!

示例详细输出:

$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1
Checking integrity of Uploads
- 1..1350: Failures: 0
- 1351..2743: Failures: 0
- 2745..4349: Failures: 2
  - Upload: 3573: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/7a77cc52947bfe188adeff42f890bb77/image.png>
  - Upload: 3580: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/2840ba1ba3b2ecfa3478a7b161375f8a/pug.png>
- 4357..5762: Failures: 1
  - Upload: 4636: #<Google::Apis::ServerError: Server error>
- 5764..7140: Failures: 2
  - Upload: 5812: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
  - Upload: 5837: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
- 7142..8651: Failures: 0
- 8653..10134: Failures: 0
- 10135..11773: Failures: 0
- 11777..13315: Failures: 0
Done!

LDAP 检查

LDAP 检查 Rake 任务测试绑定 DN 和密码凭据(如果已配置)并列出 LDAP 用户示例。此任务也作为 gitlab:check 任务的一部分执行,但可以独立运行。有关详细信息,请参阅 LDAP Rake 任务 - LDAP 检查

验证数据库值是否可以使用当前 secrets 进行解密

该任务遍历数据库中所有可能的加密值,验证它们是否可以使用当前的 secrets 文件(gitlab-secrets.json)解密。

尚未实施自动解决方案。

这可能需要很长时间,具体取决于数据库的大小,因为它会检查所有表中的所有行。

  • Linux 软件包安装:

    sudo gitlab-rake gitlab:doctor:secrets
    
  • 自编译安装:

    bundle exec rake gitlab:doctor:secrets RAILS_ENV=production
    

示例输出

I, [2020-06-11T17:17:54.951815 #27148]  INFO -- : Checking encrypted values in the database
I, [2020-06-11T17:18:12.677708 #27148]  INFO -- : - ApplicationSetting failures: 0
I, [2020-06-11T17:18:12.823692 #27148]  INFO -- : - User failures: 0
[...] other models possibly containing encrypted data
I, [2020-06-11T17:18:14.938335 #27148]  INFO -- : - Group failures: 1
I, [2020-06-11T17:18:15.559162 #27148]  INFO -- : - Operations::FeatureFlagsClient failures: 0
I, [2020-06-11T17:18:15.575533 #27148]  INFO -- : - ScimOauthAccessToken failures: 0
I, [2020-06-11T17:18:15.575678 #27148]  INFO -- : Total: 1 row(s) affected
I, [2020-06-11T17:18:15.575711 #27148]  INFO -- : Done!

详细模式

要获取有关哪些行和列无法解密的更多详细信息,您可以传递一个 VERBOSE 环境变量:

  • Linux 软件包安装:

    sudo gitlab-rake gitlab:doctor:secrets VERBOSE=1
    
  • 自编译安装:

    bundle exec rake gitlab:doctor:secrets RAILS_ENV=production VERBOSE=1
    

示例详细输出

I, [2020-06-11T17:17:54.951815 #27148]  INFO -- : Checking encrypted values in the database
I, [2020-06-11T17:18:12.677708 #27148]  INFO -- : - ApplicationSetting failures: 0
I, [2020-06-11T17:18:12.823692 #27148]  INFO -- : - User failures: 0
[...] other models possibly containing encrypted data
D, [2020-06-11T17:19:53.224344 #27351] DEBUG -- : > Something went wrong for Group[10].runners_token: Validation failed: Route can't be blank
I, [2020-06-11T17:19:53.225178 #27351]  INFO -- : - Group failures: 1
D, [2020-06-11T17:19:53.225267 #27351] DEBUG -- :   - Group[10]: runners_token
I, [2020-06-11T17:18:15.559162 #27148]  INFO -- : - Operations::FeatureFlagsClient failures: 0
I, [2020-06-11T17:18:15.575533 #27148]  INFO -- : - ScimOauthAccessToken failures: 0
I, [2020-06-11T17:18:15.575678 #27148]  INFO -- : Total: 1 row(s) affected
I, [2020-06-11T17:18:15.575711 #27148]  INFO -- : Done!

故障排查

以下是使用上面记录的 Rake 任务可能发现的问题的解决方案。

Dangling 对象

gitlab-rake gitlab:git:fsck 任务可以找到 dangling 对象,例如:

dangling blob a12...
dangling commit b34...
dangling tag c56...
dangling tree d78...

如果问题仍然存在,请尝试通过 Rails 控制台 触发垃圾回收:

p = Project.find_by_path("project-name")
Repositories::HousekeepingService.new(p, :gc).execute

如果 dangling 对象小于 2 周的默认宽限期,并且您不想等到它们自动过期,请运行:

Repositories::HousekeepingService.new(p, :prune).execute

删除对丢失的远程上传的引用

gitlab-rake gitlab:uploads:check VERBOSE=1 检测不存在的远程对象,因为它们被外部删除,但它们的引用仍然存在于 GitLab 数据库中。

带有错误消息的示例输出:

$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1
Checking integrity of Uploads
- 100..434: Failures: 2
- Upload: 100: Remote object does not exist
- Upload: 101: Remote object does not exist
Done!

要删除这些对外部删除的远程上传的引用,请打开 GitLab Rails 控制台 并运行:

uploads_deleted=0
Upload.find_each do |upload|
  next if upload.retrieve_uploader.file.exists?
  uploads_deleted=uploads_deleted + 1
  p upload                            ### allow verification before destroy
  # p upload.destroy!                 ### uncomment to actually destroy
end
p "#{uploads_deleted} remote objects were destroyed."

删除对缺失产物的引用

gitlab-rake gitlab:artifacts:check VERBOSE=1 检测产物(或 job.log 文件):

  • 在极狐GitLab 之外被删除。
  • 在极狐GitLab 数据库中仍有引用

当检测到这种情况时,Rake 任务会显示一条错误消息。例如:

Checking integrity of Job artifacts
- 1..15: Failures: 2
  - Job artifact: 9: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /var/opt/gitlab/gitlab-rails/shared/artifacts/4b/22/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a/2022_06_30/8/9/job.log>
  - Job artifact: 15: Remote object does not exist
Done!

要删除这些对丢失的本地和/或远端产物(job.log 文件)的引用:

  1. 打开 Rails 控制台。
  2. 运行以下 Ruby 代码:

    artifacts_deleted = 0
    ::Ci::JobArtifact.find_each do |artifact|                      ### Iterate artifacts
    #  next if artifact.file.filename != "job.log"                 ### Uncomment if only `job.log` files' references are to be processed
      next if artifact.file.file.exists?                           ### Skip if the file reference is valid
      artifacts_deleted += 1
      puts "#{artifact.id}  #{artifact.file.path} is missing."     ### Allow verification before destroy
    #  artifact.destroy!                                           ### Uncomment to actually destroy
    end
    puts "Count of identified/destroyed invalid references: #{artifacts_deleted}"
    

删除对缺失 LFS 对象的引用

如果 gitlab-rake gitlab:lfs:check VERBOSE=1 检测到存在于数据库中但不在磁盘上的 LFS 对象,按照 LFS 文档中的步骤,删除数据库条目。

更新 dangling 对象存储引用

如果您已经从对象存储迁移到本地存储 并且文件丢失,那么 dangling 数据库引用仍然存在。

这在迁移日志中可见,错误如下:

W, [2022-11-28T13:14:09.283833 #10025]  WARN -- : Failed to transfer Ci::JobArtifact ID 11 with error: undefined method `body' for nil:NilClass
W, [2022-11-28T13:14:09.296911 #10025]  WARN -- : Failed to transfer Ci::JobArtifact ID 12 with error: undefined method `body' for nil:NilClass

在禁用对象存储后尝试,删除对缺失产物的引用,会导致以下错误:

RuntimeError (Object Storage is not enabled for JobArtifactUploader)

要更新这些引用,指向本地存储:

  1. 打开极狐GitLab Rails 控制台
  2. 运行以下 Ruby 代码:

    artifacts_updated = 0
    ::Ci::JobArtifact.find_each do |artifact|                    ### Iterate artifacts
      next if artifact.file_store != 2                           ### Skip if file_store already points to local storage
      artifacts_updated += 1
      # artifact.update(file_store: 1)                           ### Uncomment to actually update
    end
    puts "Updated file_store count: #{artifacts_updated}"
    

删除对缺失产物的引用的脚本现在可以正常运行并清理数据库。