GitLab备份、恢复与迁移实战:从数据构成到完整演练指南
GitLab的备份、恢复和迁移说简单很简单无非一条命令打个tar包恢复时restore一下说麻烦也真的麻烦。我见过太多团队数据没丢时信誓旦旦真到服务器故障或者换机器那天才发现备份文件是残的、版本不匹配、密钥丢了、恢复完首页能开但项目全是404。GitLab这东西日常用着像个黑盒子代码一推一拉没什么感觉可它的内部结构比普通应用复杂得多——PostgreSQL数据库、裸仓库、LFS对象、容器镜像、CI/CD流水线记录还有一堆上传附件全揉在一起任何一个环节断掉恢复出来的都是一具残缺的骨架。这篇文章不打算讲那种“照着官方文档敲三条命令”的流水账而是把我自己从备份规划、定时任务、恢复演练到跨机器迁移整条链路踩过的坑和验证过的方法摊开说。无论你是刚用Omnibus包装好GitLab的小团队还是已经在生产环境跑了两三年的运维只要涉及“别让代码数据死掉”这件事这篇都能给你一些直接能用的东西。1. 先把GitLab的数据构成盘清楚1.1 除了仓库还有哪些东西隐藏在备份包里很多人以为GitLab备份就是“把代码仓库打个包”这是最大的误解。GitLab的完整数据至少包括这么几类PostgreSQL数据库存项目元数据、Issue、Merge Request、用户、权限、Webhook、CI/CD变量几乎一切“非仓库内容”都在这套数据库里。Git仓库数据默认在/var/opt/gitlab/git-data/repositories按命名空间和项目名组织。上传的文件头像、附件、设计稿、导入的归档文件等默认在/var/opt/gitlab/uploads。LFS对象大文件存储路径在/var/opt/gitlab/gitlab-lfs。Container Registry镜像如果开了GitLab自带的镜像仓库组件这个也占大量空间。CI/CD流水线记录构建日志、Artifacts在/var/opt/gitlab/gitlab-rails/shared/artifacts。配置文件/etc/gitlab/gitlab.rb和/etc/gitlab/gitlab-secrets.json这两个文件极其关键但不在备份tar包里面。官方那条gitlab-backup create命令默认会把数据库、仓库、上传文件、LFS、容器镜像等打包进一个带时间戳的tar包路径通常在/var/opt/gitlab/backups。这么说吧它打包的是“运行数据”但配置类和密钥类文件是单独躺在/etc/gitlab下面的恢复时如果不同时还原这两个文件就会出现“数据回来了但服务起不来”或者“Runner失联、2FA失效”这类诡异问题。1.2 备份迁移前必须想清楚的三个原则在做任何备份、恢复、迁移动作之前我建议先在脑子里过一遍这三个原则它们是后面所有操作的底层逻辑。第一版本一致性原则。GitLab备份包和恢复目标实例的主流大版本必须一致至少主版本号要匹配。比如你用14.10备份出来的包非要恢复到16.x官方文档说只能沿大版本逐步升级恢复直接跨版本恢复基本就是自找麻烦。迁移时最稳妥的办法是先让新旧两台机器跑完全相同的版本再动备份包。第二先备份再操作原则。升级、迁移、恢复实验任何动作之前先做一次全新备份并且确认备份文件完整、可读。遇到过太多人拿着三天前的备份想恢复结果发现那天的备份因为磁盘满了只打了半包。第三恢复验证原则。备份的最终目的是“恢复出来能用”不是“生成了一个文件”。所以每次备份完之后至少要在测试环境做一次恢复演练确认项目能打开、代码能clone、Issue页面能渲染、CI能正常触发。这个原则看上去多此一举但真能在关键时刻救你一回。2. GitLab备份实操那条命令背后的完整机制2.1 备份命令的版本差异与标准用法GitLab的备份命令在不同版本里有个名字变化。老版本用的是gitlab-rake gitlab:backup:create新版本大概从12.x之后推荐用gitlab-backup create本质是同一个Rake任务的封装。我自己惯用的完整命令是sudo gitlab-backup create STRATEGYcopy GZIP_RSYNCABLEyes这里解释一下两个参数为什么值得加STRATEGYcopyGitLab备份时默认会直接读取当前数据目录生产环境仓库正在被访问时直接打tar容易出现文件状态不一致的情况。这个参数会让GitLab先把数据复制到临时目录再基于副本打包虽然会多占一份磁盘空间但保的是数据一致性我认为这笔投入值得。GZIP_RSYNCABLEyes让生成的tar包内部用rsyncable方式压缩这样未来用rsync做增量同步、断点续传时能省很多事。执行完后在/var/opt/gitlab/backups下会生成类似1700000000_2023_11_04_16.10.5_gitlab_backup.tar的文件。前面那串数字是时间戳中间的版本号是备份时GitLab的版本。这个版本号特别重要恢复时一定要对着它。2.2 配置文件单独备份这件事决定你能不能恢复gitlab-backup create不会备份/etc/gitlab/gitlab.rb和/etc/gitlab/gitlab-secrets.json这是个很容易被忽略但后果严重的点。gitlab.rb是GitLab的一切配置external_url、数据库连接、邮件服务、LDAP、备份路径全在里面。gitlab-secrets.json则存着各种加密密钥——数据库加密密钥、OTP密钥、CI/CD Runner的共享密钥、第三方集成的密钥。恢复备份时如果这个文件和新环境不匹配会出现这些现象CI/CD Runner连接失败提示403或者Failed to authorize。启用了2FA的用户全部无法验证。导入导出项目时提示加密密钥不一致。“记住我”的Session失效。所以我的习惯是每次备份完数据的同时立刻把这两个文件单独拷贝走sudo cp /etc/gitlab/gitlab.rb /var/opt/gitlab/backups/gitlab.rb.$(date %F) sudo cp /etc/gitlab/gitlab-secrets.json /var/opt/gitlab/backups/gitlab-secrets.json.$(date %F)如果备份目录在本地磁盘我还会用scp或rsync把它们同步到另一台机器因为这两份文件体积很小与其躺在本地等磁盘一起坏掉不如放异地。2.3 定时备份脚本与保留策略手工备份只能算“有备份”真正扛事的是自动化。我这边用了最简单的cron方案核心思路是数据备份每天凌晨执行配置文件同步执行备份文件保留7天超过的自动清理。脚本大致长这样#!/bin/bash BACKUP_DIR/var/opt/gitlab/backups KEEP_DAYS7 # 记录日志 echo GitLab Backup Start: $(date) /var/log/gitlab-backup.log # 数据备份 /usr/bin/gitlab-backup create STRATEGYcopy GZIP_RSYNCABLEyes /var/log/gitlab-backup.log 21 # 配置文件备份 sudo cp /etc/gitlab/gitlab.rb $BACKUP_DIR/gitlab.rb.$(date %Y%m%d) sudo cp /etc/gitlab/gitlab-secrets.json $BACKUP_DIR/gitlab-secrets.json.$(date %Y%m%d) # 清理超过7天的备份 find $BACKUP_DIR -name *_gitlab_backup.tar -mtime $KEEP_DAYS -delete find $BACKUP_DIR -name gitlab.rb.* -mtime $KEEP_DAYS -delete find $BACKUP_DIR -name gitlab-secrets.json.* -mtime $KEEP_DAYS -delete echo GitLab Backup End: $(date) /var/log/gitlab-backup.logcrontab配置0 2 * * * /usr/local/bin/gitlab_backup.sh选凌晨两点是因为这个时段团队基本不提交代码仓库读写和数据库压力都小备份过程中对线上服务的影响降到最低。之前我贪省事放在中午跑结果刚好赶上开发集中合并代码备份过程中仓库一直在变最后打出来的包虽然能恢复但恢复完总有那么几个分支的状态跟最新代码不一致这才把时间改到凌晨。保留7天这件事也要说一句。GitLab官方推荐保留时间和你的实际风险承受能力挂钩。比如你每天备份一次保留7天最坏情况下只能恢复到7天前的数据那一个周的提交和Issue记录全部丢失。如果团队对数据丢失容忍度低建议保留到14天甚至30天代价只是磁盘空间。备份文件一般压缩后占原始数据的30%到60%左右按仓库大小先估算一下再决定保留周期。2.4 备份的“物资”检查文件大小与完整性初判备份完成后不要看一眼文件存在就走了。我在脚本后面加了一段自动检查判断新生成的备份tar包大小是否小于某个阈值比如小于500M就报警。因为如果仓库很多但备份包只有几M说明大概率漏了什么。更靠谱的完整性验证是直接tar -tftar -tf /var/opt/gitlab/backups/1700000000_2023_11_04_16.10.5_gitlab_backup.tar | head -50能正常列出包内文件列表至少说明压缩包没损坏。但真正保险的验证还是我说的“恢复演练”。3. 从备份恢复严格按顺序执行3.1 恢复前的版本匹配与停机准备恢复备份这件事最大的一个前置条件目标机器的GitLab主版本必须和备份包版本号一致。我恢复前通常先看版本cat /opt/gitlab/version然后看备份包文件名后的版本号两者一致才动手。不一致的话要么升级目标机器要么找一份匹配版本的备份包。版本确认后恢复操作需要在维护窗口执行意味着要停掉GitLab的关键服务。官方推荐停的是Puma应用服务器和Sidekiq后台任务队列sudo gitlab-ctl stop puma sudo gitlab-ctl stop sidekiq这两个一停GitLab基本就不对外提供正常写入了避免恢复过程中再有新数据写进来污染备份状态。但注意如果机器上还开了其他组件比如Gitaly或者PostgreSQL它们可以暂时继续跑恢复工具会自动处理。3.2 恢复命令与非交互模式的坑恢复命令本身不长sudo gitlab-backup restore BACKUP1700000000_2023_11_04_16.10.5BACKUP参数填的是备份tar包文件名中间的时间戳和版本号部分不带_gitlab_backup.tar后缀。这里有个坑命令执行到一半会问你是否确认要恢复因为它会覆盖当前数据库和仓库目录一确认就不可逆。脚本化或无人值守时要加上FORCEyessudo gitlab-backup restore BACKUP1700000000_2023_11_04_16.10.5 FORCEyes恢复完成后第一时间还原配置文件。提前把gitlab.rb和gitlab-secrets.json拷贝回/etc/gitlab/然后执行sudo gitlab-ctl reconfigure sudo gitlab-ctl restart如果不做这一步可能出现服务起不来或者Runner密钥对不上的问题。我自己就吃过一次亏当时只恢复了数据tar包忘了secrets文件界面能登录但CI Runner全部失联最后排查了一下午才发现是密钥文件不匹配。3.3 恢复之后怎么验证才算真的成功恢复完成后不要急着对外宣布“已恢复”。我有一套固定的验收清单挨个过一遍GitLab首页能打开登录没报错。随机挑几个项目看README、Issue、Merge Request页面是否正常。挑一个大仓库执行git clone确认仓库内容完整。检查Webhook列表确认URL没有丢失。去Admin区域看Runner是否在线。看后台日志sudo gitlab-ctl status sudo gitlab-rake gitlab:check RAILS_ENVproductiongitlab:check这个命令会做一整套自检包括数据库连接、Redis状态、Sidekiq进程、仓库目录权限等。虽然它不一定能检测出所有业务数据问题但至少能排除环境层面的故障。跑完没有红色错误我才会把维护窗口关掉。4. GitLab迁移方案同版本迁移、跨版本迁移与容器化迁移4.1 同版本迁移最稳的方案是“备份-恢复-校验”同版本迁移场景很常见旧机器磁盘快满了或者公司要换服务器但GitLab版本不变。这种迁移痛苦最小因为不需要考虑版本兼容问题。操作链路清晰明确旧机器执行一次完整备份同步备份配置文件。新机器安装同一版本的GitLab Omnibus包。把备份tar包和两个配置文件的副本拷贝到新机器。新机器停掉Puma和Sidekiq把配置文件放回/etc/gitlab/。执行sudo gitlab-backup restore BACKUPxxx FORCEyes。执行sudo gitlab-ctl reconfigure和sudo gitlab-ctl restart。修改DNS解析或者把旧机器的流量切到新机器。按3.3的验收清单逐项验证。这条链路最关键的一步其实在步骤7。我建议先切一半流量或者先让部分开发手动访问新地址测试确认无问题后再改正式DNS的TTL等确认一切稳定后再下线旧机器。4.2 跨版本迁移内核是“阶梯式升级再迁移”如果说同版本迁移是换车不换发动机那跨版本迁移就是连发动机带底盘一起换最容易翻车。GitLab官方规定备份包只能恢复到备份时的同主版本环境或者比该版本更高的后续版本。但实际操作中比如你手上是13.x的备份想恢复到16.x的新实例直接恢复会报错或者行为异常。我踩过一次之后学乖了老老实实按阶梯升级假设旧环境是13.4先升级到13.12最新版再升到14.x再到15.x最后到16.x。每升一个大版本前做一次全量备份升级完验证一次基本功能。跨版本迁移时gitlab-secrets.json的兼容性也会变化新版本升级过程通常会自己调整密钥文件格式只要别手动覆盖、别混版本复制旧文件一般问题不大。另外跨版本后数据库会做迁移时间长短取决于数据量。百万级Issue和几十G仓库的实例数据库迁移可能要跑一两个小时运维窗口要留够别把时间卡得太死。4.3 Docker方式部署的GitLab迁移如果GitLab是用Docker跑的备份和迁移的姿势有些不一样。容器内执行备份docker exec -t gitlab gitlab-backup create STRATEGYcopy备份文件会生成在容器内/var/opt/gitlab/backups而这个目录通常挂载到了宿主机的某个路径上比如docker run -d \ --name gitlab \ -v /srv/gitlab/data:/var/opt/gitlab \ -v /srv/gitlab/config:/etc/gitlab \ gitlab/gitlab-ce:latest这种情况下备份出来的tar包直接落在宿主机的/srv/gitlab/data/backups里。迁移到新机器时有两种做法方法一推荐在新容器里执行docker exec -t gitlab gitlab-backup restore BACKUPxxx同时把srv/gitlab/config下的配置和secrets一起拷贝过去。方法二直接把整个/srv/gitlab挂载目录原样拷到新机器再把容器跑起来。这个方法看起来省事但要求宿主机文件权限、数据目录结构完全一致处理不好容易出现权限错乱。除非整体系统盘级别克隆否则我更推荐备份-恢复的方式。4.4 迁移后最容易忽略的几个收尾项代码搬过去了页面能开了不代表迁移收尾完成。这几件事我每次迁移后都会单独处理修改external_url如果新机器的域名或IP变了/etc/gitlab/gitlab.rb里的external_url要改否则仓库的clone地址、CI回调用地址全是旧的。通知团队更新remote地址所有开发本地git remote指向旧服务器迁移后要统一通知给一份新地址的替换命令。CI/CD和Webhook里的回调地址项目配置里如果有GitLab URL写死的地方要同步检查。HTTPS证书新机器的SSL证书如果不在GitLab处理范围要确认Nginx层或GitLab内置的HTTPS配置正常。5. 常见问题与排查技巧实录5.1 运维中高频故障速查表我把日常备份、恢复、迁移中遇到的高频问题整理成一个速查表后面详细展开说现象常见原因处理方向备份命令报Permission denied备份目录权限不对检查/var/opt/gitlab/backups属主是否为git用户备份文件生成但只有几M某个组件本身没数据或备份中断用tar -tf检查包内目录结构恢复后项目打开404数据库里元数据丢失或仓库目录没恢复完整检查git-data目录确认namespace路径恢复后登录页面500gitlab-secrets.json不匹配重新拷贝旧环境的secrets文件Runner全部离线Runner注册token变更在Admin区域重新注册RunnerWebhook请求失败external_url或回调地址没改修改gitlab.rb后reconfigure5.2 恢复时“项目数据存在但页面404”的经典场景这个问题的原因通常不在仓库本身而在于数据库里的项目记录和仓库目录对不上。GitLab判断一个项目是否存在先查数据库再关联仓库目录。如果恢复时数据库没有完整恢复或者仓库目录的namespace路径和数据库记录对不上就会表现成“仓库文件明明在但网页打开404”。排查入口是看生产日志sudo tail -f /var/log/gitlab/gitlab-rails/production_json.log里面有详细的项目查找和异常栈。如果日志提示找不到仓库路径说明仓库目录有问题如果日志提示数据库查询为空说明数据库恢复不完整。对症下药别一上来就怀疑是权限问题。5.3 备份文件损坏的提前预警有一次我例行检查备份包发现某天的tar包能tar -tf列出文件但到真正恢复演练时发现里头有一个项目的sidecar文件CRC不对。后来再也不敢只看文件大小而是每个月抽一次备份包找一台闲置机器完整做一次恢复演练。演练时间成本不低但对比真出事时全团队干瞪眼这半小时太值了。如果备份包在拷贝到异地时发生损坏tar -tf会直接报错这种还算好发现的。最怕的是备份时数据正在写入导致逻辑层面的不一致比如某个仓库当时正在执行GC仓库目录刚好被打包了一半。这也是我坚持加STRATEGYcopy参数的原因——让备份工具先做一次内部快照复制尽量避免逻辑不一致。5.4 Jenkins、API Token等集成连接失败GitLab和Jenkins等工具集成时经常会遇到连接失败错误提示类似Failed to authenticate或login failed。这种问题在恢复和迁移后尤其高发。常见原因有三个GitLab的API token是绑定用户、绑定应用的恢复后的数据库如果回滚到了token创建之前的时间点token自然失效。gitlab-secrets.json不匹配导致加密上下文变化。external_url改了Jenkins那边配置的GitLab URL还是旧地址。处理方式很直接在GitLab里重新生成API token到Jenkins里更新凭据顺便把GitLab的URL配置改成新地址。6. 备份体系之外的个人建议6.1 把备份和“对象存储”结合别让备份和主数据死在同一台机器很多团队把备份文件和GitLab放在同一台服务器的同一块磁盘上这在我看来约等于没备份。磁盘整块挂了的时候主数据和备份一起陪葬。更合理的做法是配置GitLab的备份路径到NAS或对象存储挂载点或者备份完立刻用rsync、rclone推送到异地存储。我现在就是双重策略本地备份保留7天另有一份备份通过rclone定时同步到远端对象存储保留30天。异地那份在出大事时才会用平时基本不碰但心里踏实很多。6.2 定期演练恢复最好把流程文档化我知道“演练恢复”这件事一听就是“有时间再说”的事。但恰恰是没演练过真到灾难发生时才知道有多少前置步骤被忽略。我的习惯是每季度做一次完整演练把恢复流程写成文档标注每一步大概耗时、常见报错怎么处理这样即使换人操作也能照着走。演练时甚至可以把网络切断一部分模拟极端情况比如只能拿到距今一周的备份包、配置文件和secrets全部丢失检验团队的降级应对能力。这种演练做多了遇到真实事故时就不会手忙脚乱。6.3 最后分享一个让我少走弯路的小习惯每次备份完后我都会顺手看一眼/var/log/gitlab-backup.log的最后几行确认是否有done或者错误信息。很多备份工具的坑在于退出码为0并不代表备份完全成功——某个子组件可能悄悄失败了但整体流程还是正常结束。我看日志不是看它有没有报错而是看它是否明确写了哪些组件被备份、哪些被跳过。遇到跳过项我会单独处理绝不带着疑问把备份当作有效备份保留。因为一次备份记录为“部分成功”会让后面所有的恢复方案都建立在一个不完整的基础上这比没有备份更危险。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →