Li Sheng | Backend / Distributed Storage Engineer Li Sheng | Backend / Distributed Storage Engineer
Home
Resume
Projects
Topics
Notes
GitHub (opens new window)
Home
Resume
Projects
Topics
Notes
GitHub (opens new window)
  • 李胜 分布式存储工程师
  • 项目经历
  • CephFS 合规存储
  • CephFS IOPS QoS 限速
    • 项目简介
    • 项目团队分工
    • 详细方案设计
      • 1. QoS 策略模型设计
      • 2. 策略匹配与继承规则
      • 3. 对外接口
      • 4. 请求路径限速
      • 5. 限速算法与运行状态
      • 6. 多客户端 / 多 MDS 场景
      • 7. 持久化与恢复
      • 8. 可观测性与运维能力
    • 项目踩过的一些坑
      • 目录策略匹配成本问题
      • 限速精度与性能开销取舍
      • 客户端预判与 MDS 裁决边界
      • 用户体验与错误语义
    • 当前该功能的约束条件
  • CephFS 性能测试与可观测性分析
目录

CephFS IOPS QoS 限速

# CephFS IOPS QoS 限速

# 项目简介

这个功能面向多租户共享 CephFS 的场景。典型问题是多个租户、项目或业务目录挂载到同一个 CephFS 集群后,某个热点元数据请求可能抢占 MDS 处理能力,影响其他业务目录的稳定性。MDS 侧 QoS 的目标是在文件系统元数据服务入口统一排队和调度请求,在存储系统内部提供更可控的资源隔离。

当前实现是 CephFS MDS metadata request QoS,不是完整的数据面 read/write IOPS 限速。

核心目标:

  1. 在 MDS active 状态下,对来自 CephFS client 的 metadata request 接入 dmClock 调度。
  2. 以客户端 session 的 root 元数据识别业务根路径,并归一化到 subvolume root 维度。
  3. 让同一个 subvolume 下的多个 client session 共享同一组 reservation / weight / limit。
  4. 支持默认 QoS 参数和运行时按路径覆盖 QoS 参数。
  5. 保持未启用、配置不完整、非 client 请求、非 active MDS 等场景的原有处理路径。

需求拆解:

  1. 目录级 QoS 策略配置:当前支持 enabled、reservation、weight、limit。默认值来自 MDS 配置项,单个 subvolume 可通过 admin socket 覆盖。暂不支持独立 read IOPS、write IOPS、metadata IOPS 三类字段,也没有独立 burst 字段。
  2. 策略匹配与继承:当前按 client session 的 root 元数据匹配,并将深度超过 3 的路径归一化为 /volumes/<group>/<subvolume>。这是 subvolume root 维度的匹配,不是任意层级目录继承模型。
  3. 请求路径限速:接入 CEPH_MSG_CLIENT_REQUEST,覆盖 MDS 处理的客户端元数据请求,例如 lookup、getattr、readdir、open、create、mkdir、unlink、rename、setattr 等。
  4. 限速算法:复用 Ceph dmClock PushPriorityQueue,每个 volume/subvolume 作为 dmClock client,使用 reservation / weight / limit 三元组进行调度。
  5. 多客户端 / 多 MDS 协同:同一 MDS rank 内,同一 subvolume 的多个 session 共享一个 VolumeInfo。多 MDS 场景下是各 rank 本地调度,不提供跨 rank 的全局强一致计数。
  6. 可观测性与运维:提供 dump qos、qos set、qos get、qos rm admin socket 命令,可查看默认状态、队列长度、inflight 请求数、session 列表、平均调度延迟和 throttle 计数。

# 项目团队分工

存储系统侧(1人):

  1. 需求拆解:将“IOPS QoS”收敛为 MDS metadata request QoS,优先解决元数据热点目录或 subvolume 对 MDS 的冲击。
  2. 方案设计:选择 MDS 侧集中调度而不是 client 侧自限速;选择 dmClock 而不是手写令牌桶,复用已有 reservation / weight / limit 语义。
  3. 核心开发:新增 MDSDmclockScheduler,在 Server::dispatch() 的 client request 入口接入调度器,在 session open/reconnect/close/kill 路径维护 volume 与 session 关系。
  4. 运维接口:提供qos set、qos get、qos rm 等libcpehfs-jni 进行参数配置,以及类似dump qos的观测命令。

管理平台侧(1人):如果需要对接统一通过新增的libcpehfs-jni接口:展示当前 MDS rank 的 subvolume QoS 状态,在管理界面进行桌面操作时调用 qos set 调整 reservation / weight / limit,调用 qos rm 恢复默认参数。

# 详细方案设计

# 1. QoS 策略模型设计

当前 QoS 策略绑定 inode_t中,在inode加载时直接读取,维护到内存中的 MDSDmclockScheduler::volume_info_map。key 是 VolumeId,也就是从 client session metadata 中取到的 root 路径,经过 convert_subvol_root() 归一化后的 subvolume root。

路径归一化规则:

  1. 如果 root 路径深度大于 3,截断为前三层。
  2. 典型 subvolume 路径 /volumes/_nogroup/subvolume/dir/a 会归一化为 /volumes/_nogroup/subvolume。
  3. 如果 root 路径深度不超过 3,则使用原路径。

核心数据结构:

  1. mds_dmclock_conf default_conf:MDS rank 的默认 QoS 配置,包含 enabled、reservation、weight、limit。
  2. VolumeInfo:单个 volume/subvolume 的运行时状态,继承 QoSInfo,字段包括 reservation、weight、limit、use_default、updated、session_list、inflight_requests、throttle、latency_sum、latency_cnt。
  3. ClientRequest:进入 QoS 调度队列的客户端请求,保存原始 MClientRequest、volume id、到达时间和 cost。
  4. UpdateRequest:用于异步更新某个 volume 的 dmClock client info。

# 2. 策略匹配与继承规则

请求到达 MDS 后,调度器通过 mds->get_session(mds_req) 获取对应 session,再从 session->info.client_metadata["root"] 读取 root 路径。该路径代表客户端被授权或挂载的根目录。调度器随后调用 convert_subvol_root() 把路径归一化为 subvolume root。

例如:

/volumes/_nogroup/subvolume
/volumes/_nogroup/subvolume/dir-a
/volumes/_nogroup/subvolume/dir-a/file-b
1
2
3

都会归并到:

/volumes/_nogroup/subvolume
1

这意味着当前实现是 subvolume 级别 QoS。它可以覆盖很多“目录级”业务诉求,因为 CephFS 多租户通常通过 subvolume 暴露租户根目录;但它不是对任意深层目录逐级查找最近祖先策略的通用目录树 QoS。

多级目录策略当前不支持:

  1. 不会从请求目标 inode 向上查找父目录 QoS。
  2. 不存在“最近祖先目录优先”或“最严格策略优先”。
  3. 不存在父子目录限速叠加。
  4. qos set path=... 只能对已有 VolumeInfo 生效,也就是该路径必须已经有活跃 session 被调度器识别。

当前这种设计的优点是匹配成本低:请求路径不需要额外解析目标 inode,也不需要每次沿目录树向上查找策略;调度 key 直接来自 session metadata 和内存 map。

# 3. 对外接口

  1. libcephfs_jni 系列接口中新增一批用于增删改查 子卷QoS参数的接口,用于提供到管理平台在界面做设置调用

  2. MDS admin socket 新增一批用于查询qos状态的观测命令,用于底层观测调试该功能的运行状态

# 4. 请求路径限速

server的 op 分发函数会判断以下条件:

  1. 是否开启Qos配置功能。
  2. 请求来源是 client。
  3. 当前 MDS rank 处于 active 状态。
  4. 对应 volume 的 QoS 信息已经被更新到 dmClock。
  5. reservation / weight / limit 都大于 0。

满足条件时,请求进入调度器的 request_queue,再由调度线程提交到 dmClock,不满足条件时,直接调用原有路径不受影响。

当前覆盖的是客户端元数据请求。代码中按操作类型设置 cost。限速后的行为不是立即返回错误,而是排队调度。因此业务侧看到的是请求延迟增加,而不是直接收到 EAGAIN 或其他限速错误。

# 5. 限速算法与运行状态

当前实现使用 Ceph 已有 dmClock 调度库:

每个 VolumeId 是一个 dmClock client。

运行时流程:

  1. MDSRank 构造时创建 MDSDmclockScheduler,调度器启动独立线程处理 request_queue。
  2. MDS 进入 active 时,调度器设置 QoS功能,如果配置启用则扫描 OPEN / STALE session 建立 VolumeInfo。
  3. 新 session open、force open、reconnect 成功后调用 add_session(),将 session 加入对应 volume。
  4. session close / kill 时调用 remove_session(),session 数归零且无 inflight 请求后删除 VolumeInfo。
  5. client request 入队后,调度线程把它加入 dmClock queue,并增加 volume 和全局 inflight 计数。
  6. dmClock 回调 submit_request_to_mds() 时,记录排队延迟,调用 MDS 原始处理函数,并减少 inflight 计数。

为了避免 QoS 模块自身成为瓶颈,当前实现做了几件事:

  1. 策略匹配基于 session root,不对每个请求做目录树向上查找。
  2. 调度处理放在独立线程,主 dispatch 路径只做轻量判断和入队。
  3. volume 状态使用内存 map 和 mutex 保护,避免持久化读写进入热路径。
  4. 只有启用 QoS 且 volume 信息有效时才进入 dmClock;否则走原路径。

# 6. 多客户端 / 多 MDS 场景

多客户端场景:

  1. 同一 subvolume 下的多个 client session 会共享同一个 VolumeInfo 和 dmClock client。
  2. 例如四个 session 都挂载 /volumes/_nogroup/subvolume,如果该 subvolume 的 limit=100,那么这四个 session 在该 MDS rank 上共享约 100 metadata IOPS 的上限。
  3. dump qos 中可以通过 session_cnt 和 session_list 看到当前共享同一 QoS 的 session。

多 MDS 场景:

  1. 当前 QoS 队列状态维护在每个 MDS rank 本地。
  2. 没有跨 MDS rank 的全局令牌桶、全局计数器和中心化调度器。
  3. 当同一个 subvolume 的请求被多个 active MDS rank 处理时,每个 rank 分别执行本地 dmClock 调度;因此它是 rank-local QoS,不是全局精确 QoS。

这里做了一个工程取舍,降低了实现复杂度和热路径开销,但牺牲了全局精确限速能力。对于多 MDS 下需要强全局 QoS 的业务,后续需要引入跨 rank 策略同步与聚合计数机制。

# 7. 持久化与恢复

MDS故障后恢复时,重新加载OMAP元数据到内存中时,会主动识别 inode_t 的 iops_qos 字段,如果配置有相关参数,会更新到 subvolume_iops_qos_map 中,当新的客户端会话访问对应子卷时,可将其op放入qos队列中进行限速。MDS故障、节点重启、子树迁移等都不会导致qos策略失效。

# 8. 可观测性与运维能力

dump输出分为两部分:

  1. qos_state:全局状态,包括 qos_enabled、state、default_reservation、default_weight、default_limit、mds_dmclock_queue_size、inflight_requests。
  2. volume_infos:每个 volume 的状态,包括 volume_id、use_default、reservation、weight、limit、throttle、latency_avg、inflight_requests、session_cnt、session_list。

# 项目踩过的一些坑

# 目录策略匹配成本问题

原始“目录级 QoS”很容易设计成每个请求都解析目标路径或目标 inode,然后向上查找父目录策略。这在 MDS 热路径上成本较高,也会引入缓存一致性和目录迁移问题。

当前实现规避了这个问题:不按请求目标路径查找策略,而按 client session 的 root 元数据确定业务根路径,再归一化到 subvolume root。这样请求到达时只需要做 session 查找、内存 map 匹配,开销可控。

代价是能力边界更窄:它适合 subvolume / 租户根目录 QoS,不适合任意深层目录独立限速。

# 限速精度与性能开销取舍

当前实现没有追求跨 MDS rank 的全局强一致 IOPS 计数。原因是 MDS metadata request 本身是高频路径,如果每次请求都参与分布式计数或远程协调,会把 QoS 模块变成新的性能瓶颈。

dmClock 的 reservation / weight / limit 可以在单 MDS rank 内提供公平调度和上限控制。多 MDS 下则采用本地近似限速,适合先解决热点 subvolume 对单个 MDS rank 的冲击。

# 客户端预判与 MDS 裁决边界

当前没有在客户端做快速限速或提前拒绝。所有裁决在 MDS 侧完成:

  1. 客户端无需感知 QoS 策略。
  2. MDS 能看到实际进入 metadata server 的请求流。
  3. 被限速请求通过排队体现,不改变客户端协议语义。

这个设计降低了客户端改造成本,但无法在请求到达 MDS 前削峰。如果未来需要更早削峰,可以考虑把策略下发给客户端做预判,MDS 仍保留最终裁决。

# 用户体验与错误语义

当前限速不会直接返回错误码,而是让请求在 MDS 侧排队。业务侧表现为 metadata 操作延迟增加。这比直接拒绝更接近传统 IO throttle 语义,也能避免应用把限速误判为存储故障。

但运维侧需要把 latency_avg、throttle、MDS slow request 等指标关联起来看,避免把预期内的 QoS 排队误判为 MDS 异常。

# 当前该功能的约束条件

  1. 限速范围:仅覆盖 MDS metadata request,不覆盖 OSD 数据读写路径。
  2. 粒度:实际是 subvolume root 粒度,不是任意目录树粒度。
  3. 参数:支持 reservation / weight / limit,不支持独立 read/write IOPS、burst、继承策略。
  4. 生效条件:需要显式开启QoS配置,MDS 处于 active 状态,volume 信息已创建且 QoS 参数有效。
  5. 配置方式:新增控制面系列命令,通过inode_t的新加字段元数据变更确保持久化。
  6. 多客户端:同一 subvolume 下多个 session 在同一 MDS rank 内共享 QoS。
  7. 多 MDS:每个 active MDS rank 本地调度,不提供全局精确限速。
Last Updated: 2026/04/20, 18:15:39
CephFS 合规存储
CephFS 性能测试与可观测性分析

← CephFS 合规存储 CephFS 性能测试与可观测性分析→

最近更新
01
ceph分布式存储-对象存储(RGW)搭建
10-27
02
ceph分布式存储-管理crushmap
10-27
03
ceph分布式存储-集群客户端连接
10-27
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式