📝 前言:天下苦“叠瓦”久矣
对于 GitLab 这种重度依赖 PostgreSQL 和 Redis,且会产生大量小文件并发读写的企业级重型应用来说,把数据放在叠瓦盘(SMR)上简直是一场灾难。网页卡顿、Commit 延迟、经常性的 I/O 拥塞让人心力交瘁。
为了拯救孱弱的 GitLab 性能,我决定将其底层数据从 2T 的叠瓦盘迁移到宿主机的 SSD 固态硬盘上。我的基础环境是:ESXi -> Ubuntu 22.04 虚拟机 -> Docker -> GitLab。
在这次实操中,我经历了虚拟机系统盘扩容、挂载点“狸猫换太子”式的数据无缝迁移、深度的磁盘空间排查,甚至顺手解决了一波 Docker 代理拉取镜像超时和 GitLab 的跨版本升级问题。本文将复盘整个过程,希望能为遇到类似问题的运维同学提供一份避坑指南。
🛠️ 第一战:系统盘爆满危机与原地热扩容
在准备把 GitLab 核心数据迁移到固态硬盘前,我在 ESXi 层面将这台 Ubuntu 虚拟机的系统固态盘从 120G 扩展到了 240G。
但当我登录系统执行 df -h 时,却惊出一身冷汗:根目录 /dev/sda3 的使用率已经飙升到了 93%,仅剩不到 9G 的可用空间!更关键的是,虽然 ESXi 给足了 240G 物理空间,但 Ubuntu 内部的 sda3 分区还不知道,依然被困在 120G 的局限里。
💡 解决方案:growpart 原地热扩容
由于我的系统没有使用 LVM,而是标准的物理分区,直接使用 growpart 工具即可完成无损扩容。
1. 安装扩容工具(部分精简版 Ubuntu 未预装)
sudo apt-get update
sudo apt-get install cloud-guest-utils -y
2. 扩展物理分区
# 注意 /dev/sda 和 3 之间有空格,代表扩容第3个分区
sudo growpart /dev/sda 3
3. 扩展文件系统
sudo resize2fs /dev/sda3
再次执行 df -h | grep sda3,看着根目录容量瞬间变成 236G,使用率暴降至 46%,系统的“爆满警报”彻底解除!
🔄 第二战:挂载点“狸猫换太子”与完美数据迁移
系统的可用空间有了,接下来的任务是把位于旧叠瓦盘(挂载在 /mnt/gitlabdata)上的 11G 数据,平移到刚扩容好的系统固态盘上。
最高法则:保持路径不变。 为了不修改任何 Docker 的启动配置(省去改 docker-compose 或 run 参数的麻烦),我决定采用“换盘不换名”的策略。
1. 停机与保护现场
在进行任何底层数据操作前,第一步永远是停止容器,防止产生新数据:
sudo docker stop gitlab
2. 解除旧盘羁绊,暴露出新盘空间
尝试直接使用 mv 重命名挂载目录时,系统无情地抛出了 Device or resource busy。这是因为旧叠瓦盘依然处于挂载状态。
正确的“剥离”姿势如下:
# 创建一个临时目录,供旧叠瓦盘暂住
sudo mkdir -p /mnt/smr_temp
# 强制卸载目标目录的旧盘
sudo umount /mnt/gitlabdata
# 把旧盘重新挂载到临时目录
sudo mount /dev/sdb1 /mnt/smr_temp
(此时,原本的 /mnt/gitlabdata 就暴露出了真面目——它变成了一个位于固态硬盘上的干净空文件夹了。)
3. rsync 完美拷贝
GitLab 对文件权限要求极其严苛,绝对不能用简单的 cp,必须使用带有保留权限、所有者和软链接功能的 rsync:
# 注意源目录最后的斜杠不可省略,代表拷贝目录下的所有内容
sudo rsync -avP /mnt/smr_temp/ /mnt/gitlabdata/
4. 切断旧盘的开机自启
数据迁移完毕后,修改 /etc/fstab,找到原本挂载叠瓦盘的那一行,在最前面加上 # 号注释掉,确保下次重启时系统直接使用固态硬盘的空间。
至此,底层数据完美从 SMR 转移到了 SSD,且上层 Docker 毫无察觉。
🧹 第三战:深度体检,揪出 20G 的“空间杀手”
既然换了大 house,自然要查查之前 100 多 G 的空间到底被谁吃掉了。这里我使用了像剥洋葱一样的层层递进排查法:
执行 sudo du -h --max-depth=1 /var/lib | sort -hr 后,我发现最大的“毒瘤”并不是想象中的 Docker 容器日志,而是:19G /var/lib/containerd
💡 真相与清理:
现在的 Docker 底层由 containerd 接管。因为 GitLab 的“全家桶”镜像实在太大(解压后单版本达 4.5G),历史的镜像更新和拉取失败残留,导致这里囤积了大量无用镜像。
直接一键“暴瘦”:
sudo docker system prune -a
这一行命令直接帮我清除了所有未使用的旧镜像,瞬间夺回了十几个 G 的物理空间!
⚠️ 番外篇:拆除“日志定时炸弹”
排查中,我还检查了 /etc/docker/daemon.json,发现了一颗前人埋下的雷:
"log-opts": {
"max-size": "2g",
"max-file": "30"
}
这意味着单个容器允许写入高达 60GB (2G × 30) 的日志!我立刻将其修改为 "max-size": "500m", "max-file": "6"(最多 3GB),防患于未然。
🛡️ 第四战:无损升级与破解拉取超时
数据到了 SSD,性能有了质的飞跃,我决定顺手把 GitLab 的版本从 18.10.4-ee 升级到官方推荐的安全补丁版 18.10.8-ee。
⚠️ 升级防坑指南: 升级前,必须确保后台的数据迁移任务已经全部完成。
运行命令检查:sudo docker exec -t gitlab gitlab-rake gitlab:background_migrations:status。确认没有未完成的任务后,方可操作。
突发情况:拉取镜像超时
在执行 docker pull 时,系统报出了 proxyconnect tcp: dial tcp 172.x.x.x: i/o timeout 的错误。由于网络环境更迭,Docker 守护进程依然咬着旧代理不放。
修改 Docker Systemd 代理配置破解:
# 1. 编辑代理文件
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
# 2. 填入新代理地址
[Service]
Environment="HTTP_PROXY=http://192.168.61.61:63106"
Environment="HTTPS_PROXY=http://192.168.61.61:63106"
Environment="NO_PROXY=localhost,127.0.0.1,::1"
# 3. 重启生效
sudo systemctl daemon-reload
sudo systemctl restart docker
最终的胜利:一键升级启动
解决了网络问题,顺利拉取到新镜像后,我删除了旧容器,并使用一模一样的挂载参数执行了一行华丽的启动命令:
sudo docker run -d -p 443:443 -p 8088:8088 -p 222:22 --name gitlab --restart always -v /mnt/gitlabdata/config:/etc/gitlab -v /mnt/gitlabdata/logs:/var/log/gitlab -v /mnt/gitlabdata/data:/var/opt/gitlab gitlab/gitlab-ee:18.10.8-ee.0
启动,重建,丝滑如初。
🎉 总结:大满贯的运维之夜
确认一切正常后,我在终端执行了 sudo umount /mnt/smr_temp,去 ESXi 控制台将那块陪伴多时的 2TB 叠瓦盘安全移除。
至此,大满贯达成:
- ✅ 底层存储: 成功扩容,彻底解除系统爆满危机。
- ✅ 数据迁移: 完美将核心代码从慢速 SMR 平移到 SSD,网页响应和代码 Push 秒级完成。
- ✅ 空间瘦身: 清理废弃镜像并锁死日志上限,目前 240G 的硬盘足足剩下 197G 的可用空间!
- ✅ 无损升级: 跨过网络坑,安全升至最新补丁版。
运维心得:
- 永远先备份!(快照 + 应用层备份是底线)。
- 理清 Linux 的挂载逻辑,遇到
Device busy不要慌,找准是谁占用了它。 - 拥抱
rsync和growpart,它们是 Linux 运维者最好的朋友。 - 永远不要忽略对 Docker 容器日志的管理。
告别叠瓦,拥抱纯固态,今晚的 GitLab,速度快得让人感动。
版权属于:soarli
本文链接:https://blog.soarli.top/archives/1090.html
转载时须注明出处及本声明。