JuiceFS 元数据迁移演练手册(测试集群实战版)

目标:在你的测试 K8s 集群里,把"Redis → TiKV 元数据迁移"完整演练一遍:
只读导出 → 临时 TiKV → 导入验证 → 真挂载 → 完整切换回滚。
所有命令已按你的实测环境写好,照敲即可;玩坏了随时推倒重来。

0. 已实测的环境信息(直接可用)

项目实测值来源
文件系统名my-jfsMount Pod 的 mount 输出
Redis 地址juicefs-redis.juicefs-system.svc.cluster.local:6379 db 0Mount Pod cmdline
Redis Podjuicefs-system/juicefs-redis-0(StatefulSet + local-path)kubectl get pod
Mount Pod ×2kube-system/juicefs-liu-node1-pvc-18d437a4-1d22-4b9b-b2a9-2bbeb9d6c7bb-spcuko(node2 上还有一个 -ijvuhmkubectl get pod
业务 PVCdefault/nginx-www-pvc(动态)+ default/juicefs-archive-pvc(静态,当前闲置)kubectl get pv/pvc
Secret ×4kube-system/juicefs-sc-secretkube-system/juicefs-pvc-18d4...-secretkube-system/juicefs-juicefs-archive-pv-secretjuicefs-system/juicefs-redis-secretkubectl get secret
Redis 密码<REDIS密码> ← 占位,执行 §1.2 解码获得
master 内网 IP<MASTER_IP> ← 占位,kubectl get nodes -o wide 查看
# 全文统一用这两个变量,先 export 一次
export MOUNT_POD=juicefs-liu-node1-pvc-18d437a4-1d22-4b9b-b2a9-2bbeb9d6c7bb-spcuko
export MASTER_IP=<MASTER_IP>

1. 阶段 0:现状确认(部分已完成)

# 1.1 挂载确认 ✅ 已完成,输出:JuiceFS:my-jfs on /jfs/pvc-... type fuse.juicefs
kubectl -n kube-system exec $MOUNT_POD -- sh -c "mount | grep -i juicefs"

# 1.2 拿密码(解码主 Secret 的 metaurl,顺手看有没有 secret-key 字段)
kubectl -n kube-system get secret juicefs-sc-secret \
  -o go-template='{{range $k,$v := .data}}{{$k}}: {{$v | base64decode}}{{"\n"}}{{end}}'

# 1.3 版本(≥1.3.0 才能用 --binary,低了就去掉 --binary 改用 .json)
kubectl -n kube-system exec $MOUNT_POD -- juicefs version

# 1.4 活跃会话(对应生产"三道闸"第一道;现在有 1~2 个会话是正常的)
kubectl -n kube-system exec $MOUNT_POD -- \
  juicefs status redis://:<REDIS密码>@juicefs-redis.juicefs-system.svc.cluster.local:6379/0

# 1.5 Redis 里的 key 总量(等下和 dump 条目数对账)
kubectl -n juicefs-system exec juicefs-redis-0 -- redis-cli -a '<REDIS密码>' --no-auth-warning info keyspace
kubectl -n juicefs-system exec juicefs-redis-0 -- redis-cli -a '<REDIS密码>' --no-auth-warning -n 0 dbsize

质量门 G0:版本明确、密码拿到、keyspace 有输出。

2. 阶段 1:只读导出演练(业务不停,零风险)

dump 对 Redis 只有读操作,随便跑。正式迁移的区别只是"停写后再导一次"。
# 2.1 导出(计时!这个耗时就是未来评估真实窗口的依据)
kubectl -n kube-system exec -it $MOUNT_POD -- sh -c "
  time juicefs dump \
    'redis://:<REDIS密码>@juicefs-redis.juicefs-system.svc.cluster.local:6379/0' \
    /tmp/meta-test.zstd --binary --keep-secret-key &&
  ls -lh /tmp/meta-test.zstd"
# 记录:输出里的 entry 数量、耗时、文件大小
# entry 数应≈dbsize 数量级;差太多要贴出来分析

# 2.2 拷到 master 异地备份(养成习惯:导完必拷出)
kubectl cp kube-system/$MOUNT_POD:/tmp/meta-test.zstd ~/meta-test.zstd
md5sum ~/meta-test.zstd

质量门 G1:dump 无报错、文件已拷出、md5 已记录。

3. 阶段 2:临时 TiKV + 导入 + 免挂载验证

3.1 master 上起 tiup playground(前台运行,Ctrl+C 即销毁)

curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
source ~/.bash_profile 2>/dev/null || source ~/.bashrc

tiup playground v8.1.0 --db 0 --kv 1 --pd 1 --tiflash 0 --without-monitor
# 启动后打印 PD 地址(127.0.0.1:2379);另开一个 SSH 终端继续下面操作
playground = 一次性玩具集群,只起 PD+TiKV(JuiceFS 用不上 TiDB/TiFlash)。生产的区别是 tiup cluster 部署 3+3 并常驻,原理相同。

3.2 master 上装 JuiceFS 客户端并导入

从 master 直连 127.0.0.1:2379,网络最简单,排除 Pod→节点网络的变数。
curl -sSL https://d.juicefs.com/install | sh -
juicefs version

time juicefs load tikv://127.0.0.1:2379/jfs ~/meta-test.zstd

3.3 免挂载验证(重点教学:ls/fsck 不经过 FUSE,直接查元数据引擎)

# 新引擎侧
juicefs ls   tikv://127.0.0.1:2379/jfs /
juicefs fsck tikv://127.0.0.1:2379/jfs /

# 旧引擎侧(master 访问不了 svc 域名,用 ClusterIP 或回 Mount Pod 执行)
kubectl -n juicefs-system get svc juicefs-redis    # 拿到 ClusterIP
juicefs ls 'redis://:<REDIS密码>@<ClusterIP>:6379/0' /

# 两边目录树一致 + fsck 通过 = 迁移数据正确

质量门 G2:load 完成无报错、两侧 ls 一致、fsck 通过。

4. 阶段 3(可选):真·FUSE 挂载 TiKV 版

mkdir -p /mnt/jfs-test
juicefs mount tikv://127.0.0.1:2379/jfs /mnt/jfs-test &
sleep 3
mount | grep jfs-test                 # 确认挂上
ls -R /mnt/jfs-test | head -50        # 文件树对比
find /mnt/jfs-test -type f | head -5 | xargs -I{} sh -c 'echo "== {}"; head -c 200 {} ; echo'
# 抽样读内容:能读出 = 元数据→对象存储全链路通
umount /mnt/jfs-test

5. 进阶 A:去掉 --keep-secret-key 重跑(演练 SK 丢失预案)

对应生产最大风险项:dump 默认不导出对象存储 SK,load 后要手动补。
# 5.1 重新导出(去掉 --keep-secret-key)+ 换个 TiKV 前缀
kubectl -n kube-system exec -it $MOUNT_POD -- sh -c "
  juicefs dump 'redis://:<REDIS密码>@juicefs-redis.juicefs-system.svc.cluster.local:6379/0' \
    /tmp/meta-nosk.zstd --binary"
kubectl cp kube-system/$MOUNT_POD:/tmp/meta-nosk.zstd ~/meta-nosk.zstd
juicefs load tikv://127.0.0.1:2379/jfs-nosk ~/meta-nosk.zstd

# 5.2 此时 fsck 会报访问对象存储失败(SK 缺失)→ 演练补密钥
juicefs config tikv://127.0.0.1:2379/jfs-nosk \
  --access-key <AK> --secret-key <SK>        # AK/SK 从 juicefs-sc-secret 解码获得

# 5.3 再次 fsck 验证恢复
juicefs fsck tikv://127.0.0.1:2379/jfs-nosk /

6. 进阶 B:完整 CSI 切换演练(生产最险一步的彩排)

流程:备份 Secret → 试连通 → 改 metaurl → 重建 Mount Pod → 验证 → 回切。
在测试集群随便玩,玩坏回切即可。
# 6.1 备份所有相关 Secret(回滚的命根子)
mkdir -p ~/secret-backup
for s in "kube-system juicefs-sc-secret" \
         "kube-system juicefs-pvc-18d437a4-1d22-4b9b-b2a9-2bbeb9d6c7bb-secret" \
         "kube-system juicefs-juicefs-archive-pv-secret"; do
  set -- $s
  kubectl -n $1 get secret $2 -o yaml > ~/secret-backup/$2.yaml
done
ls -la ~/secret-backup/

# 6.2 前置试连通:Pod 能否访问 master:2379(不通则本进阶做不了,看下方备注)
kubectl -n kube-system exec $MOUNT_POD -- sh -c "nc -zv $MASTER_IP 2379 || echo UNREACHABLE"

# 6.3 停写(演练版):缩容业务 Deployment,确认 Mount Pod 消失
kubectl get deploy -n default                    # 找到用 nginx-www-pvc 的 deploy
kubectl -n default scale deploy <业务DEPLOY名> --replicas=0
sleep 10
kubectl -n kube-system get pod | grep juicefs-liu    # Mount Pod 应消失(CSI 按需创建)

# 6.4 正式导出(停写后的"一致性版本")→ 导入新前缀 /jfs-prod
kubectl -n kube-system run juicefs-migrator --image=juicedata/mount:ce-v1.3.0 \
  --restart=Never --overrides='{"spec":{"hostNetwork":true,"dnsPolicy":"ClusterFirstWithHostNet",
  "containers":[{"name":"juicefs-migrator","image":"juicedata/mount:ce-v1.3.0",
  "command":["sleep","3600"],"securityContext":{"privileged":true}}]}}'
kubectl -n kube-system exec -it juicefs-migrator -- sh -c "
  juicefs dump 'redis://:<REDIS密码>@juicefs-redis.juicefs-system.svc.cluster.local:6379/0' \
    /tmp/meta-prod.zstd --binary --keep-secret-key &&
  juicefs load tikv://$MASTER_IP:2379/jfs-prod /tmp/meta-prod.zstd &&
  juicefs fsck tikv://$MASTER_IP:2379/jfs-prod /"

# 6.5 计算新 metaurl 的 base64 并 patch 三个 CSI Secret
NEW_B64=$(echo -n "tikv://$MASTER_IP:2379/jfs-prod" | base64 -w0)
echo $NEW_B64
for s in juicefs-sc-secret juicefs-pvc-18d437a4-1d22-4b9b-b2a9-2bbeb9d6c7bb-secret juicefs-juicefs-archive-pv-secret; do
  kubectl -n kube-system patch secret $s --type merge -p "{\"data\":{\"metaurl\":\"$NEW_B64\"}}"
done
kubectl -n kube-system get secret juicefs-sc-secret -o jsonpath='{.data.metaurl}' | base64 -d; echo
# 确认显示 tikv://...

# 6.6 拉起业务 → CSI 用新 metaurl 重建 Mount Pod
kubectl -n default scale deploy <业务DEPLOY名> --replicas=2
sleep 15
kubectl -n kube-system get pod | grep juicefs-liu    # 新的 Mount Pod 出现

# 6.7 验证切换生效(cmdline 里应该是 tikv:// 了)
NEWPOD=$(kubectl -n kube-system get pod | grep juicefs-liu-node1 | awk '{print $1}' | head -1)
kubectl -n kube-system exec $NEWPOD -- sh -c "cat /proc/1/cmdline | tr '\0' ' '; echo"
kubectl -n default get pods    # 业务 Running
# 进业务 Pod 读写一个文件验证(按你的业务实际路径)

# 6.8 回切演练(完整走一遍回滚)
kubectl -n default scale deploy <业务DEPLOY名> --replicas=0
for s in juicefs-sc-secret juicefs-pvc-18d437a4-1d22-4b9b-b2a9-2bbeb9d6c7bb-secret juicefs-juicefs-archive-pv-secret; do
  kubectl replace -f ~/secret-backup/$s.yaml --force
done
kubectl -n default scale deploy <业务DEPLOY名> --replicas=2
kubectl -n kube-system exec $(kubectl -n kube-system get pod | grep juicefs-liu-node1 | awk '{print $1}' | head -1) \
  -- sh -c "cat /proc/1/cmdline | tr '\0' ' '; echo"    # 确认回到 redis://

# 6.9 清理迁移 Pod
kubectl -n kube-system delete pod juicefs-migrator
6.2 不通的备选:把 playground 绑到 node 节点上起、或给 playground 所在网卡放通、或跳过进阶 B(生产 TiKV 在同 VPC ECS 上,天然可达)。先搞清楚为什么不通,这个排错过程本身就是学习。

质量门 G3:切换后 cmdline 显示 tikv:// 且业务读写正常;回切后显示 redis:// 且业务正常。

7. 清场

# playground 终端按 Ctrl+C(TiKV 数据随之销毁,无所谓)
umount /mnt/jfs-test 2>/dev/null; rmdir /mnt/jfs-test
kubectl -n kube-system delete pod juicefs-migrator 2>/dev/null
# Secret 备份目录留着,正式迁移时还是模板
# ~/meta-test.zstd ~/meta-nosk.zstd 留作纪念/对比

8. 演练 ↔ 生产对照表

演练里做的生产对应
juicefs status 看会话停写三道闸第一道(Sessions 必须为空)
dump 计时正式窗口时长测算
tiup playground(1 PD + 1 TiKV)tiup cluster 部署 PD×3 + TiKV×3 + 监控
load + ls/fsck 验证正式导入 + 质量门(含 entry 对账)
去掉 --keep-secret-key + config 补 SKSK 丢失应急预案
改 3 个 Secret + 重建 Mount Pod生产 CSI 切换的标准动作
kubectl replace 回切生产回滚预案
master 装客户端挂载宿主机场景的验证方式

9. 自查题(能答才算练会)

  1. 为什么 dump 可以业务不停随便跑,正式切换却必须停写后再导一次?
  2. load 中断为什么可以直接重试?要不要重新 dump?
  3. 为什么 juicefs ls/fsck 不需要挂载就能验证元数据?
  4. --keep-secret-key 加与不加的区别?生产怎么选?
  5. 改完 Secret 为什么要重建 Mount Pod 才生效?(提示:metaurl 是什么时候被读进挂载命令的)
  6. 为什么切换前要试 nc -zv $MASTER_IP 2379?生产上对应的检查是什么?
  7. playground 和 tiup cluster 部署的集群,运维上有什么本质区别?

标签: none

添加新评论