GitLab备份脚本踩坑全记录:从基础到生产级的7天优化之旅
作为一名运维工程师,我一直认为备份是所有系统中最重要的环节。没有可靠的备份,再稳定的系统也只是空中楼阁。最近我在为公司的GitLab服务器配置备份脚本时,遇到了一系列看似简单却又非常棘手的问题。这篇文章记录了我从一个基础脚本开始,经过7天的踩坑和优化,最终得到一个生产级备份方案的完整过程。
初始需求与基础脚本
我们的GitLab服务器使用Omnibus方式安装,需要实现以下备份需求:
- 每天自动执行GitLab完整备份
- 将备份文件和敏感配置文件复制到统一的备份目录
- 将备份文件同步到另一台远程服务器(双备份)
- 自动清理超过7天的旧备份
基于这些需求,我写了一个最基础的备份脚本:
1 |
|
看起来很简单对不对?但当我把这个脚本配置到cron定时任务中后,第一个问题出现了。
第一个坑:sudo需要终端输入密码
第二天早上查看日志,发现脚本执行失败,错误信息如下:
1 | sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper |
问题分析
这个错误非常常见:当脚本在非交互式环境(如cron定时任务)中执行sudo命令时,无法弹出终端让用户输入密码。
解决方案
我提供了三种解决方案,按安全性从高到低排序:
- 配置sudo免密码(推荐):只给特定用户执行特定命令的免密码权限
- 直接用root用户运行脚本:最简单,但安全性稍差
- 使用sudo -S选项:将密码明文写在脚本中,安全性极差
我最初选择了方案一,并且做了一个优化:不是给一堆零散的命令免密码权限,而是只给node用户执行这一个备份脚本的权限。这样脚本本身只有root能修改,安全性极高。
1 | # 编辑sudoers文件 |
第二个优化:增量复制,避免重复拷贝
解决了sudo的问题后,脚本可以正常运行了。但我发现一个问题:每次执行脚本时,cp命令都会强制覆盖所有已经存在的备份文件,不管它们是否相同。这对于几十GB大小的GitLab备份来说,非常浪费时间和磁盘IO。
解决方案
将所有cp命令替换为rsync --update命令。rsync的--update参数会自动比较文件的修改时间和大小,只复制:
- 目标目录不存在的新文件
- 源文件比目标文件更新的文件
- 完全跳过已存在且内容/时间相同的文件
1 | # 原来的cp命令 |
这个优化让备份速度提升了10倍以上,几乎不消耗额外的资源。
第三个需求:只复制最近2天的备份文件
随着备份文件越来越多,我又提出了一个新需求:/var/opt/gitlab/backups目录中的tar文件如果大于2天就不要复制了。因为GitLab默认会保留7天的备份,但我们只需要把最近2天的新备份复制到统一目录即可。
第一次尝试:使用–max-age参数
我首先想到的是在rsync命令中添加--max-age=2d参数,这个参数可以只复制修改时间在指定天数以内的文件。
1 | rsync -av --update --max-age=2d \ |
踩坑:rsync 3.1.3不支持–max-age
但执行脚本时又报错了:
1 | rsync: --max-age=2d: unknown option |
原来我们服务器上的rsync版本是3.1.3(Ubuntu 20.04默认版本),这个旧版本不支持--max-age参数带单位的写法。我尝试将天数转换为秒数:--max-age=172800,但发现rsync 3.1.3完全不支持--max-age参数,这个参数是在rsync 3.2.0版本才新增的。
最终方案:find + rsync –files-from
对于旧版本rsync,实现按时间筛选的标准方法是先用find命令找出符合条件的文件,然后用rsync的--files-from参数只同步这些文件。
1 | # 第一步:用find找出2天以内的所有.tar文件 |
这个方案完全兼容所有rsync版本,完美解决了问题。
第四个大坑:GitLab备份必须用纯root用户运行
就在我以为一切都搞定了的时候,第二天查看日志,发现脚本又失败了,而且出现了两个新的错误:
1 | tee: /ceni/data/gitlab-backup/logs/gitlab_backup.log: Permission denied |
问题分析
这两个错误都是权限问题,而且是连锁反应:
- tee权限错误:即使通过sudo执行脚本,
tee命令仍然会以原始用户(node)身份写入日志文件,而node没有日志目录的写入权限 - chpst错误:这是最关键的问题。GitLab Omnibus安装的
gitlab-backup命令有一个特殊限制:它必须以真正的root用户运行,不能通过sudo从普通用户切换。
当你用node用户sudo gitlab-backup create时:
- sudo会保留部分node用户的环境变量
- GitLab内部的
chpst命令会尝试切换到git用户 - 但由于环境变量不纯净,chpst无法访问
/opt/gitlab/etc/gitlab-rails/env目录 - 这是GitLab一个已知的、长期存在的问题
最终解决方案
直接用root用户运行脚本,彻底解决所有权限问题。这也是GitLab官方推荐的备份方式。
步骤1:修改脚本,移除所有sudo依赖
1 |
|
步骤2:迁移SSH密钥到root用户
1 | # 复制node用户的密钥到root目录 |
步骤3:修改定时任务
1 | # 删除node用户的旧定时任务 |
最终的生产级备份脚本特性
经过这一系列的踩坑和优化,我们最终得到了一个功能完善、稳定可靠的生产级GitLab备份脚本,它具备以下特性:
- ✅ 自动生成GitLab完整备份
- ✅ 自动备份敏感配置文件(gitlab.rb和gitlab-secrets.json)
- ✅ 增量复制,只复制新增/修改过的文件
- ✅ 只复制最近2天的新备份文件
- ✅ 自动清理超过30天的旧备份
- ✅ 自动同步到远程服务器(双备份)
- ✅ 完整的日志记录
- ✅ 严格的错误处理
- ✅ 兼容旧版本rsync
- ✅ 彻底解决所有权限问题
总结与最佳实践
通过这次GitLab备份脚本的优化之旅,我总结出以下几点最佳实践:
- GitLab备份必须用纯root用户运行:不要尝试通过sudo从普通用户切换,这会导致各种奇怪的权限问题
- 优先使用rsync代替cp:rsync的增量复制功能可以节省大量时间和资源
- 注意软件版本差异:很多看似标准的参数在旧版本中可能不存在
- 备份必须包含敏感配置文件:没有gitlab.rb和gitlab-secrets.json,备份文件是无法恢复的
- 一定要配置异地备份:本地备份无法应对服务器硬件故障
- 定期测试备份恢复:备份的最终目的是恢复,只有成功恢复过的备份才是有效的
备份是一件”平时看不见,出事要人命”的事情。花一点时间把备份脚本写好、测试好,绝对是值得的。希望这篇文章能帮助到同样在配置GitLab备份的朋友们,避免踩我踩过的这些坑。
- 本文作者: Linking
- 本文链接: https://linking.fun/2026/07/01/GitLab备份脚本踩坑全记录:从基础到生产级的7天优化之旅/
- 版权声明: 版权所有,转载请注明出处!