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 章为实现等价的操作序列,以实现体为准;幂等、备份与回滚由实现体保证。
目录¶
- 背景 · 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"),补齐本地层。
二、目标¶
- 三台计算节点(
.18/.19/.20)各获得一块 50G 独立虚拟盘,挂载为/local_scratch(xfs,noatime) - 挂载持久化(fstab 按 UUID 绑定,避免 virtio 盘序变化挂错盘)
- 下发作业环境片段,导出
LOCAL_SCRATCH与TMPDIR - 清理
/mnt/lustre的 09-22 测试残留 - 幂等:连续执行两次,第二次零变更、全跳过
三、前置条件¶
| # | 条件 | 检查方式(只读) | 本次实测 |
|---|---|---|---|
| 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同步,请勿在站点侧修改)