使用 CBT 进行性能基准测试:定义 YAML 内容。第二部分
CBT 性能基准测试 - 第二部分。YAML 文件是什么,我们如何在 CBT 中使用它们?
博客系列大纲 ¶
目录
简介:YAML 文件中包含什么? ¶
完成 第一部分 后,您应该已经设置了一个擦除编码的 Ceph 集群,现在几乎可以开始在它上面运行 CBT 测试了!但是,在我们这样做之前,我们需要了解我们想要的 YAML 内容。
YAML 文件定义了我们将要在集群上运行哪些测试。
我们可以简要地将 YAML 文件描述为具有 3 个主要部分
cluster部分:YAML 描述 CBT 如何与集群通信的地方。例如,用户 ID、客户端、OSD、ceph 二进制路径等。monitoring_profiles部分:YAML 描述用于收集统计信息的监控工具的地方(在我们的例子中是 collectl)。benchmarks部分:基准测试技术(在我们的例子中是 librbdfio)的指定位置,以及工作负载放置的位置。
YAML 文件的关键部分: ¶
集群
在这里,您将描述您的 ceph 集群配置。
现在,需要 user、head、clients、osds、mons 等字段的原因是,CBT 使用并行分布式 shell (pdsh) 通过 SSH 登录到集群的各个实体,这些实体在集群部分中定义。这使得能够运行“ceph”命令,并能够在客户端端点(在“clients”字段中定义)上启动基准测试工具(例如 FIO)。
Ceph 的典型用例是,有一个单独连接的主机服务器专门用于读写存储数据。因此,可以在与集群完全分离的服务器上运行 CBT,并且可以在连接的服务器上收集性能数据。因此,连接的服务器正在编排在 Ceph 集群上启动和停止基准测试工具。
重要提示: CBT 的要求是从运行 CBT 的服务器到在 head、clients 和 osds 字段中定义的 Ceph 节点启用无密码 SSH。
示例
cluster:
user: 'exampleUser' # the SSH user ID that is going to be used for accessing the ceph cluster
head: "exampleHostAddress" # node where general ceph commands are run
clients: ["exampleHostAddress"] # nodes that will run benchmarks or other client tools
osds: ["exampleHostAddress"] # nodes where OSDs will live
mons: # nodes where mons will live
exampleHostAddress:
a: "exampleIPAddress"
mgrs:
exampleHostAddress:
a: ~
osds_per_node: 8
conf_file: '/etc/ceph/ceph.conf'
clusterid: "ceph"
tmp_dir: "/tmp/cbt"
ceph-osd_cmd: "/usr/bin/ceph-osd"
ceph-mon_cmd: "/usr/bin/ceph-mon"
ceph-run_cmd: "/usr/bin/ceph-run"
rados_cmd: "/usr/bin/rados"
ceph_cmd: "/usr/bin/ceph"
rbd_cmd: "/usr/bin/rbd"
ceph-mgr_cmd: "/usr/bin/ceph-mgr"
监控配置文件
在我们的示例中,我们将使用 collectl 来收集统计信息。
更详细地说,基准 IO 激励器 (FIO) 启动。当 ramp 周期到期时,监控工具 (collectl) 开始启动以开始收集统计信息,以便在预热/ramp 期间不收集任何数据。一旦 IO 激励器的 time 周期到期,CBT 就会停止监控工具。
示例
monitoring_profiles:
collectl:
args: '-c 18 -sCD -i 10 -P -oz -F0 --rawtoo --sep ";" -f {collectl_dir}'
基准模块
在我们的示例中,我们将使用 librbdfio。
示例
benchmarks:
librbdfio:
rbdname: "test-image"
poolname: "rbd_replicated"
cmd_path: '/usr/local/bin/fio'
<insert details here>
现在,在 librbdfio 部分中,您需要指定一些详细信息,包括您在 第一部分 第 5 步中创建的 卷名称 和 池名称。CBT 将在您的 rbdname 上附加 'hostname -f' 后跟一个卷 ID '-X',如上所述,其中 X 是从 0 到 X 的卷,如您的 volumes_per_client 字段中指定(请参阅“卷数”部分)。
例如:rbdname="test-image" 将使用:--rbdname=test-image-mycephhost1.com-1,如果:hostname -f 返回:mycephhost1.com
重要的是让 rbdname 反映您的 卷名称,让 poolname 反映您用于创建卷的 池名称。因此,上面的 YAML 示例遵循我们在 第一部分 中所做的事情,这里
rbd create –pool rbd_replicated –data-pool rbd_erasure –size 10G test-image
此外,上面显示的 cmd_path 属性很重要,这必须是 FIO 位于驱动 IO 的客户端上的路径。
YAML 文件的其他重要部分: ¶
基准测试的长度
我们为每个测试配置一个 ramp 和一个 time
- Ramp → 预热期,在此期间不收集数据。
- Time → 每个测试运行并收集结果的持续时间。
ramp 时间确保 I/O 测试进入稳定状态,然后再开始 I/O 测量。通常,写入缓存会在测试开始时提供不切实际的高性能,因为缓存正在填充,而 读取缓存会在测试开始时提供略低的性能,因为它们正在填充。缓存可以实现于驱动器或软件中。
一个非常短的 duration 测试可以更快地获得性能测量结果,但可能无法反映您在实际使用中看到的性能。造成这种情况的原因包括定期执行工作的后台进程以及碎片化等问题,这些问题通常会随着测试运行时间的延长而恶化。如果多次运行性能测试给出不同的结果,则可能是测试持续时间太短。
- 重要的是要注意,在 librbdfio 中指定的持续时间和 ramp 将适用于 YAML 中其他地方指定的所有工作负载。
- 但是,可以通过在特定工作负载中指定时间和 ramp 来覆盖这些设置。您将在预置部分中看到一个示例,其中时间被覆盖为 600(10 分钟)。
示例
librbdfio:
time: 90 #in seconds
ramp: 30 #in seconds
卷大小
存储系统可能会根据其填充程度提供不同的性能,其中固定大小的缓存的缓存命中率在测试较小的存储量时会更高,处理碎片化和垃圾收集在可用容量较少时需要更多时间。理想情况下,配置性能测试以使用超过 50% 的物理存储,以获得代表实际使用的测量结果。我们在 第一部分 中讨论了如何计算 RBD 卷大小,因此重要的是您的计算与 YAML 文件中的 vol_size 属性相匹配。
- 理想情况下,这应该与在 第一部分 设置 EC 配置文件时创建的卷大小相匹配。
- 如果此值低于 RBD 映像大小,则只会写入指定数量的数据。
- 如果该值更大,则只会写入相当于 RBD 映像大小的数据量。
示例
librbdfio:
vol_size: 52500 #in megabytes
卷数
这是您在 第一部分 中定义的卷数。
示例
librbdfio:
volumes_per_client: [8]
预填充和预置
这些在 第一部分 中更详细地讨论,因此如果您需要回顾,请参阅该部分。
- 预填充 → 使用连续写入填充所有卷。
- 预置 → 添加随机写入以模拟真实世界的工作负载。
示例
librbdfio:
prefill:
blocksize: '64k'
numjobs: 1
workloads:
precondition:
jobname: 'precond1rw'
mode: 'randwrite'
time: 600
op_size: 65536
numjobs: [ 1 ]
total_iodepth: [ 16 ]
monitor: False
因此,上述内容正在发出总 iodepth 为 16 的 64K 随机写入(跨所有卷),因此在 8 卷配置下,每个卷将使用每个卷的队列深度为 2。
- 请注意:这里的 time 正在覆盖 librbdfio(全局)部分 YAML 中指定的时间。不指定时间将使用在外部(librbdfio)部分中指定的默认值。
工作负载
示例
librbdfio:
workloads:
Seq32kwrite:
jobname: 'seqwrite'
mode: 'write'
op_size: 32768
numjobs: [ 1 ]
total_iodepth: [ 2, 4, 8, 16, 32, 64, 128, 256, 512, 768 ]
以上是一个 32k 连续写入的示例,我们配置了不同的 total_iodepth 级别。因此,此测试的工作方式是从 total_iodepth 2 开始,ramp 为 30 秒,IO 为 90 秒并收集统计信息,然后对 total_iodepth 4 执行相同的操作,依此类推,用于增加的 total_iodepth 值。这些 total_iodepth 点中的每一个都是曲线图上表示的点。
YAML 文件中的工作负载示例: 
表达队列深度 ¶
首先,什么是 队列深度?
队列深度可以定义为未决并发命令的数量。
在 CBT 中表达每个卷的队列深度有两种方法
- 使用
iodepth属性 - 使用
total_iodepth属性
iodepth n 将对每个 卷 使用相同的队列深度 n。例如,如果配置的 卷 数为 8,则设置 iodepth 2 将生成 total_iodepth 为 16,每个 卷 具有 2 个 I/O 的队列。随着队列深度的增加,所有 卷 的排队 I/O 总量将增加 倍数。
total_iodepth n 将尝试将 n 个 I/O 请求分布到一组卷中。例如,如果 total_iodepth 为 16,并且配置的 卷 数为 8,则每个 卷 的队列深度将为 2 (16/8)。Total_iodepth 不需要可以被卷数整除,在这些情况下,CBT 有些卷的队列深度会比其他卷高 1。
iodepth 相对于 total_iodepth 的主要缺点: ¶
示例:如果您有大量的卷,例如 32 个。如果您指定
iodepth: [1, 2, 4, 8]
所有 32 个卷都将被使用,因此这等效于编写一个 YAML 文件,该文件执行
total_iodepth: [32, 64, 128, 256]
如您所见,您对队列深度的控制会根据 YAML 中配置的卷数进行扩展。
现在使用 total_iodepth,您可以更细粒度地控制,如下所示
total_iodepth: [1, 2, 4, 8, 16, 32]
CBT 仅会在 YAML 中的 total_iodepth 配置小于 total_iodepth 并且卷数不能整除 total_iodepth 时使用卷的子集。这意味着有些卷的队列深度将与其他卷不同,但 CBT 会尝试以尽可能均匀的方式启动 FIO,并具有每个卷的 iodepth。
如果您难以理解这些术语之间的关系,一个很好的方法是
total_iodepth = 卷数 x 队列深度
为什么 YAML 中有这么多不同的 IO 值? ¶
我们拥有 YAML 中针对写入和读取的大量不同 IO 级别,因为我们希望获得代表真实世界中发生的所有不同场景的测试结果。还要测试可能阻碍 Ceph 集群的各种瓶颈。
就瓶颈而言
- 短 IO 通常会有 CPU 瓶颈(这就是为什么 x 轴对于小 IO 为 IOPs)。
- 较大的 IO 更容易受到网络和设备存储瓶颈的影响(这就是为什么 x 轴对于较大的 IO 大小变为带宽)。
就真实世界场景而言
- 运行在块或文件存储上的数据库,或更普遍地说是 OLTP(在线事务处理)通常会发出小的 随机读取 和 写入 I/O。通常,读取 I/O 的百分比高于写入 I/O,这可能由 70% 读取、30% 覆盖 4K I/O 工作负载表示。
- 创建备份的应用程序可能会发出较大的 读取 和 写入 I/O,这些 I/O 可能是相当顺序的。如果备份正在写入其他存储,则 I/O 工作负载将是 100% 顺序读取,如果备份正在从其他地方读取并写入存储,则 I/O 工作负载将是 100% 顺序写入。
- 传统的 S3 对象存储包含大型对象,这些对象以 顺序方式读取 和 写入。S3 对象不会被覆盖,因此 I/O 工作负载将是大型顺序读取和写入的混合。虽然 S3 对象可能为 GB 大小,但 RGW 通常会将 S3 对象拆分为 4MB 的块。
- S3 对象存储也可以用于存储小对象,并且一些应用程序将索引和表存储在对象中,并对对象中的数据进行 短随机 访问。这些应用程序可能会生成 I/O 工作负载,其中读取内容类似于 OLTP 工作负载。
- 存储集群很可能被多个应用程序使用,每个应用程序都有其自身的 I/O 工作负载。因此,集群的 I/O 工作负载可能会变得非常复杂。仅使用一种类型的 I/O 来测量 I/O 工作负载的性能是表征性能的一种好方法。然后可以使用这些数据来预测具有不同比例的混合 I/O 类型的更复杂 I/O 工作负载的性能,方法是计算调和平均值。
这里是一个完整的 YAML 文件示例,包含上述组件
示例 YAML 文件
这里是一个 YAML 文件示例,当然你可以拥有更多的工作负载,我只是为了简单起见,只保留了几个。
cluster:
user: #specify user here
head: #specify head here
clients: #specify clients here
osds: #specify OSDs here
mons:
#specify mons here
mgrs:
#specify mgrs here
osds_per_node: 8
fs: 'xfs'
mkfs_opts: '-f -i size=2048'
mount_opts: '-o inode64,noatime,logbsize=256k'
conf_file: '/cbt/ceph.conf.4x1x1.fs'
iterations: 1
use_existing: True
clusterid: "ceph"
tmp_dir: "/tmp/cbt"
ceph-osd_cmd: "/usr/bin/ceph-osd"
ceph-mon_cmd: "/usr/bin/ceph-mon"
ceph-run_cmd: "/usr/bin/ceph-run"
rados_cmd: "/usr/bin/rados"
ceph_cmd: "/usr/bin/ceph"
rbd_cmd: "/usr/bin/rbd"
ceph-mgr_cmd: "/usr/bin/ceph-mgr"
pdsh_ssh_args: "-a -x -l%u %h"
monitoring_profiles:
collectl:
args: '-c 18 -sCD -i 10 -P -oz -F0 --rawtoo --sep ";" -f {collectl_dir}'
benchmarks:
librbdfio:
time: 90
ramp: 30
time_based: True
norandommap: True
vol_size: 52500
use_existing_volumes: True
procs_per_volume: [1]
volumes_per_client: [16]
osd_ra: [4096]
cmd_path: '/usr/local/bin/fio'
create_report: True
wait_pgautoscaler_timeout: 20
log_iops: True
log_bw: True
log_lat: True
fio_out_format: 'json'
log_avg_msec: 100
rbdname: "test-image"
poolname: "rbd_replicated"
prefill:
blocksize: '64k'
numjobs: 1
workloads:
precondition:
jobname: 'precond1rw'
mode: 'randwrite'
time: 600
op_size: 65536
numjobs: [ 1 ]
total_iodepth: [ 16 ]
monitor: False
seq32kwrite:
jobname: 'seqwrite'
mode: 'write'
op_size: 32768
numjobs: [ 1 ]
total_iodepth: [ 2, 4, 8, 16, 32, 64, 128, 256, 512, 768 ]
4krandomread:
jobname: 'randread'
mode: 'randread'
op_size: 4096
numjobs: [ 1 ]
total_iodepth: [ 4, 8, 12, 16, 32, 48, 64, 128, 256, 384, 588, 768 ]
总结 ¶
在第二部分中,你学习了 YAML 文件、工作负载以及它们如何在 CBT 性能基准测试中被整合。现在我们将进入 第三部分 的博客,它将讨论需要考虑的因素以及如何开始你的第一个 CBT 性能基准测试!