注意
本文档适用于 Ceph 的开发版本。
Mantle
警告
Mantle 用于元数据均衡器算法的研究和开发,不适用于生产环境中的 CephFS 集群。
Mantle 是一个内置于 MDS 中的可编程元数据均衡器。
默认情况下(不使用 Mantle),多个活动 MDS 可以通过迁移目录来平衡元数据负载。何时、何地以及迁移多少的策略是硬编码在元数据平衡模块中的。
Mantle 通过保护用于平衡负载(迁移、复制、碎片化)的机制,同时使用 Lua 抑制平衡策略来工作。Mantle 基于 [1],但目前的实现**不**具备该论文中的以下功能:
平衡 API:在论文中,用户填写何时、何地、迁移多少以及负载计算策略。目前,Mantle 仅要求 Lua 策略返回一个目标负载表(例如,要发送给每个 MDS 的负载量)
“迁移多少”钩子:在论文中,有一个钩子允许用户控制“碎片选择器策略”。目前,Mantle 没有这个钩子。
“瞬时 CPU 利用率”作为指标。
[1] Supercomputing ‘15 论文:http://sc15.supercomputing.org/schedule/event_detail-evid=pap168.html
使用 vstart 快速入门
警告
使用 vstart 开发均衡器很困难,因为在一台节点上运行所有守护进程和客户端可能会使系统过载。让系统运行一段时间,尽管可能会出现许多心跳丢失警告和许多滞后 MDS 警告。在大多数情况下,本指南会起作用,但有时在使用 vstart 进行开发时,所有 MDS 都会锁定,您实际上无法看到它们溢出。最好在多节点集群上运行此操作。
作为先决条件,我们假设您已经安装了 mdtest 或拉取了 Docker 镜像。我们使用 mdtest 是因为我们需要生成足够的负载来超过均衡器中任意设置的 MIN_OFFLOAD 阈值。例如,这不会创建足够的元数据负载
while true; do
touch "/cephfs/blah-`date`"
done
使用 vstart.sh 的 Mantle
启动 Ceph 并调整日志记录,以便我们可以看到迁移发生
cd build ../src/vstart.sh -n -l for i in a b c; do bin/ceph --admin-daemon out/mds.$i.asok config set debug_ms 0 bin/ceph --admin-daemon out/mds.$i.asok config set debug_mds 2 bin/ceph --admin-daemon out/mds.$i.asok config set mds_beacon_grace 1500 done
将均衡器放入 RADOS
bin/rados put --pool=cephfs_metadata_a greedyspill.lua ../src/mds/balancers/greedyspill.lua
激活 Mantle
bin/ceph fs set cephfs max_mds 5 bin/ceph fs set cephfs_a balancer greedyspill.lua
在另一个窗口中挂载 CephFS
bin/ceph-fuse /cephfs -o allow_other & tail -f out/mds.a.log
请注意,如果您查看最后一个 MDS(可能是 a、b 或 c——它是随机的),您将看到尝试索引 nil 值。这是因为最后一个 MDS 试图检查其邻居的负载,而该邻居不存在。
运行一个简单的基准测试。在我们的例子中,我们使用 Docker mdtest 镜像来创建负载
for i in 0 1 2; do docker run -d \ --name=client$i \ -v /cephfs:/cephfs \ michaelsevilla/mdtest \ -F -C -n 100000 -d "/cephfs/client-test$i" done完成后,您可以使用以下命令终止所有客户端
for i in 0 1 2 3; do docker rm -f client$i; done
输出
查看第一个 MDS(可能是 a、b 或 c)的日志,我们看到所有人都处于无负载状态
2016-08-21 06:44:01.763930 7fd03aaf7700 0 lua.balancer MDS0: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=1.0 queue_len=0.0 cpu_load_avg=1.35 > load=0.0
2016-08-21 06:44:01.763966 7fd03aaf7700 0 lua.balancer MDS1: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=0.0 queue_len=0.0 cpu_load_avg=1.35 > load=0.0
2016-08-21 06:44:01.763982 7fd03aaf7700 0 lua.balancer MDS2: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=0.0 queue_len=0.0 cpu_load_avg=1.35 > load=0.0
2016-08-21 06:44:01.764010 7fd03aaf7700 2 lua.balancer when: not migrating! my_load=0.0 hisload=0.0
2016-08-21 06:44:01.764033 7fd03aaf7700 2 mds.0.bal mantle decided that new targets={}
作业开始后,MDS0 获得了大约 1953 个负载单位。贪婪溢出均衡器规定将一半负载发送给邻居 MDS,因此我们看到 Mantle 尝试向 MDS1 发送 1953 个负载单位。
2016-08-21 06:45:21.869994 7fd03aaf7700 0 lua.balancer MDS0: < auth.meta_load=5834.188908912 all.meta_load=1953.3492228857 req_rate=12591.0 queue_len=1075.0 cpu_load_avg=3.05 > load=1953.3492228857
2016-08-21 06:45:21.870017 7fd03aaf7700 0 lua.balancer MDS1: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=0.0 queue_len=0.0 cpu_load_avg=3.05 > load=0.0
2016-08-21 06:45:21.870027 7fd03aaf7700 0 lua.balancer MDS2: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=0.0 queue_len=0.0 cpu_load_avg=3.05 > load=0.0
2016-08-21 06:45:21.870034 7fd03aaf7700 2 lua.balancer when: migrating! my_load=1953.3492228857 hisload=0.0
2016-08-21 06:45:21.870050 7fd03aaf7700 2 mds.0.bal mantle decided that new targets={0=0,1=976.675,2=0}
2016-08-21 06:45:21.870094 7fd03aaf7700 0 mds.0.bal - exporting [0,0.52287 1.04574] 1030.88 to mds.1 [dir 100000006ab /client-test2/ [2,head] auth pv=33 v=32 cv=32/0 ap=2+3+4 state=1610612802|complete f(v0 m2016-08-21 06:44:20.366935 1=0+1) n(v2 rc2016-08-21 06:44:30.946816 3790=3788+2) hs=1+0,ss=0+0 dirty=1 | child=1 dirty=1 authpin=1 0x55d2762fd690]
2016-08-21 06:45:21.870151 7fd03aaf7700 0 mds.0.migrator nicely exporting to mds.1 [dir 100000006ab /client-test2/ [2,head] auth pv=33 v=32 cv=32/0 ap=2+3+4 state=1610612802|complete f(v0 m2016-08-21 06:44:20.366935 1=0+1) n(v2 rc2016-08-21 06:44:30.946816 3790=3788+2) hs=1+0,ss=0+0 dirty=1 | child=1 dirty=1 authpin=1 0x55d2762fd690]
最终负载会移动
2016-08-21 06:47:10.210253 7fd03aaf7700 0 lua.balancer MDS0: < auth.meta_load=415.77414300449 all.meta_load=415.79000078186 req_rate=82813.0 queue_len=0.0 cpu_load_avg=11.97 > load=415.79000078186
2016-08-21 06:47:10.210277 7fd03aaf7700 0 lua.balancer MDS1: < auth.meta_load=228.72023977691 all.meta_load=186.5606496623 req_rate=28580.0 queue_len=0.0 cpu_load_avg=11.97 > load=186.5606496623
2016-08-21 06:47:10.210290 7fd03aaf7700 0 lua.balancer MDS2: < auth.meta_load=0.0 all.meta_load=0.0 req_rate=1.0 queue_len=0.0 cpu_load_avg=11.97 > load=0.0
2016-08-21 06:47:10.210298 7fd03aaf7700 2 lua.balancer when: not migrating! my_load=415.79000078186 hisload=186.5606496623
2016-08-21 06:47:10.210311 7fd03aaf7700 2 mds.0.bal mantle decided that new targets={}
实现细节
大部分实现都在 MDBalancer 中。指标通过 Lua 堆栈传递给均衡器策略,负载列表返回给 MDBalancer。它与当前的均衡器实现并存,并通过 Ceph CLI 命令启用(ceph fs set cephfs balancer mybalancer.lua)。如果 Lua 策略失败(无论出于何种原因),我们将回退到原始元数据负载均衡器。均衡器存储在 RADOS 元数据池中,MDSMap 中的字符串告诉 MDS 使用哪个均衡器。
将指标暴露给 Lua
指标直接作为全局变量暴露给 Lua 代码,而不是使用定义明确的函数签名。有一个全局“mds”表,其中每个索引都是一个 MDS 编号(例如 0),每个值都是一个包含指标和值的字典。Lua 代码可以使用类似这样的方式获取指标
mds[0]["queue_len"]
这与 OSD 中的 cls-lua 不同,后者具有定义明确的参数(例如,输入/输出缓冲区列表)。直接暴露指标使得添加新指标更容易,而无需更改 Lua 端的 API;我们希望随着我们探索哪些指标重要而使 API 增长和收缩。这种方法的缺点是编写 Lua 均衡器策略的人必须查看 Ceph 源代码才能知道哪些指标被暴露。我们认为 Mantle 开发人员无论如何都会熟悉 MDS 内部结构。
暴露给 Lua 策略的指标与 mds_load_t 中已存储的指标相同:auth.meta_load()、all.meta_load()、req_rate、queue_length、cpu_load_avg。
编译/执行均衡器
在这里我们使用 lua_pcall 而不是 lua_call,因为我们想在 MDBalancer 中处理错误。我们不希望错误沿着调用链传播。cls_lua 类希望自己处理错误,因为它必须优雅地失败。对于 Mantle,我们不关心 Lua 错误是否使我们的均衡器崩溃——在这种情况下,我们将回退到原始均衡器。
使用 lua_call 而不是 lua_pcall 的性能提升在这里不会被利用,因为默认情况下均衡器每 10 秒调用一次。
将策略决策返回给 C++
我们强制 Lua 策略引擎返回一个值表,对应于要发送给每个 MDS 的负载量。这些负载直接插入到 MDBalancer 的“my_targets”向量中。我们不允许 MDS 返回一个包含 MDS 和指标的表,因为我们希望决策完全在 Lua 端做出。
迭代 Lua 返回的表是通过堆栈完成的。用 Lua 行话来说:将一个虚拟值推送到堆栈上,下一个迭代器用一个 (k, v) 对替换堆栈顶部。读取每个值后,弹出该值,但保留键用于下次调用 lua_next。
从 RADOS 读取
当 MDS Map 中的均衡器版本更改时,所有 MDS 都将从 RADOS 读取平衡代码。均衡器同步地从 RADOS 拉取 Lua 代码。我们通过设置超时来实现:如果异步读取未在平衡滴答间隔的一半内返回,则操作被取消并返回连接超时错误。默认情况下,平衡滴答间隔为 10 秒,因此 Mantle 将使用 5 秒超时。这种设计允许 Mantle 在任何与 RADOS 相关的问题发生时立即返回错误。
我们使用这种实现是因为我们不想在全局 MDS 锁内部执行阻塞 OSD 读取。这样做会导致 MDS 集群崩溃,如果任何 OSD 没有响应——这在 ceph-qa-suite 中通过将所有 OSD 设置为 down/out 并确保 MDS 集群保持活动状态来测试。
一种方法是在处理 MDS Map 时异步触发读取,并在后台填充 Lua 代码。我们不能这样做,因为 MDS 不支持守护进程本地回退,并且均衡器假设所有 MDS 同时做出相同的决策(例如,导入器、导出器等)。
调试
Lua 策略中的日志将出现在 MDS 日志中。语法与 cls 日志接口相同
BAL_LOG(0, "this is a log message")
它是通过将一个包装 dout 日志框架的函数(dout_wrapper)与 lua_register() 原语一起传递给 Lua 来实现的。Lua 代码实际上是在调用 C++ 中的 dout 函数。
警告和信息消息使用 clog/Beacon 集中化。成功的消息仅在版本更改时由第一个 MDS 发送,以避免垃圾邮件发送到 ceph -w 实用程序。这些消息用于集成测试。
测试
测试是使用 ceph-qa-suite(tasks.cephfs.test_mantle)完成的。我们不测试无效的均衡器日志记录和加载实际的 Lua VM。