Lustre 调优模块修复、残留清理与口径统一 — 变更实施手册¶
| 项 | 值 |
|---|---|
| 变更时间 | 2026-09-24 02:03 ~ 12:47 |
| 变更主机 | 192.168.31.21(MGS+MDT+客户端)、192.168.31.22(OSS) |
| 变更类型 | C 类(生产变更:代码/配置/文档 + 远程持久化机制替换 + 残留清理) |
| 是否停服 | 21 的客户端挂载点有一次 umount + 重新挂载(秒级 IO 挂起);MDS/OST 服务未停 |
| 在跑作业 | 无(测试环境,未接 Slurm/LSF 业务) |
1. 背景¶
对集群做安装进度核查时发现:首次部署虽已完成、服务健康,但调优模块「报了成功、实际未生效」。逐项取证确认了三类问题:
- 配置期望值不可达:
client_tuning中mdc.*.max_mod_rpcs_in_flight=50被内核拒绝。真实原因由 dmesg 给出:can't set max_mod_rpcs_in_flight=50 higher than mdt.*.max_mod_rpcs_in_flight=8 returned by the MDT server at connection—— 上限是 MDT 在客户端连接时返回的值,超出即ERANGE(34)。历史日志 2026-09-22 14:49:35 该命令同样返回码 34。 - 配置项在 2.17 已不存在:
llite.*.statahead_xattr读取返回码 2,却被写进了持久化文件。 - 实现体吞掉失败:
set_param返回 34 只记日志,模块仍mark_success(),导致「失败被当作成功」持续了两天未被发现。
同时发现两项既有缺陷:
- 同机多角色时持久化文件互相覆盖:21 既是服务端又是客户端,实现体按角色各写一次同一文件,后写覆盖先写 —— 21 上的
server_tuning(4 项)从未落盘过。 - 持久化机制本身不可靠:用
/etc/cron.d/lustre-tune的@reboot条目;该时机早于 lustre 挂载,且挂载动作会把 osc/llite 级参数重置为默认值。节点自 2026-09-22 14:27 未重启,cron 从未执行过(/var/log/cron只有 RELOAD 无 RUN),链路从未被验证。
此外,/etc 下堆积了 15 个历史 .bak(21 上 12 个、22 上 3 个),文档口径滞后于实机(版本 2.15.8/2.17.0、设备 /dev/sdb//dev/vdb)。
2. 目标¶
变更完成后应达到:
- 调优参数真实达标,且失败不再被吞没(有失败即模块
failed) - 调优参数在重启后仍达标(持久化单元
enabled+active,且在挂载完成后执行) - 历史 cron 持久化文件移除,
/etc下无残留.bak inspect巡检调优项零异常;巡检判据与下发逻辑一致docs/三份文档与实机口径一致- 多主机数据面分批给出规划设计(本轮不含实现)
3. 前置条件¶
| 项 | 要求 | 核查方式 |
|---|---|---|
| SSH | 操作机对 21/22 有 root 免密(paramiko 自动读取 ~/.ssh/id_*) |
ssh -n root@192.168.31.21 hostname |
| Python | 使用工程 venv(共享库经 .pth 挂载) |
.venv/bin/python3 -c "import base_module" |
| 作业 | 21/22 上无 Slurm/LSF 在跑作业 | scontrol show node / bhosts(本环境未接入) |
| 门禁 | tune 会先跑配置校验 + 安装前预检查,有 BLOCK 即终止 |
见第 5 章步骤 1 |
4. 影响范围¶
| 主机 | 组件 | 变更动作 | 影响 |
|---|---|---|---|
| 192.168.31.21 | MGS+MDT+客户端 | 运行期 lctl set_param(MDS/客户端)、写并启用 lustre-tune.service、移除历史 cron、重挂载客户端一次 |
客户端挂载点秒级 IO 挂起;MDS/OST 服务不间断 |
| 192.168.31.22 | OSS | 运行期 lctl set_param(服务端)、写并启用 lustre-tune.service、移除历史 cron |
无服务中断 |
- 窗口期:无需专门窗口,可在业务空闲时执行
- 是否需要停服:否。唯一的可用性影响是 21 客户端挂载点的
umount/mount,约 1 秒;若有客户端在跑 IO,请先停止 - 数据面:本次不涉及
mkfs、清空、迁移等不可逆动作
5. 分步操作¶
以下命令以实现体为准(实现文件:
modules/lctl_tuning.py、config/deploy.yaml、deployer.py),此处供人阅读、复核与手工复现。
5.1 预演(零系统改动)¶
cd /home/rain/Documents/lustre_ins
# 门禁:自动跑配置校验 + 安装前预检查(只读),有 BLOCK 即终止
.venv/bin/python3 main.py tune --dry-run --config config/deploy.yaml
# 核查:输出中每条变更均为 "[dry-run] 将执行变更",且远端无任何文件/单元被创建
5.2 真实执行(幂等,第二次全跳过)¶
.venv/bin/python3 main.py tune --config config/deploy.yaml
实现体按主机合并「服务端通用 + MDS/OSS 专属 + 客户端」参数后执行:
# --- 运行期调优(多角色主机合并,只处理一次)---
lctl set_param mdt.*.max_mod_rpcs_in_flight=32 # 先抬服务端上限(仅 MGS+MDS 节点)
lctl set_param mdc.*.max_mod_rpcs_in_flight=31 # 客户端值必须 < 服务端值
lctl set_param mdc.*.max_rpcs_in_flight=64
lctl set_param llite.*.statahead_max=512
lctl set_param llite.*.statahead_agl=1
lctl set_param ldlm.namespaces.*.lru_max_age=600000
lctl set_param ldlm.namespaces.*.lru_size=200 # auto 解析为 100×CPU 核数
lctl set_param timeout=600 ldlm_timeout=200 at_min=250 at_max=600
# --- modprobe 持久化(内容 SHA256 一致即跳过)---
# 写 /etc/modprobe.d/lustre.conf
# --- systemd oneshot 持久化(替代历史 cron)---
# 写 /etc/systemd/system/lustre-tune.service
systemctl daemon-reload
systemctl enable lustre-tune.service
# --- 移除历史 @reboot cron(高危:先备份到 /var/backups/lustre-ins,再删)---
cp -a /etc/cron.d/lustre-tune /var/backups/lustre-ins/etc/cron.d/lustre-tune.bak.$(date +%Y%m%d-%H%M%S)
rm -f /etc/cron.d/lustre-tune
单元内容要点(Type=oneshot、客户端带 RequiresMountsFor):
[Unit]
Description=Lustre lctl tuning (applied after mount)
After=remote-fs.target
RequiresMountsFor=/mnt/lustre # 仅客户端单元有此行
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/lctl set_param mdc.*.max_mod_rpcs_in_flight=31
# … 每条参数一行;lctl 路径按主机探测(RPM 装于 /usr/sbin)
[Install]
WantedBy=multi-user.target
5.3 服务端先行 + 客户端重连(本次实际执行过一次)¶
服务端值抬高后,已连接的客户端不会自动放宽上限,需重连:
# 1) 先确认服务端已生效
ssh root@192.168.31.21 'lctl get_param mdt.*.max_mod_rpcs_in_flight' # 预期 32
# 2) 重新挂载客户端(注意:手工 mount <挂载点> 会因 fstab 的 nofail 被当作 lustre 挂载选项而失败)
ssh root@192.168.31.21 'umount /mnt/lustre'
ssh root@192.168.31.21 'systemctl start mnt-lustre.mount'
# 3) 复核上限已放宽,再设客户端值
ssh root@192.168.31.21 "lctl set_param mdc.*.max_mod_rpcs_in_flight=31" # 预期 rc=0
5.4 残留备份清理(本次执行)¶
# 1) 盘点并白名单校验(只允许这三类命名)
ssh root@192.168.31.21 'ls -1 /etc/cron.d/lustre-tune.bak.* /etc/fstab.bak.* /etc/lnet.conf.bak.*'
# 白名单正则:^/etc/(cron\.d/lustre-tune|fstab|lnet\.conf)\.bak\.[0-9]{8}-[0-9]{6}$
# 2) 逐个 scp 拉回本地归档,并比对大小一致
# 3) 高危:按精确文件名删除(禁用通配)
ssh root@192.168.31.21 'rm -f /etc/cron.d/lustre-tune.bak.20260922-144939 …' # 12 个
ssh root@192.168.31.22 'rm -f /etc/fstab.bak.20260922-143659 …' # 3 个
# 4) 复核残留为 0
归档位置:tuning-fix-and-rollout-plan/backup/remote-192.168.31.{21,22}/;台账见 tuning-fix-and-rollout-plan/backup/README.md。
6. 验证¶
| # | 验证项 | 命令 | 实测结果 |
|---|---|---|---|
| 1 | 客户端修改类 RPC 上限 | lctl get_param mdc.*.max_mod_rpcs_in_flight |
31 ✅ |
| 2 | 客户端普通 RPC 上限 | lctl get_param mdc.*.max_rpcs_in_flight |
64 ✅ |
| 3 | 预读参数 | lctl get_param llite.*.statahead_max llite.*.statahead_agl |
512 / 1 ✅ |
| 4 | 服务端 MDT 上限 | lctl get_param mdt.*.max_mod_rpcs_in_flight |
32 ✅ |
| 5 | 持久化单元 | systemctl is-enabled lustre-tune.service、is-active |
enabled / active ✅ |
| 6 | 单元内容含全部角色参数 | grep ^ExecStart /etc/systemd/system/lustre-tune.service |
21 上 12 条(服务端 4 + MDS 1 + 客户端 7)✅ |
| 7 | 历史 cron 已移除 | ls /etc/cron.d/lustre-tune |
不存在 ✅ |
| 8 | 幂等复跑 | 再次执行 .venv/bin/python3 main.py tune --config config/deploy.yaml |
零变更、全跳过 ✅ |
| 9 | 健康巡检 | .venv/bin/python3 main.py inspect --config config/deploy.yaml |
报告 reports/inspect-20260924-124655.md,异常总数 0 ✅ |
| 10 | 重启后仍达标 | 重启 21 后重复第 1~5 项 | 已执行,第 2 次通过;细节见附录 B |
7. 回滚¶
备份位置:tuning-fix-and-rollout-plan/backup/(本地改前备份 10 个 + 远程残留归档 15 个),远程原位备份在 21/22 的 /var/backups/lustre-ins/。台账见 tuning-fix-and-rollout-plan/backup/README.md(实时台账单一源为项目根 backup/README.md)。
| 回滚对象 | 步骤 |
|---|---|
| 代码/配置/文档 | cp 备份回原路径(或 git checkout -- <路径>;改前状态对应提交 38e3f8a) |
| 调优参数(运行期) | 按 config/deploy.yaml 还原各值后重跑 tune;或直接 lctl set_param <param>=<旧值> |
| systemd 单元 | 用 /var/backups/lustre-ins/etc/systemd/system/lustre-tune.service.bak.<时间戳> 覆盖回去,再 systemctl daemon-reload && systemctl restart lustre-tune.service;彻底弃用时 systemctl disable --now lustre-tune.service 并删除单元文件 |
| 历史 cron | cp /var/backups/lustre-ins/etc/cron.d/lustre-tune.bak.20260924-0212{10,11} /etc/cron.d/lustre-tune(21/22 各一份) |
| 残留备份 | 从 backup/remote-192.168.31.{21,22}/ 还原到对应 /etc 路径 |
回滚后验证:重跑第 6 章第 1~5 项,确认参数与单元恢复为变更前状态。
8. 风险说明¶
| 风险 | 级别 | 缓解措施 |
|---|---|---|
max_mod_rpcs_in_flight 受 MDT 连接时返回值限制,改客户端前必须先抬服务端且客户端重连 |
中 | 已加配置校验:客户端值 ≥ 服务端值判 BLOCK;文档与排障手册补充 ERANGE(34) 处置流程 |
| 重挂载客户端会导致该客户端 IO 短暂挂起 | 中 | 选择业务空闲时执行;重挂载后立即核验 lctl dl 全 UP 与 findmnt |
重挂载时内核出现 lov tgt 0 not cleaned! / Device 13 not setup |
低 | 属 umount 清理期噪声;本次已核验重挂载后挂载正常、lctl dl 全 UP、lfs df 正常 |
手工 mount /mnt/lustre 失败(fstab 的 nofail 被当作 lustre 挂载选项) |
低 | 正确做法 systemctl start mnt-lustre.mount,或显式 mount -t lustre <nid>:/<fsname> <挂载点>;已写入 docs/troubleshoot.md |
重启后 mdc.*.max_mod_rpcs_in_flight 不自动达标(缺陷 C) |
低 | 已给 ExecStart 加 - 前缀避免连带失败;恢复口令见 B.4;根治需按角色拆分 unit(另一轮) |
osc.*.max_dirty_mb 无法作为期望态 |
低 | 实测运行期被内核按内存重算,已从 client_tuning 移除并在配置、SOP、RUNBOOK 三处注明 |
附录 A. 交付物清单¶
| 文件 | 说明 |
|---|---|
tuning-fix-and-rollout-plan/SOP.md |
本文件 |
tuning-fix-and-rollout-plan/batch-rollout-plan.md |
多主机数据面分批与失败阈值规划设计(本轮不含实现) |
tuning-fix-and-rollout-plan/backup/README.md |
本次备份台账 |
tuning-fix-and-rollout-plan/backup/ |
本地改前备份 12 个 + 远程残留归档 15 个 |
reports/inspect-20260924-132558.md |
最终巡检报告(异常总数 0) |
同步修改的工程文件:modules/lctl_tuning.py、modules/lnet_config.py、modules/client_mount.py、modules/inspector.py、utils/config_validate.py、deployer.py、config/deploy.yaml、config/deploy.yaml.example、docs/SOP.md、docs/RUNBOOK.md、docs/troubleshoot.md、docs/HANDOFF.md。
附录 B. 追加实施:重启验证与重启自愈修复¶
第 6 章第 10 项于同日实测执行,结果暴露了 3 个与调优变更本身无关的既有缺陷;按确认范围修复了 A、B 两项。
B.1 实测过程与结果¶
| 轮次 | 结果 |
|---|---|
| 第 1 次重启(12:54) | 失败:lnet.service 启动失败 → LNet 无 NID → mnt-mdt0.mount、mnt-lustre.mount 全部 failed,集群不可用;人工 lnetctl net add --net tcp --if ens2 后恢复 |
| 第 2 次重启(13:24,修复 A/B 后) | 通过:节点 30 秒回归,客户端挂载 24 秒内自动就绪,lnet.service active 且 NID 正常,三个 unit 全 active,lctl dl 全 UP,服务端与 llite 参数自动达标;最终巡检异常总数 0 |
B.2 修复 A:/etc/lnet.conf 格式错误(重启即瘫痪)¶
- 现象:
lnet.service执行lnetctl import /etc/lnet.conf失败(退出码 234),日志setting @lo not allowed(errno -19)、Failed to configure NI on net 'lo' rc = -22 - 根因:实现体用
lnetctl export > /etc/lnet.conf落盘,而 export 的输出必然包含net type: lo;import 会尝试在 lo 上添加 NI 并被拒绝,服务整体失败 → 无 NID → Lustre 全栈起不来 - 修复:改为按配置生成 tcp-only 的 YAML 落盘:
net:
- net type: tcp
interfaces:
0: ens2
判据:对 tcp-only 配置,lnetctl import 报的是 cannot add LNet NI(EEXIST,运行期重复添加的正常提示),不再报 @lo not allowed
- 实现体:modules/lnet_config.py 的 _apply_lnet()(生成 tcp-only 内容)与 _lnet_conf_valid()(新增「不得含 lo」判据)
- 远程修复:21/22 的 /etc/lnet.conf 已按新逻辑重写
B.3 修复 B:客户端挂载未声明对服务端挂载的依赖¶
- 现象:同机场景(21 既是 MGS+MDT 又是客户端)重启时
mnt-lustre.mount与mnt-mdt0.mount并行触发,客户端可能先于 MGS/MDT 就绪 - 修复:同机客户端的 fstab 条目追加
x-systemd.requires-mounts-for=<服务端挂载点>:
192.168.31.21@tcp:/edafs /mnt/lustre lustre defaults,nofail,x-systemd.requires-mounts-for=/mnt/mdt0 0 0
该选项由 systemd-fstab-generator 解析并剥离,不会传给 mount.lustre;实测 systemctl show mnt-lustre.mount -p RequiresMountsFor 输出 RequiresMountsFor=/mnt /mnt/mdt0
- 实现体:modules/client_mount.py 新增 _local_deps 与 _fstab_entry()
B.4 未修项(登记为缺陷 C)¶
调优 unit 内的 mdc.*.max_mod_rpcs_in_flight=31 在重启场景必然失败:开机时客户端先以 MDS 的默认值(mdt.*.max_mod_rpcs_in_flight=8)完成连接协商,上限被压到 7,而 unit 运行在该次协商之后。
已做的缓解:给 unit 的每条 ExecStart 加 - 前缀,使单条失败不中断其余参数(否则一次失败会连带丢弃 llite/osc 等参数)。
重启后的标准恢复口令:
# 仅重放参数(unit 处于 active 时 start 是 no-op,必须用 restart)
ssh root@192.168.31.21 'systemctl restart lustre-tune.service'
# 若要把 max_mod_rpcs 一并恢复到 31:先重挂载客户端让上限放宽,再重放
ssh root@192.168.31.21 'umount /mnt/lustre && mount -a && systemctl restart lustre-tune.service'
Type=oneshot+RemainAfterExit=yes的 unit 在 active 状态下systemctl start不会重跑ExecStart(真机验证中发现)。
根治方向(另一轮):调优 unit 按角色拆分——服务端单元在 target 挂载后设置 mdt.*,客户端单元只设客户端参数,并让客户端挂载依赖服务端参数就绪。
附录 C. 遗留事项¶
- 缺陷 C 未修:重启后
mdc.*.max_mod_rpcs_in_flight不自动达标,须按 B.4 的口令人工恢复;根治需按角色拆分调优 unit,建议与第 2 项合并规划。 - 多主机数据面分批尚未实现,见
batch-rollout-plan.md。 docs/troubleshoot.md中「系统盘白名单 sda/vda/nvme0n1」的旧表述与现行实现(lsblk挂载树推导 +disk_safety.system_disks覆盖)不一致,属既有待决策项(HANDOFF 3.1 第 3 条),本轮未动。
来源:
/home/rain/Documents/lustre_ins/tuning-fix-and-rollout-plan/SOP.md| 最后更新:2026-09-24 13:27(由build/sync.py同步,请勿在站点侧修改)