Ceph:迈向 1 TiB/s 的旅程

Mark Nelson (nhm)

我简直不敢相信他们先发现了。 那是我在12月中旬的想法,在花了几个星期的时间,每天工作12个小时,调试为什么这个集群速度慢。 这可能是自Inktank以来我做的最密集的性能分析。 90年代关于安抚SCSI之神的迷信想法在我脑海中闪过。 90年代?天哪,我老了。 我们完成了大约三分之二的工作,这将让我们从头开始。 既然说到这里,我就从头开始吧。

回到2023年(我差点说今年早些时候,直到我意识到我们现在是2024年),Clyso受到了一个相当前卫的公司,他们希望将他们的HDD支持的Ceph集群迁移到10PB的NVMe部署。 他们立刻引起了我的兴趣。 他们对RBD、RGW或CephFS没有特殊需求。 他们自己设计了硬件,但令我高兴的是,他们在实际购买之前向我们寻求反馈。 他们有一些略微不寻常的要求。 集群必须分布在17个机架上,每个机架有4U的空间。 电源、冷却、密度和供应商偏好都是因素。 新节点需要迁移到现有集群中,且不中断服务。 然而,网络已经构建完成,而且它是一个怪物。 这是我见过的最快的以太网设置之一。 我从一开始就知道我想帮助他们构建这个集群。 我也知道我们需要进行预生产的磨合测试,这将是展示Ceph在这种系统上能做什么的绝佳机会。 以下是关于我们如何构建和测试该集群以及我们能够将其推到何种程度的故事。

致谢

首先,我要感谢我们的优秀客户,他们使这一切成为可能。 与您合作非常愉快! 感谢您也允许我们Clyso与Ceph社区分享这次经历。 通过分享知识,我们才能让世界变得更美好。 感谢IBM/Red HatSamsung向Ceph社区提供用于比较测试的硬件。 能够将我们获得的数据与实验室中之前的测试结果进行评估非常有价值。 感谢所有为Ceph的伟大而不知疲倦地工作过的Ceph贡献者! 最后,感谢Anthony D'Atri和Lee-Ann Pullar提供的出色的文案编辑技能!

集群设置

当客户最初联系Clyso时,他们提出了一种配置,使用34个双插槽2U节点分布在17个机架上。 我们从多个供应商处提供了一些替代配置,重点是较小的节点。 最终,他们决定采用我们设计的Dell架构,该架构的报价比原始配置便宜约13%,尽管具有几个关键优势。 新配置每个OSD的内存较少(但仍然舒适地为12GiB),但内存吞吐量更快。 它还提供了更多的聚合CPU资源、显著更多的聚合网络吞吐量、更简单的单插槽配置,并使用了最新一代的AMD处理器和DDR5 RAM。 通过采用较小的节点,我们将节点故障对集群恢复的影响减半。

客户表示他们希望将每个机架增加的功耗限制在约1000-1500瓦。 每个机架有4个这些节点,总TDP估计至少为1120瓦,加上基本功耗、CPU超额峰值和电源效率低下。 也就是说,我们可能在负载下稍微超出一点,但我们预计不会超出可接受的范围。 如果情况更糟,我们估计可以通过降低处理器的cTDP来减少每个机架约100瓦。

系统的规格如下

节点68 x Dell PowerEdge R6615
CPU1 x AMD EPYC 9454P 48C/96T
内存192GiB DDR5
网络2 x 100GbE Mellanox ConnectX-6
NVMe10 x Dell 15.36TB Enterprise NVMe Read Intensive AG
操作系统版本Ubuntu 20.04.6 (Focal)
Ceph 版本Quincy v17.2.7 (上游 Deb 包)

使用1U Dell服务器的另一个好处是,它们基本上是David Galloway和我为上游Ceph性能实验室设计的系统的更新版本。 这些系统在过去两年中,在各种文章中进行了测试。 事实证明,在测试期间出现了一个对上一代实验室硬件没有影响,但影响这种新硬件的重大性能问题。 我们稍后会详细讨论这个问题。

不 вдаваясь в подробности, я еще раз повторю, что сетевая конфигурация клиента очень хорошо спроектирована и довольно быстра. Она легко имеет достаточную общую пропускную способность во всех 17 стойках, чтобы позволить кластеру такого масштаба полностью раскрыть свой потенциал.

测试设置

为了进行磨合测试,部署了临时Ceph集群,并使用CBT启动了FIO测试。 CBT配置为使用一些修改后的设置部署Ceph。 OSD被分配了8GB的osd_memory_target。 在生产环境中,更高的osd_memory_target应该是可以接受的。 客户不需要测试块或S3工作负载,因此有人可能会认为RADOS bench是自然的基准选择。 但根据我的经验,大规模使用RADOS bench进行测试很棘手。 很难确定在给定线程数下需要多少个实例才能使集群饱和。 我过去遇到过需要多个并发池才能扩展性能的问题。 我也没有准备好任何现有的RADOS bench测试来比较。 相反,我们选择使用与上游实验室中使用的相同的librbd支持的FIO测试进行磨合测试。 这使我们能够将集群划分为更小的块,并将结果与先前发布的结果进行比较。 FIO也广为人知且值得信赖。

FIO中librbd引擎的一个主要优势(相对于使用内核RBD的FIO)是没有过时挂载点可能需要系统重新启动的问题。 我们没有此集群的IPMI访问权限,并且需要在短时间内完成测试。 因此,我们最终跳过了内核RBD测试。 但是,根据之前的测试,我们预计在有足够的客户端的情况下,聚合性能大致相同。 然而,我们能够测试3X复制和6+2纠删编码。 我们还测试了msgr V2,使用以下Ceph选项,分别处于未加密和安全模式下

ms_client_mode = secure
ms_cluster_mode = secure
ms_service_mode = secure
ms_mon_client_mode = secure
ms_mon_cluster_mode = secure
ms_mon_service_mode = secure

允许OSD使用节点上的所有核心。 FIO配置为首先使用大写填充RBD卷,然后进行4MB和4KB IO测试,持续300秒(调试期间为60秒)。 某些后台进程,例如擦除、深度擦除、PG自动缩放和PG平衡被禁用。

关于PG计数的一个说明

在本文的后面,您会看到一些令人眼花缭乱的PG计数正在被测试。 这是故意的。 我们从之前的上游实验室测试中知道,PG计数可以对性能产生戏剧性的影响。 其中一部分是由于低样本(PG)计数下随机分布中的聚集性。 这可以通过额外的平衡部分缓解。 较少讨论的是OSD内部的PG锁争用。 我们已经观察到,在非常快的集群上,PG锁争用可以在整体性能中发挥重要作用。 不幸的是,这不太容易缓解,除非增加PG计数。 PG计数真的重要吗?

仅使用60个OSD,随机读取性能一直扩展到使用3X复制的RBD池上的16384个PG。 写入的峰值要早得多,但仍然受益于高达2048个PG。

让我明确一下:您不应该盲目地配置生产Ceph集群以使用像我们在这里测试的那么高的PG计数。 这尤其适用于Ceph中PG日志长度和PG状态更新等某些其他默认设置。 然而,我希望鼓励社区开始思考100个PG/OSD的传统智慧是否仍然有意义。 我希望我们重新思考为了在保持开销和内存使用量可控的同时实现更高的PG/OSD计数,我们需要做些什么。 我梦想着一个未来,1000个PG/OSD不再是异想天开,PG日志是按池自动缩放的,PG自动缩放是一种更少使用的操作。

艰难的开端

我们第一次能够登录到新硬件是在美国感恩节后的那一周。 计划是花一两周的时间进行磨合验证测试,然后将新硬件集成到现有集群中。 如果一切顺利,我们希望在新年之前完成迁移。 可悲的是,我们遇到了麻烦。 最初的低级别性能测试看起来不错。 Iperf网络测试显示我们每节点达到略低于200Gb/s。 对几个节点的随机抽样显示NVMe驱动器具有合理的基线性能。 我们立即观察到的一个问题是,所有68个节点上的操作系统意外地部署在2个OSD驱动器上,而不是内部Dell BOSS m.2启动驱动器上。 我们计划将结果与30个OSD配置(3个节点,每个节点10个OSD)进行比较,与上游实验室的结果(5个节点,每个节点6个OSD)进行比较。 相反,我们最终测试了每个节点8个NVMe驱动器。 第一次Ceph结果远低于我们希望看到的结果,即使考虑到OSD计数减少了。

唯一接近可以接受的结果是随机读取,但仍然不是很好。 显然,有些事情发生了。 我们停止运行3节点测试,并开始查看单个节点,甚至单个OSD配置。

那时事情开始变得奇怪。

诡异的行为

当我们运行单个节点集群中8-OSD和1-OSD测试的各种组合时,我们看到了截然不同的行为,但花了几天时间才能真正理解我们所看到模式。 最初在单个OSD测试中表现良好的系统在多OSD测试后停止表现良好,但在几个小时后又开始表现良好。 8-OSD测试偶尔会显示表现良好的迹象,但对于所有后续测试,表现都很糟糕,直到系统重新启动。 我们最终能够辨别一种在新鲜启动时可以粗略地在不同节点上重复的模式

步骤OSD4MB Randread (MB/s)4MB Randwrite (MB/s)
启动
11 OSD57163998
28 OSDs31902494
31 OSD5233794
48 OSDs23192931
51 OSD5513796
20-30分钟暂停
61 OSD6373724
20-30分钟暂停
71 OSD6093860
20-30分钟暂停
81 OSD3623972
20-30分钟暂停
91 OSD65813998
20-30分钟暂停
101 OSD63503999
20-30分钟暂停
111 OSD65364001

最初的单个OSD测试对于大读写看起来很棒,并且显示了与直接针对驱动器运行FIO测试时看到的几乎相同的吞吐量。 然而,一旦我们运行了8-OSD测试,我们观察到性能下降。 后续的单个OSD测试继续表现不佳,直到几个小时后才恢复。 只要没有引入多OSD测试,性能仍然很高。

令人困惑的是,我们无法通过直接对驱动器运行FIO测试来引发相同的行为。 同样令人困惑的是,我们看到在8 OSD测试期间,单个OSD会使用比其他OSD显著更多的CPU

4MB 随机读取

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND 
511067 root      20   0 9360000   7.2g  33792 S  1180   3.8  15:24.32 ceph-osd                                              
515664 root      20   0 9357488   7.2g  34560 S 523.6   3.8  13:43.86 ceph-osd                                              
513323 root      20   0 9145820   6.4g  34560 S 460.0   3.4  13:01.12 ceph-osd                                              
514147 root      20   0 9026592   6.6g  33792 S 378.7   3.5   9:56.59 ceph-osd                                              
516488 root      20   0 9188244   6.8g  34560 S 378.4   3.6  10:29.23 ceph-osd                                              
518236 root      20   0 9390772   6.9g  33792 S 361.0   3.7   9:45.85 ceph-osd                                              
511779 root      20   0 8329696   6.1g  33024 S 331.1   3.3  10:07.18 ceph-osd                                              
516974 root      20   0 8984584   6.7g  34560 S 301.6   3.6   9:26.60 ceph-osd  

OSD在负载下的时钟配置文件显示大量时间花在io_submit中,这通常是我们看到内核开始阻塞,因为驱动器的队列已满时发生的情况。

示例 tp_osd_tp 线程 io_submit 时钟配置文件

+ 31.00% BlueStore::readv(boost::intrusive_ptr<ObjectStore::CollectionImpl>&, g...
 + 31.00% BlueStore::_do_readv(BlueStore::Collection*, boost::intrusive_ptr<Blu...
  + 24.00% KernelDevice::aio_submit(IOContext*)
  |+ 24.00% aio_queue_t::submit_batch(std::_List_iterator<aio_t>, std::_List_it...
  | + 24.00% io_submit
  |  + 24.00% syscall

为什么运行8 OSD测试会导致内核在未来的单个OSD测试期间在io_submit中开始阻塞? 这不太合理。 最初,我们怀疑节流。 我们看到,使用BIOS中的默认冷却配置文件,CPU上的几个核心复合物达到了高达96摄氏度的温度。 我们推测,也许我们在8-OSD测试期间达到了CPU或NVMe驱动器的热限,这导致系统在一段时间内处于降级状态,然后才恢复。 不幸的是,该理论没有成立。 AMD/Dell确认即使在这些温度下,我们也不应该达到节流,并且我们能够通过以100%的风扇转速运行系统并降低处理器的cTDP来证明该理论。 这些更改使它们在负载下始终保持在70摄氏度左右,但没有解决问题。

在超过一周的时间里,我们查看了从BIOS设置、NVMe多路径、低级NVMe调试到更改内核/Ubuntu版本以及检查我们能想到的每个内核、OS和Ceph设置。 这些都没有完全解决问题。

我们甚至在“良好”和“不良”的单个 OSD 测试期间执行了 blktrace 和 iowatcher 分析,并可以直接观察到缓慢的 IO 完成行为

Blkparse 输出 - 良好 vs 不良

时间戳(良好)偏移量+长度(良好)时间戳(不良)偏移量+长度(不良)
10.000020431067699792 + 256 [0]10.00138551206277696 + 512 [0]
10.000021091153233168 + 136 [0]10.001388011033429056 + 1896 [0]
10.00016955984818880 + 8 [0]10.002092831031056448 + 1536 [0]
10.000188271164427968 + 1936 [0]10.003273721220466752 + 2048 [0]
10.00030241084064456 + 1928 [0]10.003288691060912704 + 2048 [0]
10.000442381067699280 + 512 [0]10.012857461003849920 + 2048 [0]
10.000466591040160848 + 128 [0]10.01286171096765888 + 768 [0]
10.000533021153233312 + 1712 [0]10.012863171060914752 + 720 [0]
10.000564821153229312 + 2000 [0]10.012871471188736704 + 512 [0]
10.000587071067694160 + 64 [0]10.012872161220468800 + 1152 [0]
10.000806241067698000 + 336 [0]10.012878121188735936 + 128 [0]
10.001110461145660112 + 2048 [0]10.012878941188735168 + 256 [0]
10.001184551067698344 + 424 [0]10.01288071188737984 + 256 [0]
10.00121413984815728 + 208 [0]10.012882861217374144 + 1152 [0]

三个修复

到目前为止,我们已经开始让硬件厂商参与进来。最终结果表明这并非必要。有一次小的,和两次主要的修复让事情重回正轨。

修复一

第一个修复很简单,但只让我们获得了适度的 10-20% 的性能提升。多年前,有人发现(如果我记得没错,是 Nick Fisk 或 Stephen Blinick)Ceph 对 CPU c 状态转换引入的延迟非常敏感。对这些节点的 bios 的快速检查显示,它们没有以 最大性能 模式运行,该模式会禁用 c 状态。这是一个不错的胜利,但不足以将结果达到我们想要的位置。

修复二

在我深入研究上面显示的 blktrace 结果时,我大约 95% 确定我们看到的是 NVMe 驱动器的问题,或者与 PCIe 根复合物相关的问题,因为这些系统没有 PCIe 交换机。我忙于研究技术手册,并试图找到调试/分析硬件的方法。一位为客户工作的非常聪明的工程师主动提出帮助。我为他设置了一个测试环境,以便他可以在另一组节点上重复一些相同的测试,他一举成功。

虽然我主要关注的是时钟配置文件,现在正在深入研究尝试调试硬件,但他想了解内核侧是否发生了一些有趣的事情(回想起来,这是显而易见的下一步!)。他在一个不良运行期间运行了一个 perf 配置文件,并做出了一个非常敏锐的发现

    77.37%  tp_osd_tp        [kernel.kallsyms]             [k] native_queued_spin_lock_slowpath
            |
            ---native_queued_spin_lock_slowpath
               |          
                --77.36%--_raw_spin_lock_irqsave
                          |          
                          |--61.10%--alloc_iova
                          |          alloc_iova_fast
                          |          iommu_dma_alloc_iova.isra.0
                          |          iommu_dma_map_sg
                          |          __dma_map_sg_attrs
                          |          dma_map_sg_attrs
                          |          nvme_map_data
                          |          nvme_queue_rq
                          |          __blk_mq_try_issue_directly
                          |          blk_mq_request_issue_directly
                          |          blk_mq_try_issue_list_directly
                          |          blk_mq_sched_insert_requests
                          |          blk_mq_flush_plug_list
                          |          blk_flush_plug_list
                          |          |          
                          |          |--56.54%--blk_mq_submit_bio

内核在更新 IOMMU 映射时花费了大量时间争夺自旋锁。他禁用了内核中的 IOMMU,并立即在 8 节点测试中看到了巨大的性能提升。我们多次重复这些测试,并反复看到更好的 4MB 读取/写入性能。为客户加一分。然而,4KB 随机写入仍然存在问题。

修复三

在被客户在 IOMMU 问题上抢先一步之后,我几乎很高兴我们还有另一个问题需要解决。4K 随机写入性能在最初两个修复之后有所改善,但仍然明显低于上游实验室(即使考虑到减少的节点/驱动器数量)。我还注意到 RocksDB 中的压缩速度远低于预期。过去曾有两次重要的案例表现类似,并且似乎相关

  1. Ceph 在没有正确编译 TCMalloc 支持的情况下可能会非常慢。
  2. Ceph 在没有使用正确的 cmake 标志和编译器优化的情况下可能会非常慢。

从历史上看,这位客户使用上游 Ceph Ubuntu 包,并且我们仍然在这里使用它们(而不是自行编译或使用 cephadm 与容器)。我验证了 TCMalloc 是否已编译。这排除了第一个问题。接下来,我挖出了 17.2.7 Ubuntu 包的上游构建日志。就在那时我注意到,我们实际上并没有使用正确的编译标志来构建 RocksDB。尚不清楚这持续了多长时间,但我们从 2018 年 开始就存在一般的构建性能问题。

事实证明 Canonical 修复 了他们自己的构建,Gentoo 在看到我在 do_cmake.sh 中写的注释后也修复了。我们的上游 Deb 构建长期遭受这个问题,但至少似乎不会影响使用 cephadm 在 Debian/Ubuntu 上使用上游容器的任何人。在理解了问题后,我们构建了带有修复的自定义 17.2.7 包。压缩时间减少了大约 3 倍,4K 随机写入性能翻倍了(虽然在图表中很难看出来)

4KB 随机写入性能仍然低于我想要的值,但至少现在我们考虑到我们拥有更少的 OSD、只有 3/5 的节点数量,以及每个 OSD 的核心数量更少,我们大致处于正确的范围内。此时,我们临近寒假。客户希望将操作系统重新部署到正确的启动驱动器,并使用我们发现的所有修复和调整更新部署。计划是在假期休息后,然后在新年的第一周完成磨合测试。希望我们可以在下周开始迁移集群。

2024 年的第一周

1 月 2 日早上,我登录 Slack,迎接我的是我将描述为适度受控混乱的场景。另一个我们参与的集群正在发生重大故障。不深入细节,将该集群拉回稳定和相对健康的状态用了 3 天。直到星期五我才能回到性能测试。我能够为周一的测试争取到额外的一天,但这意味着我需要在数据迁移过程开始之前,展示集群在负载下表现良好的时间非常紧迫。

命运眷顾

我全天工作,重新部署 CBT 并重现之前运行的测试。这一次我能够使用每个节点中的所有 10 个驱动器。我还增加了客户端数量,以保持每个 OSD 大约 1 个 FIO 客户端,io_depth 为 128。第一个 3 节点测试看起来不错。对于每个节点 10 个 OSD,我们实现了与先前测试成比例(即更高)的性能。我知道我没有太多时间进行适当的扩展测试,所以我立即从 3 个节点扩展到 10 个节点。同时,我也扩展了 PG 计数,并使用 CBT 部署了一个新的集群。在 3 个节点上,我看到 4MB 随机读取为 63GiB/s。在 10 个节点上,我看到 213.5GiB/s。这几乎是线性扩展,为 98.4%。就在这时,我知道事情终于开始好转。该集群的 68 个节点中,只有 63 个当时处于启动状态。其余节点正在进行维护以修复各种问题。我将集群大致分成两半,一半有 32 个节点(320 个 OSD),另一半有 31 个客户端节点,每个节点运行 10 个 FIO 进程。我看着 CBT 在大约 7-8 分钟内构建集群。初始写入预填充看起来非常好。我的心飞扬了。我们以 635 GiB/s 的速度读取数据。我们突破了 1500 万 4k 随机读取 IOPS。虽然这可能与单个 NVMe 驱动器相比并不令人印象深刻,但这是我见过的约 300 个 OSD Ceph 集群的最高数字。

我还绘制了扩展测试的平均延迟和尾延迟。两者看起来都一致。这可能是因为我们同时扩展了 PG 计数和 FIO 客户端计数。然而,这些测试非常注重 IO。我们有如此多的客户端流量,以至于我们可能已经进入了性能不再增加而延迟随着 IO 的增加而继续增长的拐点。

我将这些结果展示给我的同事 Dan van der Ster,他之前在 CERN 构建了 Ceph 基础设施。他和我打赌(丹,最好来一杯好啤酒!)如果我能达到 1 TiB/s。我告诉他这自始至终都是我的计划。

部分运行的死星

我没有额外的客户端节点来以满容量测试集群,所以唯一的选择是在 OSD 节点上共同定位 FIO 进程。一方面,这提供了一个非常小的网络优势。客户端将能够 1/63 的时间与本地 OSD 通信。另一方面,我们从之前的测试中知道,将 FIO 客户端与 OSD 节点共置并非免费的。通常会有性能下降,而且我不清楚这种规模的集群会受到多少影响。

我构建了一个新的 CBT 配置,以针对我拥有的 63 个节点。使用 CBT 部署集群大约需要 15 分钟才能启动所有 630 个 OSD 并构建池。我屏住呼吸,看着结果出现。

大约 950GiB/s。非常非常接近。此时已经是周五晚上,所以我收拾东西并上床睡觉。周六早上我登录并尝试了一些调整选项:降低 OSD 分片和异步信使线程,同时应用 Reef RocksDB 调整。正如你所看到的,我们实际上略微损害了读取性能,同时提高了写入性能。事实上,随机写入性能提高了近 20%。经过进一步测试,看起来 reef 调整是良性的,尽管只在写入测试中略有帮助。更大的影响似乎来自分片/线程的变化。此时,我不得不休息,直到周日晚上才能再次开始研究集群。我试图入睡,但我知道我们只剩下 24 小时的时间来完成这项工作。午夜时分,我放弃了睡眠并回到了工作岗位。

我之前提到过,我们知道 PG 计数会影响性能。我决定保留之前的“调整”配置,但将 PG 数量翻倍。在第一组测试中,我降低了客户端与 OSD 的比率,因为我们将它们共同定位在 OSD 节点上。现在我尝试再次扩展它们。4MB 随机读取性能随着客户端数量的增加略有提高,而小随机读取 IOPS 却下降了。一旦我们达到每个节点 8 个 FIO 进程(总共 504 个),顺序写入性能直线下降。

为了了解发生了什么,我重新运行了写入测试并观察了“ceph -s”输出

  services:
    mon: 3 daemons, quorum a,b,c (age 42m)
    mgr: a(active, since 42m)
    osd: 630 osds: 630 up (since 24m), 630 in (since 25m)
         flags noscrub,nodeep-scrub
 
  data:
    pools:   2 pools, 131073 pgs
    objects: 4.13M objects, 16 TiB
    usage:   48 TiB used, 8.2 PiB / 8.2 PiB avail
    pgs:     129422 active+clean
             1651   active+clean+laggy
 
  io:
    client:   0 B/s rd, 1.7 GiB/s wr, 1 op/s rd, 446 op/s wr

一旦我向集群发送了 504 个 FIO 进程执行 4MB 写入,一些 PG 开始进入 active+clean+laggy 状态。性能下降,集群无法从该状态恢复,直到工作负载完成。更糟糕的是,随着时间的推移,更多的 PG 变得滞后,即使吞吐量只是集群能力的很小一部分。从那时起,我们发现了一些关于滞后 PG 的报告,在 邮件列表 上,以及一些可能修复它们的建议。尚不清楚这些想法是否会在这里有所帮助。我们 知道 IO 会暂时暂停,当 PG 进入滞后状态时,这是因为副本未及时从主副本确认新的租约。在与其他 Ceph 开发人员讨论了这个问题后,我们认为这可能是 OSD 中的锁定问题,或者在相同的异步 msgr 线程中竞争租约消息和工作。

尽管被滞后 PG 问题分散注意力,但我还是想重新专注于达到 1.0TiB/s。睡眠不足终于开始影响我了。在某个时候,我再次将 PG 计数翻倍到 256K,只是为了看看它是否对滞后 PG 问题有任何影响。这使我们稳固地朝着我们之前显示的曲线的上限前进,但坦率地说,我认为它实际上并没有太大影响。我决定切换回默认的 OSD 分片计数,并继续使用 504 个 FIO 客户端进程进行测试。然而,我扩展了异步信使线程的数量。有两个主要的收获。第一是,将异步信使线程减少到 1,我们可以避免 PG 变得滞后,并使用 504 个客户端实现“良好”的写入吞吐量。它也极大地损害了 4MB 读取的性能。第二:Ceph 的默认值实际上是 4MB 读取的理想选择。使用 8 个分片、每个分片 2 个线程和 3 个 msgr 线程,我们终于突破了 1TiB/s。这是我在周一早上大约 4 点左右看到的最终测试集运行时的视图

  services:
    mon: 3 daemons, quorum a,b,c (age 30m)
    mgr: a(active, since 30m)
    osd: 630 osds: 630 up (since 12m), 630 in (since 12m)
         flags noscrub,nodeep-scrub
 
  data:
    pools:   2 pools, 262145 pgs
    objects: 4.13M objects, 16 TiB
    usage:   48 TiB used, 8.2 PiB / 8.2 PiB avail
    pgs:     262145 active+clean
 
  io:
    client:   1.0 TiB/s rd, 6.1 KiB/s wr, 266.15k op/s rd, 6 op/s wr

以及 FIO 结果的图表

睡眠;擦除编码

终于看到我等待了数周的“1.0 TiB/s”屏幕后,我终于去睡觉了。尽管如此,我还是醒得很早。仍然有工作要做。到目前为止,我们所做的所有测试都是使用 3X 复制,但客户会将此硬件迁移到使用 6+2 擦除编码部署的现有集群中。我们需要了解使用他们将使用的配置,该集群能够达到什么水平。

我再次重新配置集群并运行新的测试。我从之前的测试中选择了看起来效果良好的 PG/分片/客户端值。性能良好,但我看到异步信使线程正在努力工作。我决定尝试增加它们超过默认值,看看它们是否可能有所帮助,考虑到增加的网络流量。

我们可以实现超过 500GiB/s 的读取和近 400GiB/s 的写入,使用 4-5 个异步 msgr 线程。但是,为什么 EC 的读取结果比复制慢得多?使用复制,PG 的主 OSD 只需要读取本地数据并将其发送到客户端。网络开销基本上是 1X。使用 6+2 擦除编码,主 OSD 必须从副本读取 6 个分块中的 5 个,然后才能将构建的对象发送到客户端。该请求的总体网络开销约为 (1 + 5/6)X。这就是为什么我们看到读取的 3X 复制性能略好于一半的原因。我们有相反的情况写入。使用 3X 复制,客户端将对象发送到主 OSD,然后主 OSD 将副本进一步发送到两个辅助 OSD。这导致总网络开销为 3X。在 EC 的情况下,我们只需要将 7/8 个分块发送到辅助 OSD(几乎与读取情况相同,但并非完全相同)。对于大型写入,性能实际上更快。

* 最初这篇文章指出需要提取 7/8 个分块进行读取。正确的值是 5/6 个分块,除非启用了快速读取。在这种情况下,将是 7/6 个分块。感谢 Joshua Baergen 指出这一点!

然而,IOPS 是另一个故事。对于非常小的读取和写入,Ceph 将联系 PG 中所有参与的 OSD,即使它们存储的数据与操作无关。例如,如果您正在执行 4K 读取,并且您感兴趣的数据完全存储在其中一个 OSD 上的单个分块中,Ceph 仍然会从参与条带的所有 OSD 中提取数据。在 2023 年夏天,Clyso 复活 了 Xiaofei Cui 的 PR,该 PR 实现了擦除编码的局部条带读取,以避免这项额外工作。效果是巨大的

目前还不清楚我们是否能够将此合并到 Squid 中,但 Ceph 项目的核心负责人 Radoslaw Zarzynski 已经提出帮助,争取完成这项工作。

挤出 Msgr 加密测试

最终,我们想向客户提供一个关于使用 msgr 级别加密会对他们的集群产生多大影响的粗略估计。前一晚的肾上腺素已经消退,我当时非常疲惫。我设法使用 3X 复制和 6+2 纠删编码测试,并启用了 msgr v2 加密,并将其与我们之前的测试结果进行了比较。

最大的影响是大型读取速度。速度从 ~1 TiB/s 降至约 750 GiB/s。其他所有方面都受到更适度但持续的影响。到目前为止,我不得不停止。我真的想做 PG 扩展测试,甚至内核 RBD 测试。但是,现在该将系统交还给客户进行重新镜像,然后交给 Clyso 的一位优秀的同事进行集成。

终曲

自测试结束以来,这个集群发生了什么变化?所有硬件都已重新镜像,新的 OSD 已部署到客户现有的 HDD 集群中。Dan 的 upmap-remapped 脚本正在用于控制迁移过程,我们已经将大约 80% 的现有数据迁移到由 NVMe 支持的 OSD。到下周,集群应该完全迁移到新的基于 NVMe 的节点。我们选择暂时不采用我们所做的所有调整,至少目前不采用。最初,我们将确保集群在现有的大部分默认配置下表现良好。现在我们拥有大量数据,如果客户遇到任何性能问题,我们可以使用这些数据进一步调整系统。

由于这里有大量数据和图表,我想总结一下主要亮点。以下是我们在此集群上能够实现的最佳数字的概述

30 个 OSD (3x)100 个 OSD (3x)320 个 OSD (3x)630 个 OSD (3x)630 个 OSD (EC62)
协同定位 Fio
4MB 读取63 GiB/s214 GiB/s635 GiB/s1025 GiB/s547 GiB/s
4MB 写入15 GiB/s46 GiB/s133 GiB/s270 GiB/s387 GiB/s
4KB 随机读取1.9M IOPS5.8M IOPS16.6M IOPS25.5M IOPS3.4M IOPS
4KB 随机写入248K IOPS745K IOPS2.4M IOPS4.9M IOPS936K IOPS

接下来是什么?我们需要找到解决写入期间 PG 滞后的方法。我们不能让 Ceph 在写入负载增加时崩溃。除此之外,我们通过这项练习了解到 Ceph 完全能够饱和 2x 100GbE 网卡。为了进一步提高吞吐量,在使用每个节点 10 个或更多 NVMe 驱动器时,我们需要 200GbE+。IOPS 更加微妙。我们知道 PG 数量会产生很大的影响。我们还知道一般的 OSD 线程模型正在发挥重要作用。我们始终在每个节点达到大约 400-600K 随机读取 IOPS 的限制,并且在多个部署中都看到了这种情况。这部分可能是异步 msgr 接口与内核的交互方式,也可能是 OSD 线程在将新工作放入分片队列时唤醒的方式。过去我修改过 OSD 代码,以在重负载下获得更好的结果,但代价是低负载延迟。最终,我认为提高 IOPS 需要采取多管齐下的方法,并重写一些 OSD 线程代码。

据我所知,这些是迄今为止发布的单集群 Ceph 性能最快的记录,也是 Ceph 集群首次达到 1 TiB/s。我认为 Ceph 还有很大的潜力。如果您有更快的集群,我鼓励您发布您的结果!感谢您的阅读,如果您有任何问题或想进一步讨论 Ceph 性能,请随时 联系