备份极狐GitLab

备份极狐GitLab 的过程取决于许多因素。您对特定部署的使用和配置决定了存在的数据类型、数据位置和数据量。这些因素会影响您对如何执行备份、如何存储备份以及如何恢复备份的选择。

简单的备份过程

作为总体指南,如果您使用的 1k 参考架构数据少于 100 GB,请按照以下步骤操作:

  1. 运行备份命令
  2. 备份对象存储(如果适用)。
  3. 手动备份配置文件

扩展备份

随着极狐GitLab 数据量的增长,备份命令的执行时间会更长。备份选项例如并发备份 Git 仓库增量仓库备份可以减少执行时间。在某些时候,备份命令本身就不切实际。例如,可能需要 24 小时或更长时间。

有关详细信息,请参阅替代备份策略

哪些数据需要备份?

PostgreSQL 数据库

在最简单的情况下,极狐GitLab 在 PostgreSQL 服务器中拥有一个 PostgreSQL 数据库,这台 PostgreSQL 服务器和其他极狐GitLab 服务都在同一台虚拟机上。但根据配置,极狐GitLab 可能会在多个 PostgreSQL 服务器中使用多个 PostgreSQL 数据库。

一般来说,此数据是 Web 界面中大多数用户生成的内容(例如议题和合并请求内容、评论、权限和凭据)的单一事实来源。

PostgreSQL 还保存一些缓存数据,例如 HTML 渲染的 Markdown,以及默认情况下的合并请求差异。 但是,合并请求差异也可以配置为卸载到文件系统或对象存储,请参阅 Blob

Gitaly Cluster 的 Praefect 服务使用 PostgreSQL 数据库作为单一事实来源来管理其 Gitaly 节点。

一个常见的 PostgreSQL 实用程序 pg_dump会生成一个可用于恢复 PostgreSQL 数据库的备份文件。备份命令在底层使用此实用程序。

不幸的是,数据库越大,pg_dump 执行所需的时间就越长。根据您的情况,持续时间有时会变得不切实际(例如几天)。如果您的数据库超过 100 GB,pg_dump 以及扩展的备份命令可能无法使用。有关详细信息,请参阅替代备份策略

Git 仓库

极狐GitLab 实例可以有一个或多个仓库分片。每个分片都是一个 Gitaly 实例或 Gitaly 集群,负责允许对本地存储的 Git 仓库进行访问和操作。Gitaly 可以在机器上运行:

  • 使用单个磁盘。
  • 将多个磁盘挂载为单个挂载点(如 RAID 阵列)。
  • 使用 LVM。

每个项目最多可以有 3 个不同的仓库:

  • 项目仓库:存储源代码。
  • Wiki 仓库:存储 Wiki 内容。
  • 设计仓库,索引设计产物进行索引(资产实际上位于 LFS 中)。

它们都位于同一个分片中,并共享相同的基本名称,并带有 -wiki-design 后缀,用于 Wiki 和设计仓库案例。

个人和项目代码片段以及群组 Wiki 内容存储在 Git 仓库中。

使用池仓库在实时极狐GitLab 站点中对项目派生进行去重。

备份命令为每个仓库生成一个 Git 包并将它们全部打包。这会将池仓库数据复制到每个派生中。在我们的测试中,100 GB 的 Git 仓库需要 2 个多小时才能备份并上传到 S3。对于大约 400 GB 的 Git 数据,备份命令可能不适用于常规备份。有关详细信息,请参阅替代备份策略

Blob

极狐GitLab 将议题附件或 LFS 对象等 Blob(或文件)存储到以下任一位置:

  • 特定位置的文件系统。
  • 对象存储解决方案。对象存储解决方案可以:
    • 基于云,例如 Amazon S3 和 Google Cloud Storage。
    • 由您托管(例如 MinIO)。
    • 公开对象存储兼容 API 的存储设备。

对象存储

备份命令不会备份未存储在文件系统上的 Blob。如果您使用对象存储,请务必启用对象存储提供者的备份。例如,请参见:

容器镜像库

极狐GitLab 容器镜像库存储可以配置为:

  • 特定位置的文件系统。
  • 对象存储 解决方案。对象存储解决方案可以是:
    • 基于云,例如 Amazon S3 和 Google Cloud Storage。
    • 由您托管(如 MinIO)。
    • 公开对象存储兼容 API 的存储设备。

当镜像库数据存储在文件系统上的默认位置时,备份命令会对其进行备份。

对象存储

备份命令不会备份未存储在文件系统上的 Blob。如果您使用对象存储,请务必启用对象存储提供者的备份。例如,请参见:

存储配置文件

caution 极狐GitLab 提供的备份 Rake 任务存储您的配置文件。主要原因是您的数据库包含的项目包括用于双因素身份验证和 CI/CD 安全变量 的加密信息。将加密信息存储在与其密钥相同的位置,从一开始就违背了使用加密的目的。例如,secret 文件包含您的数据库加密密钥。如果丢失,极狐GitLab 应用程序将无法解密数据库中的任何加密值。
caution 升级后私密文件可能会更改。

您应该备份配置目录。您至少必须备份:

::Tabs

:::TabTitle Linux 软件包

  • /etc/gitlab/gitlab-secrets.json
  • /etc/gitlab/gitlab.rb

有关更多信息,请参见备份和恢复 Linux 软件包(Omnibus)配置

:::TabTitle 自编译

  • /home/git/gitlab/config/secrets.yml
  • /home/git/gitlab/config/gitlab.yml

:::TabTitle Docker

  • 备份存储备份文件的卷。如果您按照文档创建极狐GitLab 容器,其应该在 /srv/gitlab/config 目录中。

:::TabTitle 极狐GitLab Helm chart

::EndTabs

您可能还需要备份任何 TLS 密钥和证书(/etc/gitlab/ssl/etc/gitlab/trusted-certs),以及您的 SSH 主机密钥以避免在必须执行完整机器恢复时出现中间人攻击警告。

万一 secret 文件丢失,请参阅故障排除部分

其他数据

极狐GitLab 使用 Redis 作为缓存存储并为我们的后台作业系统 Sidekiq 保存持久数据。提供的备份命令备份 Redis 数据。这意味着为了使用备份命令进行一致的备份,必须没有挂起或正在运行的后台作业。可以手动备份 Redis

Elasticsearch 是用于高级搜索的可选数据库。它可以改善源代码级别以及用户在议题、合并请求和讨论中生成的内容中的搜索。备份命令备份 Elasticsearch 数据。恢复后可以从 PostgreSQL 数据重新生成 Elasticsearch 数据。可以手动备份 Elasticsearch

命令行界面

极狐GitLab 提供了一个命令行界面来备份您的整个实例,包括:

  • 数据库
  • 附件
  • Git 仓库数据
  • CI/CD 作业输出日志
  • CI/CD 作业产物
  • LFS 对象
  • Terraform 状态(引入于 14.7 版本)
  • 容器镜像库镜像
  • 极狐GitLab Pages 内容
  • 软件包(引入于 14.7 版本)
  • 代码片段
  • 群组 Wiki
  • 项目级安全文件(引入于 16.1 版本)

备份不包括:

caution 极狐GitLab 不备份任何配置文件(/etc/gitlab)、TLS 密钥和证书或系统文件。强烈建议您阅读存储配置文件

要求

为了能够进行备份和恢复,请确保您的系统上安装了 Rsync。如果您安装了极狐GitLab:

  • 使用 Linux 软件包,已安装 Rsync。
  • 使用自编译,检查是否安装了 rsync。如果未安装 Rsync,请安装。例如:

    # Debian/Ubuntu
    sudo apt-get install rsync
    
    # RHEL/CentOS
    sudo yum install rsync
    

备份命令

caution 备份命令不备份对象存储中的项目。
caution 当您的安装使用 PgBouncer 时,出于性能原因或与 Patroni 集群一起使用时,备份命令需要附加参数
caution 在 15.5.0 版本之前,备份命令不验证是否有其他备份正在运行。我们强烈建议您在完成所有备份后再开始新的备份。
note 您只能将备份恢复到与创建备份的极狐GitLab 完全相同的版本和类型

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create

:::TabTitle Helm chart(Kubernetes)

使用 kubectl 运行备份任务以在极狐GitLab toolbox pod 上运行 backup-utility 脚本。有关更多信息,请参阅 Chart 备份文档

:::TabTitle Docker

从主机运行备份。

  • 13.10 及更高版本:
docker exec -t <container name> gitlab-backup create

:::TabTitle 自编译

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

::EndTabs

如果您的极狐GitLab 部署有多个节点,您需要选择一个节点来运行备份命令。您必须确保指定节点:

  • 是持久的,并且不受弹性伸缩的影响。
  • 已安装极狐GitLab Rails 应用程序。如果 Puma 或 Sidekiq 正在运行,则 Rails 已安装。
  • 有足够的存储空间和内存来生成备份文件。

输出示例:

Dumping database tables:
- Dumping table events... [DONE]
- Dumping table issues... [DONE]
- Dumping table keys... [DONE]
- Dumping table merge_requests... [DONE]
- Dumping table milestones... [DONE]
- Dumping table namespaces... [DONE]
- Dumping table notes... [DONE]
- Dumping table projects... [DONE]
- Dumping table protected_branches... [DONE]
- Dumping table schema_migrations... [DONE]
- Dumping table services... [DONE]
- Dumping table snippets... [DONE]
- Dumping table taggings... [DONE]
- Dumping table tags... [DONE]
- Dumping table users... [DONE]
- Dumping table users_projects... [DONE]
- Dumping table web_hooks... [DONE]
- Dumping table wikis... [DONE]
Dumping repositories:
- Dumping repository abcd... [DONE]
Creating backup archive: $TIMESTAMP_gitlab_backup.tar [DONE]
Deleting tmp directories...[DONE]
Deleting old backups... [SKIPPING]

备份时间戳

备份存档保存在 backup_path 中,该路径在 config/gitlab.yml 文件中指定。默认路径是 /var/opt/gitlab/backups。文件名是 [TIMESTAMP]_gitlab_backup.tar,其中 TIMESTAMP 标识创建每个备份的时间以及极狐GitLab 版本。如果您需要恢复极狐GitLab 并且有多个备份可用,则需要时间戳。

例如,如果备份名称为 1493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar,则时间戳为 1493107454_2018_04_25_10.6.4-ce

备份选项

极狐GitLab 提供的用于备份实例的命令行工具可以接受更多选项。

备份策略选项

默认备份策略本质上是使用 Linux 命令 targzip 将数据从相应的数据位置流式传输到备份。在大多数情况下没有问题,但当数据快速变化时可能会导致问题。

tar 读取数据时数据发生更改,可能会出现 file changed as we read it 错误,并导致备份过程失败。在这种情况下,您可以使用称为 copy 的备份策略。该策略在调用 targzip 之前将数据文件复制到临时位置,从而避免了错误。

副作用是备份过程会额外占用 1X 的磁盘空间。该过程会尽力清理每个阶段的临时文件,因此问题不会变得复杂,但对于大型安装来说,这可能是一个相当大的变化。

要使用 copy 策略而不是默认的流策略,请在 Rake 任务命令中指定 STRATEGY=copy。例如:

sudo gitlab-backup create STRATEGY=copy

备份文件名称

caution 如果您使用自定义备份文件名,则无法限制备份的生命周期

默认情况下,备份文件是根据前面的备份时间戳部分中的规范创建的。但是,您可以通过设置 BACKUP 环境变量来覆盖文件名的 [TIMESTAMP]。例如:

sudo gitlab-backup create BACKUP=dump

生成的文件名为 dump_gitlab_backup.tar。这对于使用 rsync 和增量备份的系统非常有用,并且可以显著提高传输速度。

备份压缩

默认情况下,在备份以下内容期间应用 Gzip 快速压缩:

默认命令是 gzip -c -1。您可以使用 COMPRESS_CMD 覆盖此命令。

注意事项:

  • 压缩命令在流水线中使用,因此您的自定义命令必须输出到 stdout。
  • 如果您指定的命令未与极狐GitLab 一起打包,则您必须自行安装。
  • 结果文件名仍将以 .gz 结尾。
  • 恢复期间使用的默认解压缩命令是 gzip -cd。因此,如果您覆盖压缩命令以使用无法通过 gzip -cd 解压缩的格式,则必须在恢复期间覆盖解压缩命令。

默认压缩:最快方法的 Gzip

gitlab-backup create

最慢方法的 Gzip

gitlab-backup create COMPRESS_CMD="gzip -c --best"

如果使用 gzip 进行备份,那么恢复不需要任何选项:

gitlab-backup restore

无压缩

如果您的备份目标具有内置自动压缩功能,那么您可能希望跳过压缩。

tee 命令将 stdin 传输到 stdout

gitlab-backup create COMPRESS_CMD=tee

恢复时:

gitlab-backup restore DECOMPRESS_CMD=tee

替换 Gzip

以下是使用手动安装的压缩工具的示例:

gitlab-backup create COMPRESS_CMD="foo --bar --baz"

类似地,恢复时:

gitlab-backup restore DECOMPRESS_CMD="foo --quz"

确认可以传输存档

为了确保生成的存档可以通过 rsync 传输,您可以设置 GZIP_RSYNCABLE=yes 选项。这会将 --rsyncable 选项设置为 gzip,该选项仅与设置备份文件名选项结合使用才有用。

不保证 gzip 中的 --rsyncable 选项在所有发行版上都可用。要验证它在您的发行版中是否可用,请运行 gzip --help 或查阅手册。

sudo gitlab-backup create BACKUP=dump GZIP_RSYNCABLE=yes

从备份排除特定目录

您可以通过添加环境变量 SKIP 从备份中排除特定目录,其值是以下选项的以逗号分隔的列表:

  • db(数据库)
  • uploads(附件)
  • builds(CI 作业输出日志)
  • artifacts(CI 作业产物)
  • lfs(LFS 对象)
  • terraform_state(Terraform 状态)
  • registry(容器镜像库镜像)
  • pages(Pages 内容)
  • repositories(Git 仓库数据)
  • packages(软件包)
  • ci_secure_files (项目级安全文件)
note 备份和恢复 Helm Chart 时,有一个附加选项 packages,它指的是由极狐GitLab 软件包库管理的任何包。 有关更多信息,请参阅命令行参数

所有 Wiki 都作为 repositories 群组的一部分进行备份。备份期间会跳过不存在的 Wiki。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create SKIP=db,uploads

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production

::EndTabs

SKIP= 也用于:

跳过 tar 创建

note 使用对象存储进行备份时,无法跳过 tar 创建。

创建备份的最后一部分是生成包含所有部分的 .tar 文件。在某些情况下,创建 .tar 文件可能会浪费精力,甚至有害,因此您可以通过将 tar 添加到 SKIP 环境变量来跳过此步骤。用例示例:

  • 当其他备份软件拾取备份时。
  • 通过避免每次都提取备份来加速增量备份。(在这种情况下,不能指定 PREVIOUS_BACKUPBACKUP,否则提取指定的备份,但最终不会生成 .tar 文件。)

tar 添加到 SKIP 变量会将目录中包含备份的文件和目录用于中间文件。创建新备份时,这些文件将被覆盖,因此您应该将其复制到其他位置,因为您在系统上只能有一个备份。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create SKIP=tar

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create SKIP=tar RAILS_ENV=production

::EndTabs

创建服务器端的仓库备份

  • 引入于极狐GitLab 16.3。
  • 对恢复特定备份而不是最新备份的服务器端的支持引入于极狐GitLab 16.6。
  • 创建增量备份的服务器端的支持引入于极狐GitLab 16.6。

可以配置仓库备份,以便托管每个仓库的 Gitaly 节点负责创建备份并将其流式传输到对象存储,而不是将大型仓库备份存储在备份存档中。这有助于减少创建和恢复备份所需的网络资源。

  1. 在 Gitaly 中配置服务器端备份目标。
  2. 使用 REPOSITORIES_SERVER_SIDE 变量创建备份。请参阅以下示例。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create REPOSITORIES_SERVER_SIDE=true

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_SERVER_SIDE=true

::EndTabs

并发备份 Git 仓库

  • 引入于极狐GitLab 13.3。
  • 并发恢复引入于极狐GitLab 14.3。

使用多个仓库存储时,可以同时备份或恢复仓库以帮助充分利用 CPU 时间。以下变量可用于修改 Rake 任务的默认行为:

  • GITLAB_BACKUP_MAX_CONCURRENCY:同时备份的最大项目数。默认为逻辑 CPU 的数量(在 14.1 及更早版本中,默认为 1)。
  • GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY:每个存储上同时备份的最大项目数。允许仓库备份分布在存储中。默认为 2(在 14.1 及更早版本中,默认为 1)。

例如,有 4 个仓库存储:

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create GITLAB_BACKUP_MAX_CONCURRENCY=4 GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY=1

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create GITLAB_BACKUP_MAX_CONCURRENCY=4 GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY=1

::EndTabs

增量仓库备份

  • 引入于极狐GitLab 14.9,功能标志incremental_repository_backup。默认禁用。
  • 在私有化部署版本上启用于极狐GitLab 14.10。
  • PREVIOUS_BACKUP 选项引入于极狐GitLab 15.0。
  • 创建增量备份的服务器端的支持引入于极狐GitLab 16.6。
在私有化部署的极狐GitLab 上,默认情况下此功能可用。要隐藏该功能,管理员可以隐藏名为 incremental_repository_backup功能标志。 在 JihuLab.com,此功能不可用。
note 只有仓库支持增量备份。因此,如果您使用 INCREMENTAL=yes,该任务将创建一个独立的备份 tar 存档。这是因为除仓库之外的所有子任务仍在创建完整备份(覆盖现有完整备份)。

增量仓库备份可能比完整仓库备份更快,因为它们仅将自上次备份以来的更改打包到每个仓库的备份包中。 增量备份存档不相互链接:每个存档都是实例的独立备份。必须有现有备份才能从中创建增量备份:

  • 在 14.9 和 14.10 中,使用 BACKUP=<timestamp_of_backup> 选项来选择要使用的备份。所选的先前备份将被覆盖。
  • 在 15.0 及更高版本中,使用 PREVIOUS_BACKUP=<timestamp_of_backup> 选项来选择要使用的备份。默认情况下,将按照备份时间戳部分中的记录创建备份文件。您可以通过设置 BACKUP 环境变量覆盖文件名称的 [TIMESTAMP] 部分。

要创建增量备份,请运行:

  • 在 15.0 及更高版本中:

    sudo gitlab-backup create INCREMENTAL=yes PREVIOUS_BACKUP=<timestamp_of_backup>
    
  • 在 14.9 和 14.10 中:

    sudo gitlab-backup create INCREMENTAL=yes BACKUP=<timestamp_of_backup>
    

要从 tarred 备份创建 untarred 增量备份,请使用 SKIP=tar

sudo gitlab-backup create INCREMENTAL=yes SKIP=tar

备份特定仓库存储

引入于极狐GitLab 15.0。

当使用多个仓库存储时,可以使用 REPOSITORIES_STORAGES 选项单独备份特定仓库存储中的仓库。该选项接受以逗号分隔的存储名称列表。

示例:

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create REPOSITORIES_STORAGES=storage1,storage2

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_STORAGES=storage1,storage2

::EndTabs

备份特定仓库

引入于极狐GitLab 15.1。

您可以使用 REPOSITORIES_PATHS 选项备份特定仓库。 同样,您可以使用 SKIP_REPOSITORIES_PATHS 来跳过某些仓库。 这两个选项都接受以逗号分隔的项目或群组路径列表。如果指定群组路径,则包含或跳过该群组和子组中所有项目中的所有仓库,具体取决于您使用的选项。

例如,要备份 A 组 (group-a) 中所有项目的所有仓库,请备份 B 组项目 C 的仓库 (group-b/project- c),并跳过 A 组 中的 项目 D (group-a/project-d):

::Tabs

:::TabTitle Linux 软件包(Omnibus)

  sudo gitlab-backup create REPOSITORIES_PATHS=group-a,group-b/project-c SKIP_REPOSITORIES_PATHS=group-a/project-d

:::TabTitle 自编译

  sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_PATHS=group-a,group-b/project-c SKIP_REPOSITORIES_PATHS=group-a/project-d

::EndTabs

将备份上传到远端(云)存储

note 使用对象存储进行备份时,无法跳过 tar 创建

您可以让备份脚本上传(使用 Fog 库)它创建的 .tar 文件。在以下示例中,我们使用 Amazon S3 进行存储,但 Fog 还允许您使用其他存储提供者。 极狐GitLab 还为 AWS、Google 和阿里云导入云驱动程序。本地驱动程序也可用

阅读有关在极狐GitLab 中使用对象存储的更多信息

使用 Amazon S3

对于 Linux 软件包(Omnibus):

  1. 将以下内容添加到 /etc/gitlab/gitlab.rb

    gitlab_rails['backup_upload_connection'] = {
      'provider' => 'AWS',
      'region' => 'eu-west-1',
      'aws_access_key_id' => 'AKIAKIAKI',
      'aws_secret_access_key' => 'secret123'
      # If using an IAM Profile, don't configure aws_access_key_id & aws_secret_access_key
      # 'use_iam_profile' => true
    }
    gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
    # Consider using multipart uploads when file size reaches 100MB. Enter a number in bytes.
    # gitlab_rails['backup_multipart_chunk_size'] = 104857600
    
  2. 重新配置极狐GitLab 以使更改生效。

S3 加密存储桶

引入于极狐GitLab 14.3。

AWS 支持这些服务器端加密模式

  • Amazon S3-Managed 密钥 (SSE-S3)
  • 存储在 AWS Key Management Service (SSE-KMS) 中的客户主密钥 (CMK)
  • 客户提供的密钥 (SSE-C)

使用您选择的极狐GitLab 模式。每种模式都有类似但略有不同的配置方法。

SSE-S3

要启用 SSE-S3,请在备份存储选项中将 server_side_encryption 字段设置为 AES256。例如,在 Linux 软件包(Omnibus)中:

gitlab_rails['backup_upload_storage_options'] = {
  'server_side_encryption' => 'AES256'
}
SSE-KMS

要启用 SSE-KMS,您需要 arn:aws:kms:region:acct-id:key/key-id 格式的通过其 Amazon 资源名称 (ARN) 的 KMS 密钥。 在 backup_upload_storage_options 配置设置下,将:

  • server_side_encryption 设置为 aws:kms
  • server_side_encryption_kms_key_id 设置为密钥的 ARN。

例如,在 Linux 软件包中(Omnibus):

gitlab_rails['backup_upload_storage_options'] = {
  'server_side_encryption' => 'aws:kms',
  'server_side_encryption_kms_key_id' => 'arn:aws:<YOUR KMS KEY ID>:'
}
SSE-C

SSE-C 要求您设置以下加密选项:

  • backup_encryption:AES256
  • backup_encryption_key:未编码的 32 字节(256 位)密钥。如果不是 32 字节,则上传失败

例如,在 Linux 软件包中(Omnibus):

gitlab_rails['backup_encryption'] = 'AES256'
gitlab_rails['backup_encryption_key'] = '<YOUR 32-BYTE KEY HERE>'

如果密钥包含二进制字符且无法以 UTF-8 编码,请使用 GITLAB_BACKUP_ENCRYPTION_KEY 环境变量指定密钥。 例如:

gitlab_rails['env'] = { 'GITLAB_BACKUP_ENCRYPTION_KEY' => "\xDE\xAD\xBE\xEF" * 8 }
Digital Ocean Spaces

此示例可用于 Amsterdam (AMS3) 中的存储桶:

  1. 将以下内容添加到 /etc/gitlab/gitlab.rb

    gitlab_rails['backup_upload_connection'] = {
      'provider' => 'AWS',
      'region' => 'ams3',
      'aws_access_key_id' => 'AKIAKIAKI',
      'aws_secret_access_key' => 'secret123',
      'endpoint'              => 'https://ams3.digitaloceanspaces.com'
    }
    gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
    
  2. 重新配置极狐GitLab 以使更改生效。

如果您在使用 Digital Ocean Spaces 时看到 400 Bad Request 错误消息,原因可能是使用了备份加密。由于 Digital Ocean Spaces 不支持加密,因此请移除或评价包含 gitlab_rails['backup_encryption'] 的行。

其他 S3 提供者

并非所有 S3 提供者都与 Fog 库完全兼容。例如,如果您在尝试上传后看到 411 Length Required 错误消息,您可能需要将 aws_signature_version 值从默认值降级为 2

对于自编译安装:

  1. 编辑 home/git/gitlab/config/gitlab.yml

      backup:
        # snip
        upload:
          # Fog storage connection settings, see https://fog.io/storage/ .
          connection:
            provider: AWS
            region: eu-west-1
            aws_access_key_id: AKIAKIAKI
            aws_secret_access_key: 'secret123'
            # If using an IAM Profile, leave aws_access_key_id & aws_secret_access_key empty
            # ie. aws_access_key_id: ''
            # use_iam_profile: 'true'
          # The remote 'directory' to store your backups. For S3, this would be the bucket name.
          remote_directory: 'my.s3.bucket'
          # Specifies Amazon S3 storage class to use for backups, this is optional
          # storage_class: 'STANDARD'
          #
          # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional
          #   'encryption' must be set in order for this to have any effect.
          #   'encryption_key' should be set to the 256-bit encryption key for Amazon S3 to use to encrypt or decrypt.
          #   To avoid storing the key on disk, the key can also be specified via the `GITLAB_BACKUP_ENCRYPTION_KEY`  your data.
          # encryption: 'AES256'
          # encryption_key: '<key>'
          #
          #
          # Turns on AWS Server-Side Encryption with Amazon S3-Managed keys (optional)
          # https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
          # For SSE-S3, set 'server_side_encryption' to 'AES256'.
          # For SS3-KMS, set 'server_side_encryption' to 'aws:kms'. Set
          # 'server_side_encryption_kms_key_id' to the ARN of customer master key.
          # storage_options:
          #   server_side_encryption: 'aws:kms'
          #   server_side_encryption_kms_key_id: 'arn:aws:kms:YOUR-KEY-ID-HERE'
    
  2. 重启极狐GitLab 以使更改生效。

如果您要将备份上传到 S3,您应该创建一个具有受限访问权限的新 IAM 用户。要授予上传用户仅上传备份的访问权限,请创建以下 IAM 配置文件,并将 my.s3.bucket 替换为您的存储桶名称:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1412062044000",
      "Effect": "Allow",
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:GetBucketAcl",
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:GetObjectAcl",
        "s3:ListBucketMultipartUploads",
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Resource": [
        "arn:aws:s3:::my.s3.bucket/*"
      ]
    },
    {
      "Sid": "Stmt1412062097000",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListAllMyBuckets"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "Stmt1412062128000",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my.s3.bucket"
      ]
    }
  ]
}
使用 Google Cloud Storage

要使用 Google Cloud Storage 保存备份,您必须首先从 Google 控制台创建访问密钥:

  1. 进入 Google 存储设置页面
  2. 选择 Interoperability,然后创建访问密钥。
  3. 记下 Access KeySecret 并将其替换为以下配置。
  4. 在存储桶高级设置中,确保选择访问控制选项 Set object-level and bucket-level permissions
  5. 确保您已经创建了存储桶。

对于 Linux 软件包(Omnibus):

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

    gitlab_rails['backup_upload_connection'] = {
      'provider' => 'Google',
      'google_storage_access_key_id' => 'Access Key',
      'google_storage_secret_access_key' => 'Secret',
    
      ## If you have CNAME buckets (foo.example.com), you might run into SSL issues
      ## when uploading backups ("hostname foo.example.com.storage.googleapis.com
      ## does not match the server certificate"). In that case, uncomment the following
      ## setting. See: https://github.com/fog/fog/issues/2834
      #'path_style' => true
    }
    gitlab_rails['backup_upload_remote_directory'] = 'my.google.bucket'
    
  2. 重新配置极狐GitLab 以使更改生效。

对于自编译安装:

  1. 编辑 home/git/gitlab/config/gitlab.yml

      backup:
        upload:
          connection:
            provider: 'Google'
            google_storage_access_key_id: 'Access Key'
            google_storage_secret_access_key: 'Secret'
          remote_directory: 'my.google.bucket'
    
  2. 重启极狐GitLab 以使更改生效。

使用 Azure Blob storage

引入于极狐GitLab 13.4。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

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

    gitlab_rails['backup_upload_connection'] = {
     'provider' => 'AzureRM',
     'azure_storage_account_name' => '<AZURE STORAGE ACCOUNT NAME>',
     'azure_storage_access_key' => '<AZURE STORAGE ACCESS KEY>',
     'azure_storage_domain' => 'blob.core.windows.net', # Optional
    }
    gitlab_rails['backup_upload_remote_directory'] = '<AZURE BLOB CONTAINER>'
    
  2. 重新配置极狐GitLab 以使更改生效。

:::TabTitle 自编译

  1. 编辑 home/git/gitlab/config/gitlab.yml

      backup:
        upload:
          connection:
            provider: 'AzureRM'
            azure_storage_account_name: '<AZURE STORAGE ACCOUNT NAME>'
            azure_storage_access_key: '<AZURE STORAGE ACCESS KEY>'
          remote_directory: '<AZURE BLOB CONTAINER>'
    
  2. 重启极狐GitLab 以使更改生效。

::EndTabs

更多信息请参见 Azure 参数表格

为备份指定自定义目录

此选项仅适用于远程存储。如果要对备份进行分组,可以传递 DIRECTORY 环境变量:

sudo gitlab-backup create DIRECTORY=daily
sudo gitlab-backup create DIRECTORY=weekly

跳过向远端存储上传备份

如果您已将极狐GitLab 配置为向远端存储上传备份,您可以使用 SKIP=remote 选项跳过将备份上传到远端存储。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create SKIP=remote

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create SKIP=remote RAILS_ENV=production

::EndTabs

上传到本地挂载的共享

您可以使用 Fog Local 存储提供者将备份发送到本地挂载的共享(例如,NFSCIFSSMB)。

为此,您必须设置以下配置 key:

  • backup_upload_connection.local_root:备份复制到的挂载目录。
  • backup_upload_remote_directorybackup_upload_connection.local_root 目录的子目录。如果不存在则需要创建。 如果要将 tarball 复制到已安装目录的根目录,请使用 .

挂载时,local_root key 中设置的目录必须由以下任一者拥有:

  • git 用户。因此,使用 git 用户的 uid= 来安装 CIFSSMB
  • 您执行备份任务的用户。对于 Linux 软件包 (Omnibus),这是 git 用户。

由于文件系统性能可能会影响极狐GitLab 的整体性能,我们不建议使用基于云的文件系统进行存储。

避免冲突配置

不要将以下配置 key 设置为同一路径:

  • gitlab_rails['backup_path'](用于自编译安装的 backup.path)。
  • gitlab_rails['backup_upload_connection'].local_rootbackup.upload.connection.local_root 用于自编译安装)。

backup_path 配置 key 设置备份文件的本地位置。upload 配置 key 用于将备份文件上传到单独的服务器,也可能用于存档。

如果这些配置 key 设置为同一位置,则上传功能会失败,因为上传位置已存在备份。此失败会导致上传功能删除备份,因为它假定这是上传尝试失败后剩余的残留文件。

将上传配置到本地挂载的共享

::Tabs

:::TabTitle Linux 软件包(Omnibus)

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

    gitlab_rails['backup_upload_connection'] = {
      :provider => 'Local',
      :local_root => '/mnt/backups'
    }
    
    # The directory inside the mounted folder to copy backups to
    # Use '.' to store them in the root directory
    gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups'
    
  2. 重新配置极狐GitLab 以使更改生效。

:::TabTitle 自编译

  1. 编辑 home/git/gitlab/config/gitlab.yml

    backup:
      upload:
        # Fog storage connection settings, see https://fog.io/storage/ .
        connection:
          provider: Local
          local_root: '/mnt/backups'
        # The directory inside the mounted folder to copy backups to
        # Use '.' to store them in the root directory
        remote_directory: 'gitlab_backups'
    
  2. 重启极狐GitLab 以使更改生效。

::EndTabs

备份存档权限

极狐GitLab 创建的备份存档 (1393513186_2014_02_27_gitlab_backup.tar) 默认拥有所有者/群组 git/git 和 0600 权限。这是为了避免其他系统用户读取极狐GitLab 数据。如果您需要备份存档具有不同的权限,可以使用 archive_permissions 设置。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

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

    gitlab_rails['backup_archive_permissions'] = 0644 # Makes the backup archives world-readable
    
  2. 重新配置极狐GitLab 以使更改生效。

:::TabTitle 自编译

  1. 编辑 /home/git/gitlab/config/gitlab.yml

    backup:
      archive_permissions: 0644 # Makes the backup archives world-readable
    
  2. 重启极狐GitLab 以使更改生效。

::EndTabs

配置 cron 进行每日备份

caution 以下 cron 作业不会备份您的极狐GitLab 配置文件SSH 主机密钥

您可以安排一个 cron 作业来备份您的仓库和极狐GitLab 元数据。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

  1. root 用户配置 crontab:

    sudo su -
    crontab -e
    
  2. 添加以下内容,计划每天凌晨 2:00 进行备份:

    0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
    

:::TabTitle 自编译

  1. git 用户编辑 crontab:

    sudo -u git crontab -e
    
  2. 在底部添加以下内容:

    # Create a full backup of the GitLab repositories and SQL database every day at 2am
    0 2 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production CRON=1
    

::EndTabs

如果没有任何错误,CRON=1 环境设置会指示备份脚本隐藏所有进度输出。建议这样做以减少 cron spam。 但是,在解决备份故障时,请将 CRON=1 替换为 --trace 以详细记录。

限制本地文件的备份生命周期(修剪旧备份)

caution 如果您使用自定义文件名进行备份,本节中描述的过程将不起作用。

为了防止定期备份占用所有磁盘空间,您可能需要设置备份的有限生命周期。下次运行备份任务时,早于 backup_keep_time 的备份将被删除。

此配置选项仅管理本地文件。极狐GitLab 不会删除存储在第三方对象存储中的旧文件,因为用户可能没有列出和删除文件的权限。建议您为对象存储配置适当的保留策略(例如,AWS S3)。

::Tabs

:::TabTitle Linux 软件包(Omnibus)

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

    ## Limit backup lifetime to 7 days - 604800 seconds
    gitlab_rails['backup_keep_time'] = 604800
    
  2. 重新配置极狐GitLab 以使更改生效。

:::TabTitle 自编译

  1. 编辑 /home/git/gitlab/config/gitlab.yml

    backup:
      ## Limit backup lifetime to 7 days - 604800 seconds
      keep_time: 604800
    
  2. 重启极狐GitLab 以使更改生效。

::EndTabs

使用 PgBouncer 备份和恢复安装

不要通过 PgBouncer 连接备份或恢复极狐GitLab。这些任务必须绕过 PgBouncer 并直接连接到 PostgreSQL 主数据库节点,否则会导致极狐GitLab 中断。

当极狐GitLab 备份或恢复任务与 PgBouncer 一起使用时,会显示以下错误消息:

ActiveRecord::StatementInvalid: PG::UndefinedTable

每次运行极狐GitLab 备份时,极狐GitLab 都会开始生成 500 个错误,并且有关丢失表的错误将由 PostgreSQL 记录

ERROR: relation "tablename" does not exist at character 123

发生这种情况是因为该任务使用 pg_dump,它设置一个空搜索路径并在每个 SQL 查询中显式包含架构来解决 CVE-2018-1058

由于连接在事务池模式下与 PgBouncer 重用,因此 PostgreSQL 无法搜索默认的 public 架构。因此,搜索路径的清除会出现表和列丢失。

绕过 PgBouncer

有两种方法绕过 PgBouncer:

  1. 使用环境变量覆盖数据库设置进行备份任务。
  2. 重新配置节点以直接连接到 PostgreSQL 主数据库节点

环境变量覆盖

多个数据库支持引入于极狐GitLab 16.5。

默认情况下,极狐GitLab 使用存储在配置文件(database.yml)中的数据库配置。但是,您可以通过设置前缀为 GITLAB_BACKUP_ 的环境变量来覆盖备份和恢复任务的数据库设置:

  • GITLAB_BACKUP_PGHOST
  • GITLAB_BACKUP_PGUSER
  • GITLAB_BACKUP_PGPORT
  • GITLAB_BACKUP_PGPASSWORD
  • GITLAB_BACKUP_PGSSLMODE
  • GITLAB_BACKUP_PGSSLKEY
  • GITLAB_BACKUP_PGSSLCERT
  • GITLAB_BACKUP_PGSSLROOTCERT
  • GITLAB_BACKUP_PGSSLCRL
  • GITLAB_BACKUP_PGSSLCOMPRESSION

例如,要覆盖数据库主机和端口以通过 Linux 软件包 (Omnibus) 使用 192.168.1.10 和端口 5432:

sudo GITLAB_BACKUP_PGHOST=192.168.1.10 GITLAB_BACKUP_PGPORT=5432 /opt/gitlab/bin/gitlab-backup create

如果您在多个数据库上运行极狐GitLab,您可以通过在环境变量中包含数据库名称来覆盖数据库设置。例如,如果您的 mainci 数据库托管在不同的数据库服务器上,您可以将它们的名称附加在 GITLAB_BACKUP_ 前缀之后,而将 PG* 名称保留为原样:

sudo GITLAB_BACKUP_MAIN_PGHOST=192.168.1.10 GITLAB_BACKUP_CI_PGHOST=192.168.1.12 /opt/gitlab/bin/gitlab-backup create

有关这些参数的更多信息,请参见 PostgreSQL 文档

仓库备份和恢复的 gitaly-backup

  • 引入于极狐GitLab 14.2。
  • 部署在功能标志后,默认启用。
  • 普遍可用于极狐GitLab 14.10。移除功能标志 gitaly_backup

备份 Rake 任务使用 gitaly-backup 二进制文件从 Gitaly 创建和恢复仓库备份。gitaly-backup 取代了之前从极狐GitLab 直接调用 Gitaly 上的 RPC 的备份方法。

备份 Rake 任务必须能够找到此可执行文件。在大多数情况下,您不需要更改二进制文件的路径,因为它应该可以与默认路径 /opt/gitlab/embedded/bin/gitaly-backup 一起正常工作。 如果您有特定原因需要更改路径,可以在 Linux 软件包(Omnibus)中进行配置:

  1. 将以下内容添加到 /etc/gitlab/gitlab.rb

    gitlab_rails['backup_gitaly_backup_path'] = '/path/to/gitaly-backup'
    
  2. 重新配置极狐GitLab 以使更改生效。

替代备份策略

由于每个部署可能具有不同的功能,因此您应该首先查看需要备份哪些数据,以更好地了解是否以及如何使用这些功能。

例如,如果您使用 Amazon RDS,您可以选择使用其内置的备份和恢复功能来处理极狐GitLab PostgreSQL 数据并在使用备份命令排除 PostgreSQL 数据

在以下情况下,请考虑使用文件系统数据传输或快照作为备份策略的一部分:

  • 您的极狐GitLab 实例包含大量 Git 仓库数据,并且极狐GitLab 备份脚本太慢。
  • 您的极狐GitLab 实例有很多派生项目,定期备份任务会复制所有项目的 Git 数据。
  • 您的极狐GitLab 实例有问题,无法使用常规备份和导入 Rake 任务。
caution Gitaly Cluster 不支持快照备份

当考虑使用文件系统数据传输或快照时:

  • 不要使用这些方法从一种操作系统迁移到另一种操作系统。源和目标的操作系统应尽可能相似。例如,不要使用这些方法从 Ubuntu 迁移到 RHEL。
  • 数据一致性非常重要。在进行文件系统传输(例如使用 rsync)或拍摄快照之前,您应该使用 sudo gitlab-ctl stop 停止极狐GitLab。

示例:Amazon Elastic Block Store (EBS)

  • 使用托管在 Amazon AWS 上的 Linux 软件包 (Omnibus) 的极狐GitLab 服务器。
  • 包含 ext4 文件系统的 EBS 驱动器挂载在 /var/opt/gitlab
  • 在这种情况下,您可以通过拍摄 EBS 快照来进行应用程序备份。
  • 备份包括所有仓库、上传和 PostgreSQL 数据。

示例:Logical Volume Manager (LVM) 快照 + rsync

  • 使用 Linux 软件包 (Omnibus) 的极狐GitLab 服务器,LVM 逻辑卷挂载在 /var/opt/gitlab
  • 使用 rsync 复制 /var/opt/gitlab 目录并不可靠,因为 rsync 运行时会更改太多文件。
  • 我们创建一个临时 LVM 快照,而不是 rsync /var/opt/gitlab,并将其作为只读文件系统挂载在 /mnt/gitlab_backup 处。
  • 现在我们可以有一个运行时间更长的 rsync 作业,它在远程服务器上创建一致的副本。
  • 副本包括所有仓库、上传和 PostgreSQL 数据。

如果您在虚拟化服务器上运行极狐GitLab,您还可以创建整个极狐GitLab 服务器的 VM 快照。然而,虚拟机快照要求您关闭服务器电源的情况并不少见,这限制了该解决方案的实际使用。

分别备份仓库数据

首先确保您在跳过仓库时备份现有的极狐GitLab 数据:

::Tabs

:::TabTitle Linux 软件包(Omnibus)

sudo gitlab-backup create SKIP=repositories

:::TabTitle 自编译

sudo -u git -H bundle exec rake gitlab:backup:create SKIP=repositories RAILS_ENV=production

::EndTabs

要在磁盘上手动备份 Git 仓库数据,有多种可能的策略:

防止写入和复制 Git 仓库数据

Git 仓库必须以一致的方式复制。不应在并发写入操作期间进行复制,因为这可能会导致不一致或损坏问题。

为了防止写入 Git 仓库数据,有两种可能的方法:

  • 使用维护模式将极狐GitLab 置于只读状态。
  • 在备份仓库之前停止所有 Gitaly 服务,以创建明确的停机时间:

    sudo gitlab-ctl stop gitaly
    # execute git data copy step
    sudo gitlab-ctl start gitaly
    

您可以使用任何方法复制 Git 仓库数据,只要防止对正在复制的数据进行写入(以防止不一致和损坏问题)。按照优先级和安全性的顺序,推荐的方法是:

  1. 通过归档模式、删除和校验和选项使用 rsync,例如:

    rsync -aR --delete --checksum source destination # be extra safe with the order as it will delete existing data if inverted
    
  2. 使用 tar pipe 以将整个仓库的目录复制到另一个服务器或位置

  3. 使用 sftpscpcp 和任何其他方法。

通过将仓库标记为只读进行在线备份(实验性功能)

在不需要实例级范围停机的情况下备份仓库的一种方法是以编程方式将项目标记为只读,同时复制基础数据。

这样做有一些可能的缺点:

  • 仓库在一段时间内是只读的,该时间段随仓库的大小而变化。
  • 由于将每个项目标记为只读,备份需要更长的时间才能完成,这可能会导致不一致。例如,第一个备份项目的最新可用数据与最后一个备份项目的可用数据之间可能存在日期差异。
  • 派生网络应该是完全只读的,同时备份里面的项目以防止对池仓库的潜在更改。

故障排除

以下是您可能遇到的问题以及可能的解决方案。

secret 文件丢失

如果您没有备份 secret 文件,则必须完成几个步骤才能使极狐GitLab 再次正常工作。

Secret 文件负责存储包含所需敏感信息的列的加密密钥。如果密钥丢失,极狐GitLab 无法解密这些列,从而阻止访问以下项目:

在 CI/CD 变量和 Runner 身份验证等情况下,您可能会遇到意外行为,例如:

  • 作业卡住。
  • 500 错误。

在这种情况下,您必须重置 CI/CD 变量和 Runner 身份验证的所有令牌,这将在以下部分中进行更详细的描述。重置令牌后,您应该能够访问您的项目并且作业再次开始运行。

caution 本节中的步骤可能会导致上述项目的数据丢失

验证所有值都可以解密

您可以使用 Rake 任务确定数据库是否包含无法解密的值。

进行备份

您必须直接修改极狐GitLab 数据才能解决丢失的 secret 文件。

caution 在尝试任何更改之前,请务必创建完整的数据库备份。

禁用用户双因素身份验证(2FA)

启用 2FA 的用户无法登录极狐GitLab。在这种情况下,您必须为所有人禁用 2FA,之后用户必须重新激活 2FA。

重置 CI/CD 变量

1、进入数据库控制台:

对于 Linux 软件包 (Omnibus),14.1 及更早版本:

   sudo gitlab-rails dbconsole

对于 Linux 软件包(Omnibus),14.2 及更高版本:

   sudo gitlab-rails dbconsole --database main

对于自编译安装,14.1 及更早版本:

   sudo -u git -H bundle exec rails dbconsole -e production

对于自编译安装,14.2 及更高版本:

   sudo -u git -H bundle exec rails dbconsole -e production --database main
  1. 检查 ci_group_variablesci_variables 表格:

    SELECT * FROM public."ci_group_variables";
    SELECT * FROM public."ci_variables";
    

    这些是您需要删除的变量。

  2. 删除所有变量:

    DELETE FROM ci_group_variables;
    DELETE FROM ci_variables;
    
  3. 如果您知道要从中删除变量的特定群组或项目,则可以包含 WHERE 语句以在 DELETE 中进行指定:

    DELETE FROM ci_group_variables WHERE group_id = <GROUPID>;
    DELETE FROM ci_variables WHERE project_id = <PROJECTID>;
    

您需要重新配置或重启极狐GitLab 以使更改生效。

重置 Runner 注册令牌

  1. 进入数据库控制台:

    对于 Linux 软件包(Omnibus),14.1 及更早版本:

    sudo gitlab-rails dbconsole
    

    对于 Linux 软件包(Omnibus),14.2 及更高版本:

    sudo gitlab-rails dbconsole --database main
    

    对于自编译安装,14.1 及更早版本:

    sudo -u git -H bundle exec rails dbconsole -e production
    

    对于自编译安装,14.2 及更高版本:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 清除项目、群组和整个实例的所有令牌:

    caution The final UPDATE operation stops the runners from being able to pick up new jobs. You must register new runners.
    -- Clear project tokens
    UPDATE projects SET runners_token = null, runners_token_encrypted = null;
    -- Clear group tokens
    UPDATE namespaces SET runners_token = null, runners_token_encrypted = null;
    -- Clear instance tokens
    UPDATE application_settings SET runners_registration_token_encrypted = null;
    -- Clear key used for JWT authentication
    -- This may break the $CI_JWT_TOKEN job variable:
    -- https://gitlab.com/gitlab-org/gitlab/-/issues/325965
    UPDATE application_settings SET encrypted_ci_jwt_signing_key = null;
    -- Clear runner tokens
    UPDATE ci_runners SET token = null, token_encrypted = null;
    

重置待处理的流水线作业

  1. 进入数据库控制台:

    对于 Linux 软件包(Omnibus),14.1 及更早版本:

    sudo gitlab-rails dbconsole
    

    对于 Linux 软件包(Omnibus),14.2 及更高版本:

    sudo gitlab-rails dbconsole --database main
    

    对于自编译安装,14.1 及更早版本:

    sudo -u git -H bundle exec rails dbconsole -e production
    

    对于自编译安装,14.2 及更高版本:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 清除待处理作业的所有令牌:

    对于 15.3 及更早版本:

    -- Clear build tokens
    UPDATE ci_builds SET token = null, token_encrypted = null;
    

    对于 15.4 及更高版本:

    -- Clear build tokens
    UPDATE ci_builds SET token_encrypted = null;
    

对于其他功能可以采用类似的策略。通过删除无法解密的数据,极狐GitLab 可以恢复运行,并且可以手动替换丢失的数据。

修复集成和 Webhook

如果您丢失了 secret,集成设置Webhook 设置页面可能会显示 500 错误消息。当您尝试使用先前配置的集成或 Webhook 访问项目中的仓库时,丢失的 secret 也可能会生成 500 错误。

修复方法是截断受影响的表格(包含加密列的表)。 这将删除您配置的所有集成、Webhook 和相关元数据。 在删除数据之前,您应该验证这些 secret 是否是根本原因。

  1. 进入数据库控制台:

    对于 Linux 软件包(Omnibus),14.1 及更早版本:

    sudo gitlab-rails dbconsole
    

    对于 Linux 软件包(Omnibus),14.2 及更高版本:

    sudo gitlab-rails dbconsole --database main
    

    对于自编译安装,14.1 及更早版本:

    sudo -u git -H bundle exec rails dbconsole -e production
    

    对于自编译安装,14.2 及更高版本:

    sudo -u git -H bundle exec rails dbconsole -e production --database main
    
  2. 截断以下表格:

    -- truncate web_hooks table
    TRUNCATE integrations, chat_names, issue_tracker_data, jira_tracker_data, slack_integrations, web_hooks, zentao_tracker_data, web_hook_logs CASCADE;
    

从备份恢复后容器镜像库推送失败

如果您使用容器镜像库,则在恢复镜像库数据后,在 Linux 包 (Omnibus) 实例上恢复备份后,推送到镜像库可能会失败。

这些失败在镜像库日志中提及权限问题,类似于:

level=error
msg="response completed with error"
err.code=unknown
err.detail="filesystem: mkdir /var/opt/gitlab/gitlab-rails/shared/registry/docker/registry/v2/repositories/...: permission denied"
err.message="unknown error"

此问题是由于以非特权用户 git 身份运行恢复引起的,该用户无法在恢复过程中为镜像库文件分配正确的所有权。

要让您的镜像库再次工作:

sudo chown -R registry:registry /var/opt/gitlab/gitlab-rails/shared/registry/docker

如果更改了镜像库的默认文件系统位置,请针对自定义位置运行 chown,而不是 /var/opt/gitlab/gitlab-rails/shared/registry/docker

备份因 Gzip 错误而无法完成

运行备份时,您可能会收到 Gzip 错误消息:

sudo /opt/gitlab/bin/gitlab-backup create
...
Dumping ...
...
gzip: stdout: Input/output error

Backup failed

如果发生这种情况,请检查以下内容:

  • 确认有足够的磁盘空间用于 Gzip 操作。使用默认策略的备份在备份创建期间需要一半实例大小的可用磁盘空间的情况并不罕见。
  • 如果正在使用 NFS,请检查是否设置了挂载选项 timeout。默认值为 600,将其更改为较小的值会导致此错误。

出现 File name too long 错误,备份失败

备份时,您可能会遇到 File name too long 错误。例如:

Problem: <class 'OSError: [Errno 36] File name too long:

此问题会导致备份脚本无法完成。要解决此问题,您必须截断引发问题的文件名。最多允许 246 个字符,包括文件扩展名。

caution 本节中的步骤可能会导致数据丢失。所有步骤必须严格按照给定的顺序进行。

截断文件名以解决错误涉及以下内容:

  • 清理数据库中未跟踪的远端上传文件。
  • 截断数据库中的文件名。
  • 重新运行备份任务。

清理远端上传文件

有一个已知问题导致删除父资源后对象存储上传仍保留,此问题已得到解决。

要修复这些文件,您必须清理存储中但未在 uploads 数据库表中跟踪的所有远端上传文件。

  1. 列出所有可以移动到 lost and found 目录的对象存储上传文件(如果极狐GitLab 数据库中不存在这些文件):

    bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production
    
  2. 如果您确定想要删除这些文件并移除所有未引用的上传文件,请运行:

    caution 以下操作无法撤回
    bundle exec rake gitlab:cleanup:remote_upload_files RAILS_ENV=production DRY_RUN=false
    

截断数据库引用的文件名

您必须截断数据库引用的导致问题的文件。数据库引用的文件名存储在以下位置:

  • uploads 表格中。
  • 找到的引用中。从其他数据库表和列找到的任何引用。
  • 文件系统中。

截断 uploads 表格中的文件名:

  1. 进入数据库控制台:

对于 Linux 软件包(Omnibus),14.2 及更高版本:

   sudo gitlab-rails dbconsole --database main

对于 Linux 软件包(Omnibus),14.1 及更早版本:

   sudo gitlab-rails dbconsole

对于自编译安装,14.2 及更高版本:

   sudo -u git -H bundle exec rails dbconsole -e production --database main

对于自编译安装,14.1 及更早版本:

   sudo -u git -H bundle exec rails dbconsole -e production
  1. uploads 表格中搜索长度超过 246 个字符的文件名:

    以下查询以 0 到 10000 的批次选择文件名长度超过 246 个字符的 uploads 记录。这提高了表格具有数千条记录的大型极狐GitLab 实例的性能。

       CREATE TEMP TABLE uploads_with_long_filenames AS
       SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, id, path
       FROM uploads AS u
       WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246;
    
       CREATE INDEX ON uploads_with_long_filenames(row_id);
    
       SELECT
          u.id,
          u.path,
          -- Current filename
          (regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] AS current_filename,
          -- New filename
          CONCAT(
             LEFT(SPLIT_PART((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242),
             COALESCE(SUBSTRING((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$'))
          ) AS new_filename,
          -- New path
          CONCAT(
             COALESCE((regexp_match(u.path, '(.*\/).*'))[1], ''),
             CONCAT(
                LEFT(SPLIT_PART((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242),
                COALESCE(SUBSTRING((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$'))
             )
          ) AS new_path
       FROM uploads_with_long_filenames AS u
       WHERE u.row_id > 0 AND u.row_id <= 10000;
    

    输出示例:

       -[ RECORD 1 ]----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       id               | 34
       path             | public/@hashed/loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisit.txt
       current_filename | loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisit.txt
       new_filename     | loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt
       new_path         | public/@hashed/loremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelitsedvulputatemisitloremipsumdolorsitametconsecteturadipiscingelitseddoeiusmodtemporincididuntutlaboreetdoloremagnaaliquaauctorelits.txt

    其中:

    • current_filename:当前长于 246 个字符的文件名
    • new_filename:已被截断为最多 246 个字符的文件名。
    • new_pathnew_filename 相关的新路径(已截断)。

    验证批处理结果后,必须使用以下数字序列(10000 到 20000)更改批处理大小 (row_id)。重复此过程,直到 uploads 表中的最后一条记录。

  2. uploads 表中找到的文件从长文件名重命名为新的截断文件名。以下查询将回滚更新,以便您可以在 transaction wrapper 中安全地检查结果:

    CREATE TEMP TABLE uploads_with_long_filenames AS
    SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, path, id
    FROM uploads AS u
    WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246;
    
    CREATE INDEX ON uploads_with_long_filenames(row_id);
    
    BEGIN;
    WITH updated_uploads AS (
       UPDATE uploads
       SET
          path =
          CONCAT(
             COALESCE((regexp_match(updatable_uploads.path, '(.*\/).*'))[1], ''),
             CONCAT(
                LEFT(SPLIT_PART((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242),
                COALESCE(SUBSTRING((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$'))
             )
          )
       FROM
          uploads_with_long_filenames AS updatable_uploads
       WHERE
          uploads.id = updatable_uploads.id
       AND updatable_uploads.row_id > 0 AND updatable_uploads.row_id  <= 10000
       RETURNING uploads.*
    )
    SELECT id, path FROM updated_uploads;
    ROLLBACK;
    

    验证批量更新结果后,必须使用以下数字序列(10000 到 20000)更改批量大小 (row_id)。重复此过程,直到 uploads 表中的最后一条记录。

  3. 验证先前查询中的新文件名是否是预期的文件名。如果您确定要将上一步中找到的记录截断为 246 个字符,请运行以下命令:

    caution 以下操作无法撤回
    CREATE TEMP TABLE uploads_with_long_filenames AS
    SELECT ROW_NUMBER() OVER(ORDER BY id) row_id, path, id
    FROM uploads AS u
    WHERE LENGTH((regexp_match(u.path, '[^\\/:*?"<>|\r\n]+$'))[1]) > 246;
    
    CREATE INDEX ON uploads_with_long_filenames(row_id);
    
    UPDATE uploads
    SET
    path =
       CONCAT(
          COALESCE((regexp_match(updatable_uploads.path, '(.*\/).*'))[1], ''),
          CONCAT(
             LEFT(SPLIT_PART((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1], '.', 1), 242),
             COALESCE(SUBSTRING((regexp_match(updatable_uploads.path, '[^\\/:*?"<>|\r\n]+$'))[1] FROM '\.(?:.(?!\.))+$'))
          )
       )
    FROM
    uploads_with_long_filenames AS updatable_uploads
    WHERE
    uploads.id = updatable_uploads.id
    AND updatable_uploads.row_id > 0 AND updatable_uploads.row_id  <= 10000;
    

    完成批量更新后,必须使用以下数字序列(10000 到 20000)更改批量大小(updatable_uploads.row_id)。重复此过程,直到 uploads 表中的最后一条记录。

截断找到的引用中的文件名:

  1. 检查这些记录是否在某处被引用。一种方法是转储数据库并搜索父目录名称和文件名:

    1. 要转储数据库,您可以使用以下命令作为示例:

      pg_dump -h /var/opt/gitlab/postgresql/ -d gitlabhq_production > gitlab-dump.tmp
      
    2. 然后您可以使用 grep 命令搜索引用。您可以将父目录和文件名结合起来。例如:

      grep public/alongfilenamehere.txt gitlab-dump.tmp
      
  2. 使用通过查询 uploads 表获得的新文件名替换那些长文件名。

截断文件系统上的文件名。您必须手动将文件系统中的文件重命名为通过查询 uploads 表获得的新文件名。

重新运行备份任务

完成上述所有步骤后,重新运行备份任务。

先前启用 pg_stat_statements 时恢复数据库备份失败

PostgreSQL 数据库的极狐GitLab 备份包括启用扩展所需的所有 SQL 语句,该扩展之前在数据库中启用。

pg_stat_statements 扩展只能由具有 superuser 角色的 PostgreSQL 用户启用或禁用。 由于恢复过程使用权限有限的数据库用户,因此无法执行以下 SQL 语句:

DROP EXTENSION IF EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;

当尝试在没有 pg_stats_statements 扩展的 PostgreSQL 实例中恢复备份时,会显示以下错误消息:

ERROR: permission denied to create extension "pg_stat_statements"
HINT: Must be superuser to create this extension.
ERROR: extension "pg_stat_statements" does not exist

当尝试在启用了 pg_stats_statements 扩展的实例中进行恢复时,清理步骤失败,并显示类似于以下内容的错误消息:

rake aborted!
ActiveRecord::StatementInvalid: PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'
Caused by:
PG::InsufficientPrivilege: ERROR: must be owner of view pg_stat_statements
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:42:in `block (4 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `each'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/db.rake:41:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/service/gitlab-rails/lib/tasks/gitlab/backup.rake:71:in `block (3 levels) in <top (required)>'
/opt/gitlab/embedded/bin/bundle:23:in `load'
/opt/gitlab/embedded/bin/bundle:23:in `<main>'
Tasks: TOP => gitlab:db:drop_tables
(See full trace by running task with --trace)

防止转储文件包含 pg_stat_statements

要防止将扩展包含在作为备份包一部分的 PostgreSQL 转储文件中,请在除 public 架构之外的任何架构中启用该扩展:

CREATE SCHEMA adm;
CREATE EXTENSION pg_stat_statements SCHEMA adm;

如果该扩展之前已在 public 架构中启用,请将其移至新架构:

CREATE SCHEMA adm;
ALTER EXTENSION pg_stat_statements SET SCHEMA adm;

要在更改架构后查询 pg_stat_statements 数据,请将新架构以前缀的方式添加到视图名称中:

SELECT * FROM adm.pg_stat_statements limit 0;

为了使其与希望在 public 架构中启用的第三方监控解决方案兼容,您需要将其包含在 search_path 中:

set search_path to public,adm;

修复现有转储文件以移除对 pg_stat_statements 的引用

要修复现有备份文件,请执行以下更改:

  1. 从备份中提取以下文件:db/database.sql.gz
  2. 解压缩文件或使用能够处理压缩文件的编辑器。
  3. 移除以下行或类似行:

    CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA public;
    
    COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed';
    
  4. 保存更改并重新压缩文件。
  5. 使用修改后的 db/database.sql.gz 更新备份文件。