跳转至

SOP — 计算节点本地 scratch(Tier0 本地层)安装 与 Lustre 测试残留清理

变更日期:2026-09-24 实现体:local_scratch_deployer.py + local_scratch_assets.py(工程根 /home/rain/Documents/slurm/slurm_ins) 配置:config/slurm_deploy.yaml 的 local_scratch 段 执行目录:/home/rain/Documents/slurm/slurm_ins(所有命令均在该目录下执行) 本文第 5 章为实现等价的操作序列,以实现体为准;幂等、备份与回滚由实现体保证。

目录

  1. 背景 · 2. 目标 · 3. 前置条件 · 4. 影响范围 · 5. 分步操作 · 6. 验证 · 7. 回滚 · 8. 风险说明 · 附录

一、背景

EDA 负载以海量小文件 + 元数据密集为特征,瓶颈在元数据 IOPS 而非带宽。本集群此前只有两层存储:

层 现状
Tier1(持久) NFS:/home、/opt/modulefiles、/mnt/slurm-shared
Tier0(跨节点共享临时) Lustre:192.168.31.21@tcp:/edafs → /mnt/lustre(295G,1 MDT + 1 OST)
本地层 缺失

本地层的核心理由是卸载元数据压力(旧集群《存储目录行业标准设计》11.1),而非单纯提速: 把编译中间产物、$TMPDIR 从共享文件系统挪到节点私有盘,可避免单 MDT Lustre / 单点 NFS 被打爆。

同时,/mnt/lustre 根目录遗留 09-22 的 Lustre 连通性测试产物(4 个空目录 + 测试文件), 未形成标准 scratch 结构,需清理。

触发条件:对标旧集群五轮改造(第二轮"目录标准化 + 本地 SSD scratch"),补齐本地层。

二、目标

  1. 三台计算节点(.18/.19/.20)各获得一块 50G 独立虚拟盘,挂载为 /local_scratch(xfs,noatime)
  2. 挂载持久化(fstab 按 UUID 绑定,避免 virtio 盘序变化挂错盘)
  3. 下发作业环境片段,导出 LOCAL_SCRATCH 与 TMPDIR
  4. 清理 /mnt/lustre 的 09-22 测试残留
  5. 幂等:连续执行两次,第二次零变更、全跳过

三、前置条件

# 条件 检查方式(只读) 本次实测
1 宿主(部署机自身)sudo 免密可用 sudo -n true ✅ 可用
2 宿主 virsh 与 qemu-img 可用 command -v virsh qemu-img ✅ /usr/bin/qemu-img
3 存储池存在且有余量 virsh pool-dumpxml ssd-pool ✅ ssd-pool → /kvm/img
4 目标虚机存在 virsh list --all --name ✅ slurm-work-01/02/03
5 三节点 SSH 可达(root 免密) ssh root@192.168.31.18 echo OK ✅
6 节点无同目标设备 lsblk -e 7 -no NAME ✅ 仅 vda,无 vdb
7 工程 venv 可用 .venv/bin/python -c "import yaml" ✅
8 squeue 为空(本次不重启 slurmd,仍建议确认) squeue ✅ 空

以上 1–6 已由实现体 pre_check() 落为可执行门禁:任一项不通过即终止,不做任何变更。

四、影响范围

项 内容
涉及主机 宿主 192.168.31.249(部署机自身);计算节点 .18/.19/.20
是否需停服 否。virsh attach-disk 热插,不重启虚机;不改 Slurm 配置、不重启任何 Slurm 守护进程
在跑作业 本次执行时 squeue 为空;热插新盘不影响运行中作业
窗口期 无需专门窗口(逐台串行,batch_size: 1)
不可逆点 节点侧 mkfs.xfs /dev/vdb(仅在新附加的空盘上执行);Lustre 测试残留删除
资源影响 宿主 ssd-pool 名义占 3×50G=150G,实际为稀疏文件按需增长;宿主为共享 KVM 宿主(另有 k8s / LSF / Lustre 虚机)

五、分步操作

对应实现文件:local_scratch_deployer.py(HostDiskManager / LocalScratchInstaller / LocalScratchDeployer),以实现体为准。

5.0 前置修复:工程包路径适配(本次一并完成)

工作目录被移动后,main.py 仍按旧包路径 deploy.slurm 导入,导致工程无法启动 (ModuleNotFoundError: No module named 'deploy')。修正为按当前布局导入:

cp -a main.py eda_storage_gap/backup/main.py.bak.$(date +%Y%m%d-%H%M%S)   # 高危动作前先备份
# main.py 改动要点:_PROJECT_ROOT = _HERE.parent;导入改为 from slurm_ins.xxx import ...
# 其余模块本就使用相对导入(from .deployer),随包自动生效

5.1 预演(不改动任何系统状态)

cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --local-scratch --dry-run
# 预期:列出每台的 创建虚盘 / 热插 / 格式化 / fstab / 挂载 / profile 共 6 项,退出码 0

5.2 正式执行

.venv/bin/python main.py --config config/slurm_deploy.yaml --local-scratch

实现体内部顺序(逐台、分批,批内失败超阈值即停):

# —— 宿主侧(本机,sudo 执行;均由 virsh XML 结构化解析,不按固定列)——
sudo qemu-img create -f qcow2 /kvm/img/slurm-work-01-scratch.qcow2 50G   # 幂等:已存在即跳过
sudo virsh attach-disk slurm-work-01 /kvm/img/slurm-work-01-scratch.qcow2 vdb \
     --targetbus virtio --sourcetype file --driver qemu --subdriver qcow2 \
     --persistent                                                          # 热插,不停虚机;已插即跳过
sudo virsh dumpxml slurm-work-01                                          # 热插后回读实际设备名(以实机为准)

# —— 节点侧(SSH 到 192.168.31.18)——
# 安全判定:设备存在、非根设备、未挂载他处、无子分区、无其它文件系统 → 才允许格式化
mkfs.xfs -f /dev/vdb                                                      # ⚠️ 不可逆:仅对空盘执行
blkid -s UUID -o value /dev/vdb                                           # 取 UUID 供 fstab 使用
mkdir -p /local_scratch
# fstab:按挂载点字段精确替换(awk '$2 != mp'),不用无边界通配
awk -v mp=/local_scratch '$2 != mp' /etc/fstab > /tmp/fstab && \
  printf '%s\n' 'UUID=<uuid> /local_scratch xfs defaults,noatime 0 0' >> /tmp/fstab && \
  cp /tmp/fstab /etc/fstab && rm -f /tmp/fstab
mount /local_scratch
mkdir -p /local_scratch/users && chmod 1777 /local_scratch/users
# profile.d:SFTP 上传(内容比对 SHA256,一致即跳过)
#   /etc/profile.d/eda_scratch.sh

5.3 清理 Lustre 测试残留

# ⚠️ 高危:删除不可逆。删除前已留档清单与 hello.txt 副本(见 eda_storage_gap/backup/lustre_residue/)
TARGETS="/mnt/lustre/eda_large_files /mnt/lustre/eda_scratch /mnt/lustre/eda_shared_files \
         /mnt/lustre/eda_small_files /mnt/lustre/hello.txt /mnt/lustre/testfile"
for t in $TARGETS; do case "$t" in /mnt/lustre/*) ;; *) echo "路径越界,中止: $t"; exit 1 ;; esac; done
rm -rf $TARGETS       # 显式路径,无通配

六、验证

# 1. 幂等复跑:第二次必须零变更、全跳过
.venv/bin/python main.py --config config/slurm_deploy.yaml --local-scratch
#    预期:变更记录总数 9 / 待执行变更 0 / 幂等跳过 9 / 退出码 0
#         日志出现「✅ 状态已一致,零变更,无需部署(幂等跳过)」

# 2. 三节点实机状态
for h in 18 19 20; do ssh root@192.168.31.$h \
  'df -h /local_scratch | tail -1; ls -ld /local_scratch /local_scratch/users; grep local_scratch /etc/fstab'; done
#    预期:/dev/vdb 50G 挂到 /local_scratch;/local_scratch/users 为 drwxrwxrwt(1777);
#         fstab 为 UUID=... /local_scratch xfs defaults,noatime 0 0

# 3. 作业环境片段
ssh root@192.168.31.18 'cat /etc/profile.d/eda_scratch.sh'
#    预期:LOCAL_SCRATCH_ROOT / LOCAL_SCRATCH / TMPDIR 三行

# 4. Lustre 残留已清空
ssh root@192.168.31.18 'ls -A /mnt/lustre | wc -l'    # 预期 0

本次实测结果(2026-09-24 21:21–21:24):上述 1–4 全部通过。

七、回滚

备份位置

备份对象 位置 说明
节点 /etc/fstab 各节点 /var/backups/slurm/local_scratch_<时间戳>/fstab 由实现体 backup() 自动创建
工程 main.py eda_storage_gap/backup/main.py.bak.20260924-211134 5.0 前置修复前的手工备份
Lustre 残留留档 eda_storage_gap/backup/lustre_residue/ 清单 + hello.txt 副本(文件本体不备份,testfile 为 50MB 全零测试数据)

完整台账(含何时可删)见 eda_storage_gap/backup/README.md。

回滚步骤

# 一步回滚:节点侧 umount + 移 fstab 条目 + 删 profile + 删空目录;宿主侧 detach-disk
.venv/bin/python main.py --config config/slurm_deploy.yaml --local-scratch --rollback
# 预期:日志出现「已卸载(盘文件保留: /kvm/img/slurm-work-0x-scratch.qcow2)」

回滚后验证

for h in 18 19 20; do ssh root@192.168.31.$h \
  'findmnt /local_scratch || echo "未挂载"; grep -c local_scratch /etc/fstab || true'; done
#    预期:未挂载;fstab 无 local_scratch 行
virsh domblklist slurm-work-01        # 预期:无 vdb

不可回滚项(明确标注)

操作 可回滚性 兜底手段
mkfs.xfs /dev/vdb 不可回滚 仅对空盘执行(判据见 5.2);盘内无业务数据
删除 Lustre 测试残留 不可回滚 已留档清单与 hello.txt;testfile 为全零数据,无需恢复
删除 qcow2 文件 工具不代劳 回滚仅卸盘不删文件;确认无用后由人工删除

八、风险说明

# 风险 缓解措施
1 宿主 ssd-pool 空间超卖:名义需求 3×50G=150G > 实测可用 111–133G。qcow2 为稀疏文件,创建时几乎不占空间,但三台同时写满会 ENOSPC,并波及 sda3 上其它虚机 用户已确认接受(测试用途)。监控 df -h /;如需收紧,改 disk_size_gb 或换 hdd-pool(/data/kvm/img,余 1.6T,但与 NFS 同盘争 IO)
2 格式化不可逆 格式化前由实机事实判定:设备存在、非根设备、未挂载他处、无子分区、无其它文件系统;任一项不符即 unknown 并中止,不硬编码盘名
3 误覆盖既有虚盘 qemu-img info 改用 --force-share(运行中虚机持写锁,默认取写锁会失败);文件存在却读不出信息时判 unknown 并拒绝执行,不当作"需创建"
4 误伤其它虚机的盘 目标盘必须位于配置的存储池内、命名符合 {vm_name}-scratch.qcow2、且未被任何其它虚机引用,否则拒绝执行
5 fstab 写坏导致启动异常 修改前自动备份 /etc/fstab;替换按挂载点字段精确匹配,不用无边界通配;本次实测启动项未被破坏
6 宿主为共享 KVM 宿主,资源竞争 变更只新增盘文件,不动其它虚机;/kvm/img 现有 k8s-*、lustre-* 等文件未被触碰
7 TaskProlog 未注入 TMPDIR 本阶段只下发 profile.d(登录 shell 生效);非 login shell 的作业脚本需自行 source,或后续阶段在 task_prolog_eda.sh 中注入(须走模板,禁止手改 /usr/local/slurm/)

附录 A:交付物清单

文件 说明
local_scratch_deployer.py 实现体:宿主侧加盘 + 节点侧挂载 + 三态幂等 + 分批 + 回滚
local_scratch_assets.py 配置解析与资产渲染(profile 内容 / fstab 行 / 校验)
main.py 新增 --local-scratch 子命令;包路径适配
config/slurm_deploy.yaml / .example 新增 local_scratch 段
eda_storage_gap/backup/README.md 备份台账
eda_storage_gap/本集群存储演进差距清单.md 差距分析与顺序规划(含闸门 0 实测记录)

附录 B:实测记录(2026-09-24)

项 结果
预演 22 项待变更,退出码 0
正式执行 24 项变更,退出码 0;三节点格式化 + 挂载 + profile 全部完成
幂等复跑 9 条记录 / 待执行 0 / 跳过 9 / 退出码 0
节点实机 .18/.19/.20 均 /dev/vdb 50G → /local_scratch(xfs),fstab 按 UUID,users 权限 1777
Lustre 清理 删除 6 项,/mnt/lustre 剩余文件数 0
已知偏差 首次幂等复跑因 qemu-img 写锁问题误判 3 条"待创建",已修正为 --force-share + 三态探测,修正后全跳过

来源:/home/rain/Documents/slurm/slurm_ins/eda_storage_gap/SOP.md | 最后更新:2026-09-24 21:24(由 build/sync.py 同步,请勿在站点侧修改)