
接手一台跑了几年都没升级过的 GitLab 12.10.5客户要求迁到 16.x还要尽可能减少停机时间。这台实例撑起了公司绝大部分代码仓库和 CI 流水线里面跑着两百多个项目快七百个用户。说实话刚接到这个需求的时候我心里是有点发怵的——版本跨度这么大中间隔了 13、14、15 整整三代大版本官方升级路径本身就写得非常严格。任何一个环节抄近路大概率都会得到一地的数据库报错和起不来的服务。这篇文章就把我这次“GitLab 12 到 16”的迁移全过程记录下来从版本路径规划、备份策略、逐级升级的实操命令到迁移过程中踩过的坑和验证清单一次性都讲透。写之前我先说明一下背景我全程用的是 Omnibus 安装方式操作系统是 Ubuntu 20.04最终目标版本是 GitLab 16.11.x。如果你用的是源码安装或 Docker 部署思路同样适用但具体命令会有差异。这篇文章适合谁看自己管着 GitLab 实例的运维、DevOps或者公司里有一台常年不升级的 GitLab 正头疼要不要动它的同学都可以参考。1. 迁移前的关键认知版本跨度不是小事1.1 从 12 到 16中间到底隔了什么很多人第一反应是“不就是 apt upgrade 吗升到最新版不就完事了”。如果是从 15.11 升到 16.0这么干问题不大但从 12.10 直接升到 16.x大概率会发生两件事要么升级脚本在数据库迁移阶段直接报错中断要么服务能起来但 Sidekiq 后台任务一塌糊涂最后不得不回滚重来。GitLab 的升级机制和绝大多数应用不一样。它的数据库迁移migrate和后台数据迁移background migration是按版本顺序设计的官方文档里写得很明确支持跳版本升级的跨度极其有限。从 12 升到 16官方要求的路径一般是这样的当前版本下一目标版本备注12.10.512.10.14先升到 12 的最后一个补丁版12.10.1413.0.12进入 13 大版本13.0.1213.12.15升到 13 系的最终补丁版13.12.1514.0.12进入 14 大版本14.0.1214.10.614 系最终版14.10.615.4.6进入 15 大版本注意不是直接到 15.1115.4.616.0.x进入 16 大版本16.0.x16.11.x最终目标版本为什么这么繁琐因为 GitLab 的数据库 schema 在每个大版本里都有结构性变化。比如 13 到 14 的时候CI/CD 相关的数据表做了大量拆分14 到 15 的时候很多后台任务被重新设计。你跳过了中间版本意味着跳过了这些转换逻辑数据库里的老数据到了新版本代码里直接就是不可读的状态。1.2 迁移前要明确的三个问题动手之前先问自己三个问题目标版本到底选哪个、迁移的停机窗口有多长、如果失败怎么回滚。目标版本我建议选 16 系里比较新的补丁版比如 16.11.x。别一上来就追 17 或最新版这不只是保守也是为了稳妥。16.11 是 16 系的最后一个 minor 版本意味着这一年内的 bug 修复和安全补丁都集成进去了而且它的升级路径在官方工具里是经过验证的。停机窗口决定了你怎么排计划。我这次实际用的停机时间大约一个多小时但整个升级过程花了将近一个整天——多出来的时间全部用在每一级升级后的验证和等待后台任务完成上。如果你是通宵维护窗口时间压力会小很多。回滚策略必须在动手前想好。GitLab 官方不推荐跨版本降级也就是说你不能把 16 的备份恢复到 12 上。所以回滚靠的是升级前的完整备份和快照这一点后面详细说。2. 备份和预检升级能不能成一半看这里2.1 备份不只是跑一条命令那么简单GitLab 自带备份命令gitlab-backup create看起来一句话的事但里面有两个很容易被忽略的坑。第一个坑备份默认不包含配置文件。/etc/gitlab/gitlab.rb和/etc/gitlab/gitlab-secrets.json必须单独备份。gitlab-secrets.json这个文件尤其重要它存着数据库加密密钥、OTP 密钥、Redis 加密密钥等一堆东西。你升级后恢复备份时如果这个文件丢了或者不匹配项目仓库还能看到但一些加密字段比如 2FA、外部 webhook 密钥会全部失效。第二个坑备份文件不要放在系统盘。默认备份目录是/var/opt/gitlab/backups如果 GitLab 数据本身就很大比如有几十 GB 的仓库数据备份再放在同盘磁盘撑爆的风险极高。我这次先把备份目录改到了一个独立挂载盘在gitlab.rb里改了gitlab_rails[backup_path]再执行 reconfigure。实际执行的备份命令如下我建议备份完立刻记录文件大小和校验值# 触发全量备份会包含数据库、仓库、上传文件等 sudo gitlab-backup create # 备份关键配置文件 sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak.$(date %Y%m%d%H%M) sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak.$(date %Y%m%d%H%M) # 查看备份文件 ls -lh /var/opt/gitlab/backups/备份完成后我强烈建议你花十分钟验证一下备份能用。怎么验证准备一台同配置的闲置机器装一个相同版本的 GitLab然后把备份恢复进去跑gitlab-rake gitlab:check。这一步看着费时间但真到了要回滚的时候你会庆幸自己验证过。另外Linux 磁盘快照也要做。如果你的服务器是虚拟机登录云控制台或宿主机管理界面打一个快照。备份文件和快照是两道不同的保险一个是应用层的数据保护一个是操作系统层的整机保护。2.2 升级前的四类预检清单备份做完不要急着安装新版包。我当时整理了一个预检清单照着过一遍之后才正式开始升级磁盘空间检查。除了数据盘要留足空间系统盘也得注意。升级包本体、解压后的临时文件、数据库迁移的 WAL 日志都会吃空间。我给自己定的标准是系统盘和数据盘都至少留 50% 的空闲否则先清理日志和旧包再做打算。后台任务检查。如果当前版本的 Sidekiq 队列里有大量积压任务或者有未完成的后台数据迁移直接升级会导致新版本代码处理不了旧任务格式。检查命令是sudo gitlab-rake db:migrate:status | grep down sudo gitlab-ctl status如果有大量 down 状态的迁移说明当前实例本身就处于一个不健康的状态应该先解决当前版本的问题再谈升级。Gitaly 和 Praefect 的存储检查。新版 GitLab 对 Gitaly 存储布局的要求更严格如果仓库目录结构混乱升级后 Gitaly 可能起不来。用sudo gitlab-rake gitlab:gitaly:check确认一下。外置组件确认。如果你用了外置 PostgreSQL、Redis 或者 PgBouncer升级前要查清楚目标版本和这些组件的兼容矩阵。我这次用的是 Omnibus 自带的 PostgreSQL少了很多麻烦但升级到 13 以上的时候 PostgreSQL 大版本本身也会被跟着升级这一块要心里有数。2.3 版本路径工具GitLab 官方其实提供了一个升级路径查询工具输入你现在的版本和目标版本它会给你一串必须经过的中间版本清单。我在规划阶段直接用了这个工具省了不少查文档的功夫。路径上给出的版本基本可以理解为“硬性要求”不要自作聪明跳过任何一级。我这次实际规划的路径是12.10.5 - 12.10.14 - 13.0.12 - 13.12.15 - 14.0.12 - 14.10.6 - 15.4.6 - 16.0.7 - 16.11.6每一级之间的升级间隔我建议至少留 20 到 30 分钟的观察窗口不要急着连升。后台数据迁移是异步执行的上一版本遗留的迁移可能还没跑完新版本又叠上来很容易出问题。3. 逐级升级实操每一步都别侥幸3.1 换源与安装包准备我的服务器是 Ubuntu 20.04官方源在国内访问速度不稳定升级到一半下载超时是非常痛苦的。这一步我直接配置了清华镜像的 GitLab EE 源把gitlab.rb里的仓库地址替换掉然后 reconfigure。具体操作不展开了但有一点要提醒包版本号一定要锁死不要用apt install gitlab-ee这种不带版本号的命令因为那会直接给你装到当前源里的最新版导致跳级。正确的安装/升级命令是# 以升级到 12.10.14 为例 sudo apt update sudo apt install gitlab-ee12.10.14-ee.0每跑完一个版本都要执行sudo gitlab-ctl reconfigure sudo gitlab-ctl restart之后再进入检查环节确认这一步没问题了才能继续下一步。3.2 12.10.14 到 13.0.12第一次大版本跨越从 12.10.14 升到 13.0.12最明显的变化是 Gitaly 成为了默认的仓库存储方案很多原先由 GitLab Shell 直接处理的操作被移到了 Gitaly 里。这个阶段我没有遇到什么致命问题但 reconfigure 的时间比想象中长大概花了十几分钟。升级完成后我检查了三个东西sudo gitlab-rake gitlab:check是否全部通过sudo gitlab-rake db:migrate:status有没有 down 状态的迁移登录 Web 界面随便打开一个项目看看仓库文件列表能不能正常展示这里有个经验升级完 13.0.12 后系统里的 background migration 会开始跑一批历史数据转换任务。这个时候千万不要启动下一轮升级要等到 Sidekiq 队列里的迁移任务基本清空。查看方法是用 Grafana 或者直接看日志sudo gitlab-ctl tail sidekiq日志里如果长期没有新的迁移任务输出队列处于稳定低水位再考虑进入下一个版本。3.3 13.12.15 到 14.10.6API 和 Runner 的适配期进入 14 大版本后一个特别值得注意的变化是 Runner 的注册机制。GitLab 14 引入了 Runner 认证令牌Runner Authentication Token的概念取代了原来的注册令牌16 版本还进一步强化了令牌策略。我这次迁移里CI 的 Runner 是在升级后被强制失联的。为什么因为 14 版本开始Runner 和 GitLab 之间的通信方式变了旧的 Runner 需要重新注册。升级到 14.10.6 之后我检查了gitlab-ci的运行情况发现注册令牌已经变了于是立刻安排 Runner 重装注册避免后续升级到 16 后 CI 长时间不可用。下面是 Runner 重新注册的大致命令具体版本可能略有差异sudo gitlab-runner unregister --all sudo gitlab-runner register \ --url http://gitlab.example.com \ --token 新的注册令牌3.4 14.10.6 到 15.4.6数据库迁移的大关卡从 14 升到 15是这次迁移里我印象最深的一步。这一步的数据库迁移脚本量非常大reconfigure 直接跑了 40 多分钟。期间我盯着终端不敢离开生怕卡在某个地方。好在这台机器性能还可以8 核 16G如果配置偏低这个时间还要翻倍。15.4.6 这一步有一个必须注意的地方升级完成后一定要立刻检查 PostgreSQL 的版本。GitLab 15 开始默认使用 PostgreSQL 13/14而 12/13 时代可能还在用老版本的 PG。GitLab 自带的升级脚本会自动处理数据库软件升级但如果你的 PostgreSQL 是外置的这一步就要你自己配合维护 PG 的同事提前做兼容测试。执行完gitlab-ctl reconfigure后数据库会自动执行升级和数据迁移。如果迁移时间过长或者报错常见原因有两个一是数据库磁盘 IO 性能太差二是某些大表缺少索引。从 14 升 15 时一些跟 CI 相关的表数据量很大缺少索引会导致迁移极其缓慢。这时候可以用sudo gitlab-rake db:migrate:status看看卡在哪张表手动补充索引后重试。3.5 15.4.6 到 16.0.7进入 UI 和默认策略全面调整的一代16 版本是整个迁移中调整面最广的一个大版本。升级完成后我打开 Web 界面的一瞬间就发现导航结构、项目设置页面、管理后台布局全变了。这不是我眼花了是 16 版本对 UI 做了大规模重构。管理后台的变化是这次迁移的核心关注点。新版默认开启了更严格的密码策略要求密码最小长度改为 8 位以上个人访问令牌PAT的格式也从旧版变成了glpat-开头。如果你的团队里有不少自动化脚本是用旧版 PAT 调用 API 的升级到 16 后这些令牌全部需要重新生成不然脚本全部 401。这一点一定要提前跟研发团队通个气。从 15.4.6 升到 16.0.7总体没有前面的波折数据库迁移时间也比 14 到 15 少了很多。但升级后一小时内我观察到 Sidekiq 里的持久化任务非常多全部是历史数据的重算。必须等它们跑完才能进入最终版本的升级否则数据一致性会有问题。3.6 最终升级16.0.7 到 16.11.6最后一步反而轻松了很多。16.0.7 升到 16.11.6 属于同大版本内的 minor 升级官方支持直接跳不用担心数据库不兼容。升级命令照旧reconfigure 和 restart 之后我重点验证了 16.11 带来的新特性和老功能的表现。升级到这里整条路径算是走完了。但走完不代表结束真正的验证工作才刚刚开始。4. 升级后的验证与问题排查实录4.1 验证清单不能只看到登录页面就当成功很多人在升级完看到“能登录”“项目列表能显示”就宣布成功这其实远远不够。我这次把验证分成了四层第一层服务状态。sudo gitlab-ctl status确认所有组件都是run状态特别是 Gitaly、Sidekiq、Puma或 Unicorn。第二层系统自检。sudo gitlab-rake gitlab:check必须全绿。如果出现gitlab-shell或gitlab-workhorse异常先看配置文件是否在新版里被改动过。第三层核心功能走查。我逐项测试了新建项目、fork 项目、SSH 克隆、HTTP 克隆、Web IDE 打开、创建 Merge Request、跑一次流水线、查看制品、触发一次 webhook。每项都真实操作一遍而不是只看页面能不能打开。第四层权限与安全。随机挑了几位用户确认他们的权限、SSH Key、邮箱绑定都正常。特别是 SSH Key升级后 GitLab 会使用新的格式或路径如果客户端没有更新 known_hosts连接会直接失败。4.2 典型问题Runner 失联、Token 401 和 webhook这次升级中我实际遇到了三个典型问题在这里列成表格方便你对照排查问题现象原因解决方式CI Runner 全部离线Runner 认证令牌在 14 版本开始换新机制用新注册令牌重新注册 RunnerAPI 脚本大量 401旧版 PAT 在 16 版本中不再被默认接受让团队成员用glpat-前缀的新令牌重新生成webhook 请求失败16 版本对 webhook 的 SSL 校验默认更严格检查目标 webhook 地址的证书有效性或在 webhook 设置中处理证书校验配置SSH 连接失败 Host key 变更GitLab 升级后 SSH host key 可能重新生成更新客户端的 known_hosts并不要把旧的gitlab-secrets.json弄丢关于 SSH host key我要多说一句如果你发现客户端连接时提示 host key 变更不要急着忽略警告。先确认gitlab-secrets.json是否完好。如果这个文件还在GitLab 理论上应该能复用旧的 host key如果丢了那所有用户的 known_hosts 都要更新这是一个很容易被忽视的运维事故。4.3 PostgreSQL 版本变化与性能影响升级到 16 之后我发现一些查询明显变慢。排查了一圈发现是 PostgreSQL 大版本升级后没有重新生成统计信息。解决方案是执行一次sudo gitlab-ctl pg-upgrade sudo gitlab-rake db:migrate实际上gitlab-ctl pg-upgrade会重建整个 PG 集群并重新收集统计信息能让很多查询恢复健康。这一招在跨版本升级后强烈建议执行一次哪怕 GitLab 自动升级时没有报错。4.4 需要回滚怎么办如果你的升级在某一级失败了而且无法通过修复继续那就只能回滚。把 GitLab 包卸载掉重新装回升级前的版本然后恢复升级前生成的备份sudo gitlab-ctl stop sudo gitlab-backup restore BACKUP时间戳编号 sudo gitlab-ctl reconfigure sudo gitlab-ctl restart记住几个原则。第一恢复备份前一定先把gitlab-secrets.json恢复成升级前的版本否则各种密钥不匹配会让服务起不来。第二不要尝试用 GitLab 16 的备份文件恢复到 12 的实例上这基本等于数据自杀。第三恢复完成后立刻打快照作为回滚后的新基线。回滚虽然繁琐但它是一次性保险不要嫌麻烦。5. 迁移后的经验总结与后续维护建议这次迁移做完之后我复盘了一些非常实际的经验这里一并写出来第一定期升级才是王道。GitLab 官方大版本的支持周期并不长老是等到 EOL 或安全漏洞爆出来才被迫升级是最累的运维模式。我现在的建议是每年至少做一次升级哪怕只升几个 minor 版本也能避免跨四五个大版本的痛苦。这次 12 到 16 的折腾一半时间都花在版本路径设计和后台任务等待上如果频率高一点这些都是可以省掉的。第二备份验证必须常态化。备份不是“执行成功”就完了要每月至少验证一次恢复流程。我这次专门在测试机跑了一轮恢复验证过程中发现之前某个 GitLab 13 版本的备份即使恢复到 13.0.12 也会报错原因是数据库里有几个 background migration 未完成。如果这个问题在生产回滚时才暴露后果不敢想。第三升级后的监控不能只看三天。版本的迁移影响是深层的比如 API 调用方式、Runner 策略、令牌格式这些影响会随着团队使用浮现。我建议迁移后的一两周内重点留意 GitLab 的日志里有没有频繁的 403/401 请求、Runner 有没有断续失联、后台任务有没有积压。把监控期拉长才能真正判断升级是成功的。最后再分享一个小技巧升级前把gitlab.rb里所有非默认配置导出来和升级后的gitlab.rb做一次 diff。很多时候问题出在旧配置项在新版本里已经被改名或废弃。用git diff检查一下能少踩很多配置兼容的坑。对于一个跨四五个大版本的迁移来说这个 diff 文件就是你的配置迁移地图比什么都管用。