跳转至

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. 背景

对集群做安装进度核查时发现:首次部署虽已完成、服务健康,但调优模块「报了成功、实际未生效」。逐项取证确认了三类问题:

  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. 配置项在 2.17 已不存在:llite.*.statahead_xattr 读取返回码 2,却被写进了持久化文件。
  3. 实现体吞掉失败:set_param 返回 34 只记日志,模块仍 mark_success(),导致「失败被当作成功」持续了两天未被发现。

同时发现两项既有缺陷:

  1. 同机多角色时持久化文件互相覆盖:21 既是服务端又是客户端,实现体按角色各写一次同一文件,后写覆盖先写 —— 21 上的 server_tuning(4 项)从未落盘过。
  2. 持久化机制本身不可靠:用 /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. 目标

变更完成后应达到:

  1. 调优参数真实达标,且失败不再被吞没(有失败即模块 failed)
  2. 调优参数在重启后仍达标(持久化单元 enabled + active,且在挂载完成后执行)
  3. 历史 cron 持久化文件移除,/etc 下无残留 .bak
  4. inspect 巡检调优项零异常;巡检判据与下发逻辑一致
  5. docs/ 三份文档与实机口径一致
  6. 多主机数据面分批给出规划设计(本轮不含实现)

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. 遗留事项

  1. 缺陷 C 未修:重启后 mdc.*.max_mod_rpcs_in_flight 不自动达标,须按 B.4 的口令人工恢复;根治需按角色拆分调优 unit,建议与第 2 项合并规划。
  2. 多主机数据面分批尚未实现,见 batch-rollout-plan.md。
  3. 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 同步,请勿在站点侧修改)