Help us learn about your current experience with the documentation. Take the survey.

备份 GitLab

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab 自托管版

GitLab 备份保护您的数据并帮助灾难恢复。

最佳的备份策略取决于您的 GitLab 部署配置、数据量及存储位置。这些因素决定了应使用的备份方法、备份存储位置以及如何安排备份计划。

对于较大的 GitLab 实例,可采用的替代备份策略包括:

  • 增量备份。
  • 特定仓库的备份。
  • 多个存储位置的备份。

备份中包含的数据

GitLab 提供命令行界面来备份整个实例。默认情况下,备份会创建一个压缩的 tar 归档文件。该文件包含:

  • 数据库数据和配置
  • 账户和群组设置
  • CI/CD 工件和作业日志
  • Git 仓库和 LFS 对象
  • 外部合并请求差异(在 GitLab 17.1 中引入)
  • 包注册表数据和容器注册表镜像
  • 项目和群组维基。
  • 项目级别的附件和上传
  • 安全文件(在 GitLab 16.1 中引入)
  • GitLab Pages 内容
  • Terraform 状态
  • 片段

备份中未包含的数据

强烈建议您阅读有关存储配置文件的内容,以便单独备份它们。

简单备份流程

作为一个粗略指南,如果您使用的是1k 用户参考架构,且数据量少于 100 GB,请按照以下步骤操作:

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

扩展备份

随着 GitLab 数据量的增长,备份命令的执行时间会更长。备份选项,如并发备份 Git 仓库增量仓库备份,有助于减少执行时间。在某些时候,仅靠备份命令变得不切实际,例如可能需要 24 小时甚至更长时间。

从 GitLab 18.0 开始,针对具有大量引用(分支、标签)的仓库,其备份性能已显著提升。此改进可将受影响仓库的备份时间从数小时缩短到几分钟。无需任何配置更改即可受益于这一增强功能。有关技术细节,请参阅我们的博客文章:如何将 GitLab 仓库备份时间从 48 小时缩短至 41 分钟

在某些情况下,可能需要进行架构调整以支持备份扩展。如果您使用的是 GitLab 参考架构,请参阅备份和恢复大型参考架构

更多信息,请参见替代备份策略

需要备份哪些数据?

PostgreSQL 数据库

在最简单的情况下,GitLab 在同一台虚拟机上有一个 PostgreSQL 数据库,与其他所有 GitLab 服务位于同一台 PostgreSQL 服务器上。但根据配置不同,GitLab 可能会使用多个 PostgreSQL 数据库分布在多个 PostgreSQL 服务器中。

通常,这些数据是 Web 界面中大多数用户生成内容的单一真实来源,例如问题(issue)和合并请求(merge request)的内容、评论、权限和凭证。

PostgreSQL 还存储一些缓存数据,如渲染后的 HTML 格式的 Markdown,以及默认情况下合并请求的差异(diff)。不过,合并请求的差异也可以配置为卸载到文件系统或对象存储中,参见 Blobs

Gitaly 集群(Praefect)使用一个 PostgreSQL 数据库作为单一真实来源来管理其 Gitaly 节点。

常用的 PostgreSQL 工具 pg_dump 会生成一个备份文件,可用于恢复 PostgreSQL 数据库。备份命令 在底层使用了该工具。

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

Git 仓库

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

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

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

  • 项目仓库,用于存储源代码。
  • Wiki 仓库,用于存储 Wiki 内容。
  • 设计仓库,用于索引设计工件(资产实际上存储在 LFS 中)。

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

个人和项目的片段(snippets)、以及群组(group)的 Wiki 内容,都存储在 Git 仓库中。

在活跃的 GitLab 站点上,项目分支(forks)通过池化仓库(pool repositories)去重。

备份命令 为每个仓库生成一个 Git 包(bundle)并将它们全部打包。这会将池化仓库的数据复制到每个分支中。在我们的测试中,100 GB 的 Git 仓库备份并上传到 S3 耗时略超 2 小时。当 Git 数据量约为 400 GB 时,备份命令可能不再适用于常规备份。有关更多信息,请参阅 替代备份策略

Blob 对象

GitLab 将 blob 对象(或文件),如问题附件或 LFS 对象,存储到以下位置之一:

  • 特定位置的文件系统。
  • 对象存储 解决方案。对象存储解决方案可以是:
    • 云端服务,如 Amazon S3 和 Google Cloud Storage。
    • 由你自行托管(如 MinIO)。
    • 暴露与对象存储兼容 API 的存储设备(Storage Appliance)。

对象存储

备份命令 不会备份未存储在文件系统上的 blob 对象。如果你正在使用 对象存储,请务必启用你的对象存储提供商的备份功能。例如,参见:

容器注册表

GitLab 容器注册表 存储可以配置为以下方式之一:

  • 特定位置的文件系统。
  • 对象存储 解决方案。对象存储解决方案可以是:
    • 云端服务,如 Amazon S3 和 Google Cloud Storage。
    • 由你自行托管(如 MinIO)。
    • 暴露与对象存储兼容 API 的存储设备(Storage Appliance)。

当注册表数据存储在对象存储中时,备份命令不会备份这些数据。

存储配置文件

GitLab 提供的备份 Rake 任务不会存储您的配置文件。主要原因在于,您的数据库包含项目(包括用于双因素认证的加密信息和 CI/CD 安全变量)。如果将加密信息与其密钥存放在同一位置,就违背了最初使用加密的目的。例如,密钥文件包含您的数据库加密密钥。如果您丢失它,GitLab 应用程序将无法解密数据库中的任何加密值。

升级后密钥文件可能会更改。

您应该备份配置目录。至少必须备份以下内容:

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

更多信息请参见 备份和还原 Linux 包(Omnibus)配置

  • /home/git/gitlab/config/secrets.yml
  • /home/git/gitlab/config/gitlab.yml
  • 备份存储配置文件的卷。如果您按照文档创建了 GitLab 容器,它应该在 /srv/gitlab/config 目录下。

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

万一密钥文件丢失,请参阅 当密钥文件丢失时

其他数据

GitLab 使用 Redis 既作为缓存存储,也作为后台作业系统 Sidekiq 的持久数据存储。提供的 备份命令 不会备份 Redis 数据。这意味着为了使用 备份命令 进行一致的备份,不能有未完成或正在运行的后台作业。您可以 手动备份 Redis

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

要求

为了能够备份和还原,请确保您的系统上已安装 Rsync。如果您安装了 GitLab:

  • 使用 Linux 包安装,Rsync 已安装。

  • 使用自行编译,检查 rsync 是否已安装。如果未安装 Rsync,请安装它。例如:

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

备份命令

备份命令不会备份 Linux 软件包(Omnibus)/ Docker / 自编译安装中的对象存储(object storage)项。

当您的安装使用 PgBouncer 时(无论是出于性能原因还是与 Patroni 集群一起使用),备份命令需要额外的参数。

在 GitLab 15.5.0 之前,备份命令不会验证是否已有另一个备份正在运行,如问题 362593 所述。我们强烈建议您确保所有备份完成后再开始新的备份。

您只能将备份恢复到与其创建时完全相同版本和类型(CE/EE)的 GitLab 上。

sudo gitlab-backup create

通过 kubectl 在 GitLab 工具箱 Pod 上运行 backup-utility 脚本来执行备份任务。详情请参阅 图表备份文档

从主机运行备份。

docker exec -t <容器名称> gitlab-backup create
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production

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

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

示例输出:

转储数据库表:
- 转储表 events... [已完成]
- 转储表 issues... [已完成]
- 转储表 keys... [已完成]
- 转储表 merge_requests... [已完成]
- 转储表 milestones... [已完成]
- 转储表 namespaces... [已完成]
- 转储表 notes... [已完成]
- 转储表 projects... [已完成]
- 转储表 protected_branches... [已完成]
- 转储表 schema_migrations... [已完成]
- 转储表 services... [已完成]
- 转储表 snippets... [已完成]
- 转储表 taggings... [已完成]
- 转储表 tags... [已完成]
- 转储表 users... [已完成]
- 转储表 users_projects... [已完成]
- 转储表 web_hooks... [已完成]
- 转储表 wikis... [已完成]
转储备份库:
- 转储备份库 abcd... [已完成]
创建备份归档:<backup-id>_gitlab_backup.tar [已完成]
删除临时目录...[已完成]
删除旧备份... [跳过]

有关备份过程的详细信息,请参阅 备份归档过程

备份选项

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

备份策略选项

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

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

副作用是需要额外占用最多 1 倍的磁盘空间。该过程在每个阶段尽力清理临时文件,因此问题不会累积,但对于大型安装来说可能是一个相当大的变化。

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

sudo gitlab-backup create STRATEGY=copy

备份文件名

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

备份文件根据 特定默认值 创建文件名。但是,您可以通过设置 BACKUP 环境变量覆盖文件名中的 <backup-id> 部分。例如:

sudo gitlab-backup create BACKUP=dump

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

备份压缩

默认情况下,备份时会应用Gzip快速压缩:

默认命令是gzip -c -1。你可以通过COMPRESS_CMD覆盖此命令。同样,可以通过DECOMPRESS_CMD覆盖解压命令。

注意事项:

  • 压缩命令在管道中使用,因此你的自定义命令必须输出到stdout
  • 如果你指定的命令未被GitLab打包,则必须自行安装。
  • 生成的文件名将仍然以.gz结尾。
  • 恢复期间使用的默认解压命令是gzip -cd。因此,如果你覆盖压缩命令使用了无法被gzip -cd解压的格式,则在恢复时必须覆盖解压命令。
  • 不要将环境变量放在备份命令之后。例如,gitlab-backup create COMPRESS_CMD="pigz -c --best"不会按预期工作。
默认压缩:最快的Gzip方法
gitlab-backup create
最慢的Gzip方法
COMPRESS_CMD="gzip -c --best" gitlab-backup create

如果备份时使用了gzip,则恢复无需任何选项:

gitlab-backup restore
无压缩

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

tee命令将stdin管道传输到stdout

COMPRESS_CMD=tee gitlab-backup create

恢复时:

DECOMPRESS_CMD=tee gitlab-backup restore
使用pigz进行并行压缩

虽然我们支持使用COMPRESS_CMDDECOMPRESS_CMD覆盖默认的Gzip压缩库,但我们仅在常规基础上对默认Gzip库及其默认选项进行测试。你有责任测试并验证备份的可行性。无论是否覆盖压缩命令,我们都强烈建议将其作为备份的最佳实践。如果你遇到其他压缩库的问题,应回退到默认设置。使用替代库排查和修复错误并非GitLab的优先事项。

pigz未包含在GitLab Linux软件包中。你必须自行安装。

使用4个进程通过pigz压缩备份的示例:

COMPRESS_CMD="pigz --compress --stdout --fast --processes=4" sudo gitlab-backup create

由于pigz压缩为gzip格式,因此无需使用pigz来解压由pigz压缩的备份。不过,它仍可能在性能上优于gzip。使用pigz解压备份的示例:

DECOMPRESS_CMD="pigz --decompress --stdout" sudo gitlab-backup restore
使用zstd进行并行压缩

虽然我们支持使用COMPRESS_CMDDECOMPRESS_CMD覆盖默认的Gzip压缩库,但我们仅在常规基础上对默认Gzip库及其默认选项进行测试。你有责任测试并验证备份的可行性。无论是否覆盖压缩命令,我们都强烈建议将其作为备份的最佳实践。如果你遇到其他压缩库的问题,应回退到默认设置。使用替代库排查和修复错误并非GitLab的优先事项。

zstd未包含在GitLab Linux软件包中。你必须自行安装。

使用4个线程通过zstd压缩备份的示例:

COMPRESS_CMD="zstd --compress --stdout --fast --threads=4" sudo gitlab-backup create

使用zstd解压备份的示例:

DECOMPRESS_CMD="zstd --decompress --stdout" sudo gitlab-backup restore

确认归档可传输

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

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

sudo gitlab-backup create BACKUP=dump GZIP_RSYNCABLE=yes

从备份中排除特定数据

根据您的安装类型,在创建备份时可跳过略有不同的组件。

  • db(数据库)
  • repositories(Git仓库数据,包括维基)(repositories)
  • uploads(附件)
  • builds(CI作业输出日志)
  • artifacts(CI作业制品)
  • pages(Pages内容)
  • lfs(LFS对象)
  • terraform_state(Terraform状态)
  • registry(容器注册表镜像)
  • packages(包)
  • ci_secure_files(项目级安全文件)
  • external_diffs(外部合并请求差异)
  • db(数据库)
  • repositories(Git仓库数据,包括维基)
  • uploads(附件)
  • artifacts(CI作业制品和输出日志)
  • pages(Pages内容)
  • lfs(LFS对象)
  • terraform_state(Terraform状态)
  • registry(容器注册表镜像)
  • packages(包注册表)
  • ci_secure_files(项目级安全文件)
  • external_diffs(合并请求差异)
sudo gitlab-backup create SKIP=db,uploads

请参阅图表备份文档中的跳过组件

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

SKIP= 也用于:

跳过tar文件的创建

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

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

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

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

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

创建服务器端仓库备份

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

  1. 在 Gitaly 中配置服务器端备份目标
  2. 使用仓库服务器端选项创建备份。请参考以下示例。
sudo gitlab-backup create REPOSITORIES_SERVER_SIDE=true
sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_SERVER_SIDE=true
kubectl exec <Toolbox Pod 名称> -it -- backup-utility --repositories-server-side

当使用基于 Cron 的备份时,需向额外参数添加 --repositories-server-side 标志。

并发备份 Git 仓库

在使用多个仓库存储时,可并发备份或恢复仓库以充分利用 CPU 时间。以下变量可用于调整 Rake 任务的行为:

  • GITLAB_BACKUP_MAX_CONCURRENCY:同时备份的最大项目数量。默认值等于逻辑 CPU 数量。
  • GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY:每个存储上同时备份的最大项目数量。这可将仓库备份分散到不同存储上。默认值为 2

例如,当有 4 个仓库存储时:

sudo gitlab-backup create GITLAB_BACKUP_MAX_CONCURRENCY=4 GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY=1
sudo -u git -H bundle exec rake gitlab:backup:create GITLAB_BACKUP_MAX_CONCURRENCY=4 GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY=1
toolbox:
#...
    extra: {}
    extraEnv:
      GITLAB_BACKUP_MAX_CONCURRENCY: 4
      GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY: 1

增量仓库备份

仅仓库支持增量备份。因此,如果您使用 INCREMENTAL=yes,该任务会创建一个自包含的备份 tar 包。这是因为除了仓库外的所有子任务仍会创建全量备份(它们会覆盖现有的全量备份)。
有关支持所有子任务增量备份的功能请求,请参阅 issue 19256

增量仓库备份可能比全量仓库备份更快,因为它们仅为每个仓库将自上次备份以来的变更打包到备份包中。
增量备份归档彼此独立:每个归档都是实例的自包含备份。必须存在现有备份才能从中创建增量备份。

使用 PREVIOUS_BACKUP=<备份ID> 选项选择要使用的备份。默认情况下,备份文件按 Backup ID 部分所述创建。您可以通过设置 BACKUP 环境变量 覆盖文件名的 <备份ID> 部分。

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

sudo gitlab-backup create INCREMENTAL=yes PREVIOUS_BACKUP=<备份ID>

若要从已打包的备份创建未解压的增量备份,使用 SKIP=tar

sudo gitlab-backup create INCREMENTAL=yes SKIP=tar

备份特定仓库存储

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

例如:

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

备份特定仓库

您可使用 REPOSITORIES_PATHS 选项备份特定仓库。类似地,您可使用 SKIP_REPOSITORIES_PATHS 跳过某些仓库。两个选项均接受以逗号分隔的项目或组路径列表。如果您指定组路径,则该组及后代组下所有项目的所有仓库都会被包含或跳过(取决于您使用的选项)。

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

sudo gitlab-backup create REPOSITORIES_PATHS=group-a,group-b/project-c SKIP_REPOSITORIES_PATHS=group-a/project-d
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
REPOSITORIES_PATHS=group-a SKIP_REPOSITORIES_PATHS=group-a/project_a2 backup-utility --skip db,registry,uploads,artifacts,lfs,packages,external_diffs,terraform_state,ci_secure_files,pages

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

使用对象存储备份数据时,无法跳过tar文件的创建。

您可以允许备份脚本上传(使用Fog库)它创建的.tar文件。在下面的示例中,我们使用Amazon S3作为存储,但Fog也允许您使用其他存储提供商。GitLab还导入了适用于AWS、Google和Aliyun的云驱动程序。本地驱动程序也可用

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

使用Amazon S3

对于Linux软件包(Omnibus):

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

    gitlab_rails['backup_upload_connection'] = {
      'provider' => 'AWS',
      'region' => 'eu-west-1',
      # 选择一种认证方式
      # IAM Profile
      'use_iam_profile' => true
      # 或 AWS 访问密钥与秘密密钥
      'aws_access_key_id' => 'AKIAKIAKI',
      'aws_secret_access_key' => 'secret123'
    }
    gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
    # 当文件大小达到100MB时考虑使用分片上传。输入以字节为单位的数字。
    # gitlab_rails['backup_multipart_chunk_size'] = 104857600
  2. 如果您使用IAM Profile认证方式,请确保运行 backup-utility 的实例设置了以下策略(将 <backups-bucket> 替换为正确的bucket名称):

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:DeleteObject"
                ],
                "Resource": "arn:aws:s3:::<backups-bucket>/*"
            }
        ]
    }
  3. 重新配置GitLab 以使更改生效

S3 加密桶

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

  • Amazon S3托管密钥(SSE-S3)
  • 存储在AWS密钥管理服务(KMS)中的客户主密钥(CMK)(SSE-KMS)
  • 客户提供的密钥(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,您需要KMS密钥(通过其Amazon资源名称(ARN),格式为 arn:aws:kms:region:acct-id:key/key-id。在 backup_upload_storage_options 配置设置下,设置:

  • server_side_encryptionaws: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 }
DigitalOcean Spaces

此示例可用于阿姆斯特丹(AMS3)的bucket:

  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 以使更改生效

如果您在使用DigitalOcean Spaces时看到 400 Bad Request 错误消息,原因可能是使用了备份加密。由于DigitalOcean 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存储连接设置,请参阅 https://fog.github.io/storage/ 。
          connection:
            provider: AWS
            region: eu-west-1
            aws_access_key_id: AKIAKIAKI
            aws_secret_access_key: 'secret123'
            # 如果使用IAM配置文件,请将aws_access_key_id和aws_secret_access_key留空
            # 即 aws_access_key_id: ''
            # use_iam_profile: 'true'
          # 远程“目录”用于存储备份。对于S3来说,这将是桶名称。
          remote_directory: 'my.s3.bucket'
          # 指定用于备份的Amazon S3存储类,此选项可选
          # storage_class: 'STANDARD'
          #
          # 启用AWS服务器端加密,使用亚马逊客户提供的加密密钥来备份数据,此选项可选
          #   必须设置'encryption'才能生效。
          #   'encryption_key'应设置为256位加密密钥,供Amazon S3用于加密或解密。
          #   为避免在磁盘上存储密钥,也可以通过`GITLAB_BACKUP_ENCRYPTION_KEY`环境变量指定密钥。
          # encryption: 'AES256'
          # encryption_key: '<key>'
          #
          #
          # 启用AWS服务器端加密,使用Amazon S3托管的密钥(可选)
          # https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
          # 对于SSE-S3,将'server_side_encryption'设为'AES256'。
          # 对于SS3-KMS,将'server_side_encryption'设为'aws:kms'。设置
          # 'server_side_encryption_kms_key_id'为客户主密钥的ARN。
          # storage_options:
          #   server_side_encryption: 'aws:kms'
          #   server_side_encryption_kms_key_id: 'arn:aws:kms:YOUR-KEY-ID-HERE'
  2. 重启GitLab,使更改生效

使用Google云存储

若要使用Google云存储保存备份,你必须先从Google控制台创建一个访问密钥:

  1. 前往Google存储设置页面
  2. 选择互操作性,然后创建一个访问密钥。
  3. 记录下Access KeySecret,并在以下配置中进行替换。
  4. 在桶的高级设置中,确保选中了设置对象级和桶级权限的访问控制选项。
  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',
    
      ## 如果你使用了CNAME桶(例如foo.example.com),在上传备份时可能会遇到SSL问题
      ## (“hostname foo.example.com.storage.googleapis.com与服务器证书不匹配”)。在这种情况下,取消注释以下设置。参见: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 存储

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

    gitlab_rails['backup_upload_connection'] = {
     'provider' => 'AzureRM',
     'azure_storage_account_name' => '<AZURE 存储账户名称>',
     'azure_storage_access_key' => '<AZURE 存储访问密钥>',
     'azure_storage_domain' => 'blob.core.windows.net', # 可选
    }
    gitlab_rails['backup_upload_remote_directory'] = '<AZURE Blob 容器>'

    如果您使用 托管标识,则省略 azure_storage_access_key

    gitlab_rails['backup_upload_connection'] = {
      'provider' => 'AzureRM',
      'azure_storage_account_name' => '<AZURE 存储账户名称>',
      'azure_storage_domain' => '<AZURE 存储域>' # 可选
    }
    gitlab_rails['backup_upload_remote_directory'] = '<AZURE Blob 容器>'
  2. 重新配置 GitLab 以使更改生效

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

      backup:
        upload:
          connection:
            provider: 'AzureRM'
            azure_storage_account_name: '<AZURE 存储账户名称>'
            azure_storage_access_key: '<AZURE 存储访问密钥>'
          remote_directory: '<AZURE Blob 容器>'
  2. 重启 GitLab 以使更改生效

有关更多详细信息,请参阅 Azure 参数表

指定备份的自定义目录

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

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

跳过向远程存储上传备份

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

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

上传到本地挂载的共享

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

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

  • backup_upload_connection.local_root:备份复制到的挂载目录。
  • backup_upload_remote_directorybackup_upload_connection.local_root 目录的子目录。如果不存在则会创建该目录。如果您希望将 tarball 复制到挂载目录的根目录,请使用 .

挂载后,local_root 键中设置的目录必须由以下任一用户拥有:

  • git 用户。因此,对于 CIFSSMB,需以 git 用户的 uid= 进行挂载。
  • 执行备份任务的用户。对于 Linux 包(Omnibus),此用户为 git 用户。

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

避免冲突配置

不要将以下配置键设置为相同路径:

  • gitlab_rails['backup_path'](自行编译安装为 backup.path)。
  • gitlab_rails['backup_upload_connection'].local_root(自行编译安装为 backup.upload.connection.local_root)。

backup_path 配置键设置备份文件的本地位置。upload 配置键旨在用于备份文件上传到单独服务器的情况(可能用于存档目的)。

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

配置上传到本地挂载的共享目录
  1. 编辑 /etc/gitlab/gitlab.rb

    gitlab_rails['backup_upload_connection'] = {
      :provider => 'Local',
      :local_root => '/mnt/backups'
    }
    
    # 备份文件要复制到的挂载文件夹内的目录
    # 使用 '.' 可将其存储在根目录下
    gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups'
  2. 重新配置 GitLab 以使更改生效。

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

    backup:
      upload:
        # Fog 存储连接设置,详见 https://fog.github.io/storage/ 。
        connection:
          provider: Local
          local_root: '/mnt/backups'
        # 备份文件要复制到的挂载文件夹内的目录
        # 使用 '.' 可将其存储在根目录下
        remote_directory: 'gitlab_backups'
  2. 重启 GitLab 以使更改生效。

备份归档权限

默认情况下,由 GitLab 创建的备份归档(如 1393513186_2014_02_27_gitlab_backup.tar)的所有者/组为 git/git,权限为 0600。这样做的目的是避免其他系统用户读取 GitLab 数据。如果需要备份归档具有不同的权限,你可以使用 archive_permissions 设置。

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

    gitlab_rails['backup_archive_permissions'] = 0644 # 使备份归档对所有人可读
  2. 重新配置 GitLab 以使更改生效。

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

    backup:
      archive_permissions: 0644 # 使备份归档对所有人可读
  2. 重启 GitLab 以使更改生效。

配置 cron 以进行每日备份

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

你可以安排一个 cron 任务来备份你的仓库和 GitLab 元数据。

  1. 编辑 root 用户的 crontab:

    sudo su -
    crontab -e
  2. 在其中添加以下行,安排备份在每天凌晨 2 点运行:

    0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
  1. 编辑 git 用户的 crontab:

    sudo -u git crontab -e
  2. 在末尾添加以下行:

    # 每天凌晨 2 点创建一次完整的 GitLab 仓库和 SQL 数据库备份
    0 2 * * * cd /home/git/gitlab && PATH=/usr/local/bin:/usr/bin:/bin bundle exec rake gitlab:backup:create RAILS_ENV=production CRON=1

CRON=1 环境变量会指示备份脚本在无错误时隐藏所有进度输出,这有助于减少 cron 邮件垃圾。不过在排查备份问题时,可以将 CRON=1 替换为 --trace 来详细记录日志。

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

如果您的备份使用了自定义文件名,本节所述流程将无法生效。

为防止常规备份占满磁盘空间,您可以设置备份的生命周期上限。下次执行备份任务时,早于 backup_keep_time 的备份将被清理。

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

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

    ## 将备份生命周期限制为 7 天 - 604800 秒
    gitlab_rails['backup_keep_time'] = 604800
  2. 重新配置 GitLab 以使更改生效。

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

    backup:
      ## 将备份生命周期限制为 7 天 - 604800 秒
      keep_time: 604800
  2. 重启 GitLab 以使更改生效。

使用 PgBouncer 安装的备份与恢复

切勿通过 PgBouncer 连接进行 GitLab 备份或恢复操作。这些任务必须绕过 PgBouncer 并直接连接到 PostgreSQL 主数据库节点,否则会导致 GitLab 服务中断。

当使用 PgBouncer 执行 GitLab 备份或恢复任务时,会显示以下错误信息:

ActiveRecord::StatementInvalid: PG::UndefinedTable

每次 GitLab 备份运行时,GitLab 会开始产生 500 错误,且关于缺失表的错误会被记录在 PostgreSQL 日志中

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

这是因为任务使用了 pg_dump,它会清除搜索路径并在每条 SQL 查询中显式包含 schema,以应对CVE-2018-1058漏洞。

由于 PgBouncer 在事务池模式下会复用连接,PostgreSQL 无法搜索默认的 public schema。因此,这种搜索路径的清空会导致表和列看似“丢失”。

绕过 PgBouncer

修复此问题的方法有两种:

  1. 通过环境变量覆盖数据库设置来指定备份任务的数据库配置。
  2. 重新配置节点以直接连接到 PostgreSQL 主数据库节点
环境变量覆盖

默认情况下,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 数据库位于不同服务器上,可按如下方式设置:

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

备份 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 实例存在大量 fork 项目,常规备份任务会重复复制所有项目的 Git 数据;
  • GitLab 实例故障,无法使用常规备份与导入 Rake 任务。

Gitaly 集群(Praefect)不支持快照备份

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

  • 切勿用于跨操作系统迁移(如从 Ubuntu 迁移至 RHEL),源与目标操作系统应尽量一致;
  • 数据一致性至关重要。执行文件系统传输(如 rsync)或拍摄快照前,需停止 GitLab(sudo gitlab-ctl stop),确保内存数据全部刷盘。GitLab 由多子系统(Gitaly、数据库、文件存储)组成,各子系统有独立缓冲区、队列与存储层。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 目录不可靠(运行时文件变动过多);
  • 改为创建临时 LVM 快照,并将其作为只读文件系统挂载至 /mnt/gitlab_backup
  • 后续可通过长时间 rsync 作业在远程服务器生成一致副本;
  • 副本包含所有仓库、上传内容及 PostgreSQL 数据。

若在虚拟化服务器运行 GitLab,也可对整个 GitLab 服务器创建 VM 快照。但 VM 快照常需关闭服务器电源,限制了该方案实用性。

单独备份仓库数据

首先,确保在跳过仓库的情况下备份现有 GitLab 数据:

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

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

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

Git 仓库必须以一致的方式复制。如果在并发写操作期间复制仓库,可能会出现不一致或损坏问题。更多细节可参考 issue 270422,其中更详细地解释了潜在问题。

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

  • 使用 维护模式 将 GitLab 置于只读状态。

  • 通过停止所有 Gitaly 服务来创建明确的停机时间,然后再备份仓库:

    sudo gitlab-ctl stop gitaly
    # 执行 Git 数据复制步骤
    sudo gitlab-ctl start gitaly

你可以使用任何方法复制 Git 仓库数据,只要被复制的数据上禁止写入(以避免不一致和损坏问题)。按优先级和安全性的顺序,推荐的方法如下:

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

    rsync -aR --delete --checksum source destination # 注意顺序,若颠倒可能导致现有数据被删除,需额外谨慎
  2. 使用 tar 管道将整个仓库目录复制到另一台服务器或位置

  3. 使用 sftpscpcp 或其他任何复制方法。

通过将仓库标记为只读来创建在线备份(实验性)

一种无需实例全局停机的仓库备份方式是:以编程方式将项目标记为只读,同时复制底层的数据。

这种方法存在一些缺点:

  • 仓库在一段时间内处于只读状态,时长随仓库大小而变化。
  • 由于每个项目都被标记为只读,备份完成时间更长,可能导致不一致。例如,第一个被备份的项目与最后一个被备份的项目之间的可用数据日期可能出现差异。
  • 在项目内部进行备份时,整个派生网络应完全只读,以防止对池仓库的潜在更改。

有一个实验性脚本尝试自动化此过程,位于 Geo 团队 Runbooks 项目