把 GitLab 从 CentOS 原生环境迁到 Ubuntu 上的 Docker Compose,这活儿听起来不难,但我实际动手做了一圈之后发现,真正的坑全在“你以为没问题”的地方。老实例跑了三年,仓库几十个,用户权限、CI 任务记录、Webhook 全都在里面,任何一步丢了数据都受不了。所以这次迁移我定的基调很明确:先备份,再校验,再动手,最后逐项验证,一个字节都不能少。
这篇文章把整个过程完整拆给你看,包括为什么选这个迁移路线、备份怎么做才靠谱、Compose 文件怎么配、恢复顺序为什么不能乱、以及我踩过的那些奇奇怪怪的报错(尤其是 “login failed. check api token or gitlab version” 和登录页面 422 这种)。如果你也在准备做 GitLab 迁移,或者正被迁移后的登录问题卡住,这篇文章可以直接当操作手册用。
1. 迁移背景与方案设计
1.1 为什么放弃 CentOS 原生安装
先说这台旧机器的情况。CentOS 7 原生安装的 GitLab CE,当年图省事直接用 RPM 包装的,跑了三年多,GitLab 版本停在 16.x。日常用着还行,但有几个问题越来越扎眼。
第一是升级链路太长。原生安装的 GitLab 升级必须按版本逐级往上走,跳版本升级官方直接拒绝。我中间鸽了几个版本,想一次性升到最新,结果发现得先升到中间版本、再升到目标版本,一次升级要折腾大半天,还担心某个 minor 版本的迁移脚本出问题。
第二是环境越来越脏。CentOS 7 自带的是 Ruby、PostgreSQL、Redis 这些老版本,GitLab 原生包虽然能自洽,但和系统里其他服务抢端口、抢依赖的事情发生过不止一次。最烦的是磁盘扩容,根分区当初分得太保守,Git 仓库一多,磁盘动不动就报警。
第三是 CentOS 7 本身已经停止维护了,继续跑原生环境等于一台裸奔的机器上挂着公司核心代码仓库,这个风险我担不起。与其修补旧环境,不如直接换条更省心的路。
1.2 为什么选 Ubuntu + Docker Compose
新环境选型的时候,我在“继续原生安装”和“Docker 化”之间犹豫过。原生安装在 CentOS 上踩过的坑还历历在目,而且原生安装的备份恢复虽然成熟,但换机器时一样要处理版本匹配、依赖环境、系统差异这些问题,本质上没省心多少。
Docker Compose 的好处在于“整个 GitLab 是一个黑盒”。代码、配置、数据、日志全部映射到宿主机目录,升级就是换个镜像版本然后重新 up,备份就是打包那三个数据目录,回滚更是简单到改回镜像标签再 up 一次。这种“一切皆文件”的运维方式,对要长期维护的人来说,幸福感提升不是一点半点。
具体到宿主系统,我选 Ubuntu 22.04 LTS。原因很朴素:Docker 官方对 Ubuntu 的支持最积极,内核新、cgroups 和 overlay2 的支持最省心,而且遇到问题的时候搜到的解决方案,十有八九是基于 Ubuntu/Debian 的,踩坑成本最低。
操作系统选择这步,我的建议是不要追新,LTS 版本是底线。Ubuntu 24.04 当时刚出没多久,生态兼容还需要时间验证,22.04 这种已经被大量生产环境磨过的版本,反而更稳。
1.3 迁移链路拆解:数据零丢失的关键
整个迁移其实就四步:备份、传输、恢复、验证。听起来简单,但每一步都有容易忽略的细节。
备份阶段要解决的是“数据一致性”。GitLab 运行过程中,PostgreSQL 数据库、Git 仓库、上传文件、CI 产物都在持续变化,直接拷贝文件目录的方式做出来的备份,大概率是坏的。必须用 GitLab 自带的备份工具,它内部会协调各组件状态,打出一个时间点一致的快照。
传输阶段要解决的是“完整性校验”。备份文件从旧机器传到新机器,网络中断、磁盘出错都可能导致文件损坏。我见过有人 scp 到一半没检查,恢复的时候才发现 tar 包是坏的,那才是真正的灾难。所以传输完第一步是校验哈希,而不是急着解压。
恢复阶段要解决的是“版本匹配”。GitLab 官方对备份恢复的要求是:备份文件的主版本号必须与恢复目标实例一致,最好是同 minor 版本。我用的是接近同版本的镜像,避免恢复时数据库迁移脚本跑挂。这一步是很多人栽跟头的地方,后面细说。
验证阶段要解决的是“业务可用性”。数据恢复成功不等于迁移成功。仓库能不能 clone、用户能不能登录、CI 能不能跑、Webhook 能不能触发,每个功能都要过一遍,才算真正迁移完成。
2. 备份:数据零丢失的根基
2.1 GitLab 原生备份机制
GitLab 的备份工具分新旧两个名字,老版本叫gitlab-rake gitlab:backup:create,新版本改成了gitlab-backup create,底层是同一套逻辑。
这个命令备份的东西比你想象的多:Git 仓库数据、PostgreSQL 数据库、Redis 中的队列和缓存状态、上传的附件文件、CI/CD 的构建日志和产物、容器镜像仓库数据,以及一些配置元数据。说白了,除了/etc/gitlab下的配置文件,其他和业务数据相关的东西都在备份包里。
我当时的操作很简单,SSH 登录旧机器,先看一眼当前版本号:
sudo gitlab-rake gitlab:version然后执行备份:
sudo gitlab-backup create备份文件默认落在/var/opt/gitlab/backups目录下,文件名格式长这样:
1699999999_2024_11_15_16.11.1_gitlab_backup.tar前面的时间戳是备份时间,后面的16.11.1是 GitLab 版本号。这个版本号务必要记牢,后面拉取 Docker 镜像时要找匹配的版本,就是靠这个数字。
备份过程中我还做了一步额外操作:把 Puma 和 Sidekiq 停掉再备份。原因是这两个服务一个是 Web 入口、一个是后台任务执行器,备份过程中如果正好有请求写入数据库,虽然 GitLab 内部有锁机制,但为了保险起见,特别是在老实例上,我宁愿短暂停服几分钟,换一个绝对一致的数据快照。
sudo gitlab-ctl stop puma sudo gitlab-ctl stop sidekiq sudo gitlab-backup create sudo gitlab-ctl start sidekiq sudo gitlab-ctl start puma如果你对停机时间敏感,不停也能备份,GitLab 的设计是支持热备的,只是我这个实例太老,稳妥优先。
2.2 配置文件与密钥的备份
这是整个迁移里最容易被忽略、又最关键的一步。很多人以为备份了那个 tar 包就够了,结果恢复完发现用户登录不了、仓库 clone 地址不对、CI 的 Runner 全部失联——大概率就是配置文件没备份。
/etc/gitlab目录下有两个东西必须单独备份:
第一个是gitlab.rb,这是 GitLab 的主配置文件。external_url、SMTP 设置、LDAP 配置、SSH 端口、备份保留策略,全在这个文件里。原生安装的/etc/gitlab/gitlab.rb就是一份 ruby DSL 格式的配置。
第二个是gitlab-secrets.json,这个文件才是真正的命根子。GitLab 用它来加密数据库里的敏感字段,比如用户的两步验证密钥、CI/CD 的 token、Webhook 的密钥等。如果这个文件丢了或者和数据库不一致,恢复完成后即使密码正确也登录不进去,因为加密解密用的密钥对不上。官方文档明确说这个文件是不可恢复的,丢了等于那部分数据永远解不开。
我当时的备份命令:
sudo tar czf /var/opt/gitlab/backups/gitlab-config-backup.tar.gz /etc/gitlab/gitlab.rb /etc/gitlab/gitlab-secrets.json另外gitlab.rb里我配了 SSH 的 host keys,对应的文件在/etc/ssh/下,但 GitLab 自带的是/etc/gitlab/ssh_host_*这组密钥。如果不想让用户在迁移后重新信任 host key,也一起备份走。我这次顺手把/etc/gitlab/ssh_host_ecdsa_key和.pub一起打包了。
2.3 备份校验与安全传输
备份文件生成完之后,先别急着传。第一步先看大小是否合理,一个跑了三年的 GitLab,仓库加数据库加 CI 产物,备份文件怎么也得几十 GB,如果你发现备份包小得离谱,赶紧检查是不是有目录挂在外部存储上没被包进去。
校验文件完整性我用的是 sha256:
sha256sum /var/opt/gitlab/backups/*.tar记录下这个哈希值,传到新机器后再算一次,两个值要一模一样。别嫌这一步麻烦,我身边真实发生过备份文件传一半远程断了,scp 报错但生成了残缺文件,恢复的时候才发现 tar 包损坏,最后只能重新备份。哈希对上了再去解压看内容,至少能确认这个包结构是完整的。
传输方式我用的是 scp,因为旧机器有固定公网 IP,速度也够:
scp /var/opt/gitlab/backups/1699999999_2024_11_15_16.11.1_gitlab_backup.tar user@新机器IP:/srv/gitlab/data/backups/ scp /var/opt/gitlab/backups/gitlab-config-backup.tar.gz user@新机器IP:/srv/gitlab/config/传输这个动作最好放在所有准备工作完成之后再做,避免备份文件提前放到新机器上被误删。
3. Ubuntu 环境与 Docker Compose 部署
3.1 Ubuntu 基础环境准备
新机器是一台 Ubuntu 22.04,装完后第一步是把系统包更新到最新,然后装 Docker。现在的 Docker 安装方式已经比前几年简单太多了,官方推荐用 apt 仓库安装。
sudo apt update && sudo apt upgrade -y sudo apt install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里有个值得说的点:现在新版 Docker 默认自带 Compose 插件,docker compose(中间有空格)是独立的 compose 插件,不再需要单独安装docker-compose(中间有横杠)那个老版本的 Python 工具了。很多人还停留在apt install docker-compose的旧习惯里,装完发现版本很老、语法支持不全,其实直接装docker-compose-plugin就好。
验证一下:
docker --version docker compose version然后创建 GitLab 的数据目录,我习惯统一放在/srv/gitlab下,结构清晰,备份也方便。
sudo mkdir -p /srv/gitlab/config sudo mkdir -p /srv/gitlab/data sudo mkdir -p /srv/gitlab/logs sudo mkdir -p /srv/gitlab/data/backups这三个目录分别对应容器的/etc/gitlab、/var/opt/gitlab、/var/log/gitlab,映射出来之后,就算容器整个删掉重建,数据也还在。
3.2 编写 docker-compose.yml
GitLab 官方提供了 Docker 镜像和 Compose 示例,但直接抄官方配置是不够的,要结合自己的实际情况调。我最终的 Compose 文件长这样:
version: '3.6' services: gitlab: image: gitlab/gitlab-ce:16.11.1-ce.0 container_name: gitlab restart: always hostname: gitlab.example.com environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://gitlab.example.com' gitlab_rails['gitlab_shell_ssh_port'] = 2222 gitlab_rails['gitlab_rails_db_username'] = 'gitlab' gitlab_rails['time_zone'] = 'Asia/Shanghai' gitlab_rails['backup_keep_time'] = 604800 puma['worker_processes'] = 2 postgresql['shared_buffers'] = "256MB" postgresql['max_connections'] = 100 manage_accounts['enable'] = true gitlab_rails['gitlab_default_theme'] = 2 ports: - "2222:22" - "80:80" - "443:443" volumes: - /srv/gitlab/config:/etc/gitlab - /srv/gitlab/logs:/var/log/gitlab - /srv/gitlab/data:/var/opt/gitlab shm_size: '256m' healthcheck: test: ["CMD", "curl", "-f", "http://localhost/-/readiness"] interval: 30s timeout: 10s retries: 5版本号这里我特意指定了16.11.1-ce.0,而不是用latest。原因在恢复章节会详说,简单讲就是要和备份文件的版本匹配。镜像的版本号规则是gitlab/gitlab-ce:<版本号>-ce.0。
端口映射这里有个很容易踩的坑:宿主机 22 端口被系统自带的 sshd 占了。GitLab 容器内的 SSH 服务默认监听 22 端口,如果你把容器的 22 映射到宿主机的 22,就会和 sshd 冲突。解决办法是把宿主机的 2222 端口映射到容器的 22,同时通过gitlab_shell_ssh_port告诉 GitLab“我的 SSH 服务对外暴露在 2222 端口”。
shm_size这个参数也不要漏。GitLab 内置的 PostgreSQL 会用到共享内存,默认值可能太小,容器跑着跑着 Postgres 直接崩,日志里报 “could not resize shared memory segment” 之类的错。我直接给了 256MB,彻底避开这个问题。
3.3 首次启动与初始化检查
配置写好之后,启动容器:
cd /srv/gitlab sudo docker compose up -d第一次启动会非常慢,因为容器起来之后要先跑一遍gitlab-ctl reconfigure,初始化数据库、生成密钥、配置各类组件。我当时等了大概 3 分钟才看到 Web 界面响应。这个阶段不要频繁去重启容器,给足时间让它自己初始化。
看进度用:
sudo docker compose logs -f gitlab等到日志里出现类似 “GitLab is ready to use” 或者nginx开始监听 80 端口,就说明初始化完成了。
初始化完成后,第一次访问之前,我们还没恢复数据,此时 GitLab 里是空库。我直接在浏览器访问了一下,确认 Web 服务端口通不通。
注意这个时候不建议在浏览器里点注册或者创建项目,因为马上要恢复数据,这些操作可能会在数据库里产生脏数据,干扰后续恢复流程。
4. 数据恢复与业务验证
4.1 备份文件恢复实操
恢复前先把镜像版本和备份文件版本对一下。旧机器备份文件里带16.11.1,新机器用的镜像是16.11.1-ce.0,主版本和小版本完全一致,这条路就走对了。
如果版本不一致怎么办?我建议不要赌。GitLab 官方对备份恢复的版本要求很严格,跨大版本恢复大概率会报数据库迁移错误。如果你备份的版本太老,先按升级路径逐步升级到目标版本再备份,或者拉对应大版本的中间镜像恢复后,走 GitLab 升级流程再升到最新,千万别图省事直接跨版本恢复。
恢复的第一步,把备份文件放进容器能读到的目录。Compose 里已经把/srv/gitlab/data映射到容器的/var/opt/gitlab,所以备份文件放到宿主机的/srv/gitlab/data/backups/下,容器内路径就是/var/opt/gitlab/backups/。
先把前面 scp 过来的 tar 包确认在正确位置:
ls -lh /srv/gitlab/data/backups/接下来进到容器里,停掉 Puma 和 Sidekiq。这一步不能省,否则数据库恢复时会因为连接占用而报错:
sudo docker compose exec gitlab gitlab-ctl stop puma sudo docker compose exec gitlab gitlab-ctl stop sidekiq然后执行恢复命令。注意BACKUP=后面只写时间戳那部分,不要带_gitlab_backup.tar后缀,也不要带版本号:
sudo docker compose exec gitlab gitlab-backup restore BACKUP=1699999999_2024_11_15_16.11.1 force=yes恢复过程中会提示你是否要删除未关联的数据,我直接输入yes。这个删除动作清理的是备份文件里不存在、但数据库里存在的旧数据,如果选 no,后续可能出现数据库不一致。
恢复完成后再启动服务:
sudo docker compose exec gitlab gitlab-ctl start sudo docker compose exec gitlab gitlab-ctl reconfigurereconfigure这步会自动把配置文件重新生成一遍,同时确保服务状态符合配置要求。跑完之后gitlab-ctl status看一下各服务状态,正常的话应该是run。
4.2 配置恢复与端口地址修正
数据恢复完成后,接下来处理配置文件的恢复。
把之前备份的gitlab-config-backup.tar.gz解压到宿主机对应目录:
cd /srv/gitlab/config sudo tar xzf gitlab-config-backup.tar.gz sudo chown -R root:root /srv/gitlab/config解压之后,/srv/gitlab/config目录下就应该有gitlab.rb和gitlab-secrets.json了。这里有个容易踩的坑:旧机器的gitlab.rb里很多配置是给原生环境用的,比如external_url可能写的旧服务器的 IP 或域名。如果直接沿用,页面上的 clone 地址、CI 里回调地址都会指向旧地址。
所以我在恢复之后专门做了一步“改地址”的操作。把gitlab.rb里的external_url改成新域名(或者新机器 IP),同时调整 SSH 端口配置。注意合并到旧的gitlab.rb里的配置项时,千万别删了gitlab-secrets.json,那是加密数据的钥匙。
修改配置后,要触发一次 reconfigure 让配置生效:
sudo docker compose exec gitlab gitlab-ctl reconfigure顺带说一句,如果你担心gitlab.rb里还有什么旧配置不适用,有个笨但有效的办法:先看一遍文件内容,把看不懂的选项保留,重点改external_url、gitlab_shell_ssh_port和 SMTP 配置即可。
4.3 恢复后的功能验证清单
数据恢复完、配置修正完,不代表可以收工了。我把验证项列成了一份清单逐条过,任何一项不过都不能宣布“迁移完成”。
验证 Web 登录是最基本的。用管理员账号登录,如果之前开启了 LDAP 或 OAuth,也要逐个验证。登录没问题后,看仓库列表是否和旧实例一致,重点看仓库数量。我这次迁移完后仓库数量对上了,但有些项目在旧实例上是“已归档”状态,这里也要确认归档状态是否保留。
仓库数据验证,我习惯用 SSH 方式 clone 一个仓库到本地测试。这里有个关键配置要确认:因为宿主机 22 被 sshd 占了,SSH clone 地址里的端口得是 2222,clone 时要写成ssh://git@gitlab.example.com:2222/group/project.git这种格式。跑一次git clone、改个文件提交推上去,确认推送也正常。
CI/CD 验证,找一个最小的 pipeline 手动触发一次。重点看 Runner 能不能注册回来,如果之前用的是共享 Runner,可能需要在新的 GitLab 实例上重新注册 Runner。
还有一个经常被忽略的:Webhook。去有 Webhook 的项目里看一眼,确认通知地址还是不是指向旧系统,如果有钉钉、企微群机器人通知,最好触发一个事件实测一下。
查看用户权限和数据完整性。确认各项目的成员列表、用户角色、组的层级关系都和旧实例一致。恢复完之后数据细节出现差异的情况确实存在,我在多个项目里随机抽了几个人,对照旧实例逐个核对。
5. 疑难杂症与故障排查实录
5.1 “login failed. check api token or gitlab version” 报错处理
这个报错是我迁移完成后最头疼的一个。表现是本地 IDE 的 GitLab 插件连不上新实例,提示 “login failed. check api token or gitlab version. log in via git if the version” 的英文报错,但浏览器里访问 GitLab 又完全正常。
排查起来要先理解这个报错的来源。这个提示一般来自 GitLab 的 API 客户端工具或 IDE 插件,它们在调用 GitLab API 获取用户信息时,后端返回了鉴权失败。GitLab 要求客户端 API 版本兼容,token 权限足够,HTTP 请求的 Header 正确。
排查步骤我也是按顺序来的:
先确认 GitLab 版本号是否过旧。这个报错在老版本 GitLab 上更容易触发,因为插件调用的 API 路径在新版本上有变化。我这次迁移后的版本是 16.11.1,不算老,但如果是 13.x 这种古董版本,建议先升级。
然后检查 token 本身。在 GitLab 页面右上角头像下拉菜单里选择Preferences -> Access Tokens,创建一个具有api权限的 token。token 的类型要是Personal Access Token,不能用OAuth Application Token代替。我之前遇到过有人拿 deploy token 当成个人访问 token 用,结果 API 查询用户信息时直接被拒。
接着排查网络链路。如果新机器前面有 Nginx 反向代理、CDN 或者防火墙做了请求头过滤,API 的 Authorization Header 被吞掉就会导致这个报错。用 curl 直接验证是最高效的方式:
curl -H "PRIVATE-TOKEN: 你的token" http://gitlab.example.com/api/v4/user正常返回 JSON 中包含username字段,就说明 API 链路是通的。如果这里返回 401,再看 GitLab 的 production log:
sudo docker compose exec gitlab tail -100 /var/log/gitlab/gitlab-rails/production_json.log日志里会记录具体的请求路径和鉴权失败原因,比猜靠谱得多。
还有一个容易忽略的:如果你在旧实例上把 GitLab 跑在 HTTPS 后面,新实例改成了 HTTP,token 在传输中没问题,但一些客户端会缓存旧协议。重启一下 IDE 或者重新添加远程仓库地址就能解决。
5.2 页面 422 错误与隐身模式之谜
恢复完成后第一次用浏览器登录,遇到了一个经典问题:输入用户名密码点登录,页面直接报 422,错误信息是 “The change you requested was rejected”。更诡异的是我试了下浏览器的隐身模式,居然能正常登录。
这个现象一出来,基本可以确定问题不是 GitLab 的账号密码,而是浏览器侧的状态和服务端不匹配。Web 登录流程里有一个 CSRF 校验机制,GitLab 会在用户访问登录页时下发一个 CSRF token,放在 session cookie 里。提交登录请求时,服务端会校验提交的 token 和 session 里的 token 是否一致,不一致就直接 422。
为什么隐身模式能登录?因为隐身模式完全新开了一个 session,没有旧域名、旧路径、旧端口留下的旧 cookie,所以 CSRF token 校验自然通过。普通模式里浏览器缓存着迁移前的_gitlab_sessioncookie,里面的 token 和当前实例对不上,登录就被拦了。
处理办法也很直接:清除这个站点所有的 cookie,重新加载页面。如果实在找不到是哪个 cookie 干扰,最简单的方式是清除浏览器全部缓存和 cookie。另外,迁移前如果用过http://旧IP访问,迁移后域名变了,浏览器可能还存在旧域名的 cookie 在跨域请求中被错误携带,把所有相关域名的 cookie 都清掉最干净。
还有一个容易忽略的坑:部署了 HTTPS 并把 HSTS 打开的话,浏览器会强制跳转 HTTPS。如果新实例还没配证书,HTTP 请求会被浏览器拦掉,页面表现可能和 422 类似。所以迁移初期我建议先不用 HTTPS,确定一切正常后再上证书。
5.3 Docker 环境下的高频坑位
GitLab 容器化后的大部分问题都和端口、权限、资源限制有关,把这些坑填上,能省掉后面一大半的头疼事。
第一个坑是端口冲突。宿主机 22 被 sshd 占用,容器里 GitLab 的 SSH 服务映射到 2222。这个前面已经说过,但这里还要再提一句:修改端口映射后,gitlab.rb里的gitlab_shell_ssh_port必须同步改成 2222,否则用户在界面上看到的 clone 地址会不带端口,git 命令连不上,得手动拼端口。
第二个坑是权限问题。Docker 容器内的进程是以 root 或特定 uid 运行的。宿主机/srv/gitlab/data/backups目录如果权限不对,容器里读不了备份文件,恢复命令会报 “Permission denied”。我当时在宿主机上跑了一句:
sudo chown -R 998:998 /srv/gitlab/dataGitLab 容器内用户git的 uid 通常是 998,把这个目录的所有权交给宿主机上的 998 uid,两边就对齐了。不同镜像版本的 uid 可能有差异,用sudo docker compose exec gitlab id git查一下最准。
第三个坑是磁盘空间。GitLab 是吃磁盘的大户,备份文件、Git 仓库、CI 产物,加起来增长速度快得吓人。迁移之前要确认/srv/gitlab所在分区预留足够空间,至少是备份文件大小的 2 倍以上。恢复过程需要临时空间来解包和数据导入,空间不足会导致恢复中断,而且这种中断的恢复现场非常难收拾。
第四个坑是 DNS 和 hostname。容器内hostname参数和external_url建议保持一致的域名。如果external_url写的是 IP 而不是域名,后面改成域名的时候又得重新 reconfigure。我一开始直接用 IP,后来为了以后迁移方便改成域名,多折腾了一次,建议一步到位写域名。
5.4 排查速查表
最后整理了一份这次迁移过程中遇到的问题速查表,按症状、原因、解法整理出来,方便你直接对照:
| 症状 | 可能原因 | 处理方式 |
|---|---|---|
| 登录页报 422,隐身模式可登录 | 浏览器旧 cookie 导致 CSRF 校验失败 | 清除站点 cookie,或改用隐身模式确认 |
| IDE/API 客户端报 login failed. check api token or gitlab version | token 权限不足、版本过旧、代理吞 Header | 在 Preferences -> Access Tokens 重建 api 权限 token |
| 恢复后用户登录提示密码无效 | gitlab-secrets.json 未恢复或版本不一致 | 恢复 /etc/gitlab/gitlab-secrets.json 后 reconfigure |
| SSH clone 地址没有端口 | gitlab_shell_ssh_port 未配置 | 在 gitlab.rb 中配置 gitlab_rails['gitlab_shell_ssh_port'] = 2222 |
| 容器内 Postgres 崩溃 | shm_size 太小 | Compose 文件里配置 shm_size: 256m 后重建容器 |
| 恢复命令提示权限拒绝 | 备份目录属主不对 | 容器内id git查 uid,宿主机 chown 对齐 |
| 页面能开但 clone 超时 | 22 端口被 sshd 占用 | 端口映射改为 2222:22,并配置 gitlab_shell_ssh_port |
| Pipeline 发起的 Webhook 地址是旧域名 | 旧 gitlab.rb 配置残留 | 修改 gitlab.rb 中 external_url 后 reconfigure |
结语
这次迁移真正花时间的不是敲那几条命令,而是每做完一步都要停下来想:如果这一步出了问题,会有什么症状,怎么发现,怎么回滚。备份不校验等于白备份,配置不备份等于数据没恢复,恢复完不验证等于进度归零。这套“备份-校验-传输-恢复-验证”的流程,是我这几年做各种服务迁移总结出来的通用打法,放在 GitLab 上同样适用,放到其他有状态服务上也一样成立。
最后再分享一个迁移过程中的小技巧:新旧两台机器在切换解析之前,先在新机器的/etc/hosts里把域名指向新机器 IP,用完整域名访问一遍 GitLab,确认功能全部正常后再改 DNS。万一有问题,把 hosts 改回来就能秒切回旧实例。有了这个保底方案,整个迁移过程的心理压力都能小一大截。