背景
源端 GitLab 采用 Omnibus/Linux package 安装方式,运行目录包含 /opt/gitlab、/var/opt/gitlab、/etc/gitlab。目标端计划改为 Docker 部署,通过官方备份恢复方式完成迁移,避免直接复制运行目录导致权限、路径、数据库状态不一致。
源端环境信息脱敏示例如下:
GitLab Version: 15.9.1
Revision: <GitLab修订号>
GitLab Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 13.8
URL: http://<GitLab主机名或地址>/gitlab
HTTP Clone URL: http://<GitLab主机名或地址>/gitlab/<group>/<project>.git
SSH Clone URL: git@<GitLab主机名或地址>:<group>/<project>.git
GitLab Shell Version: 14.17.0
Repository Storage: unix:/var/opt/gitlab/gitaly/gitaly.socket
迁移重点包括版本匹配、CE/EE 类型匹配、/gitlab 相对路径保留、gitlab-secrets.json 迁移、Docker 端口映射、恢复后服务状态检查。
迁移原则
GitLab 备份恢复要求目标端版本与源端版本一致,且 CE/EE 类型一致。源端为 15.9.1,目标端 Docker 镜像需固定为对应版本,禁止直接使用 latest 或高版本镜像恢复旧版本备份。
CE 版本镜像:
image: gitlab/gitlab-ce:15.9.1-ce.0
EE 版本镜像:
image: gitlab/gitlab-ee:15.9.1-ee.0
CE/EE 类型可通过源端包信息确认:
rpm -qa | grep gitlab
Debian/Ubuntu 系统可执行:
dpkg -l | grep gitlab
源端备份
源端执行 GitLab 数据备份:
sudo gitlab-backup create
旧版本命令可使用:
sudo gitlab-rake gitlab:backup:create
备份文件默认生成在:
/var/opt/gitlab/backups/
除数据备份外,必须单独备份配置文件与密钥文件:
sudo mkdir -p /root/gitlab-migrate
sudo cp /etc/gitlab/gitlab.rb /root/gitlab-migrate/
sudo cp /etc/gitlab/gitlab-secrets.json /root/gitlab-migrate/
sudo tar czf /root/gitlab-migrate/gitlab-etc.tar.gz /etc/gitlab
gitlab-secrets.json 用于解密数据库中的敏感内容,包括 CI/CD 变量、Token、2FA、Runner 相关加密数据。缺失该文件可能导致迁移后敏感数据无法解密。
正式割接前建议停止源端服务,防止备份完成后继续产生写入:
sudo gitlab-ctl stop
文件传输
将备份文件、配置文件、密钥文件复制到目标服务器:
scp /var/opt/gitlab/backups/*_gitlab_backup.tar root@<目标服务器地址>:/root/
scp /root/gitlab-migrate/gitlab.rb root@<目标服务器地址>:/root/
scp /root/gitlab-migrate/gitlab-secrets.json root@<目标服务器地址>:/root/
目标端 Docker 目录准备
目标服务器创建 Docker 挂载目录:
export GITLAB_HOME=/srv/gitlab
sudo mkdir -p $GITLAB_HOME/config
sudo mkdir -p $GITLAB_HOME/logs
sudo mkdir -p $GITLAB_HOME/data
目录映射关系如下:
/srv/gitlab/config -> /etc/gitlab
/srv/gitlab/logs -> /var/log/gitlab
/srv/gitlab/data -> /var/opt/gitlab
Docker Compose 配置
源端访问地址包含 /gitlab 相对路径,因此 Docker 部署中必须保留相同路径结构。
示例 docker-compose.yml:
services:
gitlab:
image: gitlab/gitlab-ce:15.9.1-ce.0
container_name: gitlab
restart: always
hostname: '<GitLab主机名或地址>'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://<GitLab主机名或地址>/gitlab'
gitlab_rails['gitlab_shell_ssh_port'] = 2222
ports:
- '80:80'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
shm_size: '256m'
目标端 IP 与源端 IP 不一致时,external_url 与 hostname 需要改为目标端实际地址。例如:
external_url 'http://<新GitLab主机名或地址>/gitlab'
若源端为 EE,镜像需替换为:
image: gitlab/gitlab-ee:15.9.1-ee.0
SSH 端口处理
源端 SSH Clone URL 为:
git@<GitLab主机名或地址>:<group>/<project>.git
该地址默认使用 22 端口。Docker 部署场景下,宿主机 22 端口通常已被系统 SSH 占用,因此可将 GitLab SSH 映射到 2222:
ports:
- '2222:22'
同时配置:
gitlab_rails['gitlab_shell_ssh_port'] = 2222
迁移后 SSH Clone URL 会变为:
ssh://git@<GitLab主机名或地址>:2222/<group>/<project>.git
如需保持原有 git@IP:group/project.git 格式,需要将容器 22 端口直接映射到宿主机 22 端口,或为 GitLab 分配独立 IP。
目标端初始化
启动容器完成初始配置:
docker compose up -d
查看启动日志:
docker logs -f gitlab
初始化完成后停止容器:
docker compose stop
放入备份与密钥
将备份文件复制到 Docker 数据目录:
sudo cp /root/*_gitlab_backup.tar /srv/gitlab/data/backups/
sudo chown -R 998:998 /srv/gitlab/data/backups
复制密钥文件:
sudo cp /root/gitlab-secrets.json /srv/gitlab/config/gitlab-secrets.json
gitlab.rb 不建议直接整份覆盖。迁移到 Docker 后,旧配置中的路径、端口、证书、监听地址可能不再适配。建议比对后迁移关键配置,例如:
external_url
gitlab_rails['gitlab_shell_ssh_port']
gitlab_rails['smtp_*']
gitlab_rails['ldap_*']
gitlab_rails['omniauth_*']
gitlab_rails['object_store_*']
registry_external_url
pages_external_url
配置简单场景下可先备份目标端配置,再覆盖:
sudo cp /srv/gitlab/config/gitlab.rb /srv/gitlab/config/gitlab.rb.bak
sudo cp /root/gitlab.rb /srv/gitlab/config/gitlab.rb
覆盖后需重点检查 Docker 场景不适配的旧路径与端口配置。
执行恢复
启动容器:
docker compose start
停止恢复前不应运行的服务:
docker exec -it gitlab gitlab-ctl stop puma
docker exec -it gitlab gitlab-ctl stop sidekiq
docker exec -it gitlab gitlab-ctl status
执行恢复命令。假设备份文件名为:
<备份文件前缀>_gitlab_backup.tar
恢复命令中 BACKUP= 只填写文件名前缀,不能包含 _gitlab_backup.tar 后缀:
docker exec -it gitlab gitlab-backup restore BACKUP=<备份文件前缀>
旧版本命令可使用:
docker exec -it gitlab gitlab-rake gitlab:backup:restore BACKUP=<备份文件前缀>
恢复完成后执行重新配置并重启:
docker exec -it gitlab gitlab-ctl reconfigure
docker restart gitlab
恢复后检查
检查 GitLab 状态:
docker exec -it gitlab gitlab-rake gitlab:check SANITIZE=true
docker exec -it gitlab gitlab-rake gitlab:doctor:secrets
docker exec -it gitlab gitlab-ctl status
正常状态下,关键服务应显示 run:
gitaly
nginx
puma
redis
sidekiq
postgresql
gitlab-workhorse
访问地址应为:
http://<目标服务器地址>/gitlab
由于源端使用 /gitlab 相对路径,访问根路径 http://<目标服务器地址>/ 可能出现异常或跳转不符合预期。
首次启动日志判断
Docker GitLab 首次启动或执行 gitlab-ctl reconfigure 时,后台会持续输出 Chef 配置日志,例如创建 Prometheus 服务目录:
create new directory /opt/gitlab/sv/prometheus/control
link[/opt/gitlab/init/prometheus] action create
directory[/opt/gitlab/sv/prometheus/log] action create
template[/opt/gitlab/sv/prometheus/run] action create
此类日志属于配置过程输出,不代表错误。初始化阶段需要等待 gitlab Reconfigured! 或服务状态正常后再访问页面。
判断容器状态:
docker ps -a --filter name=gitlab
正常初始化常见状态:
Up ... (health: starting)
异常状态常见表现:
Restarting
Exited
查看最近日志:
docker logs --tail=200 gitlab
无法访问排查
检查端口映射
docker port gitlab
预期结果示例:
80/tcp -> 0.0.0.0:80
22/tcp -> 0.0.0.0:2222
检查宿主机 80 端口占用:
sudo ss -lntp | grep ':80'
若宿主机已存在 Nginx、Apache 等服务占用 80 端口,GitLab 容器端口映射可能失败。
检查服务状态
docker exec -it gitlab gitlab-ctl status
查看 Puma 日志:
docker exec -it gitlab gitlab-ctl tail puma
查看 Nginx 日志:
docker exec -it gitlab gitlab-ctl tail nginx
查看 Workhorse 日志:
docker exec -it gitlab gitlab-ctl tail gitlab-workhorse
容器内访问测试
docker exec -it gitlab curl -I http://127.0.0.1/gitlab
容器内访问正常、宿主机访问失败时,优先排查端口映射、防火墙、安全组。容器内访问失败时,优先排查 GitLab 服务状态与配置。
检查内存
GitLab 15.x Docker 对内存要求较高。内存不足可能导致初始化耗时过长、Puma 启动失败、页面 502。
查看内存:
free -h
查看容器是否发生 OOM:
docker inspect gitlab --format '{{.State.OOMKilled}}'
返回 true 表示容器曾因内存不足被系统终止。
迁移完成后验证项
迁移完成后应验证以下内容:
- 用户、组、项目是否完整
- Git 仓库是否可 clone、pull、push
- HTTP Clone URL 是否包含
/gitlab - SSH Clone URL 与端口是否符合预期
- Issue、Merge Request、Wiki 是否正常
- CI/CD 变量是否可读取
- Runner 是否可连接
- Webhook 是否可触发
- LFS、Artifacts、Package Registry 是否正常
- Container Registry 是否按需恢复
- SMTP、LDAP、Omniauth、对象存储等配置是否正常
- HTTPS 证书与反向代理配置是否符合目标端环境
后续升级建议
GitLab 15.9.1 版本较旧。迁移过程应先完成同版本 Docker 恢复,确认数据、服务、访问、仓库操作均正常后,再按照官方升级路径逐步升级。
推荐顺序:
GitLab 15.9.1 Docker 恢复成功
完整验证业务功能
备份 Docker 环境
按照 GitLab 官方升级路径逐步升级
禁止使用旧版本备份直接恢复到最新版 GitLab,避免数据库迁移跨度过大导致恢复失败或数据异常。
常见风险总结
| 风险项 | 影响 | 处理方式 |
|---|---|---|
| 版本不一致 | 备份恢复失败 | Docker 镜像固定为 15.9.1 |
| CE/EE 类型不一致 | 恢复失败或功能异常 | 源端确认包类型后选择对应镜像 |
缺失 gitlab-secrets.json |
CI/CD 变量、Token、2FA 解密失败 | 源端单独复制密钥文件 |
未保留 /gitlab 相对路径 |
页面路径、Clone URL 异常 | external_url 保留 /gitlab |
| SSH 端口映射不一致 | SSH Clone URL 变化 | 配置 gitlab_shell_ssh_port |
旧 gitlab.rb 直接覆盖 |
Docker 路径或端口异常 | 比对迁移关键配置 |
| 宿主机 80 端口占用 | 页面无法访问 | 检查端口占用与映射 |
| 内存不足 | 初始化卡住、502、容器重启 | 增加内存或排查 OOM |
| 对象存储未迁移 | Artifacts、LFS、Registry 数据缺失 | 单独迁移对象存储数据 |
最小化迁移流程
源端:
sudo gitlab-rake gitlab:env:info
sudo gitlab-backup create
sudo mkdir -p /root/gitlab-migrate
sudo cp /etc/gitlab/gitlab.rb /root/gitlab-migrate/
sudo cp /etc/gitlab/gitlab-secrets.json /root/gitlab-migrate/
scp /var/opt/gitlab/backups/*_gitlab_backup.tar root@<目标服务器地址>:/root/
scp /root/gitlab-migrate/gitlab.rb root@<目标服务器地址>:/root/
scp /root/gitlab-migrate/gitlab-secrets.json root@<目标服务器地址>:/root/
目标端:
export GITLAB_HOME=/srv/gitlab
sudo mkdir -p $GITLAB_HOME/config $GITLAB_HOME/logs $GITLAB_HOME/data
docker compose up -d
docker compose stop
sudo cp /root/*_gitlab_backup.tar /srv/gitlab/data/backups/
sudo chown -R 998:998 /srv/gitlab/data/backups
sudo cp /root/gitlab-secrets.json /srv/gitlab/config/gitlab-secrets.json
docker compose start
docker exec -it gitlab gitlab-ctl stop puma
docker exec -it gitlab gitlab-ctl stop sidekiq
docker exec -it gitlab gitlab-backup restore BACKUP=备份文件名前缀
docker exec -it gitlab gitlab-ctl reconfigure
docker restart gitlab
docker exec -it gitlab gitlab-rake gitlab:check SANITIZE=true
docker exec -it gitlab gitlab-rake gitlab:doctor:secrets
docker exec -it gitlab gitlab-ctl status
结论
GitLab 15.9.1 从 Omnibus 安装迁移到 Docker 部署,核心路径为同版本 Docker 初始化、备份文件导入、密钥文件迁移、备份恢复、服务检查。迁移成功的关键条件包括版本一致、CE/EE 一致、gitlab-secrets.json 完整、external_url 保留 /gitlab、SSH 端口配置符合目标端网络环境。首次启动期间出现 Prometheus、服务目录、Chef 配置日志属于正常初始化过程,应结合容器状态、服务状态、端口映射、内存状态判断是否异常。