GitLab 官方备份与恢复

本方案基于 GitLab 官方备份与恢复方案。详细信息请参考 GitLab 官方文档

目录

适用范围

本方案仅适用于运行 17.8 版本及以上 且已配置 对象存储 的 GitLab 实例。

如果您的 GitLab 实例尚未配置对象存储,GitLab 提供了 数据迁移方案,您可以参考该方案先完成数据迁移,再使用本方案进行备份与恢复。

如何检查是否配置了对象存储
export GITLAB_NAMESPACE=<待备份 GitLab 实例所在命名空间>
export GITLAB_NAME=<待备份 GitLab 实例名称>

kubectl get gitlabofficial ${GITLAB_NAME} -n ${GITLAB_NAMESPACE} -o jsonpath='{.spec.helmValues.global.appConfig.object_store}'

# 输出示例:
# {"connection":{"key":"connection","secret":"gitlab-rails-storage"},"enabled":true}

enabled 字段为 trueconnection 字段不为空时,表示该实例已配置对象存储。

术语说明

术语定义
源实例备份前的 GitLab 实例
目标实例恢复后的 GitLab 实例
源命名空间源实例所在的命名空间
目标命名空间目标实例所在的命名空间
GitLab CR 资源描述 GitLab 实例部署配置的自定义资源,由 Operator 用于部署 GitLab 实例

前提条件

  1. 部署 MinIO 对象存储:官方备份与恢复方案依赖对象存储保存备份数据,需预先部署 MinIO 实例。ACP 提供了
  2. 安装 mc 命令行工具:mc 是 MinIO 的命令行工具,用于管理 MinIO 实例。安装方法请参考 MinIO 官方文档
  3. 安装 kubectl 命令行工具:kubectl 是 Kubernetes 的命令行工具,用于管理 Kubernetes 集群。安装方法请参考 Kubernetes 官方文档

为方便后续操作,请先设置以下环境变量:

export MINIO_HOST=<MinIO 实例访问地址> # 示例:http://192.168.1.100:32008
export MINIO_ACCESS_KEY=<MinIO 实例访问密钥> # 示例:minioadmin
export MINIO_SECRET_KEY=<MinIO 实例访问密钥密码> # 示例:minioadminpassword
export MINIO_ALIAS_NAME=<MinIO 实例别名> # 示例:myminio

export GITLAB_NAMESPACE=<待备份 GitLab 实例所在命名空间>
export GITLAB_NAME=<待备份 GitLab 实例名称>

执行以下命令配置 mc 命令行工具并测试连接:

mc alias set ${MINIO_ALIAS_NAME} ${MINIO_HOST} ${MINIO_ACCESS_KEY} ${MINIO_SECRET_KEY}
mc ping ${MINIO_ALIAS_NAME}

# 输出示例:
#   1: http://192.168.131.56:32571:32571   min=98.86ms    max=98.86ms    average=98.86ms    errors=0   roundtrip=98.86ms 
#   2: http://192.168.131.56:32571:32571   min=29.57ms    max=98.86ms    average=64.21ms    errors=0   roundtrip=29.57ms 
#   3: http://192.168.131.56:32571:32571   min=29.57ms    max=98.86ms    average=52.77ms    errors=0   roundtrip=29.88ms 

若能成功 ping 通 MinIO 实例,说明 mc 配置正确。

备份

前置准备

创建 Bucket

需创建两个 Bucket:

  1. 用于存储备份数据的 Bucket,命名为:gitlab-backups
  2. 用于备份过程临时数据的 Bucket,命名为:gitlab-backups-tmp

执行以下命令创建 Bucket:

mc mb ${MINIO_ALIAS_NAME}/gitlab-backups
mc mb ${MINIO_ALIAS_NAME}/gitlab-backups-tmp

# 输出示例:
# Bucket created successfully `myminio/gitlab-backups`.
# Bucket created successfully `myminio/gitlab-backups-tmp`.

执行以下命令验证 Bucket 是否创建成功:

mc ls ${MINIO_ALIAS_NAME} | grep gitlab-backups

# 输出示例:
# [2025-06-30 11:58:09 CST]     0B gitlab-backups/
# [2025-06-30 11:58:13 CST]     0B gitlab-backups-tmp/

手动备份

部署 Toolbox 组件

执行官方 GitLab 备份需在 toolbox pod 内运行备份命令,备份文件会上传至对象存储,因此需提前准备对象存储配置文件。运行以下脚本生成所需配置文件:

set -e

if [[ -z "${GITLAB_NAMESPACE}" ]]; then
    echo "GITLAB_NAMESPACE 未设置,请先设置后重试"
    exit 1
fi

CLEAN_HOST=${MINIO_HOST#http://}
CLEAN_HOST=${CLEAN_HOST#https://}
use_https="False"
if [[ $MINIO_HOST == https://* ]]; then
    use_https="True"
fi
 
CONFIG_DATA=$(cat << EOF
[default]
host_base = ${CLEAN_HOST}
host_bucket = ${CLEAN_HOST}/%(bucket)
access_key = ${MINIO_ACCESS_KEY}
secret_key = ${MINIO_SECRET_KEY}
bucket_location = us-east-1
use_https = ${use_https}
EOF
)

kubectl create secret generic s3cfg \
    -n ${GITLAB_NAMESPACE} \
    --from-literal=config="$CONFIG_DATA"

# 输出示例:
# secret/s3cfg created

在 GitLab 所在集群执行以下命令编辑 GitLab 实例 CR:

kubectl edit gitlabofficial ${GITLAB_NAME} -n ${GITLAB_NAMESPACE}

根据注释先编辑以下 yaml 配置,然后添加到 CR 中:

spec:
  helmValues:
    global:
      appConfig:
        backups:
          # 这两个 Bucket 名称必须与前面创建的 Bucket 名称一致
          bucket: gitlab-backups
          tmpBucket: gitlab-backups-tmp
    gitlab:
      toolbox:
        backups:
          objectStorage:
            config:
              key: config
              # 上一步创建的 Secret 名称
              secret: s3cfg
        enabled: true
        persistence:
          accessMode: ReadWriteMany
          enabled: true
          # 备份需要在本地打包所有文件,因此需要足够空间
          # PVC 容量根据实例已用磁盘空间确定
          size: 10Gi
          # 存储类名称
          storageClass: nfs

更新 GitLab CR 后,会自动部署新的 toolbox 组件。使用以下命令检查 toolbox 组件是否部署成功:

kubectl get pod -n ${GITLAB_NAMESPACE} | grep toolbox

# 输出示例:
# xx-gitlab-toolbox-6f578f6b-f7wlw                 1/1     Running       0          4h11m

若 Pod 状态为 Running,表示 toolbox 组件部署成功。

执行备份

使用以下命令进入 toolbox pod 终端环境:

export TOOLBOX_POD_NAME=$(kubectl get pod -n ${GITLAB_NAMESPACE} | grep ${GITLAB_NAME}-toolbox | awk '{print $1}')
kubectl exec -it ${TOOLBOX_POD_NAME} -n ${GITLAB_NAMESPACE} -- bash

执行备份:

cd ~/; nohup backup-utility > output.log 2>&1 &

备份过程需要一定时间,使用以下命令查看备份日志以确认备份进度:

cd ~/; tail -f output.log

# 输出示例:
# Bucket not found: gitlab-packages. Skipping backup of packages ...
# Bucket not found: gitlab-mr-diffs. Skipping backup of external_diffs ...
# Bucket not found: gitlab-terraform-state. Skipping backup of terraform_state ...
# Bucket not found: gitlab-pages. Skipping backup of pages ...
# Bucket not found: gitlab-ci-secure-files. Skipping backup of ci_secure_files ...
# Packing up backup tar
# WARNING: Module python-magic is not available. Guessing MIME types based on file extensions.
# [DONE] Backup can be found at s3://gitlab-backups/1751272314_2025_06_30_17.11.4_gitlab_backup.tar

程序成功完成后,备份结束。

备份 ID

备份文件名根据备份时间和版本信息动态生成,例如 1751272314_2025_06_30_17.11.4_gitlab_backup.tar,其中 1751272314_2025_06_30_17.11.4 是该备份的唯一标识(备份 ID)。执行恢复操作时需提供对应的备份 ID。

定时备份

GitLab 定时备份通过 toolbox 组件完成。与手动备份不同,定时备份使用 Crontab 实现周期性备份。

在 GitLab 所在集群执行以下命令编辑 GitLab 实例 CR:

kubectl edit gitlabofficial ${GITLAB_NAME} -n ${GITLAB_NAMESPACE}

根据注释先编辑以下 yaml 配置,然后添加到 CR 中:

spec:
  helmValues:
    global:
      appConfig:
        backups:
          # 这两个 Bucket 名称必须与前面创建的 Bucket 名称一致
          bucket: gitlab-backups
          tmpBucket: gitlab-backups-tmp
    gitlab:
      toolbox:
        enabled: true
        backups:
          cron:
            enabled: true
            # 配置备份周期
            schedule: "0 1 * * *"
            persistence:
              enabled: true
              accessMode: ReadWriteMany
              # 备份需要在 toolbox pod 内打包所有文件,因此需要足够空间
              # PVC 容量根据实例已用磁盘空间确定
              size: 10Gi
              # 存储类名称
              storageClass: nfs
          objectStorage:
            config:
              key: config
              # 上一步创建的 Secret 名称
              secret: s3cfg
如何配置备份周期

Crontab 规则由五个时间字段组成,字段间以空格分隔,从左至右依次为:

  • 分钟(Minute):任务执行的分钟,范围 0-59
  • 小时(Hour):任务执行的小时,范围 0-23
  • 日期(Day of the month):任务执行的日期,范围 1-31
  • 月份(Month):任务执行的月份,范围 1-12(也可使用缩写,如 Jan、Feb、Mar 等)
  • 星期(Day of the week):任务执行的星期几,范围 0-7(0 和 7 均代表星期日,1 代表星期一,依此类推。也可使用缩写,如 Sun、Mon、Tue 等)

示例:

  1. 每天凌晨 3 点执行:0 3 * * *
  2. 每月 1 日和 15 日凌晨 3 点执行:0 3 1,15 * *
  3. 每 10 分钟执行一次:*/10 * * * *

等待 GitLab 触发备份后,可进行备份文件验证

验证备份文件

执行以下命令检查备份文件是否已上传至对象存储:

mc ls ${MINIO_ALIAS_NAME}/gitlab-backups

# 输出示例:
# [2025-06-30 16:33:18 CST] 570KiB STANDARD 1751272314_2025_06_30_17.8.5_gitlab_backup.tar
# [2025-06-30 18:19:26 CST] 570KiB STANDARD 1751278684_2025_06_30_17.8.5_gitlab_backup.tar

若能成功列出备份文件,说明备份成功。

恢复

前置准备

选择恢复的备份

使用 mc 命令获取备份文件列表:

mc ls ${MINIO_ALIAS_NAME}/gitlab-backups

# 输出示例:
# [2025-06-30 16:33:18 CST] 570KiB STANDARD 1751272314_2025_06_30_17.8.5_gitlab_backup.tar
# [2025-06-30 18:19:26 CST] 570KiB STANDARD 1751278684_2025_06_30_17.8.5_gitlab_backup.tar

根据备份文件名中的日期信息,选择需要恢复的备份,并从备份文件名中复制备份 ID,格式如 1751272314_2025_06_30_17.11.4

确定 GitLab 实例恢复方式

恢复有两种选择:

  1. 直接在源实例上恢复,会用备份数据覆盖实例数据
  2. (推荐)部署新实例进行恢复,新实例需满足以下条件:
    1. 新实例必须启用对象存储
    2. 新实例应部署 toolbox 组件,并配置与源实例相同的对象存储配置

恢复操作

WARNING

以下操作均在目标实例上执行。

请先设置以下环境变量:

export NEW_GITLAB_NAMESPACE=<新 GitLab 实例所在命名空间>
export NEW_GITLAB_NAME=<新 GitLab 实例名称>

停止工作负载

为保证恢复顺利进行,恢复期间需停止 sidekiq 和 webservice 组件。

kubectl annotate deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=sidekiq skip-sync="true"
kubectl scale deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=sidekiq --replicas=0
kubectl annotate deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=webservice skip-sync="true"
kubectl scale deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=webservice --replicas=0

# 输出示例:
# deployment.apps/xx-gitlab-sidekiq-all-in-1-v2 annotated
# deployment.apps/xx-gitlab-sidekiq-all-in-1-v2 scaled
# deployment.apps/xx-gitlab-webservice-default annotated
# deployment.apps/xx-gitlab-webservice-default scaled

执行恢复

使用以下命令进入 toolbox pod 终端:

export NEW_TOOLBOX_POD_NAME=$(kubectl get pod -n ${NEW_GITLAB_NAMESPACE} | grep ${NEW_GITLAB_NAME}-toolbox | awk '{print $1}')
kubectl exec -it ${NEW_TOOLBOX_POD_NAME} -n ${NEW_GITLAB_NAMESPACE} -- bash
DANGER

恢复数据库前会删除所有现有表,以避免后续升级问题。请注意,如果 GitLab 数据库中有自定义表,这些表及其所有数据将被删除。

在终端执行以下命令进行恢复,backup ID 替换为所需的备份 ID,如 1751272314_2025_06_30_17.11.4

export BACKUP_ID=<backup ID>
rm -rf /srv/gitlab/tmp/*
backup-utility --restore -t ${BACKUP_ID}
 
# 输出示例:
# 2025-06-30 15:33:53 UTC -- [DONE]
# 2025-06-30 15:33:53 UTC -- Source backup for the database ci doesn't exist. Skipping the task
# 2025-06-30 15:33:53 UTC -- Restoring database ... done
# 2025-06-30 15:33:53 UTC -- Deleting backup and restore PID file at [/srv/gitlab/tmp/backup_restore.pid] ... done
# 2025-06-30 15:34:12 UTC -- Restoring repositories ... 
# 2025-06-30 15:34:12 UTC -- Restoring repositories ... done
# 2025-06-30 15:34:12 UTC -- Deleting backup and restore PID file at [/srv/gitlab/tmp/backup_restore.pid] ... done

程序成功完成后,恢复结束。

新实例恢复注意事项

若在新部署实例上恢复,完成上述步骤后,还需执行以下操作:

恢复 Rails Secrets

需将新实例的 Rails secrets 更新为与源实例一致。

从源实例获取 Rails secrets 内容:

kubectl get secret ${GITLAB_NAME}-rails-secret -n ${GITLAB_NAMESPACE} -o jsonpath='{.data.secrets\.yml}' | base64 --decode

更新新实例的 Rails secrets,具体操作请参考 如何设置 Rails Secrets

切换域名

若源实例使用域名部署,恢复到新实例后需将新实例的域名切换为源实例的域名,以恢复业务访问。请参考 配置实例网络访问 修改实例访问域名。

恢复工作负载

恢复期间停止的 sidekiq 和 webservice 组件需在恢复完成后重启:

kubectl annotate deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=sidekiq skip-sync-
kubectl scale deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=sidekiq --replicas=1
kubectl annotate deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=webservice skip-sync-
kubectl scale deployment -n ${NEW_GITLAB_NAMESPACE} -l release=${NEW_GITLAB_NAME},app=webservice --replicas=1

# 输出示例:
# deployment.apps/xx-gitlab-sidekiq-all-in-1-v2 annotated
# deployment.apps/xx-gitlab-sidekiq-all-in-1-v2 scaled
# deployment.apps/xx-gitlab-webservice-default annotated
# deployment.apps/xx-gitlab-webservice-default scaled

验证恢复

等待实例状态恢复正常后,登录 GitLab 检查数据是否恢复成功。检查项包括但不限于:

  • 仓库
  • 用户
  • 合并请求