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

备份和恢复大型参考架构

  • Tier: 免费版、专业版、旗舰版
  • Offering: GitLab 自管理版

GitLab 备份可确保数据一致性,并为大规模 GitLab 部署提供灾难恢复功能。此过程:

  • 协调分布式存储组件的数据备份
  • 保存大小可达数 TB 的 PostgreSQL 数据库
  • 保护外部服务中的对象存储数据
  • 维护大型 Git 仓库集合的备份完整性
  • 创建配置和机密文件的可恢复副本
  • 支持在最小停机时间内恢复系统数据

请遵循以下步骤,为运行支持 3,000+ 用户参考架构的 GitLab 环境进行操作,特别考虑基于云的数据库和对象存储。

本文档适用于使用以下环境的情况:

配置每日备份

配置 PostgreSQL 数据备份

备份命令 使用 pg_dump,它不适合超过 100 GB 的数据库。您必须选择具有原生、强大备份功能的 PostgreSQL 解决方案。

  1. 配置 AWS Backup 来备份 RDS(和 S3)数据。为获得最大保护,同时配置持续备份和快照备份
  2. 配置 AWS Backup 将备份复制到单独的区域。当 AWS 进行备份时,备份只能在存储备份的区域中恢复。
  3. 在 AWS Backup 至少运行一次计划备份后,您可以根据需要创建按需备份

安排 Google Cloud SQL 数据的自动每日备份。 每日备份可以保留长达一年,事务日志默认可以保留 7 天以支持时间点恢复。

配置对象存储数据备份

推荐使用对象存储而非 NFS)存储 GitLab 数据,包括 blobs容器注册表

配置 AWS Backup 来备份 S3 数据。这可以在配置 PostgreSQL 数据备份的同时完成。

  1. 在 GCS 中创建备份存储桶

  2. 创建存储传输服务作业,将每个 GitLab 对象存储存储桶复制到备份存储桶。您可以创建这些作业一次,并安排它们每天运行。但这会混合新旧对象存储数据,因此在 GitLab 中删除的文件在备份中仍然存在。这会在恢复后浪费存储空间,但除此之外没有问题。这些文件对 GitLab 用户不可访问,因为它们在 GitLab 数据库中不存在。您可以在恢复后删除一些这些孤立文件,但此清理 Rake 任务仅对部分文件进行操作。

    1. 对于"何时覆盖",选择"永不"。GitLab 对象存储文件应该是不可变的。如果恶意行为者成功更改了 GitLab 文件,此选择可能会有所帮助。
    2. 对于"何时删除",选择"永不"。如果您将备份存储桶同步到源,那么如果文件被意外或恶意删除,您将无法恢复。
  3. 或者,可以将对象存储备份到按日期分隔的存储桶或子目录中。这避免了恢复后孤立文件的问题,并在需要时支持文件版本备份。但这会大大增加备份存储成本。这可以通过由 Cloud Scheduler 触发的 Cloud Function或由 cronjob 运行的脚本来完成。部分示例:

    # 设置 GCP 项目,这样您就不必在每个命令中指定它
    gcloud config set project example-gcp-project-name
    
    # 授予存储传输服务的隐藏服务帐户写入备份存储桶的权限。整数 123456789012 是 GCP 项目的 ID。
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.objectAdmin gs://backup-bucket
    
    # 授予存储传输服务的隐藏服务帐户列出和读取源存储桶中对象的权限。整数 123456789012 是 GCP 项目的 ID。
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-artifacts
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-ci-secure-files
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-dependency-proxy
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-lfs
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-mr-diffs
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-packages
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-pages
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-registry
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-terraform-state
    gsutil iam ch serviceAccount:project-123456789012@storage-transfer-service.iam.gserviceaccount.com:roles/storage.legacyBucketReader,roles/storage.objectViewer gs://gitlab-bucket-uploads
    
    # 为每个存储桶创建传输作业,目标是备份存储桶中的子目录。
    today=$(date +%F)
    gcloud transfer jobs create gs://gitlab-bucket-artifacts/ gs://backup-bucket/$today/artifacts/ --name "$today-backup-artifacts"
    gcloud transfer jobs create gs://gitlab-bucket-ci-secure-files/ gs://backup-bucket/$today/ci-secure-files/ --name "$today-backup-ci-secure-files"
    gcloud transfer jobs create gs://gitlab-bucket-dependency-proxy/ gs://backup-bucket/$today/dependency-proxy/ --name "$today-backup-dependency-proxy"
    gcloud transfer jobs create gs://gitlab-bucket-lfs/ gs://backup-bucket/$today/lfs/ --name "$today-backup-lfs"
    gcloud transfer jobs create gs://gitlab-bucket-mr-diffs/ gs://backup-bucket/$today/mr-diffs/ --name "$today-backup-mr-diffs"
    gcloud transfer jobs create gs://gitlab-bucket-packages/ gs://backup-bucket/$today/packages/ --name "$today-backup-packages"
    gcloud transfer jobs create gs://gitlab-bucket-pages/ gs://backup-bucket/$today/pages/ --name "$today-backup-pages"
    gcloud transfer jobs create gs://gitlab-bucket-registry/ gs://backup-bucket/$today/registry/ --name "$today-backup-registry"
    gcloud transfer jobs create gs://gitlab-bucket-terraform-state/ gs://backup-bucket/$today/terraform-state/ --name "$today-backup-terraform-state"
    gcloud transfer jobs create gs://gitlab-bucket-uploads/ gs://backup-bucket/$today/uploads/ --name "$today-backup-uploads"
    1. 这些传输作业在运行后不会自动删除。您可以在脚本中实现旧作业的清理。
    2. 示例脚本不会删除旧备份。您可以根据所需的保留策略实现旧备份的清理。
  4. 确保备份在 Cloud SQL 备份的同时或之后进行,以减少数据不一致性。

配置 Git 仓库备份

设置 cronjobs 来执行 Gitaly 服务器端备份:

  1. 按照配置服务器端备份的说明,在所有 Gitaly 节点上配置 Gitaly 服务器端备份目标。 此存储桶专门由 Gitaly 用于存储仓库数据。

  2. 虽然 Gitaly 将所有 Git 仓库数据备份到先前配置的指定对象存储存储桶中, 但备份实用工具(gitlab-backup)会将额外的备份数据上传到单独的存储桶。这些数据包括一个包含恢复所需基本元数据的 tar 文件。 按照将备份上传到远程(云)存储的说明,确保此备份数据正确上传到远程(云)存储,以设置上传存储桶。

  3. (可选)为了加强此备份数据的持久性,通过将先前配置的两个存储桶添加到对象存储数据备份中,使用各自的对象存储提供程序进行备份。

  4. SSH 进入 GitLab Rails 节点,即运行 Puma 或 Sidekiq 的节点。

  5. 对您的 Git 数据进行完整备份。使用 REPOSITORIES_SERVER_SIDE 变量,并跳过 PostgreSQL 数据:

    sudo gitlab-backup create REPOSITORIES_SERVER_SIDE=true SKIP=db

    这会导致 Gitaly 节点将 Git 数据和一些元数据上传到远程存储。上传、artifacts 和 LFS 等 blobs 不需要明确跳过,因为 gitlab-backup 命令默认不备份对象存储。

  6. 记下备份的备份 ID,这是下一步所需的。例如,如果备份命令输出 2024-02-22 02:17:47 UTC -- Backup 1708568263_2024_02_22_16.9.0-ce is done.,那么备份 ID 是 1708568263_2024_02_22_16.9.0-ce

  7. 检查完整备份是否在 Gitaly 备份存储桶和常规备份存储桶中都创建了数据。

  8. 再次运行备份命令,这次指定Git 仓库的增量备份和备份 ID。使用上一步的示例 ID,命令为:

    sudo gitlab-backup create REPOSITORIES_SERVER_SIDE=true SKIP=db INCREMENTAL=yes PREVIOUS_BACKUP=1708568263_2024_02_22_16.9.0-ce

    PREVIOUS_BACKUP 的值不被此命令使用,但命令需要它。有一个关于移除这个不必要要求的议题,请参阅issue 429141

  9. 检查增量备份是否成功,并向对象存储添加了数据。

  10. 配置 cron 以进行每日备份。编辑 root 用户的 crontab:

    sudo su -
    crontab -e
  11. 在其中添加以下行,以安排每天每月 2 点进行备份。为了限制恢复备份所需的增量数量,将在每月第一天对 Git 仓库进行完整备份,其余天数将进行增量备份。:

    0 2 1 * * /opt/gitlab/bin/gitlab-backup create REPOSITORIES_SERVER_SIDE=true SKIP=db CRON=1
    0 2 2-31 * * /opt/gitlab/bin/gitlab-backup create REPOSITORIES_SERVER_SIDE=true SKIP=db INCREMENTAL=yes PREVIOUS_BACKUP=1708568263_2024_02_22_16.9.0-ce CRON=1
  1. 按照 配置服务器端备份的说明,在所有 Gitaly 节点上配置 Gitaly 服务器端备份目标。此存储桶专门由 Gitaly 用于存储仓库数据。

  2. 虽然 Gitaly 将所有 Git 仓库数据备份到先前配置的指定对象存储存储桶中, 但备份实用工具(gitlab-backup)会将额外的备份数据上传到单独的存储桶。这些数据包括一个包含恢复所需基本元数据的 tar 文件。 按照将备份上传到远程(云)存储的说明,确保此备份数据正确上传到远程(云)存储,以设置上传存储桶。

  3. (可选)为了加强此备份数据的持久性,可以通过将先前配置的两个存储桶添加到 对象存储数据备份中,使用各自的对象存储提供程序进行备份。

  4. 按照配置对象存储数据备份的说明,确保两个存储桶都已备份。先前配置的 两个存储桶也应由各自的对象存储提供程序进行备份。

  5. SSH 进入 GitLab Rails 节点,即运行 Puma 或 Sidekiq 的节点。

  6. 对您的 Git 数据进行完整备份。使用 REPOSITORIES_SERVER_SIDE 变量并跳过所有其他数据:

    kubectl exec <Toolbox pod name> -it -- backup-utility --repositories-server-side --skip db,builds,pages,registry,uploads,artifacts,lfs,packages,external_diffs,terraform_state,pages,ci_secure_files

    这会导致 Gitaly 节点将 Git 数据和一些元数据上传到远程存储。请参阅Toolbox 包含的工具

  7. 检查完整备份是否在 Gitaly 备份存储桶和常规备份存储桶中都创建了数据。backup-utility 不支持使用服务器端仓库备份进行增量仓库备份,请参阅charts issue 3421

  8. 配置 cron 以进行每日备份。具体来说,设置 gitlab.toolbox.backups.cron.extraArgs 以包含:

    --repositories-server-side --skip db --skip repositories --skip uploads --skip builds --skip artifacts --skip pages --skip lfs --skip terraform_state --skip registry --skip packages --skip ci_secure_files

配置配置文件备份

如果您的配置和机密在部署之外定义,然后部署到其中,那么备份策略的实现取决于您的具体设置和要求。例如,您可以将机密存储在 AWS Secret Manager 中,并复制到多个区域,并配置脚本自动备份机密。

如果您的配置和机密仅在部署内部定义:

  1. 存储配置文件 描述了如何提取配置和机密文件。
  2. 这些文件应上传到单独的、更受限制的对象存储账户。

恢复备份

恢复 GitLab 实例的备份。

先决条件

恢复备份之前:

  1. 选择一个可工作的目标 GitLab 实例

  2. 确保目标 GitLab 实例位于存储 AWS 备份的区域中。

  3. 检查目标 GitLab 实例使用与创建备份数据时完全相同版本和类型(CE 或 EE)的 GitLab。 例如,CE 15.1.4。

  4. 将备份的机密恢复到目标 GitLab 实例

  5. 确保目标 GitLab 实例配置了相同的仓库存储。 额外的存储是可以的。

  6. 确保已配置对象存储

  7. 要使用新的机密或配置,并避免在恢复过程中处理任何意外的配置更改:

    • 所有节点上的 Linux 软件包安装:

      1. 重新配置目标 GitLab 实例。
      2. 重新启动目标 GitLab 实例。
    • Helm chart (Kubernetes) 安装:

      1. 在所有 GitLab Linux 软件包节点上,运行:

        sudo gitlab-ctl reconfigure
        sudo gitlab-ctl start
      2. 通过部署 charts 确保您有一个正在运行的 GitLab 实例。 通过执行以下命令确保 Toolbox pod 已启用并正在运行:

        kubectl get pods -lrelease=RELEASE_NAME,app=toolbox
      3. 必须重新启动 Webservice、Sidekiq 和 Toolbox pod。 重新启动这些 pod 的最安全方法是运行:

        kubectl delete pods -lapp=sidekiq,release=<helm release name>
        kubectl delete pods -lapp=webservice,release=<helm release name>
        kubectl delete pods -lapp=toolbox,release=<helm release name>
  8. 确认目标 GitLab 实例仍然正常工作。例如:

  9. 停止连接到 PostgreSQL 数据库的 GitLab 服务。

    • 在所有运行 Puma 或 Sidekiq 的节点上的 Linux 软件包安装,运行:

      sudo gitlab-ctl stop
    • Helm chart (Kubernetes) 安装:

      1. 记录数据库客户端的当前副本数以便后续重新启动:

        kubectl get deploy -n <namespace> -lapp=sidekiq,release=<helm release name> -o jsonpath='{.items[].spec.replicas}{"\n"}'
        kubectl get deploy -n <namespace> -lapp=webservice,release=<helm release name> -o jsonpath='{.items[].spec.replicas}{"\n"}'
        kubectl get deploy -n <namespace> -lapp=prometheus,release=<helm release name> -o jsonpath='{.items[].spec.replicas}{"\n"}'
      2. 停止数据库的客户端,以防止锁干扰恢复过程:

        kubectl scale deploy -lapp=sidekiq,release=<helm release name> -n <namespace> --replicas=0
        kubectl scale deploy -lapp=webservice,release=<helm release name> -n <namespace> --replicas=0
        kubectl scale deploy -lapp=prometheus,release=<helm release name> -n <namespace> --replicas=0

恢复对象存储数据

每个存储桶在 AWS 中作为单独的备份存在,每个备份可以恢复到现有或 新的存储桶。

  1. 要恢复存储桶,需要具有正确权限的 IAM 角色:

    • AWSBackupServiceRolePolicyForBackup
    • AWSBackupServiceRolePolicyForRestores
    • AWSBackupServiceRolePolicyForS3Restore
    • AWSBackupServiceRolePolicyForS3Backup
  2. 如果使用现有存储桶,它们必须 启用访问控制列表

  3. 使用内置工具恢复 S3 存储桶

  4. 在恢复作业运行时,您可以继续进行恢复 PostgreSQL 数据

  1. 创建存储传输服务作业将备份数据传输到 GitLab 存储桶。
  2. 在传输作业运行时,您可以继续进行恢复 PostgreSQL 数据

恢复 PostgreSQL 数据

  1. 使用内置工具恢复 AWS RDS 数据库, 这会创建一个新的 RDS 实例。

  2. 由于新的 RDS 实例具有不同的端点,您必须重新配置目标 GitLab 实例 以指向新数据库:

  3. 在继续之前,等待新的 RDS 实例创建完成并准备就绪。

  1. 使用内置工具恢复 Google Cloud SQL 数据库

  2. 如果您恢复到新的数据库实例,则重新配置 GitLab 以指向新数据库:

  3. 在继续之前,等待 Cloud SQL 实例准备就绪。

恢复 Git 仓库

首先,作为恢复对象存储数据的一部分,您应该已经:

  • 恢复了包含 Git 仓库的 Gitaly 服务器端备份的存储桶。
  • 恢复了包含 *_gitlab_backup.tar 文件的存储桶。
  1. SSH 进入 GitLab Rails 节点,即运行 Puma 或 Sidekiq 的节点。

  2. 在您的备份存储桶中,根据时间戳选择一个 *_gitlab_backup.tar 文件,与您恢复的 PostgreSQL 和对象存储数据对齐。

  3. tar 文件下载到 /var/opt/gitlab/backups/ 中。

  4. 恢复备份,指定您希望恢复的备份的 ID,从名称中省略 _gitlab_backup.tar

    # 此命令将覆盖您的 GitLab 数据库的内容!
    sudo gitlab-backup restore BACKUP=11493107454_2018_04_25_10.6.4-ce SKIP=db

    如果您的备份 tar 文件与安装的 GitLab 版本之间存在 GitLab 版本不匹配, 恢复命令将中止并显示错误消息。 安装正确的 GitLab 版本,然后重试。

  5. 重新配置、启动并检查 GitLab:

    1. 在所有 PostgreSQL 节点上,运行:

      sudo gitlab-ctl reconfigure
    2. 在所有 Puma 或 Sidekiq 节点上,运行:

      sudo gitlab-ctl start
    3. 在一个 Puma 或 Sidekiq 节点上,运行:

      sudo gitlab-rake gitlab:check SANITIZE=true
  6. 检查 数据库值是否可以解密 特别是如果 /etc/gitlab/gitlab-secrets.json 已恢复,或者如果不同的服务器是 恢复的目标:

    在 Puma 或 Sidekiq 节点上,运行:

    sudo gitlab-rake gitlab:doctor:secrets
  7. 为了增加保证,您可以执行 上传文件的完整性检查

    在 Puma 或 Sidekiq 节点上,运行:

    sudo gitlab-rake gitlab:artifacts:check
    sudo gitlab-rake gitlab:lfs:check
    sudo gitlab-rake gitlab:uploads:check

    如果发现缺失或损坏的文件,并不总是意味着备份和恢复过程失败。 例如,这些文件可能在源 GitLab 实例上就缺失或损坏。您可能需要交叉引用先前的备份。 如果您正在将 GitLab 迁移到新环境,您可以在源 GitLab 实例上运行相同的检查,以确定 完整性检查结果是预先存在的还是与备份和恢复过程相关。

  1. SSH 进入 toolbox pod。

  2. 在您的备份存储桶中,根据时间戳选择一个 *_gitlab_backup.tar 文件,与您恢复的 PostgreSQL 和对象存储数据对齐。

  3. tar 文件下载到 /var/opt/gitlab/backups/ 中。

  4. 恢复备份,指定您希望恢复的备份的 ID,从名称中省略 _gitlab_backup.tar

    # 此命令将覆盖 Gitaly 的内容!
    kubectl exec <Toolbox pod name> -it -- backup-utility --restore -t 11493107454_2018_04_25_10.6.4-ce --skip db,builds,pages,registry,uploads,artifacts,lfs,packages,external_diffs,terraform_state,pages,ci_secure_files

    如果您的备份 tar 文件与安装的 GitLab 版本之间存在 GitLab 版本不匹配, 恢复命令将中止并显示错误消息。 安装正确的 GitLab 版本,然后重试。

  5. 重新启动并检查 GitLab:

    1. 启动已停止的部署,使用先决条件中记录的副本数:

      kubectl scale deploy -lapp=sidekiq,release=<helm release name> -n <namespace> --replicas=<original value>
      kubectl scale deploy -lapp=webservice,release=<helm release name> -n <namespace> --replicas=<original value>
      kubectl scale deploy -lapp=prometheus,release=<helm release name> -n <namespace> --replicas=<original value>
    2. 在 Toolbox pod 中,运行:

      sudo gitlab-rake gitlab:check SANITIZE=true
  6. 检查 数据库值是否可以解密 特别是如果 /etc/gitlab/gitlab-secrets.json 已恢复,或者如果不同的服务器是 恢复的目标:

    在 Toolbox pod 中,运行:

    sudo gitlab-rake gitlab:doctor:secrets
  7. 为了增加保证,您可以执行 上传文件的完整性检查

    这些命令可能需要很长时间,因为它们会遍历所有行。因此,在 GitLab Rails 节点上运行以下命令, 而不是在 Toolbox pod 中:

    sudo gitlab-rake gitlab:artifacts:check
    sudo gitlab-rake gitlab:lfs:check
    sudo gitlab-rake gitlab:uploads:check

    如果发现缺失或损坏的文件,并不总是意味着备份和恢复过程失败。 例如,这些文件可能在源 GitLab 实例上就缺失或损坏。您可能需要交叉引用先前的备份。 如果您正在将 GitLab 迁移到新环境,您可以在源 GitLab 实例上运行相同的检查,以确定 完整性检查结果是预先存在的还是与备份和恢复过程相关。

恢复应该已完成。