SOP —— 本地 scratch(宿主 KVM 加虚盘 + 节点挂载 /local_scratch)¶
- 主题:给 LSF 三台执行节点补齐本地临时层
/local_scratch,作为部署器独立阶段 15 (--stage local-scratch)受管,避免手工操作造成漂移。 - 变更日期:2026-10-07
- 触发来源:slurm_ins《EDA 集群存储与服务拓扑巡检报告》§二 G1 的第二部分(三层存储缺
/local_scratch), 经lsf_ins HANDOFF §3 #5落地。 - 口径来源:与 Slurm 集群一致(
slurm_ins --local-scratch,见其local_scratch_deployer.py与config/slurm_deploy.yaml的local_scratch:段)。 - 交付物:本目录(
local_scratch/),含本文档与backup/README.md。
一、背景¶
巡检报告原判「.25–.27 缺 /local_scratch,交付物需在 .25–.27 各配一块本地盘(非纯软件动作)」。
本次取证推翻了「需要硬件」这一前提:
.25–.27是本机 KVM 上的虚机(virsh list可见lsf-work-01/02/03running), 磁盘只有系统盘vda(100G,根分区用 4.1G);- 同宿主上的 Slurm 三台计算节点已用同款方式各挂了一块 50G 虚盘
(
/kvm/img/slurm-work-0X-scratch.qcow2→ 节点内/dev/vdb→/local_scratchxfs)。
因此这是纯软件动作:宿主创建 qcow2 并热插(不停虚机),节点内格式化后挂载。
另注:本环境 NFS 挂载与本地挂载均由各自部署器统一管理;手工
virsh attach-disk+ 手工挂载 会让部署器--drift漏检、复跑不收敛,故本次实现为部署器阶段,不手工操作。
二、目标¶
变更完成后应达到的可验证状态:
- 宿主
/kvm/img内每台 worker 各有一块 50G qcow2(lsf-work-0X-scratch.qcow2), 且已--persistent热插为该虚机的空闲 virtio 盘; - 节点内
/local_scratch已挂载(xfs,defaults,noatime),/etc/fstab有 UUID 条目持久化; /local_scratch/users存在且为1777;/etc/profile.d/eda_scratch.sh导出LOCAL_SCRATCH;- 该阶段纳入
--drift检测,复跑幂等(第二次零变更); - 端到端:从
.28提交的作业落到执行节点后可读写/local_scratch。
三、前置条件¶
- 宿主(操作机自身)具备免密 sudo(
virsh/qemu-img需 root); local_scratch.host_pool_path指向的存储池存在且与实际路径一致;- 目标虚机在宿主上存在且运行中(热插不停机);节点 SSH 免密可达;
- 目标虚机尚无该盘(首次插入);
nodes.workers的name即 libvirt 域名; - 变更前
bjobs -u all无 RUN 作业(本次热插不停机、不重启 LSF,属额外保险)。
四、影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | 宿主(本机,libvirt)+ .25、.26、.27 三台 worker |
| 组件 | 宿主:存储池新增 qcow2、虚机 XML 增加一块 disk;节点:/etc/fstab、新增挂载点、profile.d 片段 |
| 停服 | 否。virsh attach-disk 为热插,虚机不重启;格式化对象是新建的空虚盘,不触碰系统盘 |
| 窗口 | 无窗口要求;实测单次执行 26.3 s |
| 在跑作业 | 变更前确认无 RUN 作业;热插过程不影响已登录会话 |
| 磁盘占用 | qcow2 稀疏:50G 标称、实测各约 3 MB |
与 Slurm 侧同一宿主、同一存储池(
/kvm/img);命名空间分离(lsf-work-0X-scratch.qcow2vsslurm-work-0X-scratch.qcow2),互不影响。
五、分步操作¶
实现体:deploy/lsf/stages/local_scratch.py(LocalScratchModule);配置:
deploy/lsf/config/lsf_deploy.yaml 的 local_scratch: 段。以下为等价操作序列(以实现体为准)。
步骤 1:宿主侧创建虚盘并热插¶
宿主命令一律经 sudo -n。幂等:盘已存在则跳过创建;已插则跳过热插。
# ① 创建稀疏虚盘(幂等:已存在则不重建)
sudo qemu-img create -f qcow2 /kvm/img/lsf-work-01-scratch.qcow2 50G
# ② 取该虚机已用设备名,推导空闲槽位(不硬编码 vdb)
sudo virsh dumpxml lsf-work-01 | grep -oP "target dev='vd\w'" | sort -u
# ③ 热插(不停虚机;--persistent 使其在重启后仍存在)
sudo virsh attach-disk lsf-work-01 /kvm/img/lsf-work-01-scratch.qcow2 vdb \
--targetbus virtio --sourcetype file --driver qemu --subdriver qcow2 --persistent
# ④ 回读实机设备名(不依赖预设值)
sudo virsh dumpxml lsf-work-01 | grep -A2 'lsf-work-01-scratch'
坑:运行中的虚机持有镜像写锁,探测盘是否可用须 qemu-img info --force-share 只读打开;
若文件存在却读不出信息,判 unknown 并中止,绝不当作「需创建」而误覆盖。
步骤 2:节点侧确认设备与空盘判据¶
# 等内核识别(状态量轮询,非固定长等待)
ssh root@192.168.31.25 '[ -b /dev/vdb ] && echo yes || echo no'
# 空盘判据(三条全过才允许格式化)
ssh root@192.168.31.25 'findmnt -no SOURCE / ; lsblk -nro NAME /dev/vdb; blkid -s TYPE -o value /dev/vdb'
预期:设备出现;/ 不是 /dev/vdb;/dev/vdb 无子分区;blkid 无输出(空盘)。
失败判断:任一条不符 → 实现体判 unknown 并中止(不格式化)。
步骤 3:格式化 → fstab(UUID) → 挂载 → 环境¶
# ① 格式化(高危:不可逆,仅对确认过的空盘执行)
ssh root@192.168.31.25 'mkfs.xfs -f /dev/vdb'
# ② 取 UUID
ssh root@192.168.31.25 'blkid -s UUID -o value /dev/vdb'
# ③ fstab 写入(按挂载点字段精确替换同名旧行,再追加;不用通配)
ssh root@192.168.31.25 "tmp=\$(mktemp -p /tmp) && \
awk -v mp=/local_scratch '\$2 != mp' /etc/fstab > \"\$tmp\" && \
echo 'UUID=<上一步的UUID> /local_scratch xfs defaults,noatime 0 0' >> \"\$tmp\" && \
cp \"\$tmp\" /etc/fstab && rm -f \"\$tmp\""
# ④ 挂载 + 建 users 目录
ssh root@192.168.31.25 'mkdir -p /local_scratch && mount /local_scratch'
ssh root@192.168.31.25 'mkdir -p /local_scratch/users && chmod 1777 /local_scratch/users'
# ⑤ profile.d 片段(导出 LOCAL_SCRATCH;刻意不设 TMPDIR,见第 8 章)
ssh root@192.168.31.25 'cat > /etc/profile.d/eda_scratch.sh <<"EOF"
# 本地 scratch(由 LSF 部署器写入,请勿手工修改)
LOCAL_SCRATCH_ROOT="/local_scratch/users"
export LOCAL_SCRATCH="$LOCAL_SCRATCH_ROOT/$USER"
EOF
chmod 644 /etc/profile.d/eda_scratch.sh'
步骤 4:按部署器执行(推荐入口)¶
cd deploy/lsf
.venv/bin/python main.py -c config/lsf_deploy.yaml --stage local-scratch --dry-run # 预演
.venv/bin/python main.py -c config/lsf_deploy.yaml --stage local-scratch # 执行
坑:该阶段不在主流程(宿主加盘 + 格式化属不可逆高危),必须显式 --stage local-scratch;
enabled: false 时该阶段与 --drift 均自动跳过。
六、验证¶
cd deploy/lsf
# ① 幂等复跑:应「状态已一致,无漂移(全跳过)」
.venv/bin/python main.py -c config/lsf_deploy.yaml --stage local-scratch
# ② 独立漂移检测:全阶段无漂移,退出码 0(含 阶段15(local-scratch))
.venv/bin/python main.py -c config/lsf_deploy.yaml --drift; echo "rc=$?"
# ③ 实机核对(三台)
for h in 25 26 27; do ssh root@192.168.31.$h \
'findmnt -n -o SOURCE,TARGET,FSTYPE,OPTIONS /local_scratch; grep local_scratch /etc/fstab; \
ls -ld /local_scratch/users; df -hT /local_scratch | tail -1; cat /etc/profile.d/eda_scratch.sh'; done
# ④ 端到端:提交引用 /local_scratch 的作业(输出须写**共享路径**,见第 8 章坑 2)
ssh root@192.168.31.28 "source /data/nfs/lsf/lsf/conf/profile.lsf; \
echo 'hostname; df -h /local_scratch | tail -1; \
mkdir -p /local_scratch/users/\$USER && echo LOCAL_SCRATCH_WRITABLE; \
source /etc/profile.d/eda_scratch.sh && echo LOCAL_SCRATCH=\$LOCAL_SCRATCH' \
| bsub -o /data/nfs/lsf/.ls_probe.out -n 1"
实测结果(2026-10-07):
| 检查项 | 结果 |
|---|---|
| 执行 | 状态: success,耗时 26.31 s,三台全部达标 |
| 幂等复跑 | 阶段15「状态已一致,无漂移(全跳过)」 |
--drift |
全阶段无漂移(含 阶段15(local-scratch): 无漂移),rc=0 |
| 三台挂载 | /dev/vdb → /local_scratch xfs rw,noatime,50G(用 389M) |
| fstab | 三台各 1 条 UUID=… /local_scratch xfs defaults,noatime 0 0 |
| 目录/环境 | /local_scratch/users = drwxrwxrwt;profile.d 片段内容正确 |
| 端到端作业 | 作业 1049 落在 lsf-work-01:/dev/vdb 50G … /local_scratch、LOCAL_SCRATCH_WRITABLE、LOCAL_SCRATCH=/local_scratch/users/root |
| 与 Slurm 口径一致 | 挂载源/文件系统/选项/fstab 形式/users 权限/profile 导出项逐项一致 |
七、回滚¶
备份位置:
| 备份 | 位置 | 说明 |
|---|---|---|
| 部署器文件(6 份) | local_scratch/backup/deploy/lsf/** |
各 <原名>.bak.20261007-090939 |
节点 /etc/fstab(3 份) |
.25–.27 的 /etc/fstab.bak.20261007-091231 |
保持原位(实现体回滚依赖) |
| 宿主虚机 XML(3 份) | deploy/lsf/backup/local_scratch/20261007-091231/lsf-work-0X.xml |
热插前的定义快照 |
台账见 backup/README.md。回滚命令(实现体已内置):
cd deploy/lsf
.venv/bin/python main.py -c config/lsf_deploy.yaml --stage local-scratch --rollback
其动作:节点侧 umount /local_scratch → 移除 fstab 条目 → 删 profile 片段 → 删空目录;
宿主侧 virsh detach-disk <vm> <dev> --persistent。
qcow2 文件按设计保留在存储池内(删除属不可逆动作,工具不代劳);确认无用后由人工删除:
sudo rm -f /kvm/img/lsf-work-0X-scratch.qcow2 # 高危:确认已 detach 且不再需要
回滚后验证:三台 findmnt /local_scratch 无输出;--drift 对该阶段应报「漂移」(因盘已不在,
但有 enabled: true)——此时把 local_scratch.enabled 置 false 即恢复「不纳管」状态。
八、风险说明¶
| 风险 | 说明与缓解 |
|---|---|
| 格式化不可逆 | 目标设备由实机推导:① 盘路径须在配置的存储池内且命名合规;② 不得被其它虚机引用;③ 节点侧须满足「非根设备、无子分区、无既有文件系统」三条,任一不满足即判 unknown 并中止。不硬编码 /dev/vdb(由 virsh dumpxml 的空闲槽位推导) |
| 误 attach 到他人的盘 | _assert_safe_target 遍历所有虚机的 XML,若目标盘已被别的虚机引用则拒绝执行(与 Slurm 同款判据) |
| 热插中断虚机 | virsh attach-disk 为热插,不需重启;本次实测三台作业与服务均无中断 |
| 探测失败被当成「需变更」 | 三态化:unknown 一律中止(如盘文件存在但 qemu-img 读不出、设备已在别处挂载) |
| 盘序漂移导致挂错盘 | fstab 按 UUID 绑定,不用设备名 |
坑 1:作业里 $TMPDIR 未指向 scratch |
与 Slurm 口径一致,profile.d 刻意不设 TMPDIR(profile 会被所有 shell 加载,指向尚未创建的作业目录会破坏 mktemp)。作业级 TMPDIR 应另由调度器注入——LSF 侧已于 2026-10-08 实现(lsf.conf 的 LSB_JOB_TMPDIR + LSB_SET_TMPDIR,见 ../tmpdir_inject_20261008/SOP.md) |
坑 2:作业输出写 /tmp/... 读不到 |
/tmp 是执行节点本地盘,提交节点看不到;验收输出须写共享路径(本次用 /data/nfs/lsf/),且取回要等作业真正结束(实测调度延迟可达数十秒) |
| 回滚后残留 qcow2 | 属预期(工具不删盘);已在 backup/README.md 登记位置与处置方式 |
| 与 Slurm 同宿主争用存储池 | 命名与盘文件完全分离;池容量需自行关注(本次新增 3×50G 标称、实测约 3 MB/块) |
来源:
/home/rain/Documents/LSF/lsf_ins/local_scratch/SOP.md| 最后更新:2026-10-08 03:49(由build/sync.py同步,请勿在站点侧修改)