背景

公司 Jira Data Center v10.3.3 集群的共享存储(NFS)跑在一块 7.8TB 的企业级 NVMe SSD 上,挂载路径 /data,实际数据量 7.0TB——全是 Jira 附件,不含数据库。

要腾出这块 SSD 给别的业务,替换盘是一块 45.5TB 机械硬盘(/dev/sda,未初始化)。要求在下一个工作日 9:00 前完成迁移,尽可能减少停服时间。

集群拓扑

10.177.100.25  Nginx (反向代理/负载均衡)
       │
       ├──→ 10.177.100.26  Jira Node 1
       └──→ 10.177.100.27  Jira Node 2
                │                      │
                ├──→ 10.177.100.28  MySQL 主
                ├──→ 10.177.100.29  MySQL 从
                │
                └──→ 10.177.100.30  NFS Server  ← 本次迁移对象
节点角色操作系统说明
100.25NginxUbuntu 24.04nginx/1.31.2,反向代理 + 负载均衡
100.26Jira Node 1Ubuntu 24.04应用服务器,su - jira 操作
100.27Jira Node 2Ubuntu 24.04应用服务器,与 Node 1 组成集群
100.28MySQL 主库Ubuntu 24.04MySQL 8.0.33,socket 连接
100.29MySQL 从库Ubuntu 24.04MySQL 8.0.33,主从同步
100.30NFS ServerUbuntu 24.04Jira 共享存储,全部附件

所有 IP 为企业内网地址,仅运维团队可达。

存储现状

迁移前的磁盘布局:

vdb  7.8T  NVMe SSD  ── vdb1  ext4  /dev/vdb1  →  /data   (7.0T 已用)
sda  45.5T HDD       ── 未分区,未格式化

迁移后的目标布局:

vdb  7.8T  NVMe SSD  ── 腾出
sda  45.5T HDD       ── sda1  ext4  /dev/sda1  →  /data   (7.0T 已用)

机械盘容量是 NVMe 的近 6 倍,写入速度约 110~140 MB/s——这是全流程的硬瓶颈。

方案选型

方案做法迁移总耗时停服窗口文件数影响
rsync 两遍第一遍不停服、第二遍停服增量两遍扫描,文件多则 20h+第二遍扫描时间 — 百万文件扫描几小时
rsync 一遍停服后直接拷一次性扫完写完全程 — 同上
rsync 一遍(本方案)停服后一次性拷,不做增量10-14h全程 — 10~14h 内文件数不构成瓶颈

三选一,选了停服 rsync 一遍

理由是 /data 目录下只有 Jira 附件、索引缓存等静态文件,不含写入中的数据库。停服后数据完全冻结,不存在 rsync 过程中文件被修改导致不一致的问题。7.0TB 数据,机械盘顺序写入 110+ MB/s,数学上 10~14 小时能跑完,停服窗口 24 小时绰绰有余。

没有用 dd。dd 会复制整块 7.8TB 盘(含空闲空间),额外消耗约 1 小时且浪费目标盘空间。rsync 只传实际文件,且参数 -aHAX 能完整保留软链接、硬链接、ACL 和扩展属性。

完整操作记录

所有命令在 NFS Server(10.177.100.30,root 用户)上执行。

第一步:开 tmux 防断连

tmux new -s jira-migration

SSH 连接意外断开时,tmux attach -t jira-migration 恢复会话。

第二步:停服

# 停 Jira 所有节点
systemctl stop jira

# 确认 /data 无进程占用
lsof +D /data 2>/dev/null

第三步:sda 分区与格式化

# GPT 分区表 + 单分区
parted /dev/sda --script mklabel gpt
parted /dev/sda --script mkpart primary ext4 0% 100%

# ext4 格式化,-m 0 不给 root 预留空间
mkfs.ext4 -m 0 -L jira-data /dev/sda1

-m 0 是 45TB 大容量盘的关键参数。默认 -m 5 会预留 5% 给 root,45.5TB × 5% = 2.3TB 浪费。NFS 共享盘不需要这层保护,-m 0 全部给数据。

第四步:执行迁移

mkdir -p /mnt/sda1
mount /dev/sda1 /mnt/sda1

# 核心传输命令
rsync -aHAX --info=progress2 --numeric-ids /data/ /mnt/sda1/

参数逐项说明:

选项作用为什么重要
-aarchive 模式,等同 -rlptgoD保留权限、时间戳、软链接、属主/属组、设备文件
-H保留硬链接Jira 附件目录可能有硬链接,不保留会导致空间膨胀
-A保留 ACLNFS 客户端可能依赖 ACL 做访问控制
-X保留扩展属性安全上下文(SELinux/AppArmor)和 NFS xattr
--info=progress2全局进度条7TB 传输不显示整体进度没法预估完成时间
--numeric-ids数字 UID/GID不解析用户名,避免目标机器不存在同名用户导致 ID 映射错误
/data/ 末尾斜杠复制目录内容不加斜杠会在目标盘创建 /mnt/sda1/data/ 额外层级

第五步:实际耗时记录

时间节点状态已传输
周六 23:48启动 rsync0
周日 11:03运行 11h15m4.4 TB
周日 16:20运行 16h32m6.3 TB
周日 18:10完成7.0 TB

实际耗时约 18 小时 22 分钟,平均速度 111 MB/s。比预估的 10-14 小时慢,原因是 Jira 附件目录中夹杂数十万个小文件(图标、缩略图、日志片段),随机寻道拉低了机械盘的实际写入吞吐。

第六步:校验

# 数据量对比
du -sh /data /mnt/sda1
# 两边均为 7.0T

# 文件数对比
find /data -type f | wc -l
find /mnt/sda1 -type f | wc -l
# 两边一致

第七步:切换挂载

# 卸载旧盘
umount /data

# 挂载新盘
mount /dev/sda1 /data

# 更新 fstab
cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d)
blkid /dev/sda1
# UUID=a1b2c3...  记录这个值

vi /etc/fstab
# 删除 vdb 那行,添加:
# UUID=a1b2c3...  /data  ext4  defaults,nofail  0  2

第八步:启动与排错

# 重启 NFS 服务(挂载点切换后 NFS 需要刷新文件句柄)
exportfs -ra && systemctl restart nfs-server

# 启动 Jira
systemctl start jira

Jira 启动后报错:

JIRA couldn't create the jira.home directory
Ensure JIRA has permission to create and write to the
jira.home directory /data/jirasoftware/jira-home/shared_home.

原因:NFS 内核模块缓存了旧 vdb1 的文件句柄,Jira Node 在挂载点切换后看到的仍是旧设备。exportfs -ra 刷新了 NFS 导出表,但 Jira Node 的 NFS 客户端缓存未清理。

解决:重启 Jira Node 1 和 Node 2,强制刷新 NFS 客户端缓存。

# 在 10.177.100.26 和 10.177.100.27 上执行
reboot

重启后 Jira 正常启动,附件可访问,集群状态恢复。

日常运维速查

以下命令在日常运维中高频出现,按场景归类备查。

Jira 启停

# 启动
systemctl start jira

# 停止
systemctl stop jira

# 或者用 Jira 自带脚本
/data/jirasoftware/jira/bin/start-jira.sh
/data/jirasoftware/jira/bin/stop-jira.sh

MySQL 启停

systemctl start mysql
systemctl stop mysql
systemctl restart mysql

NFS 服务

systemctl start nfs-server
systemctl stop nfs-server
systemctl restart nfs-server

# 刷新导出表(修改 /etc/exports 后必须执行)
exportfs -ra

# 查看客户端挂载情况
showmount -e 10.177.100.30

磁盘检查

# 查看挂载点与磁盘对应关系
lsblk

# 查看 /data 使用量
df -h /data

# 查看 /data 内容概览
du -sh /data

关键配置文件位置

文件路径用途
Jira 集群配置/data/jirasoftware/jira-home/cluster.properties定义集群节点、共享目录路径
Jira 服务配置/data/jirasoftware/jira/conf/server.xml端口、上下文路径、连接池
Jira JVM 参数/data/jirasoftware/jira/bin/setenv.sh堆内存、GC 策略、JVM 选项
MySQL 配置/data/mysql/conf/my.cnf数据库引擎、缓冲池、二进制日志
NFS 导出/etc/exports允许哪些 IP 挂载 /data
自动挂载/etc/fstab系统启动时挂载磁盘

注意事项

  1. 停服前做最终状态确认。生产环境停服不是小事。除了 systemctl stop jira,还要 lsof +D /data 确认无一进程占用,NFS 客户端缓存可能导致静默写入。

  2. rsync 源路径末尾斜杠是坑/data//data 的区别是一层目录结构。用错了会让目标盘里塞一个多余的 /data/ 子目录,Jira 的 jira.home 配置指向的路径就找不到了。

  3. -m 0 不是默认参数mkfs.ext4 不给 -m 参数时默认预留 5% 给 root。45TB 上的 5% 是 2.3TB——大到摸得着。NFS 数据盘生产用途明确,不需要反碎片化保护,全部空间给业务数据。

  4. NFS 切换后必须刷新两端缓存。服务端 exportfs -ra 刷新导出表,客户端重启或重新挂载清除内核 NFS 句柄缓存。只做一端的效果是 Jira 无法写入 shared_home 目录。

  5. Jira Data Center 的 shared_home 路径不能变cluster.properties 里硬编码了 /data/jirasoftware/jira-home/shared_home。迁移时目标盘挂载点必须完全一致(/data),否则需要停机修改集群配置并重启所有节点。

  6. 回滚路径要保持到验证结束。迁移完成后 vdb 上的数据不要立刻清除。等 Jira 跑 2~3 个工作日,确认附件上传、全文搜索、集群同步都没问题,再考虑回收旧盘。遇到问题只需 umount /data && mount /dev/vdb1 /data && systemctl start jira 即可回退。

小结

从一块 NVMe SSD 到一块机械盘,迁移 7TB Jira 附件数据,核心就一条 rsync -aHAX --info=progress2 --numeric-ids /data/ /mnt/sda1/。参数选对了,数据属性一件不少;停服窗口估准了,团队沟通在先,操作过程没有意外。

机械盘写入带宽是硬天花板——111 MB/s 对 7TB 来说跑 18 小时无可避免。真正把时间从"超过 24 小时"压缩到 18 小时的是文件系统参数:-m 0 省了 2.3TB 无用写入,GPT 单分区省了 MBR 扩展分区链的开销。这些微调单独看都不起眼,叠在一起就是停服窗口能不能按时交付的差距。