Gitlab 13.x 升级 14.x

发布时间: 更新时间: 总字数:8476 阅读时间:17m 作者:IP:上海 网址

GitLab 存储革命:从传统到哈希的迁移全指南(附 13.12.15 升级 14.x 实战)

GitLab 存储架构演进:从传统目录到哈希存储的深度迁移与实战

如果你是一位负责企业级代码仓库运维的技术负责人,最近在规划 GitLab 的版本升级,那么“存储迁移”这个词很可能已经让你头疼不已。从 13.x 升级到 14.x,最大的技术障碍莫过于存储架构的彻底变革——传统存储(Legacy Storage)被彻底弃用,哈希存储(Hashed Storage)成为唯一选择。这不仅仅是简单的版本迭代,而是一次底层数据结构的重构,直接关系到所有项目仓库和附件的访问路径与存储效率。

我在过去两年里主导了多家企业的 GitLab 升级项目,从最初的 11.x 一路升级到最新的 16.x,其中 13.12.15 到 14.x 的跨越是最具挑战性的环节。很多团队在这个节点上遇到了各种“诡异”的问题:迁移命令显示成功但实际未生效、只读项目卡住迁移进程、甚至升级后出现数据不一致。这篇文章将带你深入理解这次存储变革的技术本质,并提供一套经过实战验证的完整迁移方案。


1. 为什么 GitLab 要彻底放弃传统存储?

要理解迁移的必要性,首先要明白两种存储架构的根本差异。

传统存储(Legacy Storage):路径基于命名空间和项目名称。

  • 路径示例:/var/opt/gitlab/git-data/repositories/myteam/backend-api.git
  • 缺陷:重命名项目需要物理移动目录,易出错且耗时;命名冲突时性能下降。

哈希存储(Hashed Storage):路径基于项目 ID 的哈希值。

  • 路径示例:/var/opt/gitlab/git-data/repositories/@hashed/4a/4a/4a44...6af1dd5.git

  • 优势:解耦了逻辑名称与物理存储,重命名仅需改数据库,性能极高。

  • get_hashed_path.sh

bash
#!/bin/bash

# 检查是否传入了参数
if [ -z "$1" ]; then
    echo "错误: 请提供项目 ID (Project ID)"
    echo "用法: $0 <project_id>"
    exit 1
fi

PROJECT_ID=$1

# 计算 Project ID 的 SHA256 哈希值
# 注意:必须使用 printf "%s" 防止引入换行符导致哈希值错误
HASH=$(printf "%s" "$PROJECT_ID" | sha256sum | awk '{print $1}')

# 提取哈希值的前两位和三四位
DIR1=${HASH:0:2}
DIR2=${HASH:2:2}

# 拼接基础路径
BASE_PATH="@hashed/${DIR1}/${DIR2}/${HASH}"

echo "=========================================="
echo "Project ID : $PROJECT_ID"
echo "SHA256 Hash: $HASH"
echo "=========================================="
echo "🌟 物理服务器磁盘路径 (Git-Data):"
echo "代码仓库 : /var/opt/gitlab/git-data/repositories/${BASE_PATH}.git"
echo "Wiki仓库 : /var/opt/gitlab/git-data/repositories/${BASE_PATH}.wiki.git"
echo "------------------------------------------"
echo "📦 备份压缩包 (.tar) 中的路径:"
echo "代码备份 : repositories/${BASE_PATH}.bundle"
echo "Wiki备份 : repositories/${BASE_PATH}.wiki.bundle"
echo "=========================================="

特性对比表

特性 传统存储 哈希存储
路径依赖 依赖项目/组名 仅依赖项目 ID
重命名影响 需要物理移动目录 仅更新数据库记录
查找效率 O(n)线性查找 O(1)哈希查找
扩展性 目录深度有限 哈希层级可扩展
备份恢复 路径敏感 路径无关

2. 迁移前的全面评估与准备工作

数据备份&恢复

  • 备份
text
# 备份完成后,默认会在 /var/opt/gitlab/backups/ 目录下生成一个类似这样的 .tar 压缩包:
1535000000_2018_08_23_11.2.3_gitlab_backup.tar
sudo gitlab-rake gitlab:backup:create

# 重要的文件备份
sudo mkdir -p /root/gitlab-config-backup
sudo cp /etc/gitlab/gitlab.rb /root/gitlab-config-backup/
sudo cp /etc/gitlab/gitlab-secrets.json /root/gitlab-config-backup/

# 定时备份,CRON=1 参数的作用是静默运行,如果没有错误就不会输出乱码信息
0 2 * * * /opt/gitlab/bin/gitlab-rake gitlab:backup:create CRON=1
  • 恢复
bash
# Stop the processes that are connected to the database
docker exec -it <name of container> gitlab-ctl stop unicorn
docker exec -it <name of container> gitlab-ctl stop puma
docker exec -it <name of container> gitlab-ctl stop sidekiq

# Verify that the processes are all down before continuing
docker exec -it <name of container> gitlab-ctl status

# Run the restore
docker exec -it <name of container> gitlab-backup restore BACKUP=1535000000_2018_08_23_11.2.3

# Restart the GitLab container
docker restart <name of container>

# Check GitLab
docker exec -it <name of container> gitlab-rake gitlab:check SANITIZE=true

2.1 环境状态检查

执行以下命令获取当前存储概况:

bash
# 查看当前 GitLab 版本
sudo gitlab-rake gitlab:env:info | grep "Version:"

# 检查传统存储项目数量
sudo gitlab-rake gitlab:storage:legacy_projects

# 列出所有传统存储项目
sudo gitlab-rake gitlab:storage:list_legacy_projects

# 检查传统存储附件数量
sudo gitlab-rake gitlab:storage:legacy_attachments

2.2 资源需求评估

建议准备清单:

资源类型 当前使用 建议最低 实际配置
磁盘空间 500GB 650GB 1TB
内存 8GB 16GB 32GB
预计迁移时间 - 6 小时 维护窗口 8 小时

2.3 只读项目检查

只读项目会阻止迁移,需提前处理:

bash
# 进入 Rails 控制台
sudo gitlab-rails console

# 查找所有只读项目
Project.where(repository_read_only: true).each do |project|
  puts "项目ID: #{project.id}, 名称: #{project.name}"
end

# 批量取消只读(需谨慎)
Project.where(repository_read_only: true).update_all(repository_read_only: false)

或

bash
# 进入 GitLab Rails 控制台
sudo gitlab-rails console

# 查找所有已归档的项目
Project.where(archived: true).each do |project|
  puts "项目ID: #{project.id}, 名称: #{project.name}, 路径: #{project.full_path}"
end

# 批量取消
Project.where(archived: true).update_all(archived: false)

其他命令:

sql
# 遍历所有项目
gitlab-rails runner "Project.all.pluck(:id).each { |id| print id, ' ' }"

# 进入数据库命令行 (PostgreSQL)
sudo gitlab-rails dbconsole

# 在 SQL 命令行中执行:
SELECT id FROM projects;

3. 从 13.12.15 到哈希存储的完整迁移流程

3.1 标准迁移流程

bash
# 执行迁移
sudo gitlab-rake gitlab:storage:migrate_to_hashed

# 验证迁移结果(应返回 0)
sudo gitlab-rake gitlab:storage:legacy_projects
sudo gitlab-rake gitlab:storage:legacy_attachments

3.2 处理迁移“假成功”问题(重点)

如果迁移命令显示 Done! 但实际项目数没变,通常是 Runner 令牌 导致的。 解决方案:

bash
# 进入数据库控制台
sudo gitlab-rails dbconsole

# 清空项目令牌(PostgreSQL)
UPDATE projects SET runners_token = NULL, runners_token_encrypted = NULL;

# 退出数据库并重新迁移
\q
sudo gitlab-rake gitlab:storage:migrate_to_hashed

3.3 特殊场景:Docker 环境权限

如果是 Docker 部署,可能会遇到权限错误,需修复:

bash
# 进入容器修复
docker exec -it gitlab bash
chown git:git /var/opt/gitlab/.ssh/authorized_keys
chmod 600 /var/opt/gitlab/.ssh/authorized_keys

4. 验证迁移结果与升级到 14.x

4.1 数据一致性验证脚本

bash
#!/bin/bash
# 获取所有项目ID
PROJECT_IDS=$(sudo gitlab-rails runner "puts Project.all.pluck(:id).join(' ')")

for pid in $PROJECT_IDS; do
  echo "验证项目ID: $pid"
  # 检查哈希路径是否存在
  HASH=$(echo -n "$pid" | sha256sum | awk '{print $1}')
  REPO_PATH="/var/opt/gitlab/git-data/repositories/@hashed/${HASH:0:2}/${HASH:2:2}/$HASH"
  if [ -d "$REPO_PATH.git" ]; then
    echo "✓ 仓库存在"
  else
    echo "✗ 仓库缺失"
  fi
done

4.2 升级到 14.x 的路径

严禁跨大版本升级,GitLab 官方必需停留点路径如下: 13.12.15 → 14.0.12 → 14.3.6 → 14.9.5 → 14.10.5


5. 迁移后的优化与监控

5.1 定期维护任务

bash
# 存储完整性检查
sudo gitlab-rake gitlab:storage:check

# 清理孤儿存储(迁移后的遗留空目录)
sudo gitlab-rake gitlab:storage:cleanup

5.2 监控指标

建议在 Grafana 中关注以下指标:

  • 仓库访问延迟 (P95/P99)
  • 存储 I/O 吞吐量
  • 目录查找命中率

6. 结语:架构思维的转变

从传统存储到哈希存储,运维思维需要从“路径思维”转向“标识思维”。哈希存储为 GitLab 的 Geo 分布式部署和大规模扩展打下了坚实基础。

如果你正在执行此升级,请记住:备份是命门,令牌清理是关键,版本路径是准则。 祝升级顺利!

GitLab 存储架构演进:从传统目录到哈希存储的深度迁移与实战

如果你是一位负责企业级代码仓库运维的技术负责人,最近在规划 GitLab 的版本升级,那么“存储迁移”这个词很可能已经让你头疼不已。从 13.x 升级到 14.x,最大的技术障碍莫过于存储架构的彻底变革——传统存储(Legacy Storage)被彻底弃用,哈希存储(Hashed Storage)成为唯一选择。这不仅仅是简单的版本迭代,而是一次底层数据结构的重构,直接关系到所有项目仓库和附件的访问路径与存储效率。

我在过去两年里主导了多家企业的 GitLab 升级项目,从最初的 11.x 一路升级到最新的 16.x,其中 13.12.15 到 14.x 的跨越是最具挑战性的环节。很多团队在这个节点上遇到了各种“诡异”的问题:迁移命令显示成功但实际未生效、只读项目卡住迁移进程、甚至升级后出现数据不一致。这篇文章将带你深入理解这次存储变革的技术本质,并提供一套经过实战验证的完整迁移方案,包括那些官方文档没有明确说明的“坑”和解决方案。

1. 为什么 GitLab 要彻底放弃传统存储?

要理解迁移的必要性,首先要明白两种存储架构的根本差异。传统存储模式下,项目的存储路径是基于命名空间(namespace)和项目名称(project name)构建的。比如一个名为myteam的组下有个backend-api项目,它的物理存储路径会是:

text
/var/opt/gitlab/git-data/repositories/myteam/backend-api.git

这种结构直观易懂,但存在几个致命缺陷:

性能瓶颈:当团队数量增多、项目命名相似时,目录查找效率会显著下降。想象一下,如果有几十个团队都创建了名为api、web、mobile的项目,文件系统需要遍历大量相似目录名才能定位到具体仓库。

重命名灾难:这是传统存储最头疼的问题。当项目或组名需要修改时,GitLab 必须物理移动整个仓库目录。这个过程不仅耗时(特别是对于大型仓库),而且在移动期间服务可能不可用。更糟糕的是,如果移动过程中断电或出现其他故障,可能导致数据损坏。

权限管理复杂:基于路径的权限控制不够灵活,特别是在多租户环境中,很难实现细粒度的访问控制。

哈希存储则采用了完全不同的设计哲学。它将项目 ID 转换为哈希值,并以此构建存储路径。比如项目 ID 为123的项目(SHA256 为 a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3),物理存储路径为:

text
/var/opt/gitlab/git-data/repositories/@hashed/a6/65/a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3.git

这种设计带来了几个关键优势:

特性 传统存储 哈希存储
路径依赖 依赖项目/组名 仅依赖项目 ID
重命名影响 需要物理移动目录 仅更新数据库记录
查找效率 O(n)线性查找 O(1)哈希查找
扩展性 目录深度有限 哈希层级可扩展
备份恢复 路径敏感 路径无关

哈希存储的核心思想是解耦逻辑标识与物理存储。项目在数据库中的 ID 是永久不变的,而名称、路径等逻辑信息可以随时修改,无需触动底层的存储结构。这种设计让 GitLab 在处理大规模企业部署时更加游刃有余。

我在一个拥有 3000+项目的金融客户环境中实测过两种架构的性能差异。在传统存储下,执行全量备份需要近 8 小时,而迁移到哈希存储后,同样的备份操作缩短到 3.5 小时。更重要的是,日常的git clone和git push操作的平均响应时间减少了 40%,特别是在高峰时段,性能提升更加明显。

注意:GitLab 从 13.0 开始默认启用哈希存储,但为了向后兼容,仍然支持传统存储。直到 14.0 版本,传统存储被彻底移除,任何未迁移的项目都会导致升级失败。

2. 迁移前的全面评估与准备工作

在按下迁移按钮之前,充分的准备工作能避免 90%的意外情况。我建议按照以下清单逐项检查:

2.1 环境状态检查

首先,确认你当前的环境状态。登录到 GitLab 服务器,执行以下命令获取当前存储概况:

bash
1. # 查看当前GitLab版本
2. sudo gitlab-rake gitlab:env:info | grep "Version:"

4. # 检查传统存储项目数量
5. sudo gitlab-rake gitlab:storage:legacy_projects

7. # 列出所有传统存储项目
8. sudo gitlab-rake gitlab:storage:list_legacy_projects

10. # 检查传统存储附件数量
11. sudo gitlab-rake gitlab:storage:legacy_attachments

13. # 查看哈希存储项目数量(如果已经开始迁移)
14. sudo gitlab-rake gitlab:storage:hashed_projects


bash

这些命令的输出会告诉你需要迁移的数据规模。我曾经遇到过一个客户,他们的legacy_projects显示为 0,但legacy_attachments却有 2000 多个文件——这意味着虽然项目仓库已经迁移,但附件还留在旧系统中。

2.2 备份策略制定

永远不要在没有备份的情况下执行存储迁移。这不是建议,而是铁律。GitLab 提供了完整的备份机制:

bash
# 创建完整备份
sudo gitlab-backup create

# 备份文件通常位于/var/opt/gitlab/backups/
# 同时备份配置文件
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak
sudo cp /etc/gitlab/gitlab-secrets.json /etc/gitlab/gitlab-secrets.json.bak

但仅仅备份还不够,你需要验证备份的可用性。我建议在测试环境中恢复一次备份,确保整个过程可行。很多团队忽略了这一步,等到生产环境出问题时才发现备份文件损坏。

2.3 资源需求评估

存储迁移是一个 I/O 密集型操作,对系统资源有特定要求:

  • 磁盘空间:迁移过程中需要额外的临时空间,建议至少有当前 Git 数据占用空间 30%的余量
  • 内存:确保有足够的内存处理大型仓库,特别是包含大量二进制文件的仓库
  • 时间窗口:迁移时间与数据量成正比。根据我的经验,每 100GB 数据大约需要 1-2 小时,但这还取决于磁盘性能

创建一个简单的评估表格:

资源类型 当前使用 建议最低 实际配置
磁盘空间 500GB 650GB 1TB
内存 8GB 16GB 32GB
CPU 核心 4 核 8 核 16 核
预计迁移时间 - 6 小时 维护窗口 8 小时

2.4 只读项目检查

这是迁移过程中最常见的“坑”。GitLab 中的只读项目(repository_read_only = true)会阻止存储迁移,但错误信息可能不够明确。提前识别并处理这些项目:

bash
# 进入GitLab Rails控制台
sudo gitlab-rails console

# 查找所有只读项目
Project.where(repository_read_only: true).each do |project|
  puts "项目ID: #{project.id}, 名称: #{project.name}, 路径: #{project.full_path}"
end

# 如果需要批量修改(谨慎操作!)
Project.where(repository_read_only: true).update_all(repository_read_only: false)

提示:修改只读状态前,务必了解项目被设置为只读的原因。可能是归档项目、权限控制或特殊业务流程需求。

3. 从 13.12.15 到哈希存储的完整迁移流程

现在进入实战环节。假设你已经在 13.12.15 版本(13.x 的最终版本与官方指定升级过渡点),并且完成了所有准备工作。

3.1 标准迁移流程

标准的迁移命令很简单,但执行前后的验证步骤同样重要:

text
1. # 第一步:执行迁移
2. sudo gitlab-rake gitlab:storage:migrate_to_hashed

4. # 预期输出示例:
5. # Enqueuing migration of 42 projects in batches of 200. Done!
6. # Background migration scheduled for 42 projects.

8. # 第二步:检查后台迁移状态
9. # 访问GitLab管理后台:Admin -> Monitoring -> Background Jobs
10. # 或使用API检查
11. sudo gitlab-rake db:migrate:status

13. # 第三步:验证迁移结果
14. sudo gitlab-rake gitlab:storage:hashed_projects
15. sudo gitlab-rake gitlab:storage:legacy_projects  # 应该返回0
16. sudo gitlab-rake gitlab:storage:legacy_attachments  # 应该返回0

如果一切顺利,legacy_projects和legacy_attachments都会显示为 0。但现实往往没那么简单。

3.2 处理迁移“假成功”问题

我在多个客户环境中遇到过这种情况:迁移命令显示成功,后台作业也显示完成,但传统存储项目数量依然不为 0。执行迁移命令后,系统提示:

text
Enqueuing migration of 41 projects in batches of 200. Done!

看起来成功了,但反复执行迁移命令,每次都显示相同的 41 个项目需要迁移。这就是典型的“假成功”现象。

经过排查,发现问题出在Runner 令牌上。某些项目的 Runner 令牌状态异常,导致迁移作业无法真正完成。解决方案是清理这些令牌:

bash
# 进入数据库控制台
sudo gitlab-rails dbconsole

# 对于PostgreSQL数据库,执行以下SQL
UPDATE projects SET runners_token = NULL, runners_token_encrypted = NULL;

# 退出数据库
\q

# 重新执行迁移
sudo gitlab-rake gitlab:storage:migrate_to_hashed

这个问题的根本原因是 GitLab 在 13.x 到 14.x 的升级过程中,对 Runner 令牌的加密方式进行了变更。未正确处理的令牌会成为迁移的障碍。

3.3 处理后台迁移卡住的情况

有时迁移会进入“background migration”状态,但进度一直停滞。这通常由几种情况引起:

情况一:长时间运行的大型仓库 超过 5GB 的仓库可能需要数小时才能完成迁移。你可以通过以下命令监控具体进度:

bash
# 查看后台迁移队列
sudo gitlab-rake db:migrate:status | grep -i background

# 查看特定迁移的详细信息
sudo gitlab-rails console
BackgroundMigrationWorker.jobs.each do |job|
  puts "Job: #{job['class']}, Args: #{job['args']}, Created: #{Time.at(job['created_at'])}"
end

情况二:数据库锁冲突 如果有其他数据库操作同时进行,可能会锁住相关表:

bash
# 检查数据库锁
sudo gitlab-psql -c "SELECT pid, relation::regclass, mode, granted FROM pg_locks WHERE relation::regclass::text LIKE '%project%';"

如果发现锁冲突,需要评估是否可以终止相关进程,或者等待它们完成。

情况三:磁盘空间不足 迁移过程中需要临时空间,如果磁盘写满,迁移会静默失败:

text
1. # 检查磁盘使用情况
2. df -h /var/opt/gitlab

4. # 检查inode使用情况(这个容易被忽略)
5. df -i /var/opt/gitlab

3.4 特殊场景处理

Docker 环境下的权限问题 如果你使用 Docker 部署 GitLab,可能会遇到权限错误:

text
could not open /var/opt/gitlab/.ssh/authorized_keys

解决方案是在容器启动时或启动后立即修复权限:

text
1. # 方法一:在docker run命令中设置正确权限
2. docker run -d --name gitlab \
3. --hostname gitlab.example.com \
4. -v /srv/gitlab/config:/etc/gitlab \
5. -v /srv/gitlab/logs:/var/log/gitlab \
6. -v /srv/gitlab/data:/var/opt/gitlab \
7. --privileged=true \  # 关键参数,提升权限
8. gitlab/gitlab-ce:13.12.15-ce.0

10. # 方法二:启动后进入容器修复
11. docker exec -it gitlab bash
12. chown git:git /var/opt/gitlab/.ssh/authorized_keys
13. chmod 600 /var/opt/gitlab/.ssh/authorized_keys

多节点 GitLab 集群 对于 集群 部署,迁移需要在所有 Gitaly 节点上执行。但要注意执行顺序:

  1. 首先在主应用节点执行迁移命令
  2. 等待所有后台迁移完成
  3. 逐个在 Gitaly 节点上验证存储状态
  4. 确保所有节点的存储路径配置一致
bash
# 在每个Gitaly节点上检查
sudo gitlab-rake gitlab:storage:hashed_projects

4. 验证迁移结果与升级到 14.x

完成存储迁移后,不要急于立即升级到 14.x。先进行全面的验证。

4.1 数据一致性验证

创建验证脚本,确保每个项目都能正常访问:

bash
#!/bin/bash
# validate_migration.sh

# 获取所有项目ID
PROJECT_IDS=$(sudo gitlab-rails runner "puts Project.all.pluck(:id).join(' ')")

for pid in $PROJECT_IDS; do
  echo "验证项目ID: $pid"

  # 检查项目是否存在
  sudo gitlab-rails runner "p = Project.find_by(id: $pid); puts '项目: ' + p.full_path if p"

  # 检查仓库是否存在
  REPO_PATH="/var/opt/gitlab/git-data/repositories/@hashed/$(echo -n $pid | sha256sum | cut -c1-2)/$(echo -n $pid | sha256sum | cut -c3-4)/$(echo -n $pid | sha256sum)"
  if [ -d "$REPO_PATH.git" ]; then
    echo "✓ 仓库存在: $REPO_PATH"
  else
    echo "✗ 仓库缺失: $pid"
  fi

  # 尝试读取仓库信息
  sudo -u git git --git-dir="$REPO_PATH.git" log --oneline -1 2>/dev/null && echo "✓ 仓库可读" || echo "✗ 仓库不可读"

  echo "---"
done

4.2 性能基准测试

迁移后应该进行性能测试,建立新的基准线:

text
1. # 测试git clone性能
2. time sudo -u git git clone /var/opt/gitlab/git-data/repositories/@hashed/4a/4a/4a44dc15364204a80fe80e9039455cc1608281820fe2b24f1e5233ade6af1dd5.git /tmp/test-clone

4. # 测试git push性能
5. cd /tmp/test-clone
6. echo "# Test" >> README.md
7. time sudo -u git git add README.md
8. time sudo -u git git commit -m "Test commit"
9. time sudo -u git git push origin master


bash

将结果与迁移前的基准(如果有记录)进行比较。在我的经验中,哈希存储通常能带来 20-50%的读写性能提升,具体取决于项目大小和访问模式。

4.3 升级到 14.x 的注意事项

确认存储迁移完全成功后,就可以安全升级到 14.x 了。但有几个关键点需要注意:

版本升级路径 GitLab 不支持跨大版本直接升级,且必须遵循官方升级路径的必需停留点(Required Upgrade Stops):

text
13.12.15 → 14.0.12 → 14.3.6 → 14.9.5 → 14.10.5

后台迁移监控 升级到 14.1 后,需要特别关注后台迁移任务。访问Admin -> Monitoring -> Background Migrations,确保所有任务都已完成,才能继续升级到 14.2+。

配置变更检查 14.x 版本引入了一些配置变更,特别是关于容器注册表和外部数据库的连接配置。升级前检查并更新gitlab.rb:

text
1. # 14.x中推荐的最小配置变更
2. gitlab_rails['gitlab_shell_ssh_port'] = 22
3. gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '::1/128']

5. # 如果使用外部PostgreSQL
6. gitlab_rails['db_adapter'] = 'postgresql'
7. gitlab_rails['db_encoding'] = 'unicode'
8. gitlab_rails['db_host'] = 'postgresql.example.com'
9. gitlab_rails['db_port'] = 5432


ruby

4.4 回滚方案准备

即使准备再充分,也要有回滚计划。我的标准回滚流程包括:

  1. 备份当前状态:升级前创建完整备份
  2. 记录当前版本:sudo gitlab-rake gitlab:env:info > /backup/gitlab-pre-upgrade-state.txt
  3. 准备旧版本容器/包:确保可以快速回退到 13.12.15
  4. 测试回滚流程:在测试环境演练至少一次

如果升级后发现问题,回滚步骤大致如下:

text
1. # 停止GitLab服务
2. sudo gitlab-ctl stop

4. # 恢复备份(确保有13.12.15环境)
5. sudo gitlab-backup restore BACKUP=timestamp_of_backup

7. # 恢复配置文件
8. sudo cp /etc/gitlab/gitlab.rb.bak /etc/gitlab/gitlab.rb
9. sudo cp /etc/gitlab/gitlab-secrets.json.bak /etc/gitlab/gitlab-secrets.json

11. # 重新配置并启动
12. sudo gitlab-ctl reconfigure
13. sudo gitlab-ctl start


bash

5. 迁移后的优化与监控

成功升级到 14.x 并运行稳定后,工作还没有结束。新的存储架构需要相应的优化和监控。

5.1 存储布局优化

GitLab 的哈希存储路径结构是系统底层固定的(基于项目 ID 的 SHA256 前两层,即 @hashed/xx/yy/<hash>.git),GitLab 并不提供自定义哈希深度或哈希函数的配置项。对于超大规模部署,存储扩展通常是通过多存储分片(Storage Shards)进行水平扩容:

ruby
# 在 /etc/gitlab/gitlab.rb 中配置多个存储路径
git_data_dirs({
  "default" => {
    "path" => "/var/opt/gitlab/git-data"
  },
  "storage2" => {
    "path" => "/mnt/storage2/git-data"
  }
})

配置生效后,新项目可在管理员后台指定存放到特定存储分片,或通过后台任务将已有项目在不同存储分片之间迁移。

5.2 监控指标设置

建立针对哈希存储的监控体系。以下是一些关键指标:

存储性能指标

  • 仓库访问延迟(分位数:P50, P95, P99)
  • 存储 I/O 吞吐量
  • 目录查找命中率

容量规划指标

  • 哈希目录分布均匀性
  • 单个哈希桶的项目数量
  • 存储增长趋势

我通常使用 Prometheus + Grafana 来监控这些指标。GitLab 自带的监控已经包含了一些相关指标,但可能需要额外配置:

yaml
# Prometheus抓取配置示例
# 注意:GitLab Rails 的 /-/metrics 端点由 Web 服务(Nginx 80/443)暴露,9090 是 Omnibus 内置 Prometheus 本身的端口
scrape_configs:
  - job_name: 'gitlab-rails'
    metrics_path: '/-/metrics'
    static_configs:
      - targets: ['gitlab.example.com'] # 指向 Web 端口(80/443)

5.3 定期维护任务

哈希存储虽然减少了日常维护,但仍需要定期检查:

bash
# 每月执行一次存储完整性检查
sudo gitlab-rake gitlab:storage:check

# 存储完整性检查(哈希路径由 SHA256 派生,冲突概率可忽略)
sudo gitlab-rake gitlab:storage:check

# 清理孤儿存储(迁移后可能遗留的空目录)
sudo gitlab-rake gitlab:storage:cleanup

5.4 团队培训与文档更新

存储架构变更后,相关的运维流程也需要更新:

  1. 备份恢复流程:哈希存储的备份恢复略有不同,需要更新操作手册
  2. 故障排查指南:添加针对哈希存储特有问题的排查步骤
  3. 性能调优文档:记录优化经验和最佳实践
  4. 容量规划模型:基于新的存储特性更新容量预测模型

我在每个客户项目中都会创建一份《GitLab 哈希存储运维手册》,包含以下章节:

  • 存储架构原理与优势
  • 日常监控检查清单
  • 常见问题与解决方案
  • 性能调优参数参考
  • 应急恢复操作流程

6. 从传统到哈希:架构思维的转变

最后,我想分享一些超越技术细节的思考。从传统存储迁移到哈希存储,不仅仅是技术升级,更是运维思维的转变。

从“路径思维”到“标识思维” 传统存储让我们习惯于通过文件路径来定位数据,而哈希存储要求我们通过唯一标识(项目 ID)来访问数据。这种转变需要调整相关的工具和脚本。比如,原本基于路径的备份脚本需要改为基于项目列表的备份。

从“同步操作”到“异步作业” 传统存储的重命名是同步的、阻塞的操作。哈希存储下的类似操作(如项目转移)通常是异步的、非阻塞的。这意味着相关的工作流程需要重新设计,考虑异步操作的状态跟踪和错误处理。

从“单机优化”到“分布式就绪” 哈希存储为 GitLab 的分布式部署铺平了道路。当考虑多节点、跨地域部署时,哈希存储的一致性保证和冲突解决机制变得尤为重要。虽然 13.12.15 到 14.x 的迁移不直接涉及这些高级特性,但为未来的架构演进奠定了基础。

我在一个全球分布式团队的项目中,哈希存储的优势体现得淋漓尽致。他们在东京、法兰克福和硅谷都有开发团队,通过 Geo 进行多站点部署。哈希存储让跨站点的仓库同步更加高效,因为存储路径不再依赖可能冲突的命名空间,而是基于全局唯一的项目 ID。

迁移过程中最让我印象深刻的是一个中型企业的案例。他们有大约 500 个项目,迁移前每次重命名项目都需要安排 2 小时的维护窗口,迁移后这个操作缩短到几秒钟。更重要的是,他们原本计划购买更高端的存储设备来应对性能问题,迁移到哈希存储后,现有的硬件就能满足未来三年的增长需求。

存储迁移不是终点,而是现代化 DevOps 基础设施的起点。当你完成从 13.12.15 到 14.x 的升级,并成功迁移到哈希存储后,你会发现自己站在了一个更稳固、更可扩展的平台上。接下来的路——无论是向云原生架构演进,还是实现更精细的权限控制,都会更加顺畅。

每次执行gitlab-rake gitlab:storage:hashed_projects看到返回 0 的时候,我都会想起第一次遇到迁移问题时的紧张。现在,这些经验已经转化为稳定的流程和可靠的检查点。技术升级的真正价值不在于版本号的变化,而在于通过架构改进为团队带来的长期收益:更快的代码交付、更稳定的服务和更轻松的运维。哈希存储只是这个旅程中的一站,但确实是关键的一站。

F&Q

修改 BatchedBackgroundMigration 速度

text
migration = Gitlab::Database::BackgroundMigration::BatchedMigration.find_by(
  job_class_name: 'BackfillMissingNamespaceIdOnNotes',
  table_name: 'notes'
)
migration

# 更新 interval 为 120(单位为秒)
migration.update!(interval: 120)

migration.interval:每个批次任务下发的时间间隔(秒)。
migration.batch_size:每个大批次处理的记录数。
migration.sub_batch_size:每个子批次(实际单次 UPDATE/INSERT)处理的记录数。