头图
编者按:本文转载自公众号运维识堂,已经联系作者取得转载授权。

GitLab 在发展的十余年中,在国内积累了大量的 CE 用户,但是很多 CE 用户并不会跟随 GitLab 的发版节奏(月度发版)进行版本升级,在 GitLab 已经发布 16.4 版本的情况下,还有大量的用户使用 10 以下的版本。下面文章的作者就讲述了将使用源码编译安装的 GitLab 9.x 版本进行升级的过程,中间涉及了数据库的切换(从 Mysql 到 Postgres)、数据的备份和恢复、容器的操作等,过程虽然“看似简单”,但是却需要深厚的技术储备以及强大的信心才能完成这些操作,而且从测试验证到上生产历时两个月。对于很多运维人员来讲,下面的升级流程从测试环境到上生产环境都是一个极其大的挑战,如果出现问题,就可能导致企业内部的源代码托管平台不可用,影响公司的软件研发。

从另外一个角度来讲,这也是使用 CE 版带来的技术债,很多 CE 用户,担心升级过程出问题,秉承“不动就不出问题”的原则,一直在使用老旧版本,这样极容易导致产品不仅无法获取到最新的功能特性,还可能由于老旧版本存在的安全漏洞,容易让 GitLab 遭受外部攻击,长此以往,就造成了“升级是找死,不升级是等死”的困境。

其实,企业用户应该尽量使用企业版本(EE),除了能及时获取最新功能,还能享受企业级的服务支持,原厂服务轻松搞定产品升级、安全问题修复等,不仅能够将运维人员从升级恐惧症中解脱出来,还能利用最新的功能特性提升研发效率。

开始之前

从 2019 年开始,SonarQube 和 GitLab 相继宣布不在提供对 MySQL 方式存储数据的支持(Consider removing support for MySQL)。由于 Oracle 和 SQL Server 都是收费的商业数据库,而 PostgreSQL 则是开源的,加之最近几年针对 PostgreSQL 的创新不断和 ChatGPT 的火热,Postgres 再次进入我的 TODO 列表。从云厂 RDS Postgres 插件列表和几个开源项目(比如:Neon: Serverless Postgres无服务的 Postgres,timescaledb:基于 Postgres 的时序数据库扩展,FerretDB:将 MongoDB 协议转化为 SQL 使用 Postgres 作为存储后端)以及像这种基于 PostgreSQL 构建由 LLM 驱动的专属 ChatBot(其中用到的 pgvector)AI 应用场景。可见 Postgres 的火爆,所以想一探究竟。

因为 Postgres 在未来肯定是必须需要去了解和使用的,因为它已经变成很多开源软件的首选或者说默认数据库,所以到了必须要学习的地步。看完 PostgreSQL 教程,顿时想起之前 GitLab 迁移失败的痛苦经历(因为数据量大,一次测试 3 个小时没了,时间成本太高,以及一次次失败带来的压力让人精神崩溃)。最终在多次失败后不断总结原因,终于找到问题以及可能的解决办法。

破解之谜

因为最开始搭建的代码库是基于 GitLab 9.0.6 源代码部署并且采用外置 RDS MySQL 5.6 搭建,日常维护也就是扩盘或者升级 CPU 和内存,6 年从未升级过 GitLab(主要还是怕升级影响近千人的日常工作,又不是不能用何必自找麻烦呢)。一年前尝试做过升级测试,也咨询过厂商和社区(能搞这方面的人是真的少),最终失败(一个字穷)。现在回过头来看,还是没有认真看官方给的文档,从一开始升级思路就错了(下面给正确的解法)。

因为从 GitLab 12.1开始不在支持 MySQL 数据库,所以必须完成 MySQL 到 Postgres 的迁移。由于官方给的一体包(Omnibus GitLab Packages)默认就采用的 Postgres 数据库,为了后面版本升级方便,所以需要优先将当前版本的数据库迁移到 Postgres 上,下面主要分享 MySQL 迁移 Postgres 核心流程。

备份当前环境

#源代码安装的备份方式
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
# 注意:备份时注意磁盘空间,备份后会在备份目录产生一个带时间戳的 tar 包文件

安装一台新的 Linux 环境

➤ 安装 Docker
➤ 准备 3 个容器(mysql/postgres/pgloader)和 迁移配置文件
file: /data/gitlab/migrate/docker-compose.yml
version: '3'

services:
  pgloader:
    image: seanly/toolset:pgloader
    tty: true
    volumes:
      - ./:/ws
    restart: always
    security_opt:
      - seccomp:unconfined
  mysql:
    image: seanly/dbextra:mysql-5.7.35
    restart: unless-stopped
    volumes:
      - ./mysql-data:/var/lib/mysql
      - ./:/ws
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: 'gitlabhq_production'
    healthcheck:
      test: mysql --user=root --password=$$MYSQL_ROOT_PASSWORD -e "SHOW DATABASES;"
      interval: 3s
      timeout: 1s
      retries: 5
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_general_ci
      - --max_allowed_packet=512M

  postgres:
    image: postgres:11-alpine
    restart: always
    environment:
      - POSTGRES_USER=gitlab
      - POSTGRES_PASSWORD=gitlab123
      - POSTGRES_DB=gitlabhq_production
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
      - ./:/ws
    healthcheck:
      test: pg_isready -U gitlab -h 127.0.0.1
      interval: 5s
file: /data/gitlab/migrate/gitlab.loader
LOAD DATABASE     
  FROM mysql://root:root123@mysql:3306/gitlabhq_production     
  INTO pgsql://gitlab:gitlab123@postgres:5432/gitlabhq_production
  
WITH include no drop, truncate, disable triggers, create no tables,     
     create no indexes, preserve index names, no foreign keys,     
     data only,     
     workers = 8, concurrency = 16,     
     batch rows = 100000, batch size = 512MB, multiple readers per thread, rows per range = 500000
     
     SET MySQL PARAMETERS
     net_read_timeout = '90',
     net_write_timeout = '180'
     
     ALTER SCHEMA 'gitlabhq_production' RENAME TO 'public'
     
     ;

执行命令 docker-compose up -d 启动容器。

获取 MySQL 的备份

# 1. 将备份文件拷贝到新主机,假设存储在 /data/gitlab/backup 目录
# 2. 从 TIMESTAMP_gitlab_backup.tar 包获取 db/database.sql.gz

cd /data/gitlab/backup
tar xf TIMESTAMP_gitlab_backup.tar db
gunzip db/database.sql.gz

# /data/gitlab/backup/db/database.sql 就是 MySQL 的备份
mv /data/gitlab/backup/db/database.sql /data/gitlab/migrate/mysql_database.sql

获取 Postgres 的备份

这一步很关键,为了确保后面的升级成功,所以需要完整的一个空的数据库结构,数据可以通过 MySQL 库迁移。处理方式是通过容器初始化一个版本,然后备份里面的 Postgres。

➤ 启动一个 GitLab CE 9.0.6 实例
file: /data/gitalab/migrate/gitlab/docker-compose.yml
version: '3'
services:  
  gitlab:    
    image: 'gitlab/gitlab-ce:9.0.6-ce.0'    
    restart: always    
    volumes:      
      - './:/ws'    
    shm_size: '256m'

启动容器并备份数据库

cd /data/gitalab/migrate/gitlab
docker-compose up -d
docker-compose exec gitlab bash
# 容器内部
su - gitlab-psql
pg_dump -U gitlab-psql -h /var/opt/gitlab/postgresql -d gitlabhq_production -f /ws/gitlab_backup.sql
exit
# 容器外部
docker-compose down
mv gitlab_backup.sql /data/gitalab/migrate/

迁移 MySQL 到 Postgres

➤ 恢复 MySQL
cd /data/gitalab/migrate/
docker-compose exec mysql bash

# 进入 msyql 容器
mysql -u root -p gitlabhq_production < /ws/mysql_database.sql
# 输入密码 root123
➤ 恢复 Postgres
cd /data/gitalab/migrate/
docker-compose exec postgres bash

# 进入 postgres 容器

psql -U gitlab -d gitlabhq_production -f /ws/gitlab_backup.sql
➤ 迁移 MySQL 到 Postgres
docker-compose exec pgloader pgloader /ws/gitlab.loader

➤ 导出 Postgres
cd /data/gitalab/migrate/
docker-compose exec postgres bash

# 进入容器
pg_dump -U gitlab -d gitlabhq_production -f /ws/database.sql
exit
# 退出容器

➤ 恢复 Postgres 到备份包

mv /data/gitalab/migrate/database.sql /data/gitlab/backup/db/database.sql
cd /data/gitlab/backup/
# 打包 sql 为 sql.gz
gzip db/database.sql
# 更新备份包的 db/database.sql.gz
tar rf TIMESTAMP_gitlab_backup.tar db/database.sql.gz

恢复备份并升级

➤ 安装一个 GitLab CE 9.0.6 容器
file: /data/gitlab/test-upgrade/docker-compose.yml
version: '3'
services:  
  gitlab:    
  image: 'gitlab/gitlab-ce:9.0.6-ce.0'    
  restart: always    
  ports:      
    - '80:80'      
    - '443:443'      
    - '2222:22'    
  volumes:      
    - './config:/etc/gitlab'      
    - './data:/var/opt/gitlab'      
    - '/data/backup:/var/opt/gitlab/backups'    
  shm_size: '256m'
➤ 启动容器并恢复
mkdir -p /data/gitlab/test-upgrade/{data/backup,config}
cp -r /data/gitlab/backup/TIMESTAMP_gitlab_backup.tar /data/gitlab/test-upgrade/backup/

docker-compose up -d 
docker-compose exec gitlab bash 

# 进入容器
gitlab-rake gitlab:backup:restore RAILS_ENV=production BACKUP=${TIMESTAMP}

# 退出容器
docker-compose logs -f
➤ 升级版本

版本升级方式很简单,版本选择请看官方升级路径,通过修改 docker-compose.yml 里面的镜像版本,然后执行 docker-compose up -d 进行版本升级。

最后

虽然升级验证过程痛苦,但是学习到的东西也挺多。现在回看整个过程最关键的地方是只需要将 MySQL 数据通过 pgloader 迁移到GitLab Postgres 的数据库中,其他都是标准升级流程,再借助容器屏蔽工具对环境的依赖。

关于极狐GitLab

极狐GitLab 是 GitLab 面向国内用户推出的一体化 DevOps 平台。GitLab 作为极狐GitLab 的上游,意味着极狐GitLab 拥有和 GitLab 同等的功能,而且极狐GitLab 还针对国内用户开发了中专门面向国内企业的功能特性,比如和微信、钉钉、飞书的集成等。

极狐GitLab 本土化运营团队能够为国内企业用户提供专业的企业级服务支持,目前已经帮助数百家企业实现了 DevOps 的落地实践。极狐GitLab 专业的服务能力能够帮助企业实现 GitLab/极狐GitLab 的平滑升级。


极狐GitLab
64 声望36 粉丝

极狐(GitLab) 以“核心开放”为原则,面向中国市场,提供开箱即用的开放式一体化安全DevOps平台——极狐GitLab。通过业界领先的优先级管理、安全、风险和合规性功能,实现产品、开发、QA、安全和运维团队间的高效协同...