背景
公司 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.25 | Nginx | Ubuntu 24.04 | nginx/1.31.2,反向代理 + 负载均衡 |
| 100.26 | Jira Node 1 | Ubuntu 24.04 | 应用服务器,su - jira 操作 |
| 100.27 | Jira Node 2 | Ubuntu 24.04 | 应用服务器,与 Node 1 组成集群 |
| 100.28 | MySQL 主库 | Ubuntu 24.04 | MySQL 8.0.33,socket 连接 |
| 100.29 | MySQL 从库 | Ubuntu 24.04 | MySQL 8.0.33,主从同步 |
| 100.30 | NFS Server | Ubuntu 24.04 | Jira 共享存储,全部附件 |
所有 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/
参数逐项说明:
| 选项 | 作用 | 为什么重要 |
|---|---|---|
-a | archive 模式,等同 -rlptgoD | 保留权限、时间戳、软链接、属主/属组、设备文件 |
-H | 保留硬链接 | Jira 附件目录可能有硬链接,不保留会导致空间膨胀 |
-A | 保留 ACL | NFS 客户端可能依赖 ACL 做访问控制 |
-X | 保留扩展属性 | 安全上下文(SELinux/AppArmor)和 NFS xattr |
--info=progress2 | 全局进度条 | 7TB 传输不显示整体进度没法预估完成时间 |
--numeric-ids | 数字 UID/GID | 不解析用户名,避免目标机器不存在同名用户导致 ID 映射错误 |
/data/ 末尾斜杠 | 复制目录内容 | 不加斜杠会在目标盘创建 /mnt/sda1/data/ 额外层级 |
第五步:实际耗时记录
| 时间节点 | 状态 | 已传输 |
|---|---|---|
| 周六 23:48 | 启动 rsync | 0 |
| 周日 11:03 | 运行 11h15m | 4.4 TB |
| 周日 16:20 | 运行 16h32m | 6.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 | 系统启动时挂载磁盘 |
注意事项
停服前做最终状态确认。生产环境停服不是小事。除了
systemctl stop jira,还要lsof +D /data确认无一进程占用,NFS 客户端缓存可能导致静默写入。rsync 源路径末尾斜杠是坑。
/data/和/data的区别是一层目录结构。用错了会让目标盘里塞一个多余的/data/子目录,Jira 的jira.home配置指向的路径就找不到了。-m 0不是默认参数。mkfs.ext4不给-m参数时默认预留 5% 给 root。45TB 上的 5% 是 2.3TB——大到摸得着。NFS 数据盘生产用途明确,不需要反碎片化保护,全部空间给业务数据。NFS 切换后必须刷新两端缓存。服务端
exportfs -ra刷新导出表,客户端重启或重新挂载清除内核 NFS 句柄缓存。只做一端的效果是 Jira 无法写入shared_home目录。Jira Data Center 的 shared_home 路径不能变。
cluster.properties里硬编码了/data/jirasoftware/jira-home/shared_home。迁移时目标盘挂载点必须完全一致(/data),否则需要停机修改集群配置并重启所有节点。回滚路径要保持到验证结束。迁移完成后 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 扩展分区链的开销。这些微调单独看都不起眼,叠在一起就是停服窗口能不能按时交付的差距。