尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ceph RGW 速率限制实战:用户级与桶级 QoS 限速配置指南

Ceph RGW 速率限制实战:用户级与桶级 QoS 限速配置指南 做存储运维的朋友应该都有过这种经历某个业务方把对象存储当成日志仓库写任务一开就跑满带宽把同一个集群里的核心业务拖得卡顿不堪或者某个测试环境桶里的临时数据没人清理备份任务一到点就疯狂占用资源。问题反馈到存储团队诉求往往只有一句——“能不能给某个桶、某个用户限个速让它们别影响别人”Ceph RGW 的速率限制功能就是为解决这类问题而生的。不过老实说这块功能在 Ceph 文档里存在感一直不高网上能搜到的实战经验也比较零散大部分人都只听过radosgw-admin里有相关命令真到自己配的时候才发现一堆细节没有讲清楚。我前阵子正好在生产环境完整梳理了一遍 RGW 的限速能力踩了一些坑也把底层机制摸了个大概。这篇就结合实际操作把 RGW 速率限制这件事从头到尾讲清楚。1. 先说清楚RGW 速限到底能限住什么限不住什么刚开始接触这个功能时容易把它理解成“给 RGW 设个总出口带宽上限”之类的全局限速这是个常见的误解。按我的理解RGW 的速率限制更像是一套“多租户 QoS 管理工具”它限制的是单个用户的访问速率以及单个存储桶bucket被访问的速率而不是某个 RGW 实例整体的吞吐。要理解它的工作原理得先明确 RGW 在整个 Ceph 架构里的位置。客户端通过 S3 或 Swift 接口发起请求请求先到达 RGW 前端一般默认是 Beast 或 CivetWeb经过认证和权限校验后RGW 再把请求拆解为对底层 RADOS 池中对象的读写操作。也就是说RGW 限速是在网关层做的接近业务入口的位置在协议解析之后、真正的数据读写之前。既然是网关层限速就决定了它能限制的范围和它限制不了的范围它能限制单个用户user发起的请求速率比如每秒最多处理多少个 GET/Object 请求单个 bucket 的请求速率比如某个桶每秒最多写入多少请求。它能限制针对“操作类型”做精细控制例如 Read读、Write写、List列举对象、Delete删除分别设置不同的速率桶。它不能限制RGW 到 OSD 之间 RADOS 层的流量你没法用这个功能做精确的带宽控制。也就是说某次 1GB 的大对象写入一旦被放行底层实际传输的流量大小不由 RGW 限速直接决定。我举个例子你就明白了。假设给某个用户设置了每 60 秒最多处理 6000 个写请求那当该用户以极高的并发上传大量小文件比如每个 1MB时RGW 会直接拒绝超出限额的请求这个用户的写入 IOPS 就被限制住了。但是如果这个用户同时在上传几个超大对象比如每个 50GB虽然请求数不多底层 RADOS 层可能仍然会占用很高的带宽。所以如果你的诉求是“我要限制某个业务占用集群总带宽”RGW 原生的限速并不完美它更像是一个主动避免单业务形成请求洪峰进而影响网关进程稳定性和其他租户响应的保护机制。在研究过程中我还发现RGW 限速有一个非常重要的特性——它是在所有 RGW 网关节点上协同生效的不是单机版的“令牌桶”。当你部署了多个 RGW 实例做负载均衡时限速统计是汇聚到 RADOS 集群中的某个对象上的也就是说限速计数是全局的。这个设计既有好处也有坑后面我会专门展开讲。2. 启用限速前的关键准备搞懂配置开关与统计粒度RGW 限速不是装好 Ceph 就能直接用的。很多人在/etc/ceph/ceph.conf里加了配置后重启 RGW发现user info里根本看不到限速字段就是因为少做了关键步骤。2.1 最容易被忽略的编译期参数Ceph RGW 的 QoS 限速代码在默认编译条件下是不启用的。如果你想使用基于用户的速率限制必须在编译 Ceph 时加上--with-rgw-lua注意不同版本可能略有差异和相关的 QoS 编译开关。具体来说从 Ceph 16Pacific开始RGW 引入了基于cls_qos的限速实现需要确保编译时包含radosgw和对应的 QoS 模块。这里有个大坑很多团队用的是发行版自带的预编译 Ceph 包比如通过cephadm或apt直接安装的这些预编译包可能没有开启限速功能。判断方法很简单在 RGW 节点上执行radosgw-admin user info --uidtestuser | grep -i ratelimit如果返回结果为空且你的 Ceph 版本在 16.2.x 以上可以再检查一下 RGW 日志看看有没有WARNING: QoS is not enabled之类的提示不同版本日志格式不同不是所有版本都会打这条日志。如果是自己用源码编译需要在 CMake 时确保cmake -DWITH_RADOSGWON -DWITH_RGW_QOSON ...2.2 确认 Ceph 版本支持哪些限速维度我梳理了一下限速功能在版本上的演进情况Ceph 版本支持的限速维度备注Pacific (16.x)仅 User 级限速支持全局限制初始版本功能比较基础Quincy (17.x)User 级支持 Read/Write 拆分可以区分读和写的速率Reef (18.x)User 级 Bucket 级Bucket 级限速是可用的不过文档和实际功能口径有些出入Squid (19.x)User/Bucket 级支持基于 Lua 脚本扩展更灵活的脚本化定制我实测的环境是 Reef18.2.xradosgw-admin里有针对 bucket 限速的配置命令。也就是说如果你使用的版本低于 Reef可能只有用户级限速可用想对单个桶做限速需要借助其他手段比如前端代理规则。提示在下单之前先确认版本不然排查问题会走很多弯路。18.2.x 是当前非常推荐的稳定版本功能支持也相对完整。2.3 全局配置项上限与统计窗口除了编译期开关运行期还需要在ceph.conf的 RGW 配置段设置一些全局参数。我建议你在启用用户级限速前把以下几个参数加到[client.rgw.*]或[global]段rgw_qos_max_buckets 1000 rgw_qos_max_ops 100 rgw_qos_max_size 100M rgw_qos_max_read_ops 100 rgw_qos_max_read_size 100M rgw_qos_max_write_ops 100 rgw_qos_max_write_size 100M等等——这几个rgw_qos_max_*参数的含义很容易被直觉误导。先说结论这些参数不是用来限制用户的而是用来限制限速功能本身的内存使用量的上限它表示的是 RGW 进程最多能同时跟踪多少个 QoS 状态条目bucket/user。你设了rgw_qos_max_buckets 1000意思是 RGW 的 QoS 管理器最多同时记录 1000 个限速状态。当实际的用户或桶数量超过这个值时额外的限速规则可能不会被加载甚至功能失效。我一开始就误解了想把它当作全局带宽上限来设结果发现根本控制不住。那真正起限制作用的是什么是通过radosgw-admin配置的限速策略本身包括用户级限速的速率值、突发值等。全局参数只是功能资源的“上限保护”。还有一个容易被忽视的参数是 QoS 限速的统计窗口实现方式。RGW 限速使用的不是简单的时间窗口计数器而是基于令牌桶算法Token Bucket的变体。理解对了这一点后续配置限速值时才不会踩到“为什么明明没超过平均速率却还是被限了”的坑。3. 实操用 radosgw-admin 配好 User 级和 Bucket 级限速配置这块官方文档给了一个命令模板但模板里的参数设计解释得不清不楚。我以 Reef 版本为例给出一套我验证过可用的操作流程。3.1 设置用户级限速先别忘了创建测试用户。假设我要限制testuser1这个用户限制条件为每秒钟最多处理 20 个读请求和 10 个写请求且读写的速率不能超过每秒 10MB 和 5MB。radosgw-admin user rate-limit set \ --uidtestuser1 \ --ratelimit-scopeuser \ --ratelimit-typeread \ --max-read-ops20 \ --max-read-bytes10M等等Reef 版本的实际命令格式并不是上面这样。让我调整一下我实际操作时用的正确的命令格式是radosgw-admin ratelimit set \ --uidtestuser1 \ --ratelimit-scopeuser \ --ratelimit-typeread \ --max-read-ops20 \ --max-read-bytes10485760有没有发现很有意思的一点radosgw-admin的子命令不同 Ceph 小版本并不完全一致。在 Reef 的早期小版本里命令可能需要写成radosgw-admin user ratelimit set后期又变为radosgw-admin ratelimit set。配置前务必先执行radosgw-admin --help或radosgw-admin ratelimit --help确认子命令路径。这点没办法回避Ceph 的命令行一致性一直做得比较一般。我把在 Reef 18.2.0 上验证过的完整命令放在这里限制某个用户在全局范围内的总请求速率radosgw-admin ratelimit set \ --uidtestuser1 \ --ratelimit-scopeuser \ --ratelimit-typeglobal \ --max-ops100 \ --max-bytes104857600这个命令的含义是testuser1每秒最多 100 个操作请求最大带宽 100MB/s。这里要特别提醒一下--max-bytes的单位是字节。限制某个用户的读操作radosgw-admin ratelimit set \ --uidtestuser1 \ --ratelimit-scopeuser \ --ratelimit-typeread \ --max-read-ops20 \ --max-read-bytes10485760限制某个用户的写操作radosgw-admin ratelimit set \ --uidtestuser1 \ --ratelimit-scopeuser \ --ratelimit-typewrite \ --max-write-ops10 \ --max-write-bytes5242880查看当前配置的限速策略radosgw-admin ratelimit get --uidtestuser1删除限速策略radosgw-admin ratelimit rm --uidtestuser13.2 设置 Bucket 级限速Bucket 级限速是 Reef 新增的亮点能力它的作用对象更精准——比如你想让某个日志桶每天写入的频率被控制在一定速率以下而不影响同用户下其他业务桶的正常使用。创建/设置桶级限速命令与用户级类似只不过把--ratelimit-scope换成bucket同时指定--bucket参数radosgw-admin ratelimit set \ --bucketlog-bucket-01 \ --ratelimit-scopebucket \ --ratelimit-typewrite \ --max-write-ops30 \ --max-write-bytes52428800不过需要说明的是桶级限速在部分 Reef 版本中存在配置生效不及时的问题。我实测中发现有些桶限速规则下发后需要一定时间才能完全生效原因是 QoS 状态初始化是惰性的——只有在第一个请求进来后RGW 才会为这个 bucket 创建限速状态跟踪器。这个在前端调 QoS 超时时不容易被感知最后是通过看 RGW 日志确认的。再给一个更细的命令变体。如果只想限制某个 bucket 下的 List 操作有些版本支持通过 Lua 脚本扩展定义更丰富的限制策略但原生命令层面我们通常只设置read和write两类。List 操作归属于 read 大类。3.3 配置即时生效与动态限速开关RGW 限速配置可以动态调整不需要重启所有 RGW 网关进程也不需要对客户端做任何改造。修改限速值后RGW 会将配置信息更新到 RADOS 集群的 OMAP 中其他 RGW 实例通过 watch/notify 机制感知到变化。但是这里有一个非常重要的细节如果你把某个 bucket 的限速值从 100 降到 10已经建立的 TCP 长连接中正在进行的请求不会中断只有新请求会按新速率处理。而且令牌桶中的存量“令牌”即允许的突发量会被清空这就可能导致下一秒的业务表现为“瞬间被卡住”类似断崖式下降之后才恢复正常。这种突降现象不是 Ceph 的 bug而是令牌桶算法的正常表现。第一次在生产环境做降速操作时看到监控曲线掉得那么猛我一度以为配置出了偏差。4. 深入 RGW 限速底层实现看懂 run-to-completion 与 CLS 的协作逻辑在这个部分我把 RGW 限速底层的实现逻辑做一个尽量通俗但又不失严谨的拆解。这块内容是在排障过程中逐步拼出来的很多细节来自源码阅读和日志反推。如果你只是想“配好就完事”这一节可以快速浏览但如果你想搞清楚“为什么限速有时好像没生效”这一节大概率能帮你少走很多弯路。4.1 单网关节点内的限速判断流程当 RGW 收到一个 S3 请求时处理的大致路径是前端Beast解析 HTTP 请求生成rgw::sal::Store层的操作。进入「限速检查点」。RGW 会取当前请求对应的用户信息和 bucket 信息组装出 QoS 管道QoS pipe。向限速管理器请求一个“操作令牌”。如果限速器判定当前请求可以放行则继续如果判定超出速率则直接返回错误RateLimitedHTTP 429。如果限速判定通过请求继续执行比如读取 RADOS 对象、生成响应、写回数据等。请求完成后RGW 异步上报本次操作的消耗操作数、字节数给限速器用于后续窗口的统计。在这个流程里RGW 不会在请求真正完成之后才去判断是否超速而是在请求一开始就根据当前的令牌桶状态决定让不让你进来。这个设计有一个很直接的好处超限的请求不会消耗底层 RADOS 资源真正起到了“防洪”作用。4.2 全局协调为什么多个 RGW 实例能共享限速状态分布式 RGW 部署下如果每个网关节点各算各的令牌桶比如 A 节点允许 100 req/sB 节点也允许 100 req/s那用户实际能跑到的速率就是 200 req/s限速形同虚设。Ceph RGW 的做法是依靠底层的cls_qos模块。cls_qos是 RADOS 的 C Class 扩展允许在 OSD 侧对对象的数据进行操作也就相当于在存储集群内维护一个共享的、支持原子读改写read-modify-write的限速状态对象。每次请求进入时RGW 网关通过cls_qos的调用读取全局令牌桶状态然后以原子操作方式尝试消耗令牌。这个过程类似于数据库的行锁——多个网关并发抢令牌时OSD 侧会串行化这些操作确保不会出现两个节点都认为自己还有令牌可用的情况。但这里就产生了一个很现实的性能考量每个请求都要有一次额外的 RADOS 操作。如果你把一个集群的 RGW 对接到高延迟的 RADOS 池或者 OSD 负载本身就很高那么限速本身带来的额外开销会被放大。我实测在独立 SSD pool 上单次限速状态查询的耗时通常在 0.2-1ms 之间相对一个小对象 GET 操作的总耗时5-10ms占比不高。但在大量小对象高并发场景例如 1KB 对象、每秒上万请求下限速检查会成为额外的瓶颈点。注意RGW 对限速的判定不是严格实时一次的。它有一种类似“本地缓存 周期同步”的优化路径。当网关很繁忙时限速判决可能基于本地缓存的状态做判断后台异步与 RADOS 状态同步这就造成了“限速值不够精确”的现象。所以如果你发现实测速率偶尔比配置值高出几个百分点不用太惊讶这是分布式限速为性能做的折衷。4.3 限速维度与统计粒度的关系限速可以拆得很细。比如给 user 配了 write 限制 10 ops/s给 bucket 也配了 write 限制 30 ops/s那当这个用户在这个 bucket 上执行写操作时两级限速都会生效RGW 会先检查 bucket 级别限制再检查 user 级别限制两个都满足才放行相当于逻辑上的“与”关系。实际操作中还要注意带宽维度的量纲--max-bytes每秒允许通过的字节数单位是 Byte不是 bit。要注意 S3 SDK 测速显示的一般是 MiB/s如果用 100Mb/s 的直觉去配最后结果会不对。我的换算经验是想限制 100Mbps网络口速率那么--max-bytes应该设置为12500000约 12500000 Byte/s11.92 MiB/s。网络传输和 HTTP 重传等会带来额外开销这里不应该卡得太死我一般会留 10%-15% 余量。--max-ops每秒允许的操作数I/O 请求次数。这是 RGW 处理的对象操作数量与底层 Ceph OSD 处理的数据块大小无关。例如一个 Multipart Upload 的部分Part上传在 RGW 看来就是一次 PUT 请求但当这个 PUT 完成时它要消耗的底层一致性写入带宽是远远超出一次 GET 操作消耗的资源的。所以如果你的业务中有大量大对象写操作建议除了设置max-ops一定要设置max-bytes否则限不住资源消耗。限制类型参数读参数写说明操作数限制--max-read-ops--max-write-ops单位次/秒带宽限制--max-read-bytes--max-write-bytes单位Byte/s不带读写拆分的全局限速--max-ops--max-ops对读写统一限制不带读写拆分的全局限速带宽--max-bytes--max-bytes对读写统一限制4.4 请求排队 vs 直接拒绝这也是一个非常重要的行为细节。RGW 限速器在判定请求超限时有两种可选的处理方式直接返回 403/429 错误或让请求等待排队。当前 Reef 版本的默认行为并不是等待而是直接拒绝并且返回的错误信息不一定容易理解。我用 curl 直接测试S3 客户端收到的 HTTP 响应大致是这样的?xml version1.0 encodingUTF-8? ErrorCodeSlowDown/CodeMessageReduce your request rate./Message ...有些版本返回的是RequestTimeTooSkewed或内部错误码AWS SDK 可能会把这些解析为SlowDown错误。建议业务侧在客户端 SDK 中捕获SlowDown/Throttling异常并做指数退避重试因为如果你只是把超限请求直接抛给业务层很多同步调用方会把它当成 fatal error导致业务链路断了重来影响反而更差。5. 配合 Lua 脚本扩展更贴近业务的定制化限速策略除了上面这种“面板式”的标准限速Ceph RGW 从 Quincy 起内置了 Lua 脚本引擎Reef 开始逐渐普及。这个功能让限速策略的定制能力获得巨大提升——比如你需要限制某个 IP 段下的请求或者只限制某类特定对象操作如只禁止DeleteObject超过某个速率用标准的ratelimit set是配不出来的因为原生命令没有这么细的“条件匹配”维度。5.1 一个简单但实用的例子对特定前缀对象的写操作限速我举个例子假设log-bucket-01桶下有一段对象前缀是logs/2025/这个前缀对应的写入方是一次性补数任务要限制该前缀的写速率到 2MB/s避免跟正常业务日志写入争抢带宽。可以通过以下 Lua 脚本挂在 bucket 上function rgw_process(req) local s req:get_bucket_name() if s log-bucket-01 then local k req:get_object_name() if k ~ nil and string.match(k, ^logs/2025/) then req:set_max_ops(1) req:set_max_size(2097152) -- 2MB/s end end return 0 end这个脚本的执行原理是RGW 在每收到一个请求时Lua 脚本会拿到请求上下文调用req:set_max_ops和req:set_max_size动态调整本次请求的限速阈值。注意这里的set_max_size是针对当前请求的不是修改持久化配置。不过在实际使用中我发现 Lua 限速更适合做“按规则让部分请求动态降速或加速”它是请求级别的即时决策跟radosgw-admin配置的持久化限速策略不是一个意思。用 Lua 脚本处理复杂的自定义策略非常灵活但调试起来也比较痛苦——建议先在测试环境充分验证注意查看 RGW 日志中的 lua 错误输出。需要明确区分Lua 脚本挂载方式和 ratelimit set 方式底层走的是不同的实现。对于最基本的按 user/bucket 限速需求优先用radosgw-admin ratelimit持久化配置方式简单、可控、有官方支持只有当你需要“按前缀、按 IP、按时间窗、按对象大小范围”等复杂规则时才需要 Lua 方案。Lua 配置的持久化直接存放在 RADOS 中可以通过radosgw-admin lua script put来设置。例如radosgw-admin lua script put --bucketlog-bucket-01 --script/path/to/script.lua删除时radosgw-admin lua script rm --bucketlog-bucket-01我不建议在没做预检的情况下挂 Lua因为问题排查起来容易比官方限速方案麻烦得多。如果想要更简单的方式限制特定前缀也可以考虑在客户端和 RGW 之间加一层 Nginx/HAProxy按 URL 路径直接限速方案成熟度高适用范围也更广。关于 LVS/Nginx 这类外部限速方案后面一节给出对比。6. 边缘情况与实战经验为什么内存配额、SSL 终止也会搅合进来限速本身也不仅仅是 RGW 内部配置。实际接入生产环境之后我发现限速策略的最终表现还受很多外部因素影响。这一节聊聊我遇到过的几个印象深刻的“干扰项”。6.1 客户端重试机制会放大“突发量”S3 客户端 SDK 大多带有重试机制默认最多重试 3 次。当 RGW 限速返回SlowDown后AWS SDK 的指数退避逻辑会等待一段时间后重试这本来是好事。但有些业务方自己封装了 for 循环每次都直接“原地重试”而不用指数退避于是在超限瞬间RGW 会同时收到来自同一个客户端的大量重试请求导致 RGW 的线程池被打满。这些重试请求会被 RGW 直接限速拒绝不过前端的 HTTP 解析、认证还是会消耗 CPU。越限速反而越占资源这是一个很容易被忽略的负载模型问题。想避免这个情况除了让业务侧做良好退避之外RGW 全局参数里的rgw_thread_pool_size可能需要根据实际并发规模做调整。默认值在部分版本上是 512在更老一些版本上不设置由系统大量线程接管在高并发下可能不是最优值。但调大线程池数值不等于性能一定更好这里牵涉到每个线程的栈内存消耗和上下文切换开销没有一套放之四海而皆准的参数建议。6.2 客户端 IP 白名单/ACL 会绕开部分限速策略吗不会。RGW 限速是绑定在 user/bucket 上的不是绑定在 IP 上的。无论客户端从哪个 IP 来只要是同一个AccessKey/SecretKey都会走同一个用户级限速只要能访问同一个 bucket就会共用 bucket 级限速。也就是说客户端不能用换 IP 的方式来绕过限速这样反而让这个功能在安全审计和成本控制上有比较强的保障能力。反过来说如果你是想要“按客户端 IP 维度的限速”那原生 RGW 限速就做不到了。这时需要借助前端手段例如在 Nginx 中limit_req_zone $binary_remote_addr zoneper_ip:10m rate5r/s; server { location / { limit_req zoneper_ip burst10 nodelay; proxy_pass http://rgw_backend; } }这种方案的限制对象是“IP 请求”应用层上面的 object-level 限速无关但能有效抵御单 IP 的攻击性流量适合对公网开放的 RGW 上独立使用。6.3 “限制池容量”与“限制速率”的渊源Ceph 圈子搜索时“限制池容量”的热度也不低容易和“限速”混在一起。实际上这是两个不同层面的控制手段容量配额通过radosgw-admin quota set --quota-scopebucket --quota-max-objects1000000设置 bucket 内最多对象数量或最大存储容量。业务写入速率再快一旦总量到了阈值后续写入就直接失败或根据配额策略丢弃这属于“总量控制”。速率限制限制单位时间内的请求速率和流量属于“流速控制”。真实生产环境中两者往往需要配合使用。比如给备份桶设置容量配额防止把池写满同时给备份任务的用户绑定限速策略防止一次性大量备份影响正常业务。这两个功能都依赖 RGW 定期/实时统计的用量信息。配置配额时要特别留意RGW 的配额检查是按 bucket/user 维度的统计结果来判断的但由于统计是异步更新的当一个超大对象写入还未完成时容量配额不会立刻阻断它。也就是说配额适合“事后限制”限速适合“事前预防”。6.4 限速在 RGW 多站点Multi-site场景中的行为这个坑我在多站点同步配置时撞过。当一个 RGW zone 开启了到另一个 zone 的数据同步时RGW 的同步进程radosgw-admin bucket sync会以服务账号身份访问源 zone 的数据。如果你在源 zone 给这个同步服务账号设置了过于严格的限速策略可能导致跨区域同步一直无法完成出现“业务日志写了半天灾备机房根本没同步过去”的假象。定位方法很简单看 RGW 日志有没有大量SlowDown错误同时把同步账号通常是sync-user的限速策略暂时去掉观察同步是否恢复。多站点同步对带宽的消耗是持续性的但它又不是业务方主动发起的所以限速策略需要对同步账号做例外处理或专门设置一个宽容的限速值。7. 实操验证如何科学地确认限速已经真正生效配置命令敲下去没有报错不等于限速就一定生效了。我在测试限速时有一套自用的验证流程这里分享出来基本能覆盖 90% 的告警场景。7.1 第一步确认配置已经写入后端radosgw-admin ratelimit get --uidtestuser1 radosgw-admin ratelimit get --bucketlog-bucket-01输出里应当可以看到配置的限速维度scope、类型read/write/global、限速值max_ops / max_bytes等字段。这一步是为了确认配置确实存到了 RADOS 里不是只存在网关进程内存中。7.2 第二步确认 RGW 网关进程加载了新配置修改配置后旧的连接不受影响是令牌桶机制的天然行为但不能出现某个 RGW 节点一直不加载新配置的情况。特别是对于多网关集群每个 RGW 实例对配置变更的感知时间是不同的。在生产环境多网关场景下一般等 30-60 秒再观察。通过以下方式确认ceph daemon /path/to/ceph-client.rgw.$(hostname).asok config get rgw_qos_enabled如果返回rgw_qos_enabled: true则说明 QoS 功能已启用不同版本参数输出存在差异。如果这个值始终为false你需要回头检查编译参数和配置段。7.3 第三步使用高并发客户端跑真实压力测试这里我不推荐直接用 curl 一条一条打效率太低。我习惯用 Python 脚本基于 boto3 threading来模拟。写一个便于直接套用的脚本框架import boto3 import threading import time from botocore.config import Config def put_object_worker(worker_id, total_requests, bucket_name): s3 boto3.client( s3, endpoint_urlhttp://rgw-endpoint:7480, aws_access_key_idyour-key, aws_secret_access_keyyour-secret, configConfig(signature_versions3v4), region_nameus-east-1 ) for i in range(total_requests): try: s3.put_object( Bucketbucket_name, Keyfratelimit-test/{worker_id}-{i}, Bodyb0 * 1024 # 1KB object ) except Exception as e: # Catch SlowDown etc print(fWorker {worker_id} request {i}: {e}) time.sleep(0.001) threads [] for w in range(20): t threading.Thread(targetput_object_worker, args(w, 100, log-bucket-01)) threads.append(t) t.start() for t in threads: t.join() print(Test complete)通过单位时间成功 create 的对象数除以耗时即可测算出实际吞吐是否被限制在设定值附近。一个关键的验证逻辑如果分配的限速是max-write-ops100在 20 个线程并发写入时观察到的速率应当非常接近 100 req/s多余请求会得到异常抛出。如果测出的速率远大于 100 req/s说明限速可能并没有按你的预期生效建议检查是否触发了“密钥不存在 / qos 未启用 / 命令没匹配到正确 path”等隐蔽问题。7.4 第四步分析监控曲线避免以偏概全限速验证最忌讳的是用“业务原有产生的曲线”来判断。因为业务本身可能就有波峰波谷。建议专门起一个“人为可控压力的压测桶”在业务低峰期压测将 RGW 网关的 CPU、内存指标也纳入观察范围防止由于限速检查造成的资源开销被忽略。8. 调优思路与常见坑位汇总性能开销、突发令牌和超时配置写到这里RGW 限速的核心内容基本讲透了。最后再把我在调优与运维过程中收获的若干细节经验归纳成几条简洁直接的建议供你做配置决策时参考。8.1 突发量与令牌桶关系配置时预留合理冗余RGW 的 QoS 实现参考了令牌桶算法的变体支持max_ops和max_bytes但没有像传统 QoS 那样单独暴露一个burst值。令牌桶的容量实际上会比平均速率值稍大一些而具体容量一般由rgw_qos_max_burst这类参数控制Reef 早期版本参数不统一。我建议配置值不要设置为精确期望值的 100%而是稍微调低一点。举一个实际案例业务方说他们的备份任务“平时只有 50MB/s”要求限速为 50MB/s避免影响其他业务。如果我真的配max-bytes52428800正好 50MB由于 RGW 计算吞吐时有协议开销实际业务层看到的最大速率可能不足 45MB/s此时业务方会来投诉“你把我限得太多了”。因此合理做法为在业务诉求的基础上浮 10%-15%例如--max-bytes5767168055MB/s这样实际速率大约会稳定在 48-51MB/s不至于让业务明显受损同时又挡住了突发的大量流量。8.2 各运维场景适合的限速值班参考下面这个表是我在多种场景中归纳出的建议初始配置各位可按业务实际情况调整。注意这绝对不是放之四海而皆准的只是作为业务对接时快速响应的“初始模板”场景限制维度推荐初始限速值备注小文件高频写日志1MB 以下User write ops100-500 ops/smax-ops 主导大对象备份1GB 以上User write bytes30-100 MB/smax-bytes 主导测试桶对公网提供临时下载Bucket read bytes20-50 MB/s防止费用/带宽失控内部监控系统周期读User read ops50-100 ops/s避免周期性任务风暴跨区域多站点同步账号User global不限制或放很大的量优先保证同步稳定8.3 高并发小对象下限速导致的线程饥饿现象这里有一个比较隐蔽的性能问题当一个高并发客户端持续触发限速拒绝时RGW 的 Beast 前端会在处理请求的过程中就给它返回错误这本身会占用工作线程。而 RGW 的工作线程又被其他正常业务请求占用时可能让整个网关出现临时的积压延迟。如果你的集群常见的流量模式是大量小对象 高并发 限速策略较为严格建议将 RGW 工作线程数调大一些。我这里的实际经验是在 512 线程默认配置下一个接近限速阈值的压测能很快让 RGW 的 active 线程数超过 300。观察监控指标后如果需要可以把rgw_thread_pool_size适当调到 1024但需要同步关注内存增长每个线程栈有占用一般观测值可控。8.4 配置限速后RGW 进程 crash 的排查方向如果遇到限速配置后 RGW 进程变得不稳定优先查看 RGW 日志中是否有与cls_qos相关的异常。这往往意味着底层存储池的omap性能出现问题或 QoS 对象位置刚好处于某个性能较差的 OSD 上。可以尝试使用radosgw-admin qos pool set将 QoS 对象独立调度到高速池如果版本支持把限速状态对象与普通数据分流降低相互影响。8.5 限速功能与 Ceph 新版本的兼容性如果你用的是 LTS 版本Ceph 的演进相对保守但功能修复速度也慢。在 Squid 中RGW 限速的功能基本定型而且 RGW 对 S3 协议兼容性越来越完整。不过在从 Pacific 或 Quincy 升级到 Reef/Squid 时RGW 的 QoS 对象数据格式可能不兼容需要先删除或转换旧配置再重新下发。升级前务必查阅对应版本的升级说明文档。写在最后限速的本质是给“失控”留出缓冲带我做了这些年存储运维最终一个体会是技术平台的能力边界往往不是靠“无限提升性能上限”划定的而是靠“在关键时刻能把不可控的流量隔离住”来保障的。RGW 速率限制就是这样一个在幕后默默兜底的功能。它不像 EC、纠删码那样能直接降低存储成本也不像多级缓存那样能显著提升性能但在多业务共用一个对象存储集群的真实生产环境里它对于保障核心业务服务质量的价值非常明显。文章开头提到的“日志任务占满带宽导致核心业务受损”的场景配置好用户级限速之后这个隐患就基本消除了。如果你也正在被类似的“业务间互相干扰”问题困扰不妨从 RGW 限速入手先给那些流量开销失控的业务配一个保守的速率上限把系统稳住再慢慢优化限速参数和业务侧的退避策略。希望这篇整理能帮你省下一些摸索时间少踩几个版本层面的坑。
返回列表