介绍
通常情况下,Ceph 的整体性能还是不错的,大量的场景优化为 Ceph 集群提供了可靠的性能保障。但是,很少有人知道 Ceph 当前并没有充分发挥出硬件的性能,也就说集群的性能与硬件的性能并不是呈线性增长的。
目前,我们正在进行多种方法来优化 Ceph 的数据路径,但现实情况是 Ceph 一直都是需要相当多的 CPU 才能充分发挥出比如像 NVMe 这样高速存储设备的性能。
之前,一位用户向我们提出了对低 CPU Core 下性能的担忧。我们给出的建议是,在使用 NVMe 磁盘时,可以为每个 OSD 分配 2 个 CPU Core 。我们并没有去解释原因。最终,用户购买了相应的硬件服务器,且服务器只插了一半的 NVMe 磁盘(即每个 OSD 有 4 个CPU Core 可用)。正常情况下,该配置的性能是可以接受的。但用户依然比较关心当服务器插满所有的 NVMe 磁盘时候,性能是否有影响。
遗憾的是,我们不得不告诉他们,添加更多磁盘后,他们很可能只会看到容量增加,而不是性能增加。但我们的建议并非完全没有价值。如果用户不关心小型随机 IO 的性能,则每个 OSD 2 个 CPU Core 可能是一个很好的推荐。除了提高小型随机 IO 性能之外,在 NVMe 磁盘运行 Ceph 还有很多好处。然而,对于该用户来说,小型随机 IO 是必须要关注的,而这恰恰是 CPU 资源最重要的情况。
所以不得不告诉他们,在增加磁盘后,他们很可能只会看到容量增加,而不是性能的增加。
遗憾的是,这不是第一次出现这个问题,并且该问题依然有很多困扰点。2 年前,我们更新了上游 Ceph 文档,试图在 PR (https://github.com/ceph/ceph/pull/32093 ) 中提供更好的案例。当时,我们的推荐场景要求如下:
- 1 core per 200-500 MB/s
- 1 core per 1000-3000 IOPS
不过这里最重要的方面是 IOPS 性能。本文将重点介绍 Ceph 小型随机 IOPS 性能如何随着 CPU 资源的增加而扩展。
集群配置
Nodes | 10 x Dell PowerEdge R6515 |
CPU | 1 x AMD EPYC 7742 64C/128T |
Memory | 128GiB DDR4 |
Network | 1 x 100GbE Mellanox ConnectX-6 |
NVMe | 6 x 4TB Samsung PM983 |
OS Version | CentOS Stream release 8 |
Ceph Version | Pacific V16.2.9 (built from source) |
所有节点通过 100GbE QSFP28 连接到同一台 Juniper QFX5200 交换机上。安装集群并使用 CBT (https://github.com/ceph/cbt/) 执行 fio 测试。除非单独说明,否则每个节点都配置为最多 6 个 OSD,并使用 4 个 fio 进程使用 librbd 进行测试。
英特尔系统上一个重要的操作系统级优化是将调整后的配置文件设置为 “latency-performance” 或 “network-latency”。这主要有助于避免与 CPU C/P 状态转换相关的延迟峰值。基于 AMD Rome 的系统在这方面似乎不那么敏感,但是为这些测试调整的配置文件仍然设置为 “network-latency”。
测试配置
基于 CBT 测试的 Ceph 集群有几个配置需要修改。首先,禁用 rbd 缓存,为每个 OSD 分配 8GB 内存,并在禁用 cephx 的情况下使用 msgr V1。在最近的测试中,我们发现使用默认 cephx 身份验证的 Msgr V2 似乎结果与使用 Msgr V1 一样,尽管启用加密可能会导致高达 30-40% 的性能损失,并且两台服务器和客户端上的 CPU 使用率相近甚至更高。
首先通过 Fio 预写入填充 RBD 卷,然后是循环 3 次 4K 随机读取,接下来是 iodepth=128 的 4K 随机写入,每次持续 5 分钟。CBT 允许 OSD 与其他工具或环境变量一起工作,numactl 用于控制 OSD 可以在系统上使用多少 CPU Core。初始测试使用单个 OSD 和 1 副本进行。多 OSD 测试是使用多个 OSD 和 3 副本进行的。
单个 OSD 测试
在多集群上,ceph 以伪随机算法实现数据分布存储。不同的 OSD 承载的热点数据是不同的。有些 OSD 可能比其他 OSD 承受更多的压力,因此总体性能可能也会受到影响。最终,我们可以看到 Ceph 集群的性能是受到集群中最慢 OSD 性能的限制(木桶原理——最短的木片决定木桶的容量)。
针对单个 OSD 的测试可以避免这种问题,同时也进一步消除了额外的复制延迟和开销,确保 OSD 以最高效率工作。测试单个 OSD 并不能代表整个集群的性能,但它确实展示了对应的 OSD 在最佳条件下的性能。
这里首先要注意的是,CPU 在 2 到 4 Core 之间,性能大约提高了 100%。这几乎是线性增加。但是在 4 个 CPU Core 之后,增加开始放缓。从 4 个 CPU Core 增加到 16 个 CPU Core 仅产生 100% 的性能增长,在 10 个 CPU Core 时增长几乎完全趋于平稳。不过,写入性能会更高,在 14-16 个 CPU Core 时最高可达 350% 左右。但是在测试中 Ceph OSD 是否真的使用了所有这些被分配的 CPU Core 吗?
事实证明,为 OSD 分配更多 CPU Core 可以持续提高性能,最多可达 14-16 Core,但在 CPU 高 Core 数时,OSD 不会使用所有 Core。对于读取尤其如此。更多的 CPU Core 意味着更高的性能,但效率会随着您的提升而降低。然而,使用的每个 CPU Core 的 IOPS 仍然相对平稳。
为什么会这样,限制是什么?默认情况下,Ceph OSD 每个 OSD 有 80 多个线程,但从资源消耗的角度来看,最重要的是:
- 16 个 OSD 工作线程(8 个分片,每个分片有 2 个线程)
- 3 个异步消息线程
- 1 个 bluestore key/value 线程
- 1 个 bluestore “finisher” 线程
- RocksDB flush(高优先级)和compaction(低优先级)后台线程
此处无需深入了解细节(我们将在稍后的文章中讨论),常用 OSD 的实际最大 CPU Core 使用率可能约为 23 个 Core。我们在实验中, 5 分钟内测试下来的最高使用率是 4K 随机写入大约 占用18-19 Core,对 OSD 没有限制并且禁用了 RocksDB 的预写日志。那么为什么我们在这些测试中看不到呢?可能的答案是 ceph 根本无法让所有 16 个工作线程一直处于忙碌状态。工作线的等待时间是很短的。虽然一个 OSD 平均可能使用 6 或 8 Core,但当它可以在短时间内爆发到 16 个以上的 Core 数时,它可能表现最佳,而其他时候它可能只需要 3-4 个 CPU Core。
60 个 OSD 集群测试
在部署完整集群时是否会出现在单个 OSD 测试中观察到的趋势?
在查看 60 OSD 集群测试结果时,有几个结果是很明显的。虽然曲线看起来类似于单个 OSD 测试,但性能最高时每个 OSD 大约 8-10 Core 用于读取,每个 OSD 大约 12 Core 用于写入。在单个 OSD 测试中,读取和写入增益分别达到约 200% 和 350%。在完整集群配置中,增益达到 100% 和 250%。
简单地看一下 OSD.0,看起来更大规模集群中的 OSD 在随机读取测试中使用的 Core 更少。同时,分配的每个 Core 的 IOPS 和使用的每个 Core 的 IOPS 数量也低得多。在写入端,现在使用 3 副本。为了能够与单个 OSD 测试进行比较,我们必须确认 OSD 的 IOPS 并考虑复制因素。即使这样做,每个 Core 的写入性能也比单个 OSD 测试低很多。
在读取方面,Ceph 为每 Core 提供大约 7500 IOPS,并且根据分配给 OSD 的 Core 数量,每个 Core 分配的 IOPS 从 2400 到 8500 不等。在写入端,Ceph 为每个使用的 Core 提供大约 3500 IOPS,每个分配的 Core 提供 1600 到 3900 IOPS 。这些结果比我们 2 年前结果要好一些,我们在最近的 Quincy 版本中进行了进一步的改进。
单 OSD 与多 OSD NVMe 性能对比
另一个经常出现的问题是 Ceph 如何很好地利用 NVMe 磁盘。通常的测试方式是直接从本地连接的驱磁盘写入或读取数据。
用户想知道为什么 Ceph 在有大量磁盘的情况下,速度依然慢。简单点说,Ceph 确实比直接写入磁盘是要慢的,原因有很多。主要的原因如下:
- 计算 crush placement、校验和、加密、纠删码、网络开销等带来的延迟。
- 处理数据(编码/解码/等)并在线程甚至 RocksDB 之间分配/复制/移动内存中的数据。
- Ceph 不只是写入数据,还会写出关于该数据的元数据。这在执行小型写入时是很重要的。
- 允许线程在没有任务时进入休眠状态,并在任务到来时唤醒它们。这样做是为了减少低负载期间的 CPU 开销,但是当线程进入休眠和唤醒太快时它会对性能产生重大影响。
如果不做任何调整与优化,其中一些问题是很难改进的。Ceph 很容易受到网络的性能影响(尽管 dpdk 之类的优化可以提供些帮助)。Crush 确定数据的分布时也会带来一些延迟,并且总会有一些由 crc32、编码/解码等引起的额外延迟。话虽如此,单 OSD 和多 OSD 之间存在非常大的性能差异—— OSD 测试。
上述的图表可能有些粗糙。尽管 60 个 OSD 的集群提供了大约 200 万次随机读取 IOPS,但单独的 OSD 能够以更高的效率提供近 4 倍于每个 NVMe 的性能。在写入方面,情况更接近一些,但单个 OSD 仍然比每个 NVMe 快大约 2 倍。在 Ceph Quincy 中,我们努力提高写路径性能。在 Ceph Quincy 版本的改进和选择性 RocksDB 调整之间,我们在完整的 60 个 OSD 集群上实现了超过 40% 的 4K 随机写入 IOPS 改进。
要了解我们是如何获得这些结果的,请在此处查看 RocksDB 调优深入研究文章 (https://ceph.io/en/news/blog/2022/rocksdb-tuning-deep-dive/)。
结论
最终,我们可以看到,在集群级别和 OSD 内部实现更高性能还是有很大空间的。
在以后的博文中,我们将深入探讨一些在低 CPU Core 数和高 CPU Core 数下限制性能的问题,以及一些如何进一步改进 Ceph 性能的想法。在此之前,每个 OSD 分配多少个 CPU Core 是需要权衡取舍的。为每个 OSD 分配 2-4 个CPU Core,Ceph 可以在小型读取和小型写入期间可充分使用所有 CPU Core。当分配更多的的CPU Core 数(甚至每个 OSD 最多 16+)时,是可以提高性能,但每添加一个 CPU Core 的增益就会降低。
正常情况下,OSD 能够正常稳定使用 CPU Core,但是当 OSD 分配了更高的CPU Core数量时,OSD 则无法充分使用每个 CPU Core。也就是说支出并不能带来同等的收益。因此需要综合考量 Ceph 硬件架构设计以及软件资源上的架构设计的支出与收益。
最后,这篇文章是基于 Ceph Pacific 版本的,通过调优后, Ceph 的性能有所提高。另外,我们需要注意,如果是使用Ceph Quincy 版本的话,结果可能有些差异。当前测试也是在一个新集群上进行的,如果是在一个运行时间比较长的旧的集群可能结果也是不一样的。不过,本文至少为 CPU 是如何影响基于 NVMe OSD 性能提供一个思路。
*原文链接:Ceph.io — Ceph OSD CPU Scaling - Part (https://ceph.io/en/news/blog/2022/ceph-osd-cpu-scaling/) 推荐阅读