Files
wiki/存储/Ceph/知识库/07-RBD-块存储篇.md
T
2026-09-18 13:49:11 +08:00

224 KiB
Raw Blame History

07 · RBD 块存储篇

基于 Ceph 官方文档整理 | 文档版本:Tentacle v20.2.4 | 抓取日期:2026-09-17 | 源文:ceph/ceph@v20.2.4 doc/rbd/ 官网入口:https://docs.ceph.com/en/tentacle/rbd/


本册导读

本册覆盖 Ceph 官方文档 doc/rbd/ 目录下的全部 38 个 rst 页面(含 index.rst 与 man/index.rst),按"概念 → 基本操作 → 高级特性 → 配置参考 → 平台集成 → 协议网关"的顺序重排,便于从零到生产级使用逐章推进。

阅读顺序 章节 覆盖的官方页面 建议读者
0 本册导读 index.rst 全部
1 一、RBD 是什么 index.rst 全部
2 二、核心概念 index.rst、rbd-config-ref.rst 全部
3 三、基本操作 rados-rbd-cmds.rst 全部(必读)
4 四、内核模块与客户端 rbd-ko.rst、rbd-windows.rst 需要裸机映射的场景
5 五、快照与克隆 rbd-snapshot.rst 全部(必读)
6 六、exclusive lock 与 object map rbd-exclusive-locks.rst 全部
7 七、缓存机制 rbd-persistent-read-only-cache.rst、rbd-persistent-write-log-cache.rst、rbd-config-ref.rst 性能调优
8 八、加密 rbd-encryption.rst 合规/安全场景
9 九、实时迁移 rbd-live-migration.rst 存储迁移、异构导入
10 十、镜像与灾备 rbd-mirroring.rst(27KB,本册最大页面) 灾备/双活
11 十一、配置参考 rbd-config-ref.rst + src/common/options/*.yaml.in 全部(查阅)
12 十二、平台与集成 qemu-rbd.rst、libvirt.rst、rbd-kubernetes.rst、rbd-openstack.rst、rbd-cloudstack.rst、rbd-nomad.rst、rbd-windows.rst 云平台/虚拟化
13 十三、iSCSI iscsi-*.rst(11 个页面) 无原生 Ceph 客户端的场景
14 十四、NVMe-oF nvmeof-*.rst(6 个页面) 新部署的高性能场景
15 十五、其他 rbd-replay.rst、rbd-ko.rst 压测/内核映射
16 十六、命令速查表 汇总 全部

重要提示(官方原文):iSCSI gateway 自 2022 年 11 月起进入 maintenance(仅维护) 状态,不再新增特性。新部署建议优先考虑 NVMe-oF gateway 或原生 librbd/krbd。


一、RBD 是什么

1.1 定位

块(block)是一串字节序列(通常 512 字节)。基于块的存储接口是一种成熟且通用的数据存取方式,广泛用于 HDD、SSD、CD、软盘乃至磁带。块设备接口的普适性,使其非常适合与 Ceph 这样的海量数据存储系统对接。

Ceph 块设备(Ceph Block Device,简称 RBD)具备三个基础属性:

  1. thin-provisioned(精简配置)——创建时不占用物理空间,实际写入才分配;
  2. resizable(可调整大小)——支持在线扩容与缩容;
  3. striped over multiple OSDs(跨 OSD 条带化)——数据被切分并散布到集群内多个 OSD 上。

RBD 直接复用 RADOS(Reliable Autonomic Distributed Object Store)的能力,包括快照(snapshotting)、复制(replication)与强一致性(strong consistency)。

Ceph 块存储客户端通过两条路径访问集群:

            +------------------------+ +------------------------+
            |     Kernel Module      | |        librbd          |
            +------------------------+-+------------------------+
            |                   RADOS Protocol                  |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+
  • 内核模块(krbd):可以使用 Linux page cache。
  • librbd:用户态库,无法利用 Linux page cache,因此 Ceph 为其提供了独立的 RBD Caching 机制。

RBD 交付高性能与高可扩展性,服务对象包括内核模块、KVM(如 QEMU),以及依赖 libvirt 与 QEMU 集成 Ceph 块设备的云计算系统(OpenStack、OpenNebula、CloudStack)。同一个集群可以同时运行 RGW(对象网关)、CephFS(文件存储)和 Ceph 块设备。

⚠️ 前置条件(官方 important):使用 Ceph Block Devices 之前,你必须有一个正在运行的 Ceph 集群。

1.2 与本地磁盘 / 其他块存储形态对比

💡 补充:官方 doc/rbd/index.rst 没有提供与其他存储形态的对比表。下表为便于选型而补充,其中「Ceph RBD」列的每一条均来自本册源文;「本地磁盘」「传统 SAN LUN」两列属于通用存储常识,非官方文档内容,请仅作定性参考,不要当作官方口径引用。

维度 Ceph RBD 本地磁盘(直连 HDD/SSD) 传统 SAN LUN(FC/iSCSI 阵列)
数据分布 条带化散布到多个 OSD(index.rst) 单盘,无分布 集中在阵列控制器后端
容量分配 thin-provisioned,写入才占空间 预分配/整盘 多为厚置备或阵列级精简
扩容 rbd resize 在线扩/缩(--allow-shrink) 换盘 / LVM 扩展 阵列侧扩容,主机侧 rescan
副本与故障域 RADOS 多副本/纠删码,跨节点、跨机架 无(依赖 RAID) 依赖阵列双控与 RAID
一致性 RADOS 强一致性 本地顺序一致 阵列保证
快照 image 级快照 + COW 克隆(layering) 需 LVM/VSS 等 阵列快照(数量常受限、可能收费)
克隆效率 快照 protect 后可秒级创建 COW 克隆 全量拷贝 依赖阵列克隆能力
远程复制 内建 rbd-mirror(journal / snapshot 两种模式) 无 需阵列级远程复制许可
客户端形态 krbd(内核)/ librbd(QEMU、ceph-csi)/ NBD / iSCSI GW / NVMe-oF GW 本地 需 FC HBA 或 iSCSI initiator
无原生客户端的机器 可通过 iSCSI GW / NVMe-oF GW 接入 — —
多客户端并发写 需外部协调;exclusive-lock 只做线性化不做互斥(rbd-exclusive-locks.rst) 不适用(单机) 依赖集群文件系统/阵列锁
文件系统的坑 缓存开启时不能跑 GFS/OCFS(rbd-config-ref.rst) 无 同左

1.3 适用场景

依据本册源文,RBD 的适用场景可归纳为:

场景 依据(源文出处)
虚拟机/云主机系统盘与数据盘(OpenStack Nova/Cinder/Glance、CloudStack Primary Storage) rbd-openstack.rst、rbd-cloudstack.rst
容器持久化卷(Kubernetes PVC/PV、Nomad CSI volume) rbd-kubernetes.rst、rbd-nomad.rst
VDI(大量 VM 克隆自同一 base image,配合只读父镜像缓存) rbd-persistent-read-only-cache.rst
数据库/低延迟写(配合 PWL 持久写日志缓存,PMEM/SSD) rbd-persistent-write-log-cache.rst
需要静态加密的合规场景(LUKS1/LUKS2) rbd-encryption.rst
跨机房灾备(单向/双向镜像) rbd-mirroring.rst
无 Ceph 客户端的操作系统(Windows / VMware ESXi / 老 Linux) rbd-windows.rst、iscsi-*、nvmeof-*
异构数据导入(raw / qcow / HTTP / S3 / NBD → RBD) rbd-live-migration.rst
存储池/布局在线重整(换 pool、换 image format、换 layout) rbd-live-migration.rst
工作负载录制与回放(性能回归测试) rbd-replay.rst

引自:Ceph Block Device


二、核心概念

2.1 image(镜像/卷)

一个 RBD image 就是一个块设备:对客户端表现为一段可按 512B 块寻址的字节序列,在集群侧则被切分为多个 RADOS 对象。

  • 创建:rbd create --size {megabytes} {pool-name}/{image-name}
  • image 必须先有一个 pool,且该 pool 需先执行 rbd pool init。
  • 默认 pool 名为 rbd(rbd_default_pool,默认值取自 src/common/options/rbd.yaml.in @ v20.2.4:rbd)。

官方还给出一条命名注意事项:除了 {pool}/{image},userid/<uuid> 也是一种可能的 RBD image 命名方式,这类命名至少会造成混淆。

2.2 条带化(striping)

块设备 image 的数据被条带化(striped)分布到多个 OSD 上。这带来两点直接收益:

  • 大 image 的性能优于独立服务器上的单块盘(rbd-kubernetes.rst、rbd-openstack.rst 均明确:large volumes/images 可以比 standalone server 上的本地盘性能更好);
  • 顺序读写负载通过并行化获得吞吐提升(striping v2 feature 描述:Striping helps with parallelism for sequential read/write workloads)。

相关配置项(src/common/options/rbd.yaml.in @ v20.2.4):

配置项 类型 默认值 含义
rbd_default_order uint 22 新建 image 的 object size 的幂次:default_object_size = 2 ^ rbd_default_order,取值 12–25(即 4KiB–32MiB),默认 2^22 = 4MiB
rbd_default_stripe_count uint 0 新建 image 的默认 stripe count
rbd_default_stripe_unit size 0 新建 image 的默认 stripe width

(上表默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

💡 补充:源文未给出 rbd_default_stripe_count = 0 / rbd_default_stripe_unit = 0 的具体语义展开(通常理解为沿用 object size / 不启用 fancy striping),此处不下结论。

2.3 精简配置(thin provisioned)

Ceph Block Device images 是 thin provisioned 的:在真正写入数据之前不占用任何物理存储,但有一个由 --size 设置的最大容量上限。

配套语义:

  • object-map feature 的描述明确块设备是 sparse 的(只存实际写过的数据),object map 用来追踪哪些 object 真实存在;
  • 删除快照后容量不会立刻释放:OSD 异步删除数据,该过程叫 snaptrim,在 ceph status 输出中以 "snaptrim" 显示。

2.4 快照(snapshot)

快照是 image 在某一时间点的只读逻辑副本(read-only logical copy),即一个 checkpoint。

关键限制(官方 note):RBD 不感知 image 内的文件系统,因此快照仅仅是 crash-consistent,除非在挂载/附加的操作系统内部做了协调。官方建议:

  • 打快照前 pause 或 stop I/O;
  • 若卷内有文件系统,文件系统应处于内部一致状态;未做 write quiescing 就拿的快照,重新挂载前可能需要 fsck;
  • 可以用 fsfreeze 命令 quiesce I/O(详见 fsfreeze(8) man page);
  • 对虚拟机,可以用 qemu-guest-agent 在打快照时自动冻结文件系统。

2.5 写时复制克隆(copy-on-write clone / layering)

Ceph 支持从一个块设备快照创建多个 COW 克隆。快照是只读的,克隆一个只读对象在语义上更简单,因此可以非常快速地创建克隆。

  • 每个子 image(child)保存一个指向父快照(parent)的引用,从而可以打开父快照并读取它;
  • 克隆引用中包含 pool ID、image ID、snapshot ID;由于包含 pool ID,可以跨 pool 克隆;
  • 只有 "RBD format 2" image 支持克隆(即创建时未指定 --image-format 1);Linux kernel client 从 3.10 开始支持克隆 image。

2.6 exclusive lock

见第六章。一句话概括:防止多个进程以不协调的方式访问同一个 RBD image;在虚拟化中防止 VM 互相覆盖写入,在 RBD mirroring 中是 journaling 的前置条件,也是 snapshot 模式快速生成增量 diff 的基础。

2.7 object map

  • 依赖 exclusive-lock;
  • 块设备是精简配置的(sparse),object map 用于追踪哪些 object 真正存在(有数据);
  • 启用后能加速:克隆、导入/导出稀疏 image、删除操作。

2.8 fast-diff

  • 依赖 object-map 与 exclusive-lock;
  • 在 object map 上增加一个属性,使生成两个快照之间的 diff 快得多,计算快照/卷的实际数据用量(rbd du)也快得多。

2.9 deep flatten

  • 使 rbd flatten 能作用于 image 的所有快照,而不仅仅是 image 本身;
  • 没有它时,image 的快照仍依赖 parent,必须先删除快照才能删除 parent;
  • deep-flatten 让 parent 独立于其克隆(即使克隆有快照),代价是额外占用 OSD 空间。

2.10 image features 全表

RBD 的高级特性可在创建 image 时通过命令行指定,或通过 rbd_default_features = <numeric values sum> 或 rbd_default_features = <comma-delimited list of CLI values> 配置默认值。

Feature 内部值 CLI 值 引入版本 KRBD 支持 默认 说明
Layering 1 layering v0.52 (Bobtail) since v3.10 yes 启用克隆能力
Striping v2 2 striping v0.55 (Bobtail) since v3.10(仅 default striping;"fancy" striping 自 v4.17) yes 将数据散布到多个 object,提升顺序读写并行度
Exclusive locking 4 exclusive-lock v0.92 (Hammer) since v4.9 yes 写前需先获取锁;仅在任意时刻只有单个客户端访问 image 时才应启用
Object map 8 object-map v0.93 (Hammer) since v5.3 yes 依赖 exclusive lock;追踪真实存在的 object,加速克隆/导入导出/删除
Fast-diff 16 fast-diff v9.0.1 (Infernalis) since v5.3 yes 依赖 object map + exclusive lock;加速快照间 diff 与 rbd du
Deep-flatten 32 deep-flatten v9.0.2 (Infernalis) since v5.1 yes 让 rbd flatten 作用于所有快照,使 parent 独立于克隆
Journaling 64 journaling v10.0.1 (Jewel) no no 依赖 exclusive lock;按发生顺序记录所有修改,供 RBD mirroring 复制 crash-consistent image。最好只让 rbd-mirror 按需管理,长期开启会显著增加 OSD 空间占用
Data pool 128 (无 CLI 值) v11.1.0 (Kraken) since v4.11 no 在纠删码 pool 上,image 数据块对象需要与 image 元数据分开存放
Operations 256 (无 CLI 值) v13.0.2 (Mimic) since v4.16 文档未明确 用于限制旧客户端对 image 执行某些维护操作(如 clone、snap create)
Migrating 512 (无 CLI 值) v14.0.1 (Nautilus) no 文档未明确 image 处于迁移状态时,限制旧客户端打开它
Non-primary 1024 (无 CLI 值) v15.2.0 (Octopus) no 文档未明确 用于限制对 snapshot-based mirroring 中 non-primary image 的修改

(rbd_default_features 的实际默认值取自 src/common/options/rbd.yaml.in @ v20.2.4:layering,exclusive-lock,object-map,fast-diff,deep-flatten,即 1+4+8+16+32 = 61)

逐个 feature 的启用与关闭后果

layering

  • 启用:可以创建 COW 克隆。
  • 关闭:无法从快照克隆;rbd clone 不可用。
  • 命令:rbd feature enable|disable {pool}/{image} layering

striping(striping v2)

  • 启用:数据跨多个 object 条带化,顺序读写并行度提升。
  • 关闭:image 按单一 object 序列布局,顺序大 IO 的并行度下降。
  • 注意:krbd 从 v3.10 起只支持 default striping,"fancy" striping 需 v4.17+。若 kernel < 4.17 且使用了 fancy striping,krbd 将无法映射。

exclusive-lock

  • 启用(默认):写前需获取锁;object-map、fast-diff、journaling、PWL cache 均依赖它。
  • 关闭:上述依赖特性会被连带影响;官方明确 Disabling the exclusive-lock feature will negatively affect the performance of some operations。
  • 注意:exclusive-lock 不阻止两个及以上并发客户端打开同一 image 并轮流写入——它们的写只是被自动协作式地线性化(见第六章)。
  • 与 RBD advisory lock(rbd lock add / rbd lock rm)不兼容。

object-map

  • 启用:加速克隆、稀疏 image 的导入导出、删除。
  • 关闭:上述操作需要扫描/遍历更多对象,变慢;fast-diff 也无法启用(依赖链断裂)。

fast-diff

  • 启用:快照间 diff 生成快;rbd du 快。
  • 关闭:需要全量扫描 image 才能生成 diff,导致 snapshot-based mirroring 的增量同步显著变慢、rbd du 变慢。

deep-flatten

  • 启用:rbd flatten 覆盖所有快照,parent 可独立于克隆被删除。
  • 关闭:image 的快照仍依赖 parent,必须先删快照才能删 parent;代价是省下 OSD 空间。

journaling

  • 启用:journal-based mirroring 的前提;每次写先落 journal 再改 image,写延迟几乎翻倍(因为一次写变成两次写)。
  • 关闭:无法做 journal-based mirroring;长期开启会显著消耗 OSD 空间,故官方建议只让 rbd-mirror 按需开关。
  • krbd 不支持(KRBD support: no)。
  • 依赖 exclusive-lock;若 exclusive-lock 未启用,应先启用它。

data-pool

  • 启用:纠删码 pool 上,image 数据块对象存放到独立的数据 pool,元数据留在原 pool。
  • 关闭/不适用:副本池无需此特性。
  • 在 mirroring 中,目标集群选 data pool 的规则见第十章「Data Pools」。

operations / migrating / non-primary

  • 这三个是"限制旧客户端"型的内部特性,官方未给出 CLI 值;日常运维不需要手工开关,由 rbd-mirror 与 live-migration 机制自动管理。

引自:Config Settings


三、基本操作(rados-rbd-cmds)

rbd 命令可以创建、列出、查看、删除块设备 image,也可以克隆 image、创建快照、回滚 image 到快照、查看快照等。

引自:Basic Block Device Commands

3.1 创建 Block Device Pool

# 1) 用 ceph 工具创建 pool(略,参见 Storage Pools 文档)
# 2) 用 rbd 工具初始化该 pool 供 RBD 使用
rbd pool init <pool-name>

注意:如果命令中未指定 pool 名,rbd 工具假定默认 pool 名为 rbd。

3.2 创建 Block Device User

除非另行指定,rbd 命令使用 Ceph user ID admin 访问集群,而 admin 拥有集群的完全管理权限。官方建议:使用一个权限更小的 Ceph user ID(称为 "block device user" 或 "Ceph user")。

ceph auth get-or-create client.{ID} mon 'profile rbd' osd 'profile {profile name} [pool={pool-name}][, profile ...]' mgr 'profile rbd [pool={pool-name}]'

示例:创建名为 qemu 的 Ceph user ID,对 pool vms 读写、对 pool images 只读:

ceph auth get-or-create client.qemu mon 'profile rbd' osd 'profile rbd pool=vms, profile rbd-read-only pool=images' mgr 'profile rbd pool=images'

ceph auth get-or-create 的输出即该 user ID 的 keyring,可写入 /etc/ceph/ceph.client.{ID}.keyring。

注意:使用 rbd 命令时通过 --id {id} 指定 Ceph user ID,该参数可选。

3.3 创建 image

rbd create --size {megabytes} {pool-name}/{image-name}

示例:在 swimmingpool pool 中创建 1GB 的 image bar:

rbd create --size 1024 swimmingpool/bar

不指定 pool 则落在默认 pool rbd:

rbd create --size 1024 foo

注意:必须先创建 pool,才能把它指定为 image 的存放位置。

3.4 列出 image

rbd ls                  # 列出默认 pool rbd
rbd ls {poolname}       # 列出指定 pool
rbd ls swimmingpool

列出"延迟删除(deferred delete)"的 image:

rbd trash ls
rbd trash ls {poolname}
rbd trash ls swimmingpool

3.5 查看 image 信息

rbd info {image-name}
rbd info foo

rbd info {pool-name}/{image-name}
rbd info swimmingpool/bar

注意:官方文档提示存在其他可能的命名约定并可能与此处冲突,例如 userid/<uuid> 也可能是某个 RBD image 的名字,至少会造成混淆。

3.6 调整 image 大小

image 是精简配置的,但有由 --size 设定的最大容量。增大(或减小):

# 增大到 2048 MB
rbd resize --size 2048 foo

# 减小到 2048 MB,必须显式加 --allow-shrink
rbd resize --size 2048 foo --allow-shrink

3.7 删除 image

rbd rm {image-name}
rbd rm foo

rbd rm {pool-name}/{image-name}
rbd rm swimmingpool/bar

3.8 延迟删除(trash)与恢复

# 移动到 trash(延迟删除)
rbd trash mv {pool-name}/{image-name}
rbd trash mv swimmingpool/bar

# 从 trash 中彻底删除(注意这里用 image-id)
rbd trash rm {pool-name}/{image-id}
rbd trash rm swimmingpool/2bf4474b0dc51

# 恢复(默认 pool)
rbd trash restore {image-id}
rbd trash restore 2bf4474b0dc51

# 恢复(指定 pool)
rbd trash restore {pool-name}/{image-id}
rbd trash restore swimmingpool/2bf4474b0dc51

# 恢复的同时重命名
rbd trash restore swimmingpool/2bf4474b0dc51 --image new-name

trash 相关规则(官方 note):

  • 即使 image 有快照、或正被克隆使用,也可以把它移入 trash;但在这些条件下不能从 trash 中删除它;
  • 可以用 --expires-at 设置延迟时间(默认为 now)。延迟时间未到时不能删除,除非使用 --force。

相关配置项(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4):

配置项 类型 默认值 含义
rbd_move_to_trash_on_remove bool false 删除 image 时自动移入 trash
rbd_move_to_trash_on_remove_expire_seconds uint 0 已删除 image 在 trash 中的默认保护秒数
rbd_move_parent_to_trash_on_remove bool false 删除时把带有 clone format v2 子 image 的 parent 也移入 trash

3.9 cephx 认证下的 rbd 用法

启用 cephx(默认启用)时,必须指定用户名或 ID,以及包含对应 key 的 keyring 路径:

rbd --id {user-ID} --keyring /path/to/secret [commands]
rbd --name {username} --keyring /path/to/secret [commands]

# 示例
rbd --id admin --keyring /etc/ceph/ceph.keyring [commands]
rbd --name client.admin --keyring /etc/ceph/ceph.keyring [commands]

Tip:可以把 user 和 secret 加到 CEPH_ARGS 环境变量中,避免每次重复输入。

引自:Snapshots(Cephx Notes 段)


四、内核模块与客户端(krbd)

引自:Kernel Module Operations

4.1 列出 image

挂载块设备 image 之前,先列出 image:

rbd list

4.2 map(映射)

使用 rbd 把 image 名映射到内核模块。必须指定 image 名、pool 名和用户名。如果 RBD 内核模块尚未加载,rbd 会代为加载:

sudo rbd device map {pool-name}/{image-name} --id {user-name}
sudo rbd device map rbd/myimage --id admin

启用 cephx 认证时还必须指定 secret,可以来自 keyring 或存放 secret 的文件:

sudo rbd device map rbd/myimage --id admin --keyring /path/to/keyring
sudo rbd device map rbd/myimage --id admin --keyfile /path/to/file

4.3 查看已映射设备

rbd device list

4.4 unmap(取消映射)

设备名按约定与块设备 image 名相同:

sudo rbd device unmap /dev/rbd/{poolname}/{imagename}
sudo rbd device unmap /dev/rbd/rbd/foo

4.5 内核版本要求

⚠️ 文档未明确:rbd-ko.rst 页面没有给出 krbd 的通用最低内核版本要求。本册能确认的内核版本信息只有 image feature 表中的 KRBD 支持版本,汇总如下(引自 rbd-config-ref.rst):

能力 / Feature 所需最低内核版本
支持克隆 image(clone) v3.10
layering v3.10
striping(default striping) v3.10
striping(fancy striping) v4.17
exclusive-lock v4.9
data-pool v4.11
operations v4.16
deep-flatten v5.1
object-map v5.3
fast-diff v5.3
journaling 不支持
migrating 不支持
non-primary 不支持

4.6 已知限制(krbd)

限制 出处
不支持 image 级加密(LUKS1/LUKS2) rbd-encryption.rst
不支持 live-migration rbd-live-migration.rst
不支持 journaling feature rbd-config-ref.rst
不能在所有 Ceph CRUSH tunables / RBD image features 上都受支持(ceph-csi 默认使用 RBD 内核模块,因此这是一条对 Kubernetes/Nomad 也生效的限制) rbd-kubernetes.rst、rbd-nomad.rst
内核模块可以使用 Linux page cache(这是优势,也是与 librbd 行为差异的来源) index.rst
Windows 上没有 krbd,改用 rbd-wnbd(WNBD 驱动),见第十二章 rbd-windows.rst

相关配置项:

配置项 类型 默认值 含义
rbd_default_map_options str 文档未明确(rbd.yaml.in 中无 default: 字段) 默认 krbd map options

(上表取自 src/common/options/rbd.yaml.in @ v20.2.4)


五、快照与克隆(rbd-snapshot)

引自:Snapshots

5.1 快照基础操作

操作 命令 示例
创建快照 rbd snap create {pool-name}/{image-name}@{snap-name} rbd snap create rbd/foo@snapname
列出快照 rbd snap ls {pool-name}/{image-name} rbd snap ls rbd/foo
回滚快照 rbd snap rollback {pool-name}/{image-name}@{snap-name} rbd snap rollback rbd/foo@snapname
删除快照 rbd snap rm {pool-name}/{image-name}@{snap-name} rbd snap rm rbd/foo@snapname
清除全部快照 rbd snap purge {pool-name}/{image-name} rbd snap purge rbd/foo
保护快照 rbd snap protect {pool-name}/{image-name}@{snapshot-name} rbd snap protect rbd/foo@snapname
取消保护 rbd snap unprotect {pool-name}/{image-name}@{snapshot-name} rbd snap unprotect rbd/foo@snapname
克隆快照 rbd clone {pool-name}/{parent-image}@{snap-name} {pool-name}/{child-image-name} rbd clone rbd/foo@snapname rbd/bar
列出快照的子 image rbd children {pool-name}/{image-name}@{snapshot-name} rbd children rbd/foo@snapname
flatten 克隆 rbd flatten {pool-name}/{image-name} rbd flatten rbd/bar

5.2 回滚 vs 克隆(性能提示)

官方 note:回滚 image 到快照意味着用快照数据覆盖 image 的当前版本,执行时间随 image 大小增长。因此从快照克隆比回滚到快照更快,官方推荐用"克隆"作为回到某个已有状态的首选方法。

5.3 删除快照不会立刻释放空间

Ceph OSD 异步删除数据,所以删除快照不会立刻释放底层 OSD 容量。这个过程叫 snaptrim,在 ceph status 输出中就显示为该名称。

5.4 Layering 原理

Ceph 支持从一个块设备快照创建多个写时复制(COW)克隆。快照是只读的,克隆一个只读对象在语义上更简单,因此可以非常快速地创建克隆。

           +-------------+              +-------------+
           | {s} c999    |              | {s}         |
           |  Snapshot   | Child refers |  COW Clone  |
           |  of Image   |<------------*| of Snapshot |
           |             |  to Parent   |             |
           | (read only) |              | (writable)  |
           +-------------+              +-------------+

               Parent                        Child
  • "parent" = Ceph 块设备快照;"child" = 从该快照克隆出来的 image;
  • 每个克隆 image(child)保存一个指向父快照的引用,使其可以打开并读取父快照;
  • 克隆的引用包含 pool ID、image ID、snapshot ID。因为包含 pool ID,可以从一个 pool 的快照克隆到另一个 pool 的 image;
  • COW 克隆的行为与其他任何 Ceph 块设备 image 完全一致:可以读、写、克隆、resize,没有任何特殊限制;
  • 但 COW 克隆依赖该快照,因此克隆前必须保护(protect)快照;
  • 只有 "RBD format 2" image 支持克隆(创建时未指定 --image-format 1);Linux kernel client 从 3.10 起支持克隆 image。

5.5 使用 layering 的标准流程

+----------------------------+        +-----------------------------+
|  Create Block Device Image |------->|      Create a Snapshot      |
+----------------------------+        +-----------------------------+
                                                     |
                    +--------------------------------+
                    v
+----------------------------+        +-----------------------------+
|   Protect the Snapshot     |------->|     Clone the Snapshot      |
+----------------------------+        +-----------------------------+

四种典型用法(官方原文列举):

  1. Image Template(镜像模板):创建一个基础 image 并打快照作为克隆模板。例如为某个 Linux 发行版(如 Ubuntu 22.04)创建 image 并做快照;偶尔更新 image 并创建新快照(sudo apt-get update / upgrade / dist-upgrade 后跟 rbd snap create);随着 image 成熟,可以克隆其中任意一个快照。
  2. Extended Template(扩展模板):在模板 image 之上继续扩展,安装数据库、CMS、分析系统等软件,然后对扩展后的 image 打快照,并像基础 image 一样持续更新。
  3. Template Pool(模板池):建一个 pool 存放 (1) 模板基础 image 和 (2) 它们的快照,然后只给用户只读权限,这样用户可以克隆快照,但无法在该 pool 内写入或执行。
  4. Image Migration/Recovery(迁移/恢复):用 layering 把数据从一个 pool 迁移或恢复到另一个 pool。

5.6 保护与取消保护

rbd snap protect rbd/foo@snapname      # 保护后该快照无法被删除
rbd snap unprotect rbd/foo@snapname    # 删除快照前必须先取消保护

规则:

  • 克隆会访问父快照;如果用户误删父快照,所有克隆都会损坏。为防止数据丢失,克隆前必须保护快照;
  • 受保护的快照无法删除;
  • 删除快照前必须先 unprotect;
  • 如果有克隆引用了该快照,则无法 unprotect:必须先 flatten 或删除该快照的每一个克隆。

5.7 flatten

rbd flatten {pool-name}/{image-name}
rbd flatten rbd/bar
  • 克隆 image 保留对父快照的引用;移除该引用时,就相当于把快照中的数据拷贝到克隆中,从而"展平(flatten)"该克隆;
  • flatten 耗时随快照大小增长;
  • 要删除快照,必须先 flatten(或删除)其子 image;
  • flatten 后的 image 包含快照中的全部数据,因此比分层克隆占用更多存储空间。

5.8 clone format

配置项 rbd_default_clone_format(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4):

配置项 类型 默认值 枚举值 含义
rbd_default_clone_format str auto 1 / 2 / auto 处理克隆的内部格式。1:必须挂到受保护快照上,克隆删除或 flatten 前不能删除该快照;2:克隆可挂到任意快照,允许删除正在使用的父快照,但要求 Mimic 或更新的客户端;auto:若集群要求 Mimic 或更新客户端,则使用 v2 格式

(flags: runtime,可运行时生效)

5.9 快照数量与性能影响

⚠️ 文档未明确:rbd-snapshot.rst 没有给出"单个 image 建议的快照数量上限"或"快照数量对性能影响的量化数据"。

本册能从源文中确认的相关事实只有:

  • 删除快照不会立即释放空间,靠异步 snaptrim 回收(rbd-snapshot.rst);
  • 快照过多时,fast-diff / object-map 会显著影响 diff 生成与 rbd du 的速度(rbd-config-ref.rst 的 feature 描述);
  • 快照数量与 RADOS 侧 snaptrim 的关系由 mon_max_snap_prune_per_epoch 控制(默认值取自 src/common/options/global.yaml.in @ v20.2.4:100,含义为单个 OSDMap epoch 内处理的已剪枝快照最大数量)——此项属于 RADOS 侧参数,不在 doc/rbd/ 源文范围内。

💡 补充:基于上述事实可以给出的实践建议是——保留 object-map + fast-diff,并控制长生命周期快照链的深度;但"具体保留多少个"官方未给数字,本册不臆造。


六、exclusive lock 与 object map

引自:RBD Exclusive Locks

6.1 什么是 exclusive lock

Exclusive locks 是一种机制,用于防止多个进程以不协调的方式访问同一个 RBD。它被大量用于:

  • 虚拟化:防止多个 VM 互相覆盖(clobber)对方的写入;
  • RBD mirroring:在 journal-based mirroring 中,它是 journaling 的前置条件;在 snapshot-based mirroring 中,它是快速生成增量 diff 的基础。

exclusive-lock feature 在新创建的 image 上默认启用。该默认可通过配置项 rbd_default_features,或 rbd create 的 --image-feature / --image-shared 选项覆盖。

官方 note:许多 image feature(包括 object-map 和 fast-diff)依赖 exclusive locking。禁用 exclusive-lock 会对某些操作的性能产生负面影响。

6.2 协作式锁转移(automatic cooperative lock transitions)

为了维持多客户端访问,exclusive-lock 实现了客户端之间的自动协作式锁转移,保证任意时刻只有一个客户端能写入 RBD image,从而保护 image 的内部结构(object map、journal、PWL cache)不被并发修改。

对用户基本透明,行为如下:

  1. 当某个客户端(librbd 进程;对 krbd 来说是客户端节点的内核)需要写入一个启用了 exclusive locking 的 RBD image 时,它首先获取该 image 的 exclusive lock。如果该锁已被其他客户端持有,则请求那个客户端释放锁。
  2. 持有锁的客户端收到释放请求后,停止处理写操作、刷写其缓存、然后释放锁。
  3. 持有锁的客户端优雅终止时,锁也被优雅释放。
  4. 一次优雅释放(无论是被请求还是因客户端终止)使得另一个后续客户端可以获取锁并开始处理写入。

6.3 ⚠️ 关键警告:它不阻止并发写入

官方 warning:默认情况下,exclusive-lock feature 不会阻止两个或更多并发运行的客户端打开同一个 RBD image 并轮流写入(无论是否在同一节点)。实际上,它们的写只是随着锁的自动来回转移被**线性化(linearized)**了。

这意味着:如果你需要真正的互斥,必须在 RBD 之上由外部机制(如集群文件系统、Pacemaker、应用层仲裁)来保证。

关闭自动锁转移的方法:获取 exclusive lock 时指定 RBD_LOCK_MODE_EXCLUSIVE 标志。该标志通过 rbd device map 命令的 --exclusive 选项暴露给用户。

官方 note:exclusive-lock feature 与 RBD advisory locks(rbd lock add 和 rbd lock rm 命令)不兼容。

6.4 Blocklisting(存储级 fencing)

有时先前持有 exclusive lock 的客户端不会优雅终止,而是突然死掉:进程收到 KILL/ABRT 信号、节点硬重启或掉电。此时锁永远不会被优雅释放,任何新客户端要写入该 image 就必须**打破(break)**之前持有的锁。

但另一种情形是:进程(或内核线程)只是挂起或临时失去到 Ceph 集群的网络连接。这时打破锁可能是灾难性的——挂起的进程或网络问题可能自行恢复,原进程就会与新起的进程竞争,从而以不协调、破坏性的方式访问 RBD 数据。

因此,当锁无法以标准优雅方式获取时,接管进程不仅会打破锁,还会把先前的持锁者加入 blocklist。这个过程在新客户端进程与 Ceph Monitor 之间协商:

  1. Monitor 收到 blocklist 请求后,指示相关 OSD 不再服务来自旧客户端进程的请求;
  2. 相关的 OSD map 更新完成后,新客户端才能打破先前持有的锁;
  3. 新客户端获得锁后,才能开始写入 image。

Blocklisting 因此是一种存储级的资源 fencing。

官方 note:为了让 blocklisting 生效,客户端必须拥有 osd blocklist capability。该 capability 已包含在 profile rbd capability profile 中,应当为所有使用 RBD 的 Ceph 客户端身份(client identities)普遍设置。

6.5 与 blocklist / lock 相关的配置项

(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_blocklist_on_break_lock bool true 锁被打破时是否 blocklist 对应客户端 保持 true,这是 fencing 生效的前提
rbd_blocklist_expire_seconds uint 0 blocklist 持续秒数;0 表示使用 OSD 默认值 一般保持 0;仅在需要缩短隔离窗口时调整
rbd_request_timed_out_seconds uint 30 维护类请求的超时秒数 大集群/慢网络可适当增大
rbd_auto_exclusive_lock_until_manual_request bool true 在显式请求之前,自动获取/释放 exclusive lock 保持默认
rbd_invalidate_object_map_on_timeout bool(dev 级) true 加载或更新超时时是否使 object map 失效 保持默认

参见:mon_osd_blocklist_default_expire(默认值取自 src/common/options/mon.yaml.in @ v20.2.4:1_hr,即 1 小时),是 rbd_blocklist_expire_seconds = 0 时实际生效的 OSD 侧默认值。

6.6 object map 补充要点

  • 依赖 exclusive-lock;
  • 因为块设备是 thin provisioned(sparse),只存实际写过的数据;object map 用来追踪哪些 object 真实存在(已写入数据);
  • 启用 object map 会加速:克隆、导入稀疏 image、导出稀疏 image、删除。

七、缓存机制

RBD 有三层不同的"缓存",容易混淆,先分清:

缓存 作用层 位置 页面
Linux page cache krbd(内核模块) 客户端内核 index.rst 提及
RBD Caching(librbd 内存缓存) librbd 客户端进程内存 rbd-config-ref.rst
Shared Read-only Parent Image Cache librbd + ceph-immutable-object-cache 守护进程 客户端本地磁盘 rbd-persistent-read-only-cache.rst
Persistent Write Log Cache (PWL) librbd PMEM / SSD 持久设备 rbd-persistent-write-log-cache.rst

7.1 RBD Caching(librbd 内存缓存)

引自:Config Settings

用户态实现(librbd)无法利用 Linux page cache,因此自带内存缓存,称为 "RBD caching"。行为类似表现良好的硬盘缓存:当 OS 发送 barrier 或 flush 请求时,所有脏数据写入 OSD。这意味着使用 write-back 缓存与使用一块会正确发送 flush 的、表现良好的物理硬盘同样安全(Linux kernel >= 2.6.32)。

  • 缓存使用 LRU 算法;在 write-back 模式下可以合并连续请求以获得更好的吞吐;
  • librbd 缓存默认启用,支持三种策略:write-around、write-back、write-through;
策略 写返回时机 读是否可从缓存命中
write-around 立即返回(除非未写字节超过 rbd_cache_max_dirty) 否(不从缓存服务读)——因此对高性能写负载更快
write-back 立即返回(除非未写字节超过 rbd_cache_max_dirty) 是
write-through 仅当数据已落到所有副本的磁盘上才返回 是
  • 在收到 flush 请求之前,缓存表现为 write-through,以确保对不发送 flush 的老操作系统也能崩溃一致;
  • 如果禁用 librbd 缓存,读写直达存储集群,写仅在数据落到所有副本磁盘后返回;
  • Read-ahead(预读):librbd 支持 read-ahead/prefetching 以优化小的顺序读。通常由 VM 的 guest OS 处理,但 boot loader 可能发不出高效读。缓存被禁用或策略为 write-around 时,read-ahead 自动禁用。

⚠️ 官方 note:缓存位于客户端内存中,每个 RBD image 各有自己的一份。由于缓存是客户端本地的,若还有其他方访问该 image,则没有一致性保证。在开启缓存的 RBD 上运行 GFS 或 OCFS 是不行的。

RBD 的选项应设置在配置文件的 [client] 段或 central config store 中。

缓存相关配置项逐项

(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_cache bool true 是否启用缓存(writeback,除非 rbd_cache_max_dirty 为 0) 一般保持 true;需要跨客户端强一致或跑 GFS/OCFS 时关闭
rbd_cache_policy str writearound 处理写的缓存策略,枚举:writethrough / writeback / writearound 默认 writearound 偏重写;需要读命中时改 writeback
rbd_cache_writethrough_until_flush bool true 在收到 flush 之前以 writethrough 方式工作,确保使用方会发 flush,之后 writeback 才安全 保持 true(对老 virtio 驱动尤其重要,Linux < 2.6.32 不发 flush)
rbd_cache_size size 32_M 每个卷的 RBD 客户端缓存大小(字节) 内存充裕可增大;必须大于 rbd_cache_max_dirty
rbd_cache_max_dirty size 24_M 触发 write-back 的 dirty 上限;为 0 则使用 write-through 缓存 约束:必须小于 rbd_cache_size
rbd_cache_target_dirty size 16_M 缓存开始向数据存储写数据前的 dirty target;不阻塞对缓存的写入 约束:必须小于 rbd_cache_max_dirty
rbd_cache_max_dirty_age float 1 脏数据在缓存中停留多少秒后开始 writeback 低延迟场景可调小
rbd_cache_max_dirty_object uint 0 对象级脏数据上限;0 表示由 rbd_cache_size 自动计算 保持 0
rbd_cache_block_writes_upfront bool false 是否在 aio_write 调用完成前阻塞对缓存的写 保持 false

💡 补充:rbd-cache-config-settings 是 qemu-rbd.rst 中对本节的锚点引用名(见第十二章 QEMU 缓存选项),官方页面上这一节的标题即为 "Cache Settings"。

Read-ahead 配置项逐项

���默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_readahead_trigger_requests uint 10 触发 readahead 所需的连续请求数 默认即可
rbd_readahead_max_bytes size 512_K 单次 read-ahead 请求的最大大小;为 0 则禁用 read-ahead 顺序读场景可增大
rbd_readahead_disable_after_bytes size 50_M 从某个 RBD image 累计读取多少字节后禁用该 image 的 read-ahead(关闭再打开 image 会重置)。为 0 则 read-ahead 一直启用 默认 50_M:让 guest OS 启动后接管 read-ahead

7.2 Persistent Read-only Cache(共享只读父镜像缓存)

引自:RBD Persistent Read-only Cache

原理

克隆的 RBD image 通常只修改父 image 的一小部分。例如在 VDI 场景中,VM 都从同一个 base image 克隆而来,初始时只有 hostname 和 IP 地址不同。启动过程中,所有这些 VM 都会读取同一份父 image 数据的某些部分。如果在本地缓存父 image,就能加速该缓存主机上的读,同时减少客户端到集群的网络流量。

RBD cache 必须在 ceph.conf 中显式启用(不同于 librbd 内存缓存默认开启)。ceph-immutable-object-cache 守护进程负责把父内容缓存到本地磁盘,后续对这些数据的读将由本地缓存服务。

官方 note:RBD shared read-only parent image cache 需要 Ceph Nautilus 或更新版本。

            +--------------------------------------------------------+
            |                         QEMU                           |
            +--------------------------------------------------------+
            |                librbd (cloned images)                  |
            +-------------------+-+----------------------------------+
            |      librados     | |  ceph--immutable--object--cache  |
            +-------------------+ +----------------------------------+
            |      OSDs/Mons    | |     local cached parent image    |
            +-------------------+ +----------------------------------+

启用

在 ceph.conf 的 [client] 段添加:

        rbd parent cache enabled = true
        rbd plugins = parent_cache

对应配置项(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4):

配置项 类型 默认值 含义
rbd_parent_cache_enabled bool false 是否启用 rbd shared read-only cache
rbd_plugins str 文档未明确(rbd.yaml.in 中无 default: 字段,通常理解为空) 逗号分隔的、要启用的 librbd 插件列表

Immutable Object Cache 守护进程

ceph-immutable-object-cache 守护进程负责在其本地缓存目录中缓存父 image 内容。建议使用 SSD 作为底层存储以获得更好性能。

三大关键组件:

  1. Domain socket based IPC:守护进程启动时监听本地 domain socket,等待来自 librbd 客户端的连接;
  2. LRU based promotion/demotion policy:守护进程在内存中维护每个缓存文件的命中统计;容量达到配置阈值时降级(淘汰)冷缓存;
  3. File-based caching store:守护进程维护一个简单的基于文件的缓存存储;提升(promotion)时从 RADOS 集群取回 RADOS 对象,存放到本地缓存目录。

交互流程:每个克隆 RBD image 被打开时,librbd 尝试通过 Unix domain socket 连接缓存守护进程;连接成功后,后续每次读都与守护进程协调。未缓存的读:守护进程把该 RADOS 对象提升(promote)到本地缓存目录,下一次读该对象由缓存服务。守护进程维护简单的 LRU 统计,用于在需要时(例如缓存已满、有压力时)淘汰冷缓存文件。

守护进程位于可选的 ceph-immutable-object-cache 发行包中。

⚠️ 官方 important:ceph-immutable-object-cache 守护进程必须具备连接 RADOS 集群的能力。

运行守护进程

守护进程应使用唯一的 Ceph user ID:

ceph auth get-or-create client.ceph-immutable-object-cache.{unique id} mon 'allow r' osd 'profile rbd-read-only'

用 systemd 管理(以 user ID 作为守护进程实例名):

systemctl enable ceph-immutable-object-cache@ceph-immutable-object-cache.{unique id}

也可以前台运行:

ceph-immutable-object-cache -f --log-file={log_path}

守护进程配置项逐项

(默认值取自 src/common/options/immutable-object-cache.yaml.in @ v20.2.4;rbd-persistent-read-only-cache.rst 页面上也列出了同名默认值,两者一致)

配置项 类型 默认值 必需 含义
immutable_object_cache_sock str /var/run/ceph/immutable_object_cache_sock No librbd 客户端与 ceph-immutable-object-cache 守护进程通信用的 domain socket 路径
immutable_object_cache_path str /tmp/ceph_immutable_object_cache No immutable object cache 数据目录
immutable_object_cache_max_size size 1G No immutable cache 最大大小
immutable_object_cache_watermark float 0.9 No 缓存高水位线,取值 (0, 1)。缓存大小达到该阈值时,守护进程开始按 LRU 统计删除冷缓存
immutable_object_cache_max_inflight_ops uint 128 No 守护进程最大在途提升(promoting)请求数
immutable_object_cache_client_dedicated_thread_num uint 2 No 每个 immutable object cache 客户端的专用线程数

💡 补充:后两项(max_inflight_ops、client_dedicated_thread_num)在 rbd-persistent-read-only-cache.rst 页面未列出,来自 src/common/options/immutable-object-cache.yaml.in @ v20.2.4。

QoS 配置项逐项

(默认值取自 src/common/options/immutable-object-cache.yaml.in @ v20.2.4)

配置项 类型 默认值 含义
immutable_object_cache_qos_schedule_tick_min millisecs 50 immutable object cache 的最小调度 tick(毫秒),min: 1
immutable_object_cache_qos_iops_limit uint 0 期望的 immutable object cache 每秒 IO 操作数上限
immutable_object_cache_qos_iops_burst uint 0 期望的 immutable object cache IO 操作突发上限
immutable_object_cache_qos_iops_burst_seconds secs 1 期望的 immutable object cache IO 操作突发持续秒数,min: 1
immutable_object_cache_qos_bps_limit uint 0 期望的 immutable object cache 每秒 IO 字节数上限
immutable_object_cache_qos_bps_burst uint 0 期望的 immutable object cache IO 字节突发上限
immutable_object_cache_qos_bps_burst_seconds secs 1 期望的 immutable object cache IO 字节突发持续秒数,min: 1

适用场景与风险

  • 适用:VDI / 大量 VM 克隆自同一 base image、读多写少、客户端本地有 SSD、希望降低客户端到集群的网络流量。
  • 风险 / 限制:需 Nautilus+;必须显式在 [client] 段启用(默认 false);守护进程需要独立的 Ceph 用户与到集群的连接;缓存目录默认在 /tmp(生产环境必须改到 SSD 挂载点);只缓存父 image 内容,对克隆自身的新写数据无加速作用。

7.3 Persistent Write Log Cache(PWL)

引自:RBD Persistent Write Log Cache

原理

PWL cache 为基于 librbd 的 RBD 客户端提供一个持久的、容错的 write-back 缓存。

  • 采用 log-ordered write-back 设计,内部维护 checkpoint,使得回刷到集群的写始终是崩溃一致的;
  • 即使客户端缓存整体丢失,磁盘 image 依然是一致的,只是数据会显得陈旧(stale)。

缓存设备两种:

缓存模式 设备 说明
rwl(replica write log) PMEM 目前仅支持本地缓存,replica(副本)功能仍在开发中
ssd SSD —
disabled — 默认,缓存关闭

两种持久化模式:

模式 写完成时机 崩溃后可读性
persistent-on-write 写持久化到缓存设备后才完成 崩溃后可读
persistent-on-flush 不再需要调用方数据缓冲区即可完成,不保证崩溃后可读;收到 flush 请求时数据才持久化到缓存设备 不保证

初始默认为 persistent-on-write 模式,在收到第一个 flush 请求后切换为 persistent-on-flush 模式。

依赖:PWL cache 依赖 exclusive-lock feature。只有在获取到 exclusive lock 之后,缓存才能被加载。

启用

        rbd_persistent_cache_mode = {cache-mode}
        rbd_plugins = pwl_cache

{cache-mode} 取值:rwl、ssd、disabled。默认缓存是禁用的。

rwl 模式依赖 libpmem 库(PMDK 的一部分):在 x86_64 架构上应普遍可用,在 ppc64le 与 aarch64 上某些发行版也可获得,在 s390x 架构上不可用。

配置项逐项

(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_persistent_cache_mode str disabled 为该卷启用持久 write-back 缓存;枚举:disabled / rwl / ssd 有 PMEM 用 rwl;有 NVMe SSD 用 ssd
rbd_persistent_cache_size uint 1_G 每个 image 的持久 write-back 缓存大小;最小 1 GB(min: 1_G) 按单卷写工作集设置,不得低于 1G
rbd_persistent_cache_path str /tmp 持久 write-back 缓存在持久内存上 DAX 启用文件系统中的位置 生产必须改为 PMEM/SSD 挂载点;使用 rwl 模式时该目录必须启用 DAX,否则性能下降

官方说明:上述配置可以 per-host、per-pool、per-image 等粒度设置。per-host:写到该主机 ceph.conf 的相应 section;per-pool / per-image:参见 rbd config 命令(man page rbd(8) 的 "commands" 部分)。

缓存状态查看

PWL cache 在获取 exclusive lock 时启用,在 exclusive lock 释放时关闭。用 rbd status 查看:

rbd status {pool-name}/{image-name}

输出示例(含 present、clean、cache size、location 以及一些基础指标):

$ rbd status rbd/foo
Watchers:
        watcher=10.10.0.102:0/1061883624 client.25496 cookie=140338056493088
Persistent cache state:
        host: sceph9
        path: /mnt/nvme0/rbd-pwl.rbd.101e5824ad9a.pool
        size: 1 GiB
        mode: ssd
        stats_timestamp: Sun Apr 10 13:26:32 2022
        present: true   empty: false    clean: false
        allocated: 509 MiB
        cached: 501 MiB
        dirty: 338 MiB
        free: 515 MiB
        hits_full: 1450 / 61%
        hits_partial: 0 / 0%
        misses: 924
        hit_bytes: 192 MiB / 66%
        miss_bytes: 97 MiB

刷写与失效

# 刷写缓存文件到 OSD
rbd persistent-cache flush {pool-name}/{image-name}
rbd persistent-cache flush rbd/foo

# 使缓存失效(丢弃)
rbd persistent-cache invalidate {pool-name}/{image-name}
rbd persistent-cache invalidate rbd/foo
  • 如果应用异常死亡,rbd persistent-cache flush 也可用来把缓存刷回 OSD;
  • invalidate 会移除对应 image 的缓存元数据、禁用缓存特性,并删除本地缓存文件(如果存在)。

适用场景与风险

  • 适用:librbd 客户端(QEMU/KVM、ceph-csi 用户态模式)、有本地 PMEM 或 NVMe SSD、写密集且希望降低写延迟;需要崩溃一致保证(log-ordered 设计 + checkpoint)。
  • 风险 / 限制:
    • 依赖 exclusive-lock;锁释放时缓存关闭;
    • 默认 disabled;
    • rbd_persistent_cache_size 最小 1 GB;
    • rwl 模式要求目录启用 DAX,否则性能下降;且 s390x 架构不可用;
    • krbd 不使用此机制(PWL 是 librbd 插件,通过 rbd_plugins = pwl_cache 加载);
    • 缓存丢失时 image 一致但数据陈旧——这是设计保证,不是无数据丢失保证;
    • rbd exclusive-locks 文档把 PWL cache 列为受 exclusive lock 保护的 image 内部结构之一。

八、加密(rbd-encryption)

引自:Image Encryption

从 Pacific 版本开始,image 级加密可以由 RBD 客户端内部处理:你可以设置一个密钥,用于加密某个特定的 RBD image。

⚠️ 官方 note:krbd 内核模块目前不支持加密。

官方 note:外部工具(如 dm-crypt、QEMU)也可用于加密 RBD image,那种用法的功能集与限制集可能与此处描述的不同。

8.1 Encryption Format(格式化)

  • 默认情况下 RBD image 不加密。要加密,需要把它格式化(format)为某种受支持的加密格式;
  • format 操作会把加密元数据持久化到 image 上。元数据通常包括:加密格式与版本、密码算法与模式规范,以及用于保护加密密钥的信息;
  • 加密密钥本身由用户保管的 secret(通常是 passphrase)保护,且该 secret 永不被持久化;
  • 基本的 format 操作需要指定:加密格式 + 一个 secret;
  • 部分加密元数据可能作为 image 数据的一部分存储——通常在原始 image 数据的开头写入一个加密头。这意味着加密后 image 的有效(effective)大小会小于原始 image 大小(详见 Supported Formats)。

规则(官方 note 汇总):

  • 除非显式(重新)格式化,加密 image 的克隆会以相同的格式与 secret 天然加密;
  • 加密 image 的克隆始终是加密的;不支持重新格式化为明文(plaintext);
  • format 之前写入 image 的任何数据可能变得不可读,尽管它可能仍占用存储资源;
  • 启用了 journal feature 的 image 不能被 RBD 客户端格式化并加密。

8.2 Encryption Load(加载)

  • 格式化是启用加密的必要前提;但格式化后的 image 在所有 RBD API 看来仍是原始未加密 image:可以用同样的 API 打开,可以读写原始未加密数据。这类原始 IO 可能破坏加密格式的完整性(例如覆盖了位于 image 开头的加密元数据);
  • 要在格式化后的 image 上安全执行加密 IO,打开 image 之后还需要执行一次额外的 encryption load 操作。该操作需要为该 image 本身以及每一个被显式格式化的祖先 image 提供加密格式与用于解锁加密密钥的 secret;
  • load 成功后,该打开 image 的所有 IO 都会被加密/解密;对克隆 image,这也包括对其祖先 image 的 IO;
  • 加密密钥由 RBD 客户端保存在内存中,直到 image 关闭。

load 之后的语义变化(官方 note 汇总):

  • 一旦加载了加密,不能再对已打开 image 的上下文执行其他 encryption load / format 操作;
  • 获取 image size 与 parent overlap 的 API 将返回有效 image size 与有效 parent overlap;
  • resize API 会把指定的目标大小解释为有效 image size;
  • 如果克隆 image 被显式格式化,则 flatten 不再是透明操作——从父快照拷贝过来的父数据必须按照克隆 image 的格式重新加密。如果在发起 flatten 前没有加载加密,之前在克隆 image 中可访问的父数据可能变为不可读。
  • 同理,如果克隆 image 被显式格式化,缩容(shrink)也不再透明(某些情况下需要从父快照拷贝数据,如克隆有快照,或缩容到未按 object size 对齐的大小)。若在发起 shrink 前未加载加密,父数据可能变为不可读。
  • 加密 load 可以在通过 rbd-nbd 挂载 RBD image 为块设备时自动应用。

8.3 支持的格式:LUKS / LUKS2

LUKS1 与 LUKS2 均受支持,数据布局完全符合 LUKS 规范。因此:

  • RBD 格式化的 image 可以用外部支持 LUKS 的工具(如 dm-crypt 或 QEMU)加载;
  • 在 RBD 之外创建的既有 LUKS 数据,也可以通过把原始 LUKS 数据拷贝进 image 的方式导入,并由 RBD encryption 加载。

官方 note:LUKS 格式仅在基于 Linux 的系统上受支持。

官方 note:目前仅支持 AES-128 与 AES-256 加密算法;且 xts-plain64 是目前唯一支持的加密模式。

格式化命令

rbd encryption format [--cipher-alg {aes-128|aes-256}] {image-spec} {luks1|luks2} {passphrase-file}
  • format 操作会生成一个 LUKS header 并写入 image 开头;header 后附加单个 keyslot,其中存放一个随机生成的加密密钥,并由从 passphrase-file 读出的 passphrase 保护;
  • 默认使用 AES-256 + xts-plain64(当前推荐模式,也是其他工具的常见默认);也允许选择 AES-128;
  • RBD 目前不支持添加/删除 passphrase,但可以用 cryptsetup 等兼容工具在原始 RBD 数据上操作;
  • 官方 note:在旧版本中,如果 passphrase-file 内容以换行符结尾,该换行符会被剥离。

头部大小与性能

  • LUKS header 大小可变(LUKS2 中最大可达 136MiB),但通常最大 16MiB,取决于安装的 libcryptsetup 版本;
  • 为获得最优性能,encryption format 会把 data offset 对齐到 image 的 stripe period size;
    • 例:使用 8MiB object size 配置的 image,预期最小开销 8MiB;
    • 例:使用 4MiB object size 且 stripe count 为 3 的 image,预期最小开销 12MiB;
  • sector(最小加密单元):LUKS1 固定为 512 字节;LUKS2 支持更大的 sector,为获得更好性能,默认 sector size 设为最大 4KiB;
  • 小于一个 sector、或未对齐到 sector 起始位置的写,会在客户端触发一条 guarded read-modify-write 链,带来可观的延迟惩罚;一批这样的未对齐写可能导致 IO 竞争,进一步恶化性能。因此在无法保证写入是对齐的场景下,建议避免使用 RBD 加密。

映射 LUKS 格式的 image

rbd device map -t nbd -o encryption-passphrase-file={passphrase-file} {image-spec}

安全提示(官方原文):出于安全原因,encryption format 与 encryption load 操作都是 CPU 密集型的,可能需要几秒才能完成。对于实际 image IO 的加解密操作,假设 AES-NI 已启用,应只增加相对较小的微秒级延迟,以及少量 CPU 使用率上升。

8.4 完整示例

(1) 创建一个有效大小为 50GiB 的 LUKS2 格式 image:

rbd create --size 50G mypool/myimage
rbd encryption format mypool/myimage luks2 passphrase.bin
rbd resize --size 50G --encryption-passphrase-file passphrase.bin mypool/myimage

最后的 rbd resize 用于把 image 增大,以补偿 LUKS2 header 带来的开销。

(2) 给定一个 LUKS2 格式的 image,创建一个相同有效大小的 LUKS2 格式克隆:

rbd snap create mypool/myimage@snap
rbd snap protect mypool/myimage@snap
rbd clone mypool/myimage@snap mypool/myclone
rbd encryption format mypool/myclone luks2 clone-passphrase.bin

(3) 给定一个有效大小 50GiB 的 LUKS2 image,创建一个相同有效大小的 LUKS1 克隆:

rbd snap create mypool/myimage@snap
rbd snap protect mypool/myimage@snap
rbd clone mypool/myimage@snap mypool/myclone
rbd encryption format mypool/myclone luks1 clone-passphrase.bin
rbd resize --size 50G --allow-shrink --encryption-passphrase-file clone-passphrase.bin --encryption-passphrase-file passphrase.bin mypool/myclone

LUKS1 header 通常比 LUKS2 header 小,因此最后的 rbd resize 用于缩小克隆 image 以去掉多余的空间预留。

(4) 给定一个有效大小 50GiB 的 LUKS1 image,创建一个相同有效大小的 LUKS2 克隆:

rbd resize --size 51G mypool/myimage
rbd snap create mypool/myimage@snap
rbd snap protect mypool/myimage@snap
rbd clone mypool/myimage@snap mypool/myclone
rbd encryption format mypool/myclone luks2 clone-passphrase.bin
rbd resize --size 50G --allow-shrink --encryption-passphrase-file passphrase.bin mypool/myimage
rbd resize --size 50G --allow-shrink --encryption-passphrase-file clone-passphrase.bin --encryption-passphrase-file passphrase.bin mypool/myclone

LUKS2 header 通常比 LUKS1 大,因此最开始的 rbd resize 临时增大父 image,以在父快照(进而克隆 image)中预留额外空间,这是为了让全部父数据在克隆中可访问所必需的。最后两条 rbd resize 把父 image 缩回原大小(这不影响父快照),并把克隆 image 也缩小以去掉未使用的预留空间。

同样的逻辑适用于从一个未格式化(plaintext)image 创建格式化克隆——因为未格式化 image 根本没有 header。

(5) 映射一个格式化的克隆

需要为克隆自身以及所有被显式格式化的父 image 提供加密格式与 passphrase。encryption-format 与 encryption-passphrase-file 选项的提供顺序基于 image 层级:先是克隆 image 自己的,然后是其父 image 的,依次向上。

rbd device map -t nbd -o encryption-passphrase-file=clone-passphrase.bin,encryption-passphrase-file=passphrase.bin mypool/myclone

8.5 加密相关速查表

项 值 / 结论
引入版本 Pacific
支持格式 luks1、luks2
支持算法 aes-128、aes-256(默认 aes-256)
支持模式 xts-plain64(唯一)
平台 仅 Linux 系统(LUKS 格式)
krbd 支持 不支持
journaling image 不能格式化并加密
克隆加密 image 克隆必然加密;不可重新格式化为明文
LUKS header 大小 通常 ≤ 16MiB,LUKS2 最大 136MiB(取决于 libcryptsetup 版本)
对齐开销示例 8MiB object size → 最小 8MiB;4MiB object size + stripe count 3 → 最小 12MiB
LUKS1 sector 固定 512 B
LUKS2 sector 默认最大 4 KiB
自动 load 通过 rbd-nbd 挂载时可自动应用
passphrase 增删 RBD 不支持(可用 cryptsetup 在原始数据上做)

九、实时迁移(rbd-live-migration)

引自:Image Live-Migration

9.1 能力概述

RBD image 可以被实时迁移(live-migrated):

  • 在同一 Ceph 集群内的不同 pool、不同 image format 和/或不同 layout 之间;
  • 从另一个 Ceph 集群的 image 迁移过来;
  • 从外部数据源迁移过来。

迁移启动后,源会被**深度拷贝(deep-copied)**到目标 image,同时拉取全部快照历史,并尽可能保留数据的稀疏分配(sparse allocation)。

默认行为(同集群内):源 image 被标记为只读,所有客户端的 IO 被重定向到新的目标 image。此外,这种模式可以:

  • 可选地保留到源 image 父镜像的链接,以保留稀疏性;或
  • 在迁移过程中 flatten 该 image,以去除对源 image 父镜像的依赖。

import-only 模式:源 image 保持不变,目标 image 可以链接到另一个 Ceph 集群中的 image,或链接到外部数据源,例如 backing file、HTTP(s) 文件、S3 对象或 NBD export。

拷贝过程可以在新目标 image 正在被使用的同时安全地后台运行。当不使用 import-only 模式时,目前有一个要求:在 prepare 迁移之前必须临时停止使用源 image,以确保使用该 image 的客户端被更新为指向新的目标 image。

           +-------------+               +-------------+
           | {s} c999    |               | {s}         |
           |  Live       | Target refers |  Live       |
           |  migration  |<-------------*|  migration  |
           |  source     |  to Source    |  target     |
           |             |               |             |
           | (read only) |               | (writable)  |
           +-------------+               +-------------+

               Source                        Target

官方 note:image live-migration 需要 Ceph Nautilus 或更新版本。对外部数据源的支持需要 Ceph Pacific 或更新版本。krbd 内核模块目前不支持 live-migration。

9.2 三步流程

步骤 命令 说明
1. Prepare rbd migration prepare 创建新的目标 image 并把它链接到源。非 import-only 模式下,源 image 也会被链接到目标 image 并被标记为只读
2. Execute rbd migration execute 后台操作:把源 image 中所有已初始化的块深度拷贝到目标。此步骤可在客户端正活跃使用新目标 image 时运行
3. Finish rbd migration commit / rbd migration abort commit:移除源与目标之间的交叉链接,并在非 import-only 模式下删除源 image。abort:移除交叉链接并删除目标 image

Prepare 之后的行为细节:与 layered image 类似,对目标 image 中未初始化数据区段的读会被内部重定向到源 image;对目标中未初始化区段的写会内部把源 image 中重叠的块深度拷贝到目标 image。

9.3 Prepare(同集群默认模式)

$ rbd migration prepare migration_source [migration_target]
  • rbd migration prepare 接受与 rbd create 完全相同的 layout 可选参数,因此可以改变不可变的 image on-disk layout;
  • 如果目标只是改变 on-disk layout 并保留原 image 名,可以省略 migration_target;
  • 所有使用源 image 的客户端必须在 prepare 之前停止。如果发现任何运行中的客户端以读写模式打开了该 image,prepare 步骤会失败;
  • prepare 完成后,客户端可以用新的目标 image 名重启;试图用源 image 名重启客户端会失败。

查看状态:

$ rbd status migration_target
Watchers: none
Migration:
    	source: rbd/migration_source (5e2cba2f62e)
    	destination: rbd/migration_target (5e2ed95ed806)
    	state: prepared

注意:源 image 会被移动到 RBD trash,以避免在迁移过程中被误用:

$ rbd info migration_source
rbd: error opening image migration_source: (2) No such file or directory
$ rbd trash ls --all
5e2cba2f62e migration_source

9.4 Prepare(import-only 模式)

$ rbd migration prepare --import-only --source-spec "<JSON>" migration_target

source-spec 是一个 JSON 编码的描述,说明如何访问源 image 数据。可以通过 --source-spec 直接传入,也可以通过 --source-spec-path 从文件或 STDIN 传入。

通用格式:

{
    "type": "<format-type>",
    <format unique parameters>
    "stream": {
        "type": "<stream-type>",
        <stream unique parameters>
    }
}

当前支持的 format:native、qcow、raw 当前支持的 stream:file、http、s3、nbd

$ rbd status migration_target
Watchers: none
Migration:
        source: {"stream":{"file_path":"/mnt/image.raw","type":"file"},"type":"raw"}
        destination: rbd/migration_target (ac69113dc1d7)
        state: prepared

format: native(另一个 Ceph 集群中的原生 RBD image)

{
    "type": "native",
    ["cluster_name": "<cluster-name>",]      // 若 image 在另一个集群,需指定;要求存在 <cluster-name>.conf 文件
    ["client_name": "<client-name>",]        // 用于连接另一个集群,默认 client.admin
    "pool_name": "<pool-name>",
    ["pool_id": <pool-id>,]                  // pool_name 的可选替代
    ["pool_namespace": "<pool-namespace>",]  // 可选
    "image_name": "<image-name>",
    ["image_id": "<image-id>",]              // 若 image 在 trash 中则指定
    "snap_name": "<snap-name>",
    ["snap_id": "<snap-id>",]                // snap_name 的可选替代
}

native format 不包含 stream 对象,因为它使用原生 Ceph 操作。

示例——导入 rbd/ns1/image1@snap1:

{
    "type": "native",
    "pool_name": "rbd",
    "pool_namespace": "ns1",
    "image_name": "image1",
    "snap_name": "snap1"
}

format: qcow

用于描述 QCOW(QEMU copy-on-write)块设备。QCOW (v1) 与 QCOW2 均受支持,但不支持压缩、加密、backing files、external data files 等高级特性(未来版本可能加入)。qcow 数据可以链接到下面任一受支持的 stream 源:

{
    "type": "qcow",
    "stream": {
        <stream unique parameters>
    }
}

format: raw

用于描述厚置备(thick-provisioned)的 raw 块设备导出(即 rbd export --export-format 1 <snap-spec>)。raw 数据可链接到任一受支持的 stream 源:

{
    "type": "raw",
    "stream": {
        <stream unique parameters for HEAD, non-snapshot revision>
    },
    "snapshots": [
        {
            "type": "raw",
            "name": "<snapshot-name>",
            "stream": {
                <stream unique parameters for snapshot>
            }
        },
    ] // 可选;快照按从旧到新排序
}

snapshots 数组是可选的,目前只支持厚置备的 raw 快照导出。 更多格式(如 RBD export-format v2、RBD export-diff 快照)将在未来版本加入。

stream: file(本地 POSIX 文件)

{
    "type": "raw",
    "stream": {
        "type": "file",
        "file_path": "/mnt/image.raw"
    }
}

stream: http(远程 HTTP/HTTPS)

{
    "type": "raw",
    "stream": {
        "type": "http",
        "url": "https://download.ceph.com/image.raw"
    }
}

stream: s3(远程 S3 bucket)

{
    "type": "raw",
    "stream": {
        "type": "s3",
        "url": "https://s3.ceph.com/bucket/image.raw",
        "access_key": "NX5QOQKC6BH2IDN8HC7A",
        "secret_key": "LnEsqNNqZIpkzauboDcLXLcYaWwLQ3Kop0zAnKIn"
    }
}

官方 note:access_key 与 secret_key 参数支持把密钥存放在 MON config-key store 中——把值前缀设为 config://,后跟该值在 MON config-key store 中的路径。可用 ceph config-key set <key-path> <value> 存入,例如 ceph config-key set rbd/s3/access_key NX5QOQKC6BH2IDN8HC7A。

stream: nbd(远程 NBD export)

{
    "type": "raw",
    "stream": {
        "type": "nbd",
        "uri": "nbd://nbd.ceph.com/image.raw",
    }
}

nbd-uri 参数应遵循 NBD URI 规范。默认 NBD 端口为 10809。

9.5 Execute

$ rbd migration execute migration_target
Image migration: 100% complete...done.

rbd status 也会给出块深度拷贝的进度:

$ rbd status migration_target
Watchers:
	watcher=1.2.3.4:0/3695551461 client.123 cookie=123
Migration:
    	source: rbd/migration_source (5e2cba2f62e)
    	destination: rbd/migration_target (5e2ed95ed806)
    	state: executing (32% complete)

9.6 Commit

$ rbd status migration_target
Watchers: none
Migration:
    	source: rbd/migration_source (5e2cba2f62e)
    	destination: rbd/migration_target (5e2ed95ed806)
    	state: executed
$ rbd migration commit migration_target
Commit image migration: 100% complete...done.
  • 如果 migration_source image 是一个或多个克隆的父,在确认所有后代克隆 image 都未被使用之后,需要指定 --force 选项;
  • commit 会移除源与目标之间的交叉链接,并删除源 image:
$ rbd trash list --all

9.7 Abort

$ rbd migration abort migration_target
Abort image migration: 100% complete...done.

abort 会删除目标 image 并恢复对原源 image 的访问:

$ rbd ls
migration_source

9.8 限制汇总

限制 说明
版本 live-migration 需 Nautilus+;外部数据源需 Pacific+
krbd 不支持 live-migration
客户端停机 非 import-only 模式下,prepare 前必须停止所有使用源 image 的客户端;prepare 后发现读写模式的打开者会直接失败
源 image 名 prepare 后源 image 被移入 RBD trash,用旧名字访问会失败
克隆父 commit 时若源是克隆的父,需在确认后代克隆未被使用后用 --force
qcow 高级特性 压缩、加密、backing files、external data files 不支持
raw snapshots 仅支持厚置备 raw 快照导出
未来格式 RBD export-format v2、RBD export-diff 快照尚未支持

十、镜像与灾备(rbd-mirroring)

引自:RBD Mirroring

RBD image 可以在两个 Ceph 集群之间异步镜像。有两种模式:

10.1 两种镜像模式对比

维度 Journal-based Snapshot-based
机制 使用 RBD journaling image feature,确保时间点、崩溃一致的复制 使用周期性调度或手工创建的 RBD image mirror-snapshots 来复制崩溃一致的 RBD image
数据路径 每次对 RBD image 的写先记录到关联的 journal,再修改实际 image;远端集群读取该 journal 并重放到本地副本 远端集群确定两个 mirror-snapshot 之间的数据/元数据更新,并把**增量(deltas)**拷贝到本地副本
一致性粒度 细(每次写) 粗(两个快照之间的完整 delta)
延迟影响 由于每次写变成两次写,写延迟几乎翻倍 无 journal 双写开销
性能依赖 — 借助 RBD fast-diff feature,可以快速确定更新的数据块,无需扫描整个 RBD image
故障切换 直接可用 故障切换前必须先把两个快照之间的完整 delta 同步完成
部分应用 不适用 故障切换时,任何部分应用的 delta 集合会被回滚
版本要求 Ceph Jewel 或更新 Ceph Octopus 或更新

10.2 池 / 命名空间 / 单向 / 双向

  • 镜像在对端集群(peer clusters)的 pool 级别配置,可以进一步配置到 pool 内的命名空间或特定 image 子集;
  • 使用 journal-based mirroring 时,也可以镜像给定 pool 或命名空间内的所有 image;
  • 镜像用 rbd 命令配置;rbd-mirror 守护进程负责从远端对端集群拉取 image 更新并应用到本地集群的 image 上;
  • Pool 名在两个对端集群上必须相同(重命名 pool 参见 RADOS 文档的 Renaming a Pool)。
复制方向 定义 rbd-mirror 守护进程运行位置
One-way(单向) 数据只从 primary 集群镜像到 secondary 集群 仅在 secondary 集群上运行
Two-way(双向) 数据从一个集群的 primary image 镜像到另一个集群的 non-primary image(反之亦然) 两个集群都运行

⚠️ 官方 important:每个 rbd-mirror 守护进程实例必须能够同时连接本地与远端两个 Ceph 集群(即所有 monitor 和 OSD 主机)。此外,两个数据中心之间必须有足够带宽承载镜像工作负载。

官方 note:本文档中所有 "namespace" 一词均指 RBD 命名空间。

10.3 Pool 配置

以下步骤应在两个对端集群上执行。示例假定两个集群名为 "site-a" 和 "site-b",并且都能从同一台主机访问(为表述清晰)。

官方 note:示例中的集群名对应同名的 Ceph 配置文件(如 /etc/ceph/site-b.conf)。rbd-mirror 不要求源和目标集群有不同的内部名——两者都可以、也应该都叫 ceph。 rbd-mirror 所需的本地/远程集群配置文件可以任意命名,把守护进程容器化是避免 /etc/ceph 下命名混淆的一种策略。

启用镜像

rbd mirror pool enable [--site-name {local-site-name}] {pool-name} {mode}

mode 取值:

模式 含义
image 必须对 pool 默认命名空间中每个期望的 image 显式启用镜像。其他命名空间不受影响,需单独配置
pool pool 默认命名空间中所有启用了 journaling feature 的 image 都会被镜像。其他命名空间不受影响,需单独配置
init-only pool 默认命名空间中的任何 image 都不会被镜像,但其他命名空间仍可配置。用于允许某个其他命名空间镜像到远端 pool 的默认命名空间,也可单独使用
$ rbd --cluster site-a mirror pool enable --site-name site-a image-pool image
$ rbd --cluster site-b mirror pool enable --site-name site-b image-pool image
  • site name 也可以在创建或导入 bootstrap token 时指定;
  • site name 之后可以用同一个 mirror pool enable 子命令更改,但注意本地 site name 与远端集群使用的对应 site name 通常必须匹配。

禁用镜像

rbd mirror pool disable {pool-name}

以这种方式禁用 pool 上的镜像时,pool 内所有显式启用了镜像的 image 上的镜像也会被禁用。

$ rbd --cluster site-a mirror pool disable image-pool
$ rbd --cluster site-b mirror pool disable image-pool

Bootstrap Peers(引导对端)

为了让 rbd-mirror 守护进程发现其对端集群,必须注册对端并创建一个用户账户。该过程可用 rbd 的 mirror pool peer bootstrap create 与 mirror pool peer bootstrap import 命令自动化。

创建 token(在 site-a):

rbd mirror pool peer bootstrap create [--site-name {local-site-name}] {pool-name}
$ rbd --cluster site-a mirror pool peer bootstrap create --site-name site-a image-pool
eyJmc2lkIjoiOWY1MjgyZGItYjg5OS00NTk2LTgwOTgtMzIwYzFmYzM5NmYzIiwiY2xpZW50X2lkIjoicmJkLW1pcnJvci1wZWVyIiwia2V5IjoiQVFBUnczOWQwdkhvQmhBQVlMM1I4RmR5dHNJQU50bkFTZ0lOTVE9PSIsIm1vbl9ob3N0IjoiW3YyOjE5Mi4xNjguMS4zOjY4MjAsdjE6MTkyLjE2OC4xLjM6NjgyMV0ifQ==

输出是一个 token,应提供给 mirror pool peer bootstrap import 命令。

导入 token(在 site-b):

rbd mirror pool peer bootstrap import [--site-name {local-site-name}] [--direction {rx-only or rx-tx}] {pool-name} {token-path}
  • token-path 可以是文件路径,或 -(从标准输入读取);
  • --direction 默认为 rx-tx(双向镜像),也可设为 rx-only(单向镜像)。
$ cat <<EOF > token
eyJmc2lkIjoiOWY1MjgyZGItYjg5OS00NTk2LTgwOTgtMzIwYzFmYzM5NmYzIiwiY2xpZW50X2lkIjoicmJkLW1pcnJvci1wZWVyIiwia2V5IjoiQVFBUnczOWQwdkhvQmhBQVlMM1I4RmR5dHNJQU50bkFTZ0lOTVE9PSIsIm1vbl9ob3N0IjoiW3YyOjE5Mi4xNjguMS4zOjY4MjAsdjE6MTkyLjE2OC4xLjM6NjgyMV0ifQ==
EOF
$ rbd --cluster site-b mirror pool peer bootstrap import --site-name site-b image-pool token

手工添加集群对端(Add Cluster Peer Manually)

如果需要手工指定,或当前安装的 Ceph 版本没有上述 bootstrap 命令,可以手工添加。

先创建一个供远端守护进程使用的本地 Ceph 用户:

$ ceph auth get-or-create client.rbd-mirror-peer mon 'profile rbd-mirror-peer' osd 'profile rbd'

生成的 keyring 需要拷贝到对端集群的 rbd-mirror 守护进程主机(除非使用下述 monitor config-key store)。

rbd mirror pool peer add {pool-name} {client-name}@{cluster-name}
$ rbd --cluster site-a mirror pool peer add image-pool client.rbd-mirror-peer@site-b
$ rbd --cluster site-b mirror pool peer add image-pool client.rbd-mirror-peer@site-a
  • 默认情况下,rbd-mirror 守护进程需要能访问 /etc/ceph/{cluster-name}.conf(提供对端集群 monitor 地址),以及在默认或已配置的 keyring 搜索路径中的 {client-name} keyring(如 /etc/ceph/{cluster-name}.{client-name}.keyring);
  • 另一种方式:把对端集群的 monitor 和/或 client key 安全存放在本地 Ceph monitor 的 config-key store 中。添加镜像对端时用 --remote-mon-host 与 --remote-key-file 选项:
$ cat <<EOF > remote-key-file
AQAeuZdbMMoBChAAcj++/XUxNOLFaWdtTREEsw==
EOF
$ rbd --cluster site-a mirror pool peer add image-pool client.rbd-mirror-peer@site-b --remote-mon-host 192.168.1.1,192.168.1.2 --remote-key-file remote-key-file
$ rbd --cluster site-a mirror pool info image-pool --all
Mode: pool
Peers: 
  UUID                                 NAME   CLIENT                 MON_HOST                KEY                                      
  587b08db-3d33-4f32-8af8-421e77abb081 site-b client.rbd-mirror-peer 192.168.1.1,192.168.1.2 AQAeuZdbMMoBChAAcj++/XUxNOLFaWdtTREEsw== 

移除集群对端

rbd mirror pool peer remove {pool-name} {peer-uuid}     # peer-uuid 可从 rbd mirror pool info 获得
$ rbd --cluster site-a mirror pool peer remove image-pool 55672766-c02b-4729-8567-f13a66893445
$ rbd --cluster site-b mirror pool peer remove image-pool 60c0e299-b38f-4234-91f6-eed0a367be08

Data Pools

在目标集群创建 image 时,rbd-mirror 按以下顺序选择 data pool:

  1. 如果目标集群配置了默认 data pool(配置项 rbd_default_data_pool),则使用它;
  2. 否则,如果源 image 使用了独立的 data pool,且目标集群上存在同名的 pool,则使用该 pool;
  3. 如果以上都不成立,不设置 data pool。

(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4:rbd_default_data_pool 无 default: 字段 → 文档未明确具体默认值;仅能从 validator 推断其合法值为不含 @ 与 / 的字符串,非法值时被重置为空字符串。)

10.4 命名空间(Namespace)配置

镜像可以在 pool 的非默认命名空间上启用,且独立于默认命名空间。pool 必须已预先配置好镜像。 一个命名空间可以镜像到远端 pool 中同名或不同名的命名空间,包括镜像到默认命名空间(记作 '' 或 "")。

启用

rbd mirror pool enable {pool-name}/{local-namespace-name} {mode} [--remote-namespace {remote-namespace-name}]

mode 只能是 image 或 pool:

模式 含义
image 必须为命名空间中每个期望的 image 显式启用镜像
pool 命名空间中所有启用了 journaling feature 的 image 都被镜像
$ rbd --cluster site-a mirror pool enable image-pool/namespace-a image --remote-namespace namespace-b
$ rbd --cluster site-b mirror pool enable image-pool/namespace-b image --remote-namespace namespace-a
  • 本地集群上配置的 namespace 与 remote-namespace 对,必须与远端集群上的 remote-namespace 与 namespace 分别匹配;
  • 若未提供 --remote-namespace,则镜像到远端 pool 中同名的命名空间。

示例 A:在 site-a 的 image-pool(默认命名空间)与 site-b 的 image-pool/namespace-c 之间建立 pool 模式镜像:

$ rbd --cluster site-a mirror pool enable image-pool pool --remote-namespace namespace-c
$ rbd --cluster site-b mirror pool enable image-pool init-only
$ rbd --cluster site-b mirror pool enable image-pool/namespace-c pool --remote-namespace ""

示例 B:site-a 的 image-pool(默认)与 site-b 的 image-pool/namespace-d 之间建立 pool 模式镜像,同时 site-b 的 image-pool(默认)与 site-a 的 image-pool/namespace-e 之间建立 image 模式镜像:

$ rbd --cluster site-a mirror pool enable image-pool pool --remote-namespace namespace-d
$ rbd --cluster site-a mirror pool enable image-pool/namespace-e image --remote-namespace ""
$ rbd --cluster site-b mirror pool enable image-pool image --remote-namespace namespace-e
$ rbd --cluster site-b mirror pool enable image-pool/namespace-d pool --remote-namespace ""

禁用

rbd mirror pool disable {pool-name}/{namespace-name}

在 image 模式下,命名空间中任何已启用镜像的 image 必须先被显式禁用,才能禁用该命名空间的镜像。

$ rbd --cluster site-a mirror pool disable image-pool/namespace-a
$ rbd --cluster site-b mirror pool disable image-pool/namespace-b

10.5 Image 配置

与 pool 配置不同,image 配置只需要针对单个镜像对端集群执行。

  • 被镜像的 RBD image 被指定为 primary 或 non-primary。这是 image 的属性,不是 pool 的属性;
  • 被指定为 non-primary 的 image 不能被修改;
  • 首次在 image 上启用镜像时,image 会被自动提升为 primary(如果 pool 镜像模式是 pool 且该 image 启用了 journaling feature,则隐式提升;如果 pool 镜像模式是 image,则由 rbd 命令显式启用时提升)。

启用 image 镜像

如果 image 所在 pool 配置为 image 模式,则必须为 pool 内的每个 image 显式启用镜像:

rbd mirror image enable {pool-name}/{image-name} {mode}

mode 取值:

模式 说明
journal(默认) 使用 RBD journaling image feature 复制 image 内容。如果该 image 尚未启用 journaling feature,会自动启用
snapshot 使用 RBD image mirror-snapshots 复制 image 内容。启用后会自动创建初始 mirror-snapshot;可用 rbd 命令创建更多 mirror-snapshots
$ rbd --cluster site-a mirror image enable image-pool/image-1 snapshot
$ rbd --cluster site-a mirror image enable image-pool/image-2 journal

启用 image journaling feature

  • image 镜像模式下,image 上启用镜像时会自动启用 journaling feature;
  • pool 镜像模式下,在 image 能被镜像到对端集群之前,必须已启用 RBD image journaling feature。可以在创建 image 时通过 --image-feature exclusive-lock,journaling 启用;

也可以对既有 RBD image 动态启用:

rbd feature enable {pool-name}/{image-name} {feature-name}
$ rbd --cluster site-a feature enable image-pool/image-1 journaling

官方 note:journaling feature 依赖 exclusive-lock feature。如果 exclusive-lock 尚未启用,应先启用它,再启用 journaling。

💡 Tip(官方原文):可以在 Ceph 配置文件中加入 rbd default features = 125,使所有新建 image 默认启用 journaling。(125 = 1 + 4 + 8 + 16 + 32 + 64 = layering + exclusive-lock + object-map + fast-diff + deep-flatten + journaling)

💡 Tip(官方原文):rbd-mirror 的可调参数默认值是为"镜像整个 pool"设定的。当用 rbd-mirror 在集群之间迁移单个卷时,在本地或集中式配置的 [client] 段设置 rbd_journal_max_payload_bytes=8388608 可能获得显著的性能提升。注意该设置可能让 rbd-mirror 对目标集群呈现可观的写工作负载:迁移期间要密切监控集群性能,并行跑多个迁移前要仔细测试。

(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4:rbd_journal_max_payload_bytes 类型为 size,默认 16_K)

创建 image mirror-snapshots

使用 snapshot-based mirroring 时,每当希望镜像 RBD image 的变化内容时,都需要创建 mirror-snapshot:

rbd mirror image snapshot {pool-name}/{image-name}
$ rbd --cluster site-a mirror image snapshot image-pool/image-1
  • 默认情况下每个 image 最多创建 5 个 mirror-snapshot。达到上限时,最近的 mirror-snapshot 会被自动剪除。该上限可通过配置项 rbd_mirroring_max_mirroring_snapshots 覆盖;
  • image 被删除或镜像被禁用时,mirror-snapshots 会被自动删除。

自动创建:如果定义了 mirror-snapshot schedule,mirror-snapshots 也可以周期性地自动创建。调度可以在 global、per-pool、per-image 级别定义。任何级别都可以定义多个调度,但只有匹配某个被镜像 image 的、最具体的(most-specific)调度会运行。

rbd mirror snapshot schedule add [--pool {pool-name}] [--image {image-name}] {interval} [{start-time}]
  • interval 可用 d(天)、h(小时)、m(分钟)后缀指定;
  • 可选的 start-time 使用 ISO 8601 时间格式。
$ rbd --cluster site-a mirror snapshot schedule add --pool image-pool 24h 14:00:00-05:00
$ rbd --cluster site-a mirror snapshot schedule add --pool image-pool --image image1 6h

移除调度:用 mirror snapshot schedule remove,选项需与对应的 add 命令匹配。

列出调度(列出某一级别:global/pool/image 的所有调度;加 --recursive 可列出该级别及其以下的所有调度):

$ rbd --cluster site-a mirror snapshot schedule ls --pool image-pool --recursive
POOL        NAMESPACE IMAGE  SCHEDULE                            
image-pool  -         -      every 1d starting at 14:00:00-05:00 
image-pool            image1 every 6h                            

查看下次快照生成时间:

rbd mirror snapshot schedule status [--pool {pool-name}] [--image {image-name}]
$ rbd --cluster site-a mirror snapshot schedule status
SCHEDULE TIME       IMAGE             
2020-02-26 18:00:00 image-pool/image1 

禁用 image 镜像

rbd mirror image disable {pool-name}/{image-name}
$ rbd --cluster site-a mirror image disable image-pool/image-1

10.6 Image Promotion / Demotion 与故障切换

故障切换流程(官方描述):在需要把 primary 身份移到对端集群 image 的故障切换场景中,应当:

  1. 停止对 primary image 的访问(例如关闭 VM 电源或从 VM 上移除关联的驱动器);
  2. demote 当前的 primary image;
  3. promote 新的 primary image;
  4. 在备用集群上恢复对该 image 的访问。

⚠️ 官方 note:RBD 只提供促成 image 有序故障切换所必需的工具。完整的故障切换过程需要外部机制来协调(例如在 demotion 之前关闭 image)。

操作 命令 示例
demote 单个 image rbd mirror image demote {pool-name}/{image-name} rbd --cluster site-a mirror image demote image-pool/image-1
demote 整个 pool 的所有 primary image rbd mirror pool demote {pool-name} rbd --cluster site-a mirror pool demote image-pool
promote 单个 image rbd mirror image promote [--force] {pool-name}/{image-name} rbd --cluster site-b mirror image promote image-pool/image-1
promote 整个 pool 的所有 non-primary image rbd mirror pool promote [--force] {pool-name} rbd --cluster site-a mirror pool promote image-pool

💡 Tip(官方原文):由于 primary / non-primary 状态是按 image 的,因此可以让两个集群分担 IO 负载,并编排 failover / failback。

⚠️ 官方 note:可以用 --force 强制提升。当 demotion 无法传播到对端集群时(例如 Ceph 集群故障、通信中断)才需要强制提升。这将导致两个对端之间出现 split-brain(脑裂)场景,且在发出 force resync 命令之前,该 image 将不再处于 in-sync 状态。

10.7 Force Image Resync(强制重新同步)

如果 rbd-mirror 守护进程检测到 split-brain 事件,它不会尝试镜像受影响的 image,直到问题被纠正。恢复镜像的步骤:

  1. 先 demote 被判定为过期的 image;
  2. 然后请求 resync 到 primary image。
rbd mirror image resync {pool-name}/{image-name}
$ rbd mirror image resync image-pool/image-1
  • 官方 note:rbd 命令只是把该 image 标记为需要 resync。本地集群的 rbd-mirror 守护进程负责异步执行 resync。

  • 官方 note:对于 snapshot-based mirroring,resync 只复制到最近的 mirror-snapshot 为止的 image 内容。要同步最新更新,需要在 primary image 上创建一个新的 mirror-snapshot。

10.8 Mirror Status

对端集群的复制状态会为每个 primary 被镜像 image 保存。可用以下命令获取:

rbd mirror image status {pool-name}/{image-name}
$ rbd mirror image status image-pool/image-1

rbd mirror pool status {pool-name}
$ rbd mirror pool status image-pool

官方 note:给 mirror pool status 命令加 --verbose 选项,会额外输出 pool 中每个被镜像 image 的状态详情。

10.9 rbd-mirror 守护进程

  • 两个 rbd-mirror 守护进程负责监视远端对端集群上的 image journal,并把 journal 事件重放到本地集群;
  • 位于可选的 rbd-mirror 发行包中;
  • ⚠️ 官方 important:每个 rbd-mirror 守护进程必须能够同时连接到两个集群;

  • ⚠️ 官方 warning:Luminous 之前的版本:每个 Ceph 集群只能运行一个 rbd-mirror 守护进程。

每个 rbd-mirror 守护进程应使用唯一的 Ceph user ID:

ceph auth get-or-create client.rbd-mirror.{unique id} mon 'profile rbd-mirror' osd 'profile rbd'

用 systemd 管理(以 user ID 作为守护进程实例名):

systemctl enable ceph-rbd-mirror@rbd-mirror.{unique id}

也可以前台运行:

rbd-mirror -f --log-file={log_path}

10.10 镜像相关配置项(含从 options/*.yaml.in 查到的默认值)

保存在 src/common/options/rbd.yaml.in 中的镜像相关项(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_mirroring_max_mirroring_snapshots uint 5(min: 3) 每个 image 的 mirroring snapshots 上限 提高可获得更长历史,但占用更多空间;不得低于 3
rbd_mirroring_resync_after_disconnect bool false 镜像因 laggy 而断开后,是否自动启动 image resync 谨慎开启,可能造成非预期的全量 resync
rbd_mirroring_delete_delay uint 0 rbd-mirror 删除操作的传播延迟秒数 防止误删快速传播时可设正值
rbd_mirroring_replay_delay uint 0 rbd-mirror 异步复制的时间延迟秒数 需要"延迟复制"以抵御误删/勒索时可设正值

rbd-mirror 守护进程配置项(默认值取自 src/common/options/rbd-mirror.yaml.in @ v20.2.4)

配置项 类型 默认值 含义 建议
rbd_mirror_journal_commit_age float 5 journal commit 时间间隔(秒) 默认即可
rbd_mirror_journal_poll_age float 5 两次 journal poll 之间的最大间隔(秒) 降低可减小复制延迟、提高开销
rbd_mirror_sync_point_update_age float 30 更新 image sync point object number 的间隔秒数 默认即可
rbd_mirror_concurrent_image_syncs uint 5 并行的 image sync 最大数量 带宽充裕且 image 多时可增大
rbd_mirror_pool_replayers_refresh_interval uint 30 rbd-mirror 守护进程刷新 peers 的间隔(秒) 默认即可
rbd_mirror_concurrent_image_deletions uint 1(min: 1) 并行的 image 删除最大数量 默认即可
rbd_mirror_delete_retry_interval float 30 检查并重试失败删除请求的间隔(秒) 默认即可
rbd_mirror_image_state_check_interval uint 30(min: 1) 从 pool watcher 获取 image 并在 replayer 中设置来源的间隔(秒) 默认即可
rbd_mirror_leader_heartbeat_interval uint 5(min: 1) mirror leader 心跳间隔(秒) 默认即可
rbd_mirror_leader_max_missed_heartbeats uint 2 非锁持有者尝试获取锁所需的错过心跳数 默认即可
rbd_mirror_leader_max_acquire_attempts_before_break uint 3 错过心跳后、打破锁之前尝试获取锁的失败次数上限 默认即可
rbd_mirror_image_policy_type str simple active/active 场景下把 image 映射到实例的策略类型;枚举:none / simple 多实例 active/active 时启用
rbd_mirror_image_policy_migration_throttle uint 300 一个 image 可以被再次 reshuffle(迁移)之前需等待的秒数 默认即可
rbd_mirror_image_policy_update_throttle_interval float 1(min: 1) 为 mirror daemon peer 更新而对 image 做节流的间隔(秒) 默认即可
rbd_mirror_image_policy_rebalance_timeout float 0 策略空闲多少秒后触发 image 的 reshuffle(rebalance) 0 表示不自动 rebalance
rbd_mirror_perf_stats_prio int 5(min: 0,max: 11) mirror 守护进程复制 perf counter 的优先级;不低于 mgr_stats_threshold 时才会把数据发给 mgr 需要采集镜像复制指标时调高
rbd_mirror_image_perf_stats_prio int 5(min: 0,max: 11) mirror 守护进程按 image 的复制 perf counter 优先级 同上
rbd_mirror_memory_autotune bool(dev) true 在尊重 min 值的前提下自动调优缓存比例 保持默认
rbd_mirror_memory_target size 4_G 启用 tcmalloc 与缓存自动调优时,尝试维持在内存中的字节数 按主机内存调整
rbd_mirror_memory_base size(dev) 768_M 自动调优时,rbd-mirror 守护进程所需的最小内存估算 默认即可
rbd_mirror_memory_expected_fragmentation float(dev) 0.15(min: 0,max: 1) 自动调优时的内存碎片率估算 默认即可
rbd_mirror_memory_cache_min size(dev) 128_M 自动调优时用于缓存的最小内存量 默认即可
rbd_mirror_memory_cache_resize_interval float(dev) 5 自动调优时两次缓存 resize 之间等待的秒数 默认即可
rbd_mirror_memory_cache_autotune_interval float(dev) 30 启用缓存自动调优时,两次 rebalance 之间等待的秒数 默认即可

journal 相关配置项(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

配置项 类型 默认值 含义
rbd_journal_order uint 24(min: 12,max: 26) journal 数据对象的 order(object size)
rbd_journal_splay_width uint 4 活跃的 journal 对象数量
rbd_journal_commit_age float 5 journal commit 时间间隔(秒)
rbd_journal_object_writethrough_until_flush bool true 启用时,在收到第一个 flush 之前忽略 rbd_journal_object_flush* 配置,以便成批的 journal IO 被确认对一致性是安全的
rbd_journal_object_flush_interval uint 0 每个 journal 对象的最大挂起 commit 数
rbd_journal_object_flush_bytes size 1_M 每个 journal 对象的最大挂起字节数
rbd_journal_object_flush_age float 0 挂起 commit 的最大存活时间(秒)
rbd_journal_object_max_in_flight_appends uint 0 每个 journal 对象的最大在途 append 数
rbd_journal_pool str 文档未明确(rbd.yaml.in 中无 default: 字段) 存放 journal 对象的 pool
rbd_journal_max_payload_bytes size 16_K 拆分前的最大 journal payload 大小(迁移场景官方建议调为 8388608)
rbd_journal_max_concurrent_object_sets uint 0 journal client 在被自动注销之前最多可以落后的 object set 数量

10.11 镜像命令速查表

命令 用途
rbd mirror pool enable [--site-name X] {pool} {image|pool|init-only} 在 pool(或命名空间)上启用镜像
rbd mirror pool disable {pool|pool/ns} 禁用 pool / 命名空间上的镜像
rbd mirror pool peer bootstrap create [--site-name X] {pool} 创建 bootstrap token
rbd mirror pool peer bootstrap import [--site-name X] [--direction rx-only|rx-tx] {pool} {token-path} 导入对端 token
rbd mirror pool peer add {pool} {client-name}@{cluster-name} 手工添加对端集群
rbd mirror pool peer remove {pool} {peer-uuid} 移除对端集群
rbd mirror pool info {pool} --all 查看 pool 镜像模式与对端信息
rbd mirror image enable {pool}/{image} {journal|snapshot} 启用 image 镜像
rbd mirror image disable {pool}/{image} 禁用 image 镜像
rbd feature enable {pool}/{image} journaling 动态启用 journaling feature
rbd mirror image snapshot {pool}/{image} 手工创建 mirror-snapshot
rbd mirror snapshot schedule add [--pool P] [--image I] {interval} [{start-time}] 添加快照调度
rbd mirror snapshot schedule remove ... 移除快照调度(参数需与 add 匹配)
rbd mirror snapshot schedule ls [--pool P] [--image I] [--recursive] 列出快照调度
rbd mirror snapshot schedule status [--pool P] [--image I] 查看下次快照时间
rbd mirror image demote {pool}/{image} demote 单个 image
rbd mirror pool demote {pool} demote pool 内所有 primary image
rbd mirror image promote [--force] {pool}/{image} promote 单个 image
rbd mirror pool promote [--force] {pool} promote pool 内所有 non-primary image
rbd mirror image resync {pool}/{image} 标记 image 需要 resync
rbd mirror image status {pool}/{image} 查看 image 镜像状态
rbd mirror pool status {pool} [--verbose] 查看 pool 镜像汇总状态

十一、配置参考(rbd-config-ref)

引自:Config Settings

📌 说明:rbd-config-ref.rst 中的 .. confval:: 指令在源文里只有配置项名,没有默认值(默认值由构建期从 src/common/options/*.yaml.in 生成)。本章所有"默认值"均标注了来源。凡在 *.yaml.in 中也找不到 default: 字段的,一律写「文档未明确」。

RBD 的选项应设置在配置文件的 [client] 段,或 central config store 中。

11.1 Generic IO Settings

配置项 类型 默认值 含义 建议
rbd_compression_hint str none 写操作时发给 OSD 的压缩提示。设为 compressible 且 OSD bluestore_compression_mode 为 passive 时,OSD 会尝试压缩;设为 incompressible 且 OSD 压缩设置为 aggressive 时,OSD 不会尝试压缩。枚举:none / compressible / incompressible。(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4;flags: runtime) 配合 OSD 侧压缩策略使用;默认 none
rbd_read_from_replica_policy str default 决定哪个 OSD 接收读操作的策略。default:始终用每个 PG 的 primary OSD;balance:发给副本集中随机选出的 OSD;localize:发给 CRUSH map 判定的最近 OSD。与 rbd_balance_snap_reads / rbd_localize_snap_reads / rbd_balance_parent_reads / rbd_localize_parent_reads 不同,它影响所有读操作,而不仅是 snap 或 parent。要求集群 minimum compatible OSD release 为 Octopus。 枚举:default / balance / localize。(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4;flags: runtime) 读多副本分散可设 balance;跨机架/跨 AZ 降延迟可设 localize
rbd_default_order uint 22 新建 image 的默认 order(数据块 object size)。以 2 为底取幂:default_object_size = 2 ^ rbd_default_order。可配 12–25,即 4KiB–32MiB。默认 2^22 = 4MiB。(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4) 大顺序 IO 可增大到 23/24(8/16MiB);小随机 IO 保持 22

11.2 Cache Settings(逐项,详见第七章)

配置项 类型 默认值 含义 建议
rbd_cache bool true 是否启用缓存(writeback,除非 rbd_cache_max_dirty 为 0) 默认开启;跑 GFS/OCFS 必须关闭
rbd_cache_policy str writearound 处理写的缓存策略:writethrough / writeback / writearound 需读命中改为 writeback
rbd_cache_writethrough_until_flush bool true 收到 flush 前以 writethrough 方式工作 保持 true
rbd_cache_size size 32_M 每个卷的 RBD 客户端缓存大小 内存充裕可增大
rbd_cache_max_dirty size 24_M 触发 write-back 的 dirty 上限;0 = write-through 必须小于 rbd_cache_size
rbd_cache_target_dirty size 16_M 开始写回的 dirty target 必须小于 rbd_cache_max_dirty
rbd_cache_max_dirty_age float 1 脏数据在缓存中停留多少秒后开始 writeback 低延迟场景可调小
rbd_cache_max_dirty_object uint 0 对象级脏数据上限;0 = 由 rbd_cache_size 自动计算 保持 0
rbd_cache_block_writes_upfront bool false 是否在 aio_write 完成前阻塞对缓存的写 保持 false

(后两项来自 src/common/options/rbd.yaml.in @ v20.2.4,rbd-config-ref.rst 未列出;前七项为 rbd-config-ref.rst 明确列出)

11.3 Read-ahead Settings(逐项,详见第七章)

配置项 类型 默认值 含义 建议
rbd_readahead_trigger_requests uint 10 触发 readahead 所需的连续请求数 默认即可
rbd_readahead_max_bytes size 512_K 单次 read-ahead 请求的最大大小;0 = 禁用 read-ahead 顺序读场景可增大
rbd_readahead_disable_after_bytes size 50_M 累计读取多少字节后禁用该 image 的 read-ahead(关闭再打开重置);0 = 一直启用 默认 50_M(让 guest OS 启动后接管)

11.4 Image Features

配置项 类型 默认值 含义 建议
rbd_default_features str layering,exclusive-lock,object-map,fast-diff,deep-flatten 新建 v2 image 的默认 feature 集合。接受整型 bitmask或逗号分隔的 feature 名列表;内部始终以整型 bitmask 存储。映射:+1 layering、+2 striping、+4 exclusive-lock、+8 object-map、+16 fast-diff、+32 deep-flatten、+64 journaling、+128 data-pool(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4;flags: runtime) 需要默认开启 journaling 时设为 125(官方 Tip)

feature 逐项说明见第二章 2.10 全表。

11.5 Clone Settings

配置项 类型 默认值 含义 建议
rbd_default_clone_format str auto 处理克隆的内部格式。1:必须挂到受保护快照上;2:可挂到任意快照、允许删除在用父快照,但要求 Mimic+ 客户端;auto:集群要求 Mimic+ 客户端时用 v2。枚举:1 / 2 / auto(默认值取自 src/common/options/rbd.yaml.in @ v20.2.4;flags: runtime) 保持 auto
rbd_clone_copy_on_read bool false 读请求时把父 image 块 copy-up 到克隆 读密集型 VDI 可考虑开启,但会增加空间占用

11.6 QoS Settings(逐项)

librbd 支持以多种方式限制单 image 的 IO。这些限制作用于给定进程内的给定 image——同一个 image 在多个地方使用(例如两个独立 VM)会有相互独立的限制。

限制维度:

维度 说明
IOPS 每秒 I/O 数(任意类型)
read IOPS 每秒读 I/O 数
write IOPS 每秒写 I/O 数
bps 每秒字节数(任意类型)
read bps 每秒读取字节数
write bps 每秒写入字节数
  • 每种限制相互独立;全部默认关闭;
  • 所有类型的限制都使用 token bucket(令牌桶)算法限速,可配置限制值(一段时间内的平均速度)与短时间内的更高突发速率(burst_seconds);
  • 达到任一限制且突发额度用尽时,librbd 会把该类型 IO 的速率降到限制值;
  • 示例(官方原文):若配置了 100MB 的 read bps 限制而写未限流,则写可以尽可能快,而读平均被限到 100MB/s。若 read bps burst 设为 150MB 且 read burst seconds 设为 5 秒,则读可以以 150MB/s 持续最多 5 秒,然后回落到 100MB/s 的限制。
配置项 类型 默认值 含义 建议
rbd_qos_iops_limit uint 0 期望的每秒 IO 操作数上限 0 = 不限
rbd_qos_iops_burst uint 0 期望的 IO 操作突发上限 0 = 无突发
rbd_qos_iops_burst_seconds uint 1(min: 1) 期望的 IO 操作突发持续秒数 默认 1
rbd_qos_read_iops_limit uint 0 期望的每秒读操作上限 0 = 不限
rbd_qos_read_iops_burst uint 0 期望的读操作突发上限 0
rbd_qos_read_iops_burst_seconds uint 1(min: 1) 期望的读操作突发持续秒数 默认 1
rbd_qos_write_iops_limit uint 0 期望的每秒写操作上限 0 = 不限
rbd_qos_write_iops_burst uint 0 期望的写操作突发上限 0
rbd_qos_write_iops_burst_seconds uint 1(min: 1) 期望的写操作突发持续秒数 默认 1
rbd_qos_bps_limit uint 0 期望的每秒 IO 字节数上限 0 = 不限
rbd_qos_bps_burst uint 0 期望的 IO 字节突发上限 0
rbd_qos_bps_burst_seconds uint 1(min: 1) 期望的 IO 字节突发持续秒数 默认 1
rbd_qos_read_bps_limit uint 0 期望的每秒读取字节数上限 0 = 不限
rbd_qos_read_bps_burst uint 0 期望的读取字节突发上限 0
rbd_qos_read_bps_burst_seconds uint 1(min: 1) 期望的读取字节突发持续秒数 默认 1
rbd_qos_write_bps_limit uint 0 期望的每秒写入字节数上限 0 = 不限
rbd_qos_write_bps_burst uint 0 期望的写入字节突发上限 0
rbd_qos_write_bps_burst_seconds uint 1(min: 1) 期望的写入字节突发持续秒数 默认 1
rbd_qos_schedule_tick_min uint 50(min: 1) QoS 的最小调度 tick(毫秒)。决定命中限流后 IO 能被解除阻塞的最小时间间隔;用令牌桶的术语说,就是向桶中添加令牌的最小间隔 默认 50
rbd_qos_exclude_ops str 文档未明确(rbd.yaml.in 中无 default: 字段) 可选地把某些 op 排除在 QoS 之外。接受整型 bitmask 或逗号分隔的 op 名列表;内部以 bitmask 存储。映射:+1 read、+2 write、+4 discard、+8 write_same、+16 compare_and_write(flags: runtime) 例如只限读写、不限 discard 时可用

(以上默认值取自 src/common/options/rbd.yaml.in @ v20.2.4)

11.7 默认参数与 image 布局(来自 rbd.yaml.in)

配置项 类型 默认值 含义 建议
rbd_default_pool str rbd 存放新建 image 的默认 pool。校验规则:不含 @ 与 /,否则重置为 rbd 建议按业务建独立 pool
rbd_default_data_pool str 文档未明确(无 default: 字段;validator 在非法值时置为空串) 存放新建 image 数据块的默认 pool 纠删码场景配置
rbd_default_format uint 2 新建 image 的默认 image format 保持 2(format 1 不支持克隆)
rbd_default_stripe_count uint 0 新建 image 的默认 stripe count 见第二章
rbd_default_stripe_unit size 0 新建 image 的默认 stripe width 见第二章
rbd_default_map_options str 文档未明确(无 default: 字段) 默认 krbd map options 需要统一 krbd 挂载参数时配置
rbd_default_snapshot_quiesce_mode str required 默认的快照 quiesce 模式;枚举:required / ignore-error / skip 无法安装 qemu-guest-agent 的环境可考虑 skip/ignore-error
rbd_op_threads uint 1 用于内部处理的线程数 大并发场景可增大
rbd_op_thread_timeout uint 60 检测线程挂起的秒数 默认即可
rbd_concurrent_management_ops uint 10(min: 1) 删除或 resize image 等管理类操作的最大在途数量 大批量运维可增大
rbd_plugins str 文档未明确(无 default: 字段) 逗号分隔的、要启用的 librbd 插件列表 只读父缓存用 parent_cache;PWL 用 pwl_cache

11.8 读路径 / 稀疏读 / discard 相关

配置项 类型 默认值 含义 建议
rbd_balance_snap_reads bool false 把 snap 读请求分发到随机 OSD 与 rbd_read_from_replica_policy 二选一思路;后者更通用
rbd_localize_snap_reads bool false 把 snap 读请求定位到最近的 OSD 同上
rbd_balance_parent_reads bool false 把 parent 读请求分发到随机 OSD 同上
rbd_localize_parent_reads bool false 把 parent 请求定位到最近的 OSD 同上
rbd_sparse_read_threshold_bytes size 64_K 触发 sparse-read 的阈值:对某个对象连续读取达到该字节数才向集群发 sparse-read 请求。0 表示必须是整对象读才发;1 表示总是用 sparse-read;任何大于最大 object size 的值会禁用所有请求的 sparse-read 默认即可
rbd_skip_partial_discard bool true 跳过对 object 内未对齐区段的 discard(zero) 保持 true,避免小块 RMW
rbd_discard_granularity_bytes uint 64_K(min: 4_K,max: 32_M) discard 操作的最小对齐大小 必须是 2 的幂(validator 强制)
rbd_discard_on_zeroed_write_same bool true 对 zeroed write same,执行 discard 而不是写零 保持 true
rbd_enable_alloc_hint bool true 写对象时向 OSD 后端发出预期对象大小的提示 保持 true
rbd_disable_zero_copy_writes bool true 禁用零拷贝写,以确保客户端的不稳定写不会引起 CRC 不匹配 保持 true
rbd_non_blocking_aio bool true 从 dispatch 线程处理 AIO 操作以避免阻塞 保持 true

11.9 Trash 相关

配置项 类型 默认值 含义 建议
rbd_move_to_trash_on_remove bool false 删除 image 时自动移入 trash 生产建议开启,防误删
rbd_move_to_trash_on_remove_expire_seconds uint 0 已删除 image 在 trash 中的默认保护秒数 与上一项配合设置一个宽限期
rbd_move_parent_to_trash_on_remove bool false 删除时把带有 clone format v2 子 image 的 parent 也移入 trash 使用大量克隆的环境建议开启

11.10 时间戳与 IO 调度

配置项 类型 默认值 含义 建议
rbd_mtime_update_interval uint 60(min: 0) RBD image modify timestamp 刷新间隔(秒);0 = 禁用 modify timestamp 更新 默认即可
rbd_atime_update_interval uint 60(min: 0) RBD image access timestamp 刷新间隔(秒);0 = 禁用 access timestamp 更新 不需要 atime 时设 0 可省开销
rbd_io_scheduler str simple RBD IO 调度器;枚举:none / simple 默认即可
rbd_io_scheduler_simple_max_delay uint 0(min: 0) simple io scheduler 的最大 IO 延迟(毫秒);设为 0 时延迟基于延迟统计计算 默认 0

11.11 排障与调试相关

配置项 类型 默认值 含义 建议
rbd_tracing bool false 是否启用 LTTng-UST tracepoints 排障时临时开启
rbd_blkin_trace_all bool false 为所有 RBD 请求创建 blkin trace 排障时临时开启
rbd_validate_pool bool(dev 级) true 校验空 pool 是否与 RBD 兼容 保持 true
rbd_validate_names bool true 校验新 image 名是否与 RBD 兼容 保持 true
rbd_invalidate_object_map_on_timeout bool(dev 级) true 加载或更新超时时是否使 object map 失效 保持 true
rbd_auto_exclusive_lock_until_manual_request bool true 在显式请求之前自动获取/释放 exclusive lock 保持 true
rbd_blocklist_on_break_lock bool true 锁被打破时是否 blocklist 对应客户端 保持 true
rbd_blocklist_expire_seconds uint 0 blocklist 持续秒数;0 = 使用 OSD 默认值(mon_osd_blocklist_default_expire,默认 1_hr,取自 src/common/options/mon.yaml.in @ v20.2.4) 一般保持 0
rbd_request_timed_out_seconds uint 30 维护类请求超时秒数 大集群/慢网络可增大
rbd_quiesce_notification_attempts uint(dev 级) 10(min: 1) quiesce 通知的尝试次数 默认即可
rbd_config_pool_override_update_timestamp uint(dev 级) 0 pool 级配置覆盖的最近更新时间戳 内部使用

11.12 Persistent Write Log Cache 配置项(详见第七章)

配置项 类型 默认值 含义
rbd_persistent_cache_mode str disabled 持久 write-back 缓存模式;枚举:disabled / rwl / ssd
rbd_persistent_cache_size uint 1_G(min: 1_G) 每个 image 的持久 write-back 缓存大小
rbd_persistent_cache_path str /tmp 持久 write-back 缓存位置(rwl 模式需在 DAX 启用文件系统上)

11.13 索引:其他章节已列的配置项

类别 章节
exclusive lock / blocklist 第六章 6.5
immutable object cache(守护进程 + QoS) 第七章 7.2
journal(mirroring 用) 第十章 10.10
mirroring(rbd.yaml.in 部分) 第十章 10.10
rbd-mirror 守护进程(rbd-mirror.yaml.in) 第十章 10.10

十二、平台与集成

12.1 QEMU / KVM

引自:QEMU and Block Devices

最常见的 Ceph Block Device 使用场景是给虚拟机提供块设备 image:用户创建一个装有 OS 与相关软件的 "golden" image,对其打快照,然后(可能多次)克隆该快照。能够创建快照的 COW 克隆,意味着 Ceph 可以快速地为虚拟机供给块设备 image,因为客户端每次启动新虚拟机时不必下载整个 image。

            +---------------------------------------------------+
            |                       QEMU                        |
            +---------------------------------------------------+
            |                      librbd                       |
            +---------------------------------------------------+
            |                     librados                      |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

用法格式

qemu-img {command} [options] rbd:{pool-name}/{image-name}[@snapshot-name][:option1=value1][:option2=value2...]
  • QEMU 假定 Ceph 配置位于默认位置(如 /etc/ceph/$cluster.conf),且以默认 client.admin 用户执行,除非显式指定其他配置文件路径或其他用户;
  • 指定用户时,QEMU 使用 ID 而不是完整的 TYPE:ID。不要在用户 ID 前面加客户端类型(即 client.),否则会收到认证错误;
  • 应把 admin 用户的 key(或你用 :id={user} 指定的其他用户的 key)放在默认路径(/etc/ceph 或本地目录)的 keyring 文件中,并设好属主与权限。
qemu-img {command} [options] rbd:glance-pool/maipo:id=glance:conf=/etc/ceph/ceph.conf

Tip:包含 :、@ 或 = 的配置值可以用前导 \ 字符转义。

创建 image

qemu-img create -f raw rbd:{pool-name}/{image-name} {size}
qemu-img create -f raw rbd:data/foo 10G

⚠️ 官方 important:raw 数据格式实际上是配合 RBD 唯一合理的 format 选项。 技术上你可以使用其他 QEMU 支持的格式(如 qcow2 或 vmdk),但那会带来额外开销,并且在启用缓存(见下)时会使卷对虚拟机实时迁移不安全。

调整大小

qemu-img resize rbd:{pool-name}/{image-name} {size}
qemu-img resize rbd:data/foo 10G

查看信息

qemu-img info rbd:{pool-name}/{image-name}
qemu-img info rbd:data/foo

用 RBD 运行 QEMU

QEMU 可以把主机的块设备透传给 guest,但自 QEMU 0.15 起,不再需要把 image 映射为主机上的块设备——QEMU 通过 librbd 直接把 image 作为虚拟块设备挂上。这种策略通过避免上下文切换并利用 RBD caching 提升了性能。

# 把既有虚拟机镜像转换为 Ceph 块设备 image
qemu-img convert -f qcow2 -O raw debian_squeeze.qcow2 rbd:data/squeeze

# 从该 image 启动虚拟机
qemu -m 1024 -drive format=raw,file=rbd:data/squeeze

# 自 QEMU 1.2 起,QEMU 的 cache 选项控制 librbd 缓存
qemu -m 1024 -drive format=rbd,file=rbd:data/squeeze,cache=writeback

# 老版本 QEMU:把 librbd 缓存配置作为 'file' 参数的一部分传入
qemu -m 1024 -drive format=raw,file=rbd:data/squeeze:rbd_cache=true,cache=writeback

⚠️ 官方 important:如果设置了 rbd_cache=true,必须设置 cache=writeback,否则有数据丢失风险。没有 cache=writeback 时,QEMU 不会向 librbd 发送 flush 请求;在这种配置下如果 QEMU 非正常退出,rbd 之上的文件系统可能损坏。

启用 Discard / TRIM

自 Ceph 0.46 与 QEMU 1.1 起,Ceph Block Devices 支持 discard 操作:guest 可以发送 TRIM 请求让 Ceph 块设备回收未使用空间。

  • 在 guest 中通过以 discard 选项挂载 ext4 或 XFS 启用;
  • 要对块设备可用,必须显式启用:为驱动器指定 discard_granularity:
qemu -m 1024 -drive format=raw,file=rbd:data/squeeze,id=drive1,if=none \
     -device driver=ide-hd,drive=drive1,discard_granularity=512

注意:上面使用的是 IDE 驱动。virtio 驱动自 Linux kernel 5.0 起支持 discard。

使用 libvirt 时,用 virsh edit 编辑 libvirt domain 配置,加入 xmlns:qemu,然后在 domain 下加一个 qemu:commandline 块。以下示例把两个设备的 qemu id= 设为不同的 discard_granularity:

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
	<qemu:commandline>
		<qemu:arg value='-set'/>
		<qemu:arg value='block.scsi0-0-0.discard_granularity=4096'/>
		<qemu:arg value='-set'/>
		<qemu:arg value='block.scsi0-0-1.discard_granularity=65536'/>
	</qemu:commandline>
</domain>

QEMU Cache Options 对应关系

QEMU cache 选项 对应的 Ceph RBD Cache 设置
writeback rbd_cache = true
writethrough rbd_cache = true + rbd_cache_max_dirty = 0
none rbd_cache = false

QEMU 的 cache 设置会覆盖 Ceph 的 cache 设置(包括在 Ceph 配置文件中显式设置的项)。

官方 note:QEMU v2.4.0 之前,如果你在 Ceph 配置文件中显式设置了 RBD Cache 设置,Ceph 的设置会覆盖 QEMU 的 cache 设置。

12.2 libvirt

引自:Using libvirt with Ceph RBD

libvirt 库在 hypervisor 接口与使用它们的软件之间创建了一层虚拟机抽象层,让开发者与系统管理员可以专注于一套通用的管理框架、通用 API 与通用 shell 接口(virsh),支持 QEMU/KVM、XEN、LXC、VirtualBox 等。

            +---------------------------------------------------+
            |                     libvirt                       |
            +------------------------+--------------------------+
                                     | configures
                                     v
            +---------------------------------------------------+
            |                       QEMU                        |
            +---------------------------------------------------+
            |                      librbd                       |
            +---------------------------------------------------+
            |                     librados                      |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

Ceph 块设备支持 QEMU/KVM。最常见的 libvirt 用例是为 OpenStack、OpenNebula 或 CloudStack 等云方案提供 Ceph 块设备:云方案用 libvirt 与 QEMU/KVM 交互,QEMU/KVM 通过 librbd 与 Ceph 块设备交互。

示例约定:pool 名 libvirt-pool、用户名 client.libvirt、image 名 new-libvirt-image。你可以用任意值,但在执行后续命令时要替换。

配置 Ceph

# 1) 创建 pool
ceph osd pool create libvirt-pool
ceph osd lspools                    # 校验

# 2) 用 rbd 工具初始化该 pool 供 RBD 使用
rbd pool init <pool-name>

# 3) 创建 Ceph 用户(0.9.7 及更早版本可用 client.admin)
ceph auth get-or-create client.libvirt mon 'profile rbd' osd 'profile rbd pool=libvirt-pool'
ceph auth ls                        # 校验

# 4) 用 QEMU 在 RBD pool 中创建 image
qemu-img create -f rbd rbd:libvirt-pool/new-libvirt-image 2G
rbd -p libvirt-pool ls              # 校验

Note

:libvirt 访问 Ceph 时使用的是 ID libvirt,而不是 Ceph 名 client.libvirt。

NOTE:也可以用 rbd create 创建 image,但官方建议用 QEMU 创建,以便顺便确认 QEMU 工作正常。

💡 Tip(可选):为这个客户端启用 debug 日志与 admin socket,可在 /etc/ceph/ceph.conf 中加入:

[client.libvirt]
log file = /var/log/ceph/qemu-guest-$pid.log
admin socket = /var/run/ceph/$cluster-$type.$id.$pid.$cctid.asok

client.libvirt 段名应与上面创建的 cephx 用户匹配。若启用了 SELinux 或 AppArmor,可能阻止客户端进程(qemu via libvirt)执行某些操作,如写日志、操作 image 或把 admin socket 放到目标位置(/var/log/ceph 或 /var/run/ceph)。另外要确保 libvirt 与 qemu 用户对指定目录有适当访问权限。

准备 VM Manager

sudo apt-get install virt-manager   # 安装虚拟机管理器
sudo virt-manager                   # 启动

创建 VM(virt-manager)

  1. 按 Create New Virtual Machine 按钮;
  2. 命名新的虚拟机 domain(示例用 libvirt-virtual-machine);
  3. 导入 image(/path/to/image/recent-linux.img)。NOTE:导入较新的 image,某些较老的 image 可能无法正确重新扫描虚拟设备;
  4. 配置并启动 VM;
  5. 可用 virsh list 校验 VM domain 存在:sudo virsh list;
  6. 登录 VM(root/root);
  7. 在为 Ceph 配置 VM 之前先停止 VM。

配置 VM(virsh)

sudo virsh edit {vm-domain-name}

<devices> 下应有一个 <disk> 条目:

<devices>
	<emulator>/usr/bin/kvm</emulator>
	<disk type='file' device='disk'>
		<driver name='qemu' type='raw'/>
		<source file='/path/to/image/recent-linux.img'/>
		<target dev='vda' bus='virtio'/>
		<address type='drive' controller='0' bus='0' unit='0'/>
	</disk>

使用更快的 virtio 总线所需的最低内核是 2.6.25。

IMPORTANT:必须使用 sudo virsh edit,不要用文本编辑器。 如果用文本编辑器修改 /etc/libvirt/qemu 下的配置文件,libvirt 可能识别不到变更。如果 /etc/libvirt/qemu 下 XML 文件的内容与 sudo virsh dumpxml {vm-domain-name} 的结果不一致,你的 VM 可能无法正常工作。

添加 Ceph RBD image 作为 <disk>:

<disk type='network' device='disk'>
	<source protocol='rbd' name='libvirt-pool/new-libvirt-image'>
		<host name='{monitor-host}' port='6789'/>
	</source>
	<target dev='vdb' bus='virtio'/>
</disk>
  • 把 {monitor-host} 换成你的主机名,必要时替换 pool 名与 image 名;
  • 可以为你的 Ceph monitor 添加多个 <host> 条目;
  • dev 属性是 VM 内 /dev 下出现的逻辑设备名;
  • 可选的 bus 属性表示要模拟的磁盘设备类型,有效取值由驱动决定(如 ide、scsi、virtio、xen、usb、sata)。

认证配置(secret)

如果 Ceph Storage Cluster 启用了 Ceph Authentication(默认启用),必须生成一个 secret:

# 1) 定义 secret
cat > secret.xml <<EOF
<secret ephemeral='no' private='no'>
	<usage type='ceph'>
		<name>client.libvirt secret</name>
	</usage>
</secret>
EOF

sudo virsh secret-define --file secret.xml
{uuid of secret}

# 2) 取 client.libvirt 的 key 并保存到文件
ceph auth get-key client.libvirt | sudo tee client.libvirt.key

# 3) 设置 secret 的值
sudo virsh secret-set-value --secret {uuid of secret} --base64 $(cat client.libvirt.key) && rm client.libvirt.key secret.xml

还必须在之前添加的 <disk> 元素中手工加入 <auth> 条目(把 uuid 换成上一条命令的输出):

sudo virsh edit {vm-domain-name}
...
</source>
<auth username='libvirt'>
	<secret type='ceph' uuid='{uuid of secret}'/>
</auth>
<target ...

Note

:示例中的 ID 是 libvirt,不是创建用户时的 Ceph 名 client.libvirt。务必使用你生成的 Ceph 名的 ID 部分。 如果因某种原因需要重新生成 secret,必须先执行 sudo virsh secret-undefine {uuid},再执行 sudo virsh secret-set-value。

校验

ceph health                                                    # Ceph 是否健康
sudo virsh list                                                # VM 是否运行
sudo virsh qemu-monitor-command --hmp {vm-domain-name} 'info block'   # VM 是否与 Ceph 通信
virsh domblklist {vm-domain-name} --details                    # 设备是否存在

12.3 Kubernetes

引自:Block Devices and Kubernetes

可以通过 ceph-csi 在 Kubernetes v1.13 及更高版本中使用 Ceph Block Device image。ceph-csi 动态供给 RBD image 来支撑 Kubernetes volumes,并把这些 RBD image 映射为块设备(可选地挂载 image 内的文件系统)到运行引用该 RBD 支撑卷的 pods 的 worker 节点上。Ceph 把块设备 image 条带化为跨集群的对象,这意味着大的 Ceph Block Device image 性能优于独立服务器。

            +---------------------------------------------------+
            |                   Kubernetes                      |
            +---------------------------------------------------+
            |                    ceph--csi                      |
            +------------------------+--------------------------+
                                     | configures
                                     v
            +------------------------+ +------------------------+
            |                        | |        rbd--nbd        |
            |     Kernel Modules     | +------------------------+
            |                        | |         librbd         |
            +------------------------+-+------------------------+
            |                   RADOS Protocol                  |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

⚠️ 官方 important:ceph-csi 默认使用 RBD 内核模块,而它可能无法支持所有的 Ceph CRUSH tunables 或 RBD image features。

创建 Pool

$ ceph osd pool create kubernetes
$ rbd pool init kubernetes

配置 ceph-csi

1) 创建 Ceph 客户端认证用户

$ ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd pool=kubernetes'
[client.kubernetes]
    key = AQD9o0Fd6hQRChAAt7fMaSZXduT3NWEqylNpmg==

2) 生成 ceph-csi ConfigMap(集群 MON 地址)

先收集 fsid 与 monitor 地址:

$ ceph mon dump
<...>
fsid b9127830-b0cc-4e34-aa47-9d1a2e9949a8
<...>
0: [v2:192.168.1.1:3300/0,v1:192.168.1.1:6789/0] mon.a
1: [v2:192.168.1.2:3300/0,v1:192.168.1.2:6789/0] mon.b
2: [v2:192.168.1.3:3300/0,v1:192.168.1.3:6789/0] mon.c

官方 note:ceph-csi 目前仅支持 legacy V1 protocol。

---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "b9127830-b0cc-4e34-aa47-9d1a2e9949a8",
        "monitors": [
          "192.168.1.1:6789",
          "192.168.1.2:6789",
          "192.168.1.3:6789"
        ]
      }
    ]
metadata:
  name: ceph-csi-config
$ kubectl apply -f csi-config-map.yaml

3) KMS ConfigMap(较新版本 ceph-csi 需要;未部署 KMS 时放空配置)

---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {}
metadata:
  name: ceph-csi-encryption-kms-config
$ kubectl apply -f csi-kms-config-map.yaml

4) ceph.conf ConfigMap(注入 CSI 容器内的 ceph.conf)

---
apiVersion: v1
kind: ConfigMap
data:
  ceph.conf: |
    [global]
    auth_cluster_required = cephx
    auth_service_required = cephx
    auth_client_required = cephx
  # keyring is a required key and its value should be empty
  keyring: |
metadata:
  name: ceph-config
$ kubectl apply -f ceph-config-map.yaml

5) 生成 cephx Secret

---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: default
stringData:
  userID: kubernetes
  userKey: AQD9o0Fd6hQRChAAt7fMaSZXduT3NWEqylNpmg==
$ kubectl apply -f csi-rbd-secret.yaml

6) 配置 ceph-csi 插件(RBAC + provisioner + node plugin)

$ kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml
$ kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml

$ wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml
$ kubectl apply -f csi-rbdplugin-provisioner.yaml
$ wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin.yaml
$ kubectl apply -f csi-rbdplugin.yaml

⚠️ 官方 important:provisioner 与 node plugin 的 YAML 默认会拉取 ceph-csi 容器的开发版(quay.io/cephcsi/cephcsi:canary)。生产工作负载应把 YAML 更新为使用发布版容器。

使用 Ceph Block Devices

StorageClass

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: b9127830-b0cc-4e34-aa47-9d1a2e9949a8
   pool: kubernetes
   imageFeatures: layering
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
   - discard
$ kubectl apply -f csi-rbd-sc.yaml

注意:在 Kubernetes v1.14 与 v1.15 中,卷扩容特性处于 alpha 状态,需要启用 ExpandCSIVolumes feature gate。

PersistentVolumeClaim —— volumeMode 与 accessMode 支持矩阵(官方原文)

volumeMode 支持的 accessMode
Filesystem ReadWriteOnce、ReadOnlyMany
Block ReadWriteOnce、ReadWriteMany、ReadOnlyMany

raw block PVC + Pod

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: raw-block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-raw-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: ["tail -f /dev/null"]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: raw-block-pvc

filesystem PVC + Pod

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-rbd-demo-pod
spec:
  containers:
    - name: web-server
      image: nginx
      volumeMounts:
        - name: mypvc
          mountPath: /var/lib/www/html
  volumes:
    - name: mypvc
      persistentVolumeClaim:
        claimName: rbd-pvc
        readOnly: false

⚠️ 文档未明确:rbd-kubernetes.rst 没有给出 Kubernetes 侧快照(VolumeSnapshot)与克隆(dataSource 指向 PVC)的官方示例 YAML。ceph-csi 支持这些能力,但不在本册源文范围内,故不在此给出具体清单,请查阅 ceph-csi 项目文档。

12.4 OpenStack

引自:Block Devices and OpenStack

可以通过 libvirt 把 Ceph Block Device image 附加到 OpenStack 实例,libvirt 会把 QEMU 接口配置到 librbd。Ceph 把块卷条带化到集群内多个 OSD 上,这意味着大卷的性能可以优于独立服务器上的本地驱动器。

前置:必须安装 QEMU、libvirt 和 OpenStack。建议为 OpenStack 安装使用独立的物理节点;OpenStack 建议最低 8GB 内存与四核处理器。

            +---------------------------------------------------+
            |                    OpenStack                      |
            +---------------------------------------------------+
            |                     libvirt                       |
            +------------------------+--------------------------+
                                     | configures
                                     v
            +---------------------------------------------------+
            |                       QEMU                        |
            +---------------------------------------------------+
            |                      librbd                       |
            +---------------------------------------------------+
            |                     librados                      |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

OpenStack 有三个部分集成 Ceph 块设备:

组件 说明
Images OpenStack Glance 管理 VM 镜像,镜像是不可变的;OpenStack 把镜像当作二进制 blob 并按需下载
Volumes 卷是块设备。OpenStack 用卷启动 VM,或把卷附加到运行中的 VM。OpenStack 用 Cinder 服务管理卷
Guest Disks guest 磁盘是 guest 操作系统盘。默认启动 VM 时,其磁盘表现为 hypervisor 文件系统上的一个文件(通常在 /var/lib/nova/instances/<uuid>/)。Havana 之前,在 Ceph 中启动 VM 的唯一方式是使用 Cinder 的 boot-from-volume 功能;现在可以不使用 Cinder 而直接在 Ceph 中启动每台虚拟机,这便于用 live-migration 做维护,且 hypervisor 宕机时可以方便地触发 nova evacuate 在别处几乎无缝地恢复虚拟机。exclusive locks 会阻止多个 compute 节点并发访问 guest 磁盘。

⚠️ 官方 important:不推荐使用 QCOW2 承载虚拟机磁盘。 如果想在 Ceph 中启动虚拟机(ephemeral backend 或 boot from volume),请在 Glance 中使用 raw 镜像格式。

提示:Glance 与 Cinder、Nova 的配置不一定要同时使用——你可以把镜像存在 Ceph 块设备中而用本地磁盘跑 VM,反之亦然。

创建 Pool 与初始化

ceph osd pool create volumes
ceph osd pool create images
ceph osd pool create backups
ceph osd pool create vms

rbd pool init volumes
rbd pool init images
rbd pool init backups
rbd pool init vms

配置 OpenStack Ceph 客户端

运行 glance-api、cinder-volume、nova-compute 和 cinder-backup 的节点都是 Ceph 客户端,每个都需要 ceph.conf:

ssh {your-openstack-server} sudo tee /etc/ceph/ceph.conf </etc/ceph/ceph.conf

安装客户端包

# glance-api 节点:需要 librbd 的 Python 绑定
sudo apt-get install python-rbd
sudo yum install python-rbd

# nova-compute、cinder-backup、cinder-volume 节点:Python 绑定 + 命令行工具
sudo apt-get install ceph-common
sudo yum install ceph-common

设置 Ceph 客户端认证

ceph auth get-or-create client.glance mon 'profile rbd' osd 'profile rbd pool=images' mgr 'profile rbd pool=images'
ceph auth get-or-create client.cinder mon 'profile rbd' osd 'profile rbd pool=volumes, profile rbd pool=vms, profile rbd-read-only pool=images' mgr 'profile rbd pool=volumes, profile rbd pool=vms'
ceph auth get-or-create client.cinder-backup mon 'profile rbd' osd 'profile rbd pool=backups' mgr 'profile rbd pool=backups'

把 keyring 放到对应节点并改属主:

ceph auth get-or-create client.glance | ssh {your-glance-api-server} sudo tee /etc/ceph/ceph.client.glance.keyring
ssh {your-glance-api-server} sudo chown glance:glance /etc/ceph/ceph.client.glance.keyring
ceph auth get-or-create client.cinder | ssh {your-volume-server} sudo tee /etc/ceph/ceph.client.cinder.keyring
ssh {your-cinder-volume-server} sudo chown cinder:cinder /etc/ceph/ceph.client.cinder.keyring
ceph auth get-or-create client.cinder-backup | ssh {your-cinder-backup-server} sudo tee /etc/ceph/ceph.client.cinder-backup.keyring
ssh {your-cinder-backup-server} sudo chown cinder:cinder /etc/ceph/ceph.client.cinder-backup.keyring

nova-compute 节点需要 nova-compute 进程的 keyring 文件:

ceph auth get-or-create client.cinder | ssh {your-nova-compute-server} sudo tee /etc/ceph/ceph.client.cinder.keyring

它们还需要把 client.cinder 用户的 secret key 存入 libvirt——libvirt 进程在从 Cinder 附加块设备访问集群时需要它。

# 在 nova-compute 节点上创建密钥的临时副本
ceph auth get-key client.cinder | ssh {your-compute-node} tee client.cinder.key

# 在 compute 节点上:把密钥加入 libvirt 并删除临时副本
uuidgen
457eb676-33da-42ec-9a8c-9293d545c337

cat > secret.xml <<EOF
<secret ephemeral='no' private='no'>
  <uuid>457eb676-33da-42ec-9a8c-9293d545c337</uuid>
  <usage type='ceph'>
    <name>client.cinder secret</name>
  </usage>
</secret>
EOF
sudo virsh secret-define --file secret.xml
Secret 457eb676-33da-42ec-9a8c-9293d545c337 created
sudo virsh secret-set-value --secret 457eb676-33da-42ec-9a8c-9293d545c337 --base64 $(cat client.cinder.key) && rm client.cinder.key secret.xml

⚠️ 官方 important:并非所有 compute 节点都必须使用同一个 UUID,但从平台一致性角度看,最好保持相同 UUID。保存该 secret 的 uuid 以便稍后配置 nova-compute。

配置 Glance

编辑 /etc/glance/glance-api.conf,在 [glance_store] 段添加(Kilo 及之后):

[glance_store]
stores = rbd
default_store = rbd
rbd_store_pool = images
rbd_store_user = glance
rbd_store_ceph_conf = /etc/ceph/ceph.conf
rbd_store_chunk_size = 8

启用镜像的 COW 克隆(注意:这会通过 Glance API 暴露后端位置,因此启用该选项的端点不应公开访问):

# 除 Mitaka 外的任何 OpenStack 版本,在 [DEFAULT] 段添加
show_image_direct_url = True

禁用缓存管理(任何版本):为避免镜像被缓存到 /var/lib/glance/image-cache/,假定配置文件中 flavor = keystone+cachemanagement:

[paste_deploy]
flavor = keystone

推荐的镜像属性:

属性 作用
hw_scsi_model=virtio-scsi 添加 virtio-scsi 控制器,获得更好性能与 discard 操作支持
hw_disk_bus=scsi 把每个 cinder 块设备接到该控制器上
hw_qemu_guest_agent=yes 启用 QEMU guest agent
os_require_quiesce=yes 通过 QEMU guest agent 发送 fs-freeze/thaw 调用

配置 Cinder

编辑 /etc/cinder/cinder.conf:

[DEFAULT]
...
enabled_backends = ceph
glance_api_version = 2
...
[ceph]
volume_driver = cinder.volume.drivers.rbd.RBDDriver
volume_backend_name = ceph
rbd_pool = volumes
rbd_ceph_conf = /etc/ceph/ceph.conf
rbd_flatten_volume_from_snapshot = false
rbd_max_clone_depth = 5
rbd_store_chunk_size = 4
rados_connect_timeout = -1

如果使用 cephx 认证,还要配置已加入 libvirt 的用户与 secret uuid:

[ceph]
...
rbd_user = cinder
rbd_secret_uuid = 457eb676-33da-42ec-9a8c-9293d545c337

注意:如果配置多个 cinder 后端,glance_api_version = 2 必须放在 [DEFAULT] 段。

配置 Cinder Backup

在 Cinder Backup 节点编辑 /etc/cinder/cinder.conf:

backup_driver = cinder.backup.drivers.ceph
backup_ceph_conf = /etc/ceph/ceph.conf
backup_ceph_user = cinder-backup
backup_ceph_chunk_size = 134217728
backup_ceph_pool = backups
backup_ceph_stripe_unit = 0
backup_ceph_stripe_count = 0
restore_discard_excess_bytes = true

注意:OpenStack Cinder Backup 需要一个特定守护进程,别忘了安装。

配置 Nova(附加 Ceph RBD 块设备)

要附加 Cinder 设备(普通块设备或 boot from volume),必须告诉 Nova(与 libvirt)附加设备时使用哪个用户与 UUID:

[libvirt]
...
rbd_user = cinder
rbd_secret_uuid = 457eb676-33da-42ec-9a8c-9293d545c337

这两个 flag 也被 Nova ephemeral 后端使用。

配置 Nova(从 Ceph 卷直接启动 VM)

必须配置 Nova 的 ephemeral backend。建议启用 RBD cache(自 Giant 版本起默认启用)。此外,启用 client admin socket 可以采集指标,对排障非常有价值。

该 socket 可以在 hypervisor(Nova compute)节点访问:

ceph daemon /var/run/ceph/ceph-client.cinder.19195.32310016.asok help

在每个 hypervisor 的 ceph.conf 中确保包含:

[client]
    rbd cache = true
    rbd cache writethrough until flush = true
    admin socket = /var/run/ceph/guests/$cluster-$type.$id.$pid.$cctid.asok
    log file = /var/log/qemu/qemu-guest-$pid.log
    rbd concurrent management ops = 20

配置目录权限:

mkdir -p /var/run/ceph/guests/ /var/log/qemu/
chown qemu:libvirtd /var/run/ceph/guests /var/log/qemu/

注意:用户 qemu 与组 libvirtd 可能因系统而异,上述示例适用于 RedHat 系系统。 Tip:如果虚拟机已在运行,只需重启它即可启用 admin socket。

重启 OpenStack

# Debian 系
sudo glance-control api restart
sudo service nova-compute restart
sudo service cinder-volume restart
sudo service cinder-backup restart

# Red Hat 系
sudo service openstack-glance-api restart
sudo service openstack-nova-compute restart
sudo service openstack-cinder-volume restart
sudo service openstack-cinder-backup restart

从块设备启动

cinder create --image-id {id of image} --display-name {name of volume} {size of volume}

可以用 qemu-img 做格式转换:

qemu-img convert -f {source-format} -O {output-format} {source-filename} {output-filename}
qemu-img convert -f qcow2 -O raw precise-cloudimg.img precise-cloudimg.raw

当 Glance 与 Cinder 都使用 Ceph 块设备时,该镜像是一个 COW 克隆,因此新卷创建得非常快。 在 OpenStack dashboard 中:

  1. Launch a new instance;
  2. 选择与该 COW 克隆关联的镜像;
  3. 选择 'boot from volume';
  4. 选择你创建的卷。

12.5 CloudStack

引自:Block Devices and CloudStack

可以通过 libvirt 在 CloudStack 4.0 及更高版本中使用 Ceph Block Device image,libvirt 把 QEMU 接口配置到 librbd。Ceph 把块设备 image 条带化为跨集群的对象,因此大的 Ceph Block Device image 性能优于独立服务器。

前置:必须安装 QEMU、libvirt、CloudStack。建议使用独立的物理主机;CloudStack 建议最低 4GB 内存与双核处理器(更多 CPU 与内存性能更好)。

            +---------------------------------------------------+
            |                   CloudStack                      |
            +---------------------------------------------------+
            |                     libvirt                       |
            +------------------------+--------------------------+
                                     | configures
                                     v
            +---------------------------------------------------+
            |                       QEMU                        |
            +---------------------------------------------------+
            |                      librbd                       |
            +---------------------------------------------------+
            |                     librados                      |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

CloudStack 集成 Ceph 块设备,为其提供 Primary Storage 后端。

官方 note:建议安装 Ubuntu 14.04 或更新版本,以便用软件包安装而不必从源码编译 libvirt。

官方 note:Ubuntu 14.04 与 CentOS 7.2 自带的 libvirt 默认已启用 RBD storage pool 支持。

为 CloudStack 安装配置 QEMU 无需特殊处理:确保有运行中的 Ceph Storage Cluster,安装 QEMU 并配置它与 Ceph 配合;然后安装 libvirt 0.9.13 或更高版本(可能需要从源码编译)并确保它与 Ceph 一起正常运行。

创建 Pool 与用户

ceph osd pool create cloudstack
rbd pool init cloudstack

ceph auth get-or-create client.cloudstack mon 'profile rbd' osd 'profile rbd pool=cloudstack'

虽然可以用 client.admin,但官方建议创建只对 cloudstack pool 有访问权限的用户。

添加 Primary Storage(UI 步骤)

  1. 登录 CloudStack UI;
  2. 点击左侧导航栏的 Infrastructure;
  3. 在 Primary Storage 下选择 View All;
  4. 点击右上角的 Add Primary Storage 按钮;
  5. 按你的基础设施填写:
字段 说明
Scope 如 Cluster 或 Zone-Wide
Zone —
Pod —
Cluster —
Name of Primary Storage —
Protocol 选择 RBD
Provider 选择适当的 provider 类型(DefaultPrimary、SolidFire、SolidFireShared 或 CloudByte),并填写与你所选 provider 相关的信息
  1. 添加集群信息(支持 cephx):
字段 说明
RADOS Monitor Ceph monitor 的 IP 地址或 DNS 名称。注意:仅在 CloudStack v4.18.0.0 或更高版本中支持逗号分隔的多个 IP/DNS 名称列表
RADOS Pool 一个 RBD pool 的名称
RADOS User 对该 RBD pool 有足够权限的用户。注意:不要包含 client. 部分
RADOS Secret 该用户的 secret
Storage Tags 可选,自行决定
  1. 点击 OK。

创建 Disk Offering

创建一个新的 disk offering,使其匹配 rbd 标签。这样 StoragePoolAllocator 在选择合适存储池时会选中 rbd pool。如果 disk offering 不匹配 rbd 标签,StoragePoolAllocator 可能选中你创建的 pool(例如 cloudstack)。

限制

  • 直到 v4.17.1.0,CloudStack 一次只会绑定一个 monitor。不过可以使用多个 DNS A 或 AAAA 记录以 round-robin 方式把连接分散到多个 monitor。

12.6 Nomad

引自:Block Devices and Nomad

与 Kubernetes 类似,Nomad 也可以使用 Ceph Block Device,通过 ceph-csi 实现,允许动态供给 RBD image 或导入既有 RBD image。

Nomad 的每个版本都与 ceph-csi 兼容,但本页生成流程与指引所用的参考版本是 Nomad v1.1.2(撰写时可用的最新版本)。

            +-------------------------+-------------------------+
            |      Container          |          ceph--csi      |
            |                         |            node         |
            |          ^              |                 ^       |
            |          |              |                 |       |
            +----------+--------------+-------------------------+
            |          |                                |       |
            |          v                                |       |
            |                       Nomad               |       |
            |                                           |       |
            +---------------------------------------------------+
            |                       ceph--csi                   |
            |                       controller                  |
            +--------+------------------------------------------+
                     |                                  |
                     | configures       maps            |
                     +---------------+ +----------------+
                                     | | 
                                     v v
            +------------------------+ +------------------------+
            |                        | |        rbd--nbd        |
            |     Kernel Modules     | +------------------------+
            |                        | |         librbd         |
            +------------------------+-+------------------------+
            |                   RADOS Protocol                  |
            +------------------------+-+------------------------+
            |          OSDs          | |        Monitors        |
            +------------------------+ +------------------------+

官方 note:Nomad 有多种 task driver,但本例只使用 Docker 容器。

⚠️ 官方 important:ceph-csi 默认使用 RBD 内核模块,可能无法支持所有 Ceph CRUSH tunables 或 RBD image features。

创建 Pool

ceph osd pool create nomad
rbd pool init nomad

配置 ceph-csi

创建用户

$ ceph auth get-or-create client.nomad mon 'profile rbd' osd 'profile rbd pool=nomad' mgr 'profile rbd pool=nomad'
[client.nomad]
        key = AQAlh9Rgg2vrDxAARy25T7KHabs6iskSHpAEAQ==

配置 Nomad 允许容器使用特权模式(编辑 /etc/nomad.d/nomad.hcl):

plugin "docker" {
  config {
    allow_privileged = true
  }
}

加载 rbd 模块

$ lsmod | grep rbd
rbd                    94208  2
libceph               364544  1 rbd

# 若未加载:
sudo modprobe rbd

重启 Nomad

sudo systemctl restart nomad

创建 ceph-csi controller 与 plugin nodes

ceph-csi 插件需要两个组件:

  • Controller plugin:与 provider 的 API 通信;
  • Node plugin:在客户端上执行任务。

注意:需要在这些文件中设置 ceph-csi 的版本,参见 ceph-csi release 了解兼容性。

controller(示例 ceph-csi-plugin-controller.nomad)

job "ceph-csi-plugin-controller" {
  datacenters = ["dc1"]
  group "controller" {
    network {
      port "metrics" {}
    }
    task "ceph-controller" {
      template {
        data        = <<EOF
[{
    "clusterID": "b9127830-b0cc-4e34-aa47-9d1a2e9949a8",
    "monitors": [
        "192.168.1.1",
        "192.168.1.2",
        "192.168.1.3"
    ]
}]
EOF
        destination = "local/config.json"
        change_mode = "restart"
      }
      driver = "docker"
      config {
        image = "quay.io/cephcsi/cephcsi:v3.3.1"
        volumes = [
          "./local/config.json:/etc/ceph-csi-config/config.json"
        ]
        mounts = [
          {
            type     = "tmpfs"
            target   = "/tmp/csi/keys"
            readonly = false
            tmpfs_options = {
              size = 1000000 # size in bytes
            }
          }
        ]
        args = [
          "--type=rbd",
          "--controllerserver=true",
          "--drivername=rbd.csi.ceph.com",
          "--endpoint=unix://csi/csi.sock",
          "--nodeid=${node.unique.name}",
          "--instanceid=${node.unique.name}-controller",
          "--pidlimit=-1",
          "--logtostderr=true",
          "--v=5",
          "--metricsport=$${NOMAD_PORT_metrics}"
        ]
      }
      resources {
        cpu    = 500
        memory = 256
      }
      service {
        name = "ceph-csi-controller"
        port = "metrics"
        tags = [ "prometheus" ]
      }
      csi_plugin {
        id        = "ceph-csi"
        type      = "controller"
        mount_dir = "/csi"
      }
    }
  }
}

node(示例 ceph-csi-plugin-nodes.nomad)

job "ceph-csi-plugin-nodes" {
  datacenters = ["dc1"]
  type        = "system"
  group "nodes" {
    network {
      port "metrics" {}
    }
    task "ceph-node" {
      driver = "docker"
      template {
        data        = <<EOF
[{
    "clusterID": "b9127830-b0cc-4e34-aa47-9d1a2e9949a8",
    "monitors": [
        "192.168.1.1",
        "192.168.1.2",
        "192.168.1.3"
    ]
}]
EOF
        destination = "local/config.json"
        change_mode = "restart"
      }
      config {
        image = "quay.io/cephcsi/cephcsi:v3.3.1"
        volumes = [
          "./local/config.json:/etc/ceph-csi-config/config.json"
        ]
        mounts = [
          {
            type     = "tmpfs"
            target   = "/tmp/csi/keys"
            readonly = false
            tmpfs_options = {
              size = 1000000 # size in bytes
            }
          }
        ]
        args = [
          "--type=rbd",
          "--drivername=rbd.csi.ceph.com",
          "--nodeserver=true",
          "--endpoint=unix://csi/csi.sock",
          "--nodeid=${node.unique.name}",
          "--instanceid=${node.unique.name}-nodes",
          "--pidlimit=-1",
          "--logtostderr=true",
          "--v=5",
          "--metricsport=$${NOMAD_PORT_metrics}"
        ]
        privileged = true
      }
      resources {
        cpu    = 500
        memory = 256
      }
      service {
        name = "ceph-csi-nodes"
        port = "metrics"
        tags = [ "prometheus" ]
      }
      csi_plugin {
        id        = "ceph-csi"
        type      = "node"
        mount_dir = "/csi"
      }
    }
  }
}

启动与检查

nomad job run ceph-csi-plugin-controller.nomad
nomad job run ceph-csi-plugin-nodes.nomad
$ nomad plugin status ceph-csi
ID                   = ceph-csi
Provider             = rbd.csi.ceph.com
Version              = 3.3.1
Controllers Healthy  = 1
Controllers Expected = 1
Nodes Healthy        = 1
Nodes Expected       = 1

使用

创建 volume(ceph-volume.hcl)

id = "ceph-mysql"
name = "ceph-mysql"
type = "csi"
plugin_id = "ceph-csi"
capacity_max = "200G"
capacity_min = "100G"

capability {
  access_mode     = "single-node-writer"
  attachment_mode = "file-system"
}

secrets {
  userID  = "admin"
  userKey = "AQAlh9Rgg2vrDxAARy25T7KHabs6iskSHpAEAQ=="
}

parameters {
  clusterID = "b9127830-b0cc-4e34-aa47-9d1a2e9949a8"
  pool = "nomad"
  imageFeatures = "layering"
  mkfsOptions = "-t ext4"
}
nomad volume create ceph-volume.hcl

在容器中使用(mysql.nomad)

job "mysql-server" {
  datacenters = ["dc1"]
  type        = "service"
  group "mysql-server" {
    count = 1
    volume "ceph-mysql" {
      type      = "csi"
      attachment_mode = "file-system"
      access_mode     = "single-node-writer"
      read_only = false
      source    = "ceph-mysql"
    }
    network {
      port "db" {
        static = 3306
      }
    }
    restart {
      attempts = 10
      interval = "5m"
      delay    = "25s"
      mode     = "delay"
    }
    task "mysql-server" {
      driver = "docker"
      volume_mount {
        volume      = "ceph-mysql"
        destination = "/srv"
        read_only   = false
      }
      env {
        MYSQL_ROOT_PASSWORD = "password"
      }
      config {
        image = "hashicorp/mysql-portworx-demo:latest"
        args  = ["--datadir", "/srv/mysql"]
        ports = ["db"]
      }
      resources {
        cpu    = 500
        memory = 1024
      }
      service {
        name = "mysql-server"
        port = "db"
        check {
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}
nomad job run mysql.nomad
nomad job status mysql-server

验证持久化:修改数据库 → purge 该 job → 用同一文件重新创建。将会使用(复用)同一个 RBD image。

12.7 Windows

引自:RBD on Windows

rbd 命令在 Windows 上可以像在 Linux 上一样创建、删除、导入、导出、map、unmap image。原生使用 Ceph 的应用还可以使用 librbd.dll。

Windows service

在 Windows 上,rbd-wnbd 守护进程由一个集中式服务管理,从而把守护进程与其发起的 Windows session 解耦;同时该服务负责在主机启动时重建持久映射。每个主机只能运行一个这样的服务。

  • 默认情况下所有 image 映射都是持久的;可以通过 rbd 的 -onon-persistent 标志请求非持久映射;
  • 持久映射在服务启动时被重建,除非被显式 unmap。服务停止时会断开这些映射。这也允许调整 Windows 服务的启动顺序,使 RBD image 能在依赖它的服务(如 VMMS)之前被映射;
  • 为了能够重连 image,rbd-wnbd 把映射信息存放在注册表:SYSTEM\CurrentControlSet\Services\rbd-wnbd。
New-Service -Name "ceph-rbd" `
            -Description "Ceph RBD Mapping Service" `
            -BinaryPathName "c:\ceph\rbd-wnbd.exe service" `
            -StartupType Automatic

Ceph MSI 安装程序会负责创建 ceph-rbd Windows 服务。

与 Linux 的差异(官方原文)

项 差异
设备路径 不能请求设备路径。磁盘号与路径由 Windows 选择。如果用户在映射时提供了设备路径,它会被用作标识符,也可在 unmap 时使用
show 命令 新增,描述某个具体映射,可用于获取磁盘路径
service 命令 新增,允许 rbd-wnbd 作为 Windows 服务运行
list 命令 额外包含 status 列

RBD image 可以暴露给 OS 并承载 Windows 分区,也可以像 iSCSI 磁盘一样附加到 Hyper-V VM。自 OpenStack Wallaby 起,Nova Hyper-V 驱动可以把 RBD Cinder 卷附加到 Hyper-V VM。

示例:导入 image 并用它启动 Hyper-V VM

# 可以换用其他镜像;cirros 便于测试:很小(~15MB)且登录提示会打印预置密码
wget http://download.cirros-cloud.net/0.5.1/cirros-0.5.1-x86_64-disk.img `
     -OutFile cirros-0.5.1-x86_64-disk.img

# 必须确保导入的镜像是 raw(不能是 qcow2 或 vhdx)
# qemu-img 可从 https://cloudbase.it/qemu-img-windows/ 获取
qemu-img convert -O raw cirros-0.5.1-x86_64-disk.img cirros-0.5.1-x86_64-disk.raw

rbd import cirros-0.5.1-x86_64-disk.raw
# 给它一个 100MB 的尺寸
rbd resize cirros-0.5.1-x86_64-disk.raw --size=100MB

rbd device map cirros-0.5.1-x86_64-disk.raw

# 查看映射
rbd device list
Get-Disk

$mappingJson = rbd-wnbd show cirros-0.5.1-x86_64-disk.raw --format=json
$mappingJson = $mappingJson | ConvertFrom-Json

$diskNumber = $mappingJson.disk_number

New-VM -VMName BootFromRBD -MemoryStartupBytes 512MB
# 磁盘必须先 offline 才能传给 Hyper-V VM
Set-Disk -Number $diskNumber -IsOffline $true
Add-VMHardDiskDrive -VMName BootFromRBD -DiskNumber $diskNumber
Start-VM -VMName BootFromRBD

示例:创建空 image 并在主机上初始化分区

rbd create blank_image --size=1G
rbd device map blank_image -onon-persistent

$mappingJson = rbd-wnbd show blank_image --format=json
$mappingJson = $mappingJson | ConvertFrom-Json

$diskNumber = $mappingJson.disk_number

# 磁盘必须先 online 才能创建或访问分区
Set-Disk -Number $diskNumber -IsOffline $false

# 初始化磁盘、分区并创建文件系统
Get-Disk -Number $diskNumber | `
    Initialize-Disk -PassThru | `
    New-Partition -AssignDriveLetter -UseMaximumSize | `
    Format-Volume -Force -Confirm:$false

# 显示分区盘符(如 "D:" 或 "F:")
(Get-Partition -DiskNumber $diskNumber).DriveLetter

SAN policy

Windows SAN policy 决定哪些磁盘会被自动挂载。默认策略 offlineShared 规定:

所有新发现的、不在共享总线(如 SCSI 和 iSCSI)上的磁盘会被 online 并设为可读写。保持 offline 的磁盘默认为只读。

注意:较新的 WNBD 驱动版本把 rbd-wnbd 磁盘报告为 SAS,SAS 也被视为共享总线,因此这些磁盘默认为 offline 且只读。

把磁盘 online(挂载其分区)并清除只读标志:

Set-Disk -Number $diskNumber -IsOffline $false
Set-Disk -Number $diskNumber -IsReadOnly $false

限制

CSV 支持:目前 Microsoft Failover Cluster 不能把 WNBD 磁盘用作 Cluster Shared Volumes (CSVs) 的底层存储。主要原因是 WNBD 与 rbd-wnbd 尚不支持 SCSI Persistent Reservations 特性。

Hyper-V 磁盘寻址 ⚠️:

官方 warning:Hyper-V 用编号而不是 SCSI ID 来标识直通 VM 磁盘,而磁盘编号在主机重启后可能变化。这意味着主机重启后 VM 可能使用错误的磁盘,这是一个重要的安全关切。该问题同样影响 iSCSI 与 Fibre Channel 磁盘。

规避方式:

  1. 用 NTFS/ReFS 分区来存放 VHDX 镜像文件,而不是直接附加 RBD image(可能轻微影响 IO 性能);
  2. 用 Hyper-V 的 AutomaticStartAction 设置阻止 VM 用错误磁盘启动,并用脚本在重新上电前更新 VM 磁盘附加关系;可用 Msvm_StorageAllocationSettingData WMI 类的 ElementName 字段给 VM 磁盘附加关系打标签;
  3. 使用 OpenStack Hyper-V 驱动,它会在重新上电前自动刷新 VM 磁盘附加关系。

自动挂载磁盘:标记为 "online" 或 "writable" 的磁盘在重连后(如主机重启、Ceph 服务重启)会保持该状态。不幸的是,Windows 基于磁盘编号恢复磁盘状态,忽略磁盘唯一标识符,而磁盘编号在重连后可能变化。假设 SAN policy 为 offlineShared,附加了三个 RBD image 且 disk 1 被设为 online;重启后 disk 1 会 online,但它可能已对应到另一个 RBD image。如果被挂载的磁盘本应给 VM 用,这会是个问题。(该问题同样影响 iSCSI 与 FC 磁盘。)


十三、iSCSI

13.1 Overview

引自:Ceph iSCSI Gateway

iSCSI Gateway 提供一个高可用(HA)的 iSCSI target,把 RADOS Block Device (RBD) image 导出为 SCSI 磁盘。iSCSI 协议允许客户端(initiators)通过 TCP/IP 网络向存储设备(targets)发送 SCSI 命令,从而让没有原生 Ceph 客户端支持的客户端也能访问 Ceph 块存储。

每个 iSCSI gateway 利用 **Linux IO target 内核子系统(LIO)**提供 iSCSI 协议支持。LIO 使用 **userspace passthrough(TCMU)**与 Ceph 的 librbd 库交互,把 RBD image 暴露给 iSCSI 客户端。借助 Ceph 的 iSCSI gateway,你可以构建一套完全集成的块存储基础设施,具备传统 SAN 的全部特性与优势。

                  Cluster Network (optional)
                 +-------------------------------------------+
                 |             |               |             |
             +-------+     +-------+       +-------+     +-------+
             |       |     |       |       |       |     |       |
             | OSD 1 |     | OSD 2 |       | OSD 3 |     | OSD N |
             |    {s}|     |    {s}|       |    {s}|     |    {s}|
             +-------+     +-------+       +-------+     +-------+
                 |             |               |             |
      +--------->|             |  +---------+  |             |<---------+
      :          |             |  |   RBD   |  |             |          :
      |          +----------------|  Image  |----------------+          |
      |           Public Network  |    {d}  |                           |
      |                           +---------+                           |
      |                                                                 |
      |                      +-------------------+                      |
      |   +--------------+   | iSCSI Initiators  |   +--------------+   |
      |   | iSCSI GW     |   |   +-----------+   |   | iSCSI GW     |   |
      +-->|  RBD Module  |<--+   | Various   |   +-->|  RBD Module  |<--+
          |              |   |   | Operating |   |   |              |
          +--------------+   |   | Systems   |   |   +--------------+
                             |   +-----------+   |
                             +-------------------+

⚠️ 官方 warning(重要):iSCSI gateway 自 2022 年 11 月起进入 maintenance(仅维护)状态,不再处于活跃开发中,也不会更新以添加新特性。

13.2 Requirements

引自:iSCSI Gateway Requirements

  • 建议配置 2 到 4 个 iSCSI gateway 节点,以构成高可用的 Ceph iSCSI gateway 方案;
  • 硬件建议参见官方 hardware-recommendations;
  • 官方 note:在 iSCSI gateway 节点上,内存占用是所映射 RBD image 数量的函数,可能增长到很大。请根据要映射的 RBD image 数量规划内存需求。

Ceph Monitor 与 OSD 没有特定的 iSCSI gateway 选项,但降低检测 down OSD 的默认心跳间隔很重要,以减少 initiator 超时的可能性。建议配置:

[osd]
osd heartbeat grace = 20
osd heartbeat interval = 5

从 Ceph Monitor 节点更新运行态:

ceph tell <daemon_type>.<id> config set <parameter_name> <new_value>

ceph tell osd.* config set osd_heartbeat_grace 20
ceph tell osd.* config set osd_heartbeat_interval 5

在每个 OSD 节点上更新运行态:

ceph daemon <daemon_type>.<id> config set osd_client_watch_timeout 15

ceph daemon osd.0 config set osd_heartbeat_grace 20
ceph daemon osd.0 config set osd_heartbeat_interval 5

请务必把这些设置持久化到 /etc/ceph.conf,或在 Mimic 及以后版本中存入 centralized config store。

(参考:默认值取自 src/common/options/global.yaml.in @ v20.2.4:osd_heartbeat_grace = 20;osd_heartbeat_interval = 6。即 iSCSI 场景建议把 osd_heartbeat_interval 从默认 6 降到 5。)

13.3 iSCSI Targets(部署前置条件)

引自:iSCSI Targets

传统上,对 Ceph 存储集群的块级访问局限于 QEMU 与 librbd——这是在 OpenStack 环境中被采用的关键推动因素。自 Ceph Luminous 版本起,块级访问扩展到提供标准的 iSCSI 支持,从而支持更广泛的平台使用,并可能开创新的用例。

前置条件:

项 要求
操作系统/内核 Red Hat Enterprise Linux/CentOS 7.5(或更新);Linux kernel v4.16(或更新)
Ceph 集群 一个可用的 Ceph Storage 集群,用 ceph-ansible 或命令行部署
Gateway 节点 iSCSI gateway 节点,可与 OSD 节点共存(colocated)或使用专用节点
网络 iSCSI 前端流量与 Ceph 后端流量使用独立的网络子网

部署方式二选一:Ansible 或 命令行界面(CLI)。

13.4 使用命令行(iscsi-target-cli)

引自:Configuring the iSCSI Target using the Command Line Interface

Ceph iSCSI gateway 既是 iSCSI target 也是 Ceph 客户端——可以把它看作 Ceph 的 RBD 接口与 iSCSI 标准之间的"翻译器"。它可以运行在独立节点上,也可以与其他守护进程(例如在 OSD 节点上)共存;共存时确保有足够的 CPU 与内存可供共享。

Requirements:

  • 运行中的 Ceph Luminous 或更新存储集群;
  • RHEL/CentOS 7.5(或更新);Linux kernel v4.16(或更新);
  • 必须从 Linux 发行版软件仓库安装以下包:
包 最低版本
targetcli 2.1.fb47 或更新
python-rtslib 2.1.fb68 或更新
tcmu-runner 1.4.0 或更新
ceph-iscsi 3.2 或更新

⚠️ 官方 important:如果已存在这些包的旧版本,必须先移除再安装新版本。

进入 Installing 之前,在 iSCSI gateway 节点上完成:

  1. 如果 iSCSI gateway 不与 OSD 节点共存,把存储集群中某个运行中的 Ceph 节点上 /etc/ceph/ 下的 Ceph 配置文件拷贝到 iSCSI Gateway 节点。这些配置文件必须位于 iSCSI gateway 节点的 /etc/ceph/ 下。

  2. 安装并配置 Ceph Command-line Interface;

  3. 如需要,在防火墙上开放 TCP 3260 和 5000 端口。

    官方 note:5000 端口的访问应限制在受信任的内部网络,或只允许运行 gwcli 或 ceph-mgr 守护进程的个别主机访问。

  4. 创建一个新的或使用一个既有的 RBD。

Installing(rpm 系):

yum install ceph-iscsi
yum install tcmu-runner

若使用上游 ceph-iscsi 包,请按照 manual install instructions(见 13.5)执行。

Setup:

  1. gwcli 需要一个名为 rbd 的 pool 来存放 iSCSI 配置等元数据:
ceph osd lspools
  1. 在 /etc/ceph/ 下创建 iscsi-gateway.cfg:
touch /etc/ceph/iscsi-gateway.cfg
[config]
# Name of the Ceph storage cluster. A suitable Ceph configuration file allowing
# access to the Ceph storage cluster from the gateway node is required, if not
# colocated on an OSD node.
cluster_name = ceph

# Place a copy of the ceph cluster's admin keyring in the gateway's /etc/ceph
# directory and reference the filename here
gateway_keyring = ceph.client.admin.keyring


# API settings.
# The API supports a number of options that allow you to tailor it to your
# local environment. If you want to run the API under https, you will need to
# create cert/key files that are compatible for each iSCSI gateway node, that is
# not locked to a specific node. SSL cert and key files *must* be called
# 'iscsi-gateway.crt' and 'iscsi-gateway.key' and placed in the '/etc/ceph/' directory
# on *each* gateway node. With the SSL files in place, you can use 'api_secure = true'
# to switch to https mode.

# To support the API, the bare minimum settings are:
api_secure = false

# Additional API configuration options are as follows, defaults shown.
# api_user = admin
# api_password = admin
# api_port = 5001
# trusted_ip_list = 192.168.0.10,192.168.0.11

官方 note:trusted_ip_list 是每个 iSCSI gateway 上用于管理操作(如 target 创建、LUN 导出等)的 IP 地址列表。该 IP 可以与用于 iSCSI 数据(如到 RBD image 的 READ/WRITE 命令)的 IP 相同,但建议使用不同的 IP。

⚠️ 官方 important:iscsi-gateway.cfg 文件在所有 iSCSI gateway 节点上必须完全相同。

  1. 把 iscsi-gateway.cfg 拷贝到所有 iSCSI gateway 节点;
  2. 在所有 iSCSI gateway 节点上启用并启动 API 服务:
systemctl daemon-reload

systemctl enable rbd-target-gw
systemctl start rbd-target-gw

systemctl enable rbd-target-api
systemctl start rbd-target-api

Configuring(gwcli):

gwcli 会创建并配置 iSCSI target 与 RBD image,并把配置复制到上一节设置好的各个 gateway。较低层工具(包括 targetcli 与 rbd)可用于查询本地配置,但不应用于修改它。

> /> cd /iscsi-targets
> /iscsi-targets>  create iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw
> /iscsi-targets> cd iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw/gateways
> /iscsi-target...-igw/gateways>  create ceph-gw-1 10.172.19.21
> /iscsi-target...-igw/gateways>  create ceph-gw-2 10.172.19.22

这里使用的 IP 是将用于 iSCSI 数据(READ/WRITE 命令)的 IP。它们可以与 trusted_ip_list 中列出的管理操作 IP 相同,但建议使用不同 IP。

如果不是使用 RHEL/CentOS,或使用上游/ceph-iscsi-test 内核,必须使用 skipchecks=true 参数,以跳过 Red Hat 内核与 rpm 检查:

> /iscsi-targets> cd iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw/gateways
> /iscsi-target...-igw/gateways>  create ceph-gw-1 10.172.19.21 skipchecks=true
> /iscsi-target...-igw/gateways>  create ceph-gw-2 10.172.19.22 skipchecks=true
# 在 rbd pool 中添加名为 disk_1 的 RBD image
> /iscsi-target...-igw/gateways> cd /disks
> /disks> create pool=rbd image=disk_1 size=90G

# 创建 initiator 名为 iqn.1994-05.com.redhat:rh7-client 的客户端
> /disks> cd /iscsi-targets/iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw/hosts
> /iscsi-target...eph-igw/hosts>  create iqn.1994-05.com.redhat:rh7-client

# 设置 target 认证 initiator 时使用的 CHAP 用户名与密码
> /iscsi-target...at:rh7-client>  auth username=myusername password=mypassword

⚠️ 官方 warning:必须始终配置 CHAP。 没有 CHAP,target 会拒绝任何登录请求。

要使用双向(mutual)认证,还要设置 initiator 认证 target 时使用的 target CHAP 用户名与密码:

> /iscsi-target...at:rh7-client>  auth username=myusername password=mypassword mutual_username=mytgtusername mutual_password=mytgtpassword
CHAP 项 规则
用户名长度 8 到 64 个字符。合法字符:0–9、a–z、A–Z、@、_、-、.、:
密码长度 12 到 16 个字符。合法字符:0–9、a–z、A–Z、@、_、-、/
双向 CHAP initiator 与 target 的用户名和密码不得相同
# 把磁盘加到客户端
> /iscsi-target...at:rh7-client> disk add rbd/disk_1

13.5 手工安装 ceph-iscsi

引自:Manual ceph-iscsi Installation

共 4 步:

  1. 从 Linux 发行版软件仓库安装通用包;
  2. 安装 Git 以便直接从 Git 仓库获取其余包;
  3. 确保使用兼容的内核;
  4. 安装 ceph-iscsi 的所有组件并启动相关守护进程:tcmu-runner、rtslib-fb、configshell-fb、targetcli-fb、ceph-iscsi。

1) 通用包(每台将成为 iSCSI gateway 的机器):

libnl3、libkmod、librbd1、pyparsing、python kmod、python pyudev、python gobject、python urwid、python pyparsing、python rados、python rbd、python netifaces、python crypto、python requests、python flask、pyOpenSSL

2) 安装 Git

sudo yum install git        # CentOS/RHEL
sudo apt install git        # Debian/Ubuntu

3) 确保使用兼容内核

  • 任何内核 v4.16 或更新的 Linux 发行版;或
  • RHEL 或 CentOS 7.5 或更新(这些发行版中 ceph-iscsi 支持被 backport)。

如果内核不兼容,需查阅发行版文档了解如何构建该内核。唯一的 Ceph iSCSI 特定要求是必须启用以下构建选项:

CONFIG_TARGET_CORE=m
CONFIG_TCM_USER2=m
CONFIG_ISCSI_TARGET=m

4) 安装组件

# tcmu-runner
git clone https://github.com/open-iscsi/tcmu-runner
cd tcmu-runner
./extra/install_dep.sh
cmake -Dwith-glfs=false -Dwith-qcow=false -DSUPPORT_SYSTEMD=ON -DCMAKE_INSTALL_PREFIX=/usr
make install
systemctl daemon-reload
systemctl enable tcmu-runner
systemctl start tcmu-runner

# rtslib-fb
git clone https://github.com/open-iscsi/rtslib-fb.git
cd rtslib-fb
python setup.py install

# configshell-fb
git clone https://github.com/open-iscsi/configshell-fb.git
cd configshell-fb
python setup.py install

# targetcli-fb
git clone https://github.com/open-iscsi/targetcli-fb.git
cd targetcli-fb
python setup.py install
mkdir /etc/target
mkdir /var/target

# ceph-iscsi
git clone https://github.com/ceph/ceph-iscsi.git
cd ceph-iscsi
python setup.py install --install-scripts=/usr/bin
cp usr/lib/systemd/system/rbd-target-gw.service /lib/systemd/system
cp usr/lib/systemd/system/rbd-target-api.service /lib/systemd/system
systemctl daemon-reload
systemctl enable rbd-target-gw
systemctl start rbd-target-gw
systemctl enable rbd-target-api
systemctl start rbd-target-api

⚠️ 官方 warning:ceph-iscsi 工具假定它们管理着系统上的所有 target。 如果已有 target 由 targetcli 设置并管理,必须禁用 target 服务。

13.6 使用 Ansible 部署

引自:Configuring the iSCSI Target using Ansible

Requirements:

  • 运行中的 Ceph Luminous (12.2.x) 或更新集群;
  • RHEL/CentOS 7.5(或更新);Linux kernel v4.16(或更新);
  • 所有 iSCSI gateway 节点上已安装 ceph-iscsi 包。

Installation:

# 在 Ansible installer 节点(可以是管理节点或专用部署节点)
yum install ceph-ansible

在 /etc/ansible/hosts 中添加 gateway 组条目:

[iscsigws]
ceph-igw-1
ceph-igw-2

官方 note:如果 iSCSI gateway 与 OSD 节点共存,则把该 OSD 节点也加到 [iscsigws] 段。

Configuration(Ansible 变量逐项):

ceph-ansible 会在 /usr/share/ceph-ansible/group_vars/ 下放一个 iscsigws.yml.sample,复制一份命名为 iscsigws.yml 后按需修改。

变量 含义/用途 默认值
seed_monitor 每个 gateway 都需要访问 ceph 集群以执行 rados 和 rbd 调用,这意味着 iSCSI gateway 必须定义了适当的 /etc/ceph/ 目录。seed_monitor 主机用于填充 iSCSI gateway 的 /etc/ceph/ 目录 文档未明确
cluster_name 定义自定义的存储集群名 文档未明确
gateway_keyring 定义自定义 keyring 名 文档未明确
deploy_settings 若设为 true,则在运行 playbook 时部署这些设置 文档未明确
perform_system_checks 布尔值,检查每个 gateway 上的 multipath 与 lvm 配置设置。首次运行时必须设为 true,以确保 multipathd 与 lvm 被正确配置 文档未明确
api_user API 的用户名 admin
api_password 使用 API 的密码 admin
api_port 使用 API 的 TCP 端口号 5000
api_secure 若必须使用 TLS 则为 true。若为 true,用户必须创建所需的证书与密钥文件(详见 gwcli man 文件) false
trusted_ip_list 有权访问 API 的 IPv4 或 IPv6 地址列表。默认只有 iSCSI gateway 节点有访问权限 文档未明确(默认仅限 gateway 节点)

完整的 advanced variables 列表参见 iscsigws.yml.sample。

Deployment:

cd /usr/share/ceph-ansible
ansible-playbook site.yml --limit iscsigws

官方 note:Ansible playbook 会处理 RPM 依赖、设置守护进程并安装 gwcli,以便用它创建 iSCSI target 并把 RBD image 导出为 LUN。在过往版本中,iscsigws.yml 可以定义 iSCSI target 以及 clients、images、LUNs 等对象,但这已不再被支持。

gwcli ls

⚠️ 官方 important:试图用 targetcli 工具修改配置会导致问题,包括 ALUA 配置错误与路径故障切换问题。有可能损坏数据、导致各 iSCSI gateway 间配置不一致、WWN 信息不一致,进而引发客户端 multipath 问题。

Service Management:

ceph-iscsi 包会安装配置管理逻辑与一个名为 rbd-target-api 的 Systemd 服务。该服务设为开机启动时,rbd-target-api 会在启动时恢复 Linux IO 状态。Ansible playbook 在部署期间会禁用 target 服务。

systemctl <start|stop|restart|reload> rbd-target-api
操作 效果
reload 强制 rbd-target-api 重新读取配置并应用到当前运行环境。通常不需要,因为变更由 Ansible 并行部署到所有 iSCSI gateway 节点
stop 关闭 gateway 的 portal 接口,断开到客户端的连接,并从内核中清除当前 LIO 配置,使 iSCSI gateway 回到干净状态。客户端断开时,活动 IO 会由客户端侧 multipathing 层重新调度到其他 iSCSI gateway

Removing the Configuration:

ceph-ansible 提供了一个用于移除 iSCSI gateway 配置及相关 RBD image 的 playbook:/usr/share/ceph-ansible/purge_gateways.yml。运行该 playbook 时会提示选择清除类型:

模式 效果
lio 清除所有已定义 iSCSI gateway 上的 LIO 配置。已创建的磁盘在 Ceph 存储集群中保持不变。
all 移除 LIO 配置,同时删除 iSCSI gateway 环境中定义的所有 RBD image;其他无关的 RBD image 不会被删除。该操作会删除数据,务必选对模式。
[root@rh7-iscsi-client ceph-ansible]# ansible-playbook purge_gateways.yml
Which configuration elements should be purged? (all, lio or abort) [abort]: all

⚠️ 官方 warning:purge 操作是对 iSCSI gateway 环境的破坏性操作。

⚠️ 官方 warning:如果通过 Ceph iSCSI gateway 导出的 RBD image 有快照或克隆,purge 操作会失败。

13.7 Initiators

引自:Configuring the iSCSI Initiators

⚠️ 官方 warning:当一个 RBD image 通过多于一个 iSCSI gateway 导出时,使用 SCSI persistent group reservations (PGR) 与 SCSI 2 based reservations 的应用不受支持。

13.7.1 Linux

引自:iSCSI Initiator for Linux

Prerequisite: iscsi-initiator-utils、device-mapper-multipath

yum install iscsi-initiator-utils
yum install device-mapper-multipath

配置 multipath:

mpathconf --enable --with_multipathd y

在 /etc/multipath.conf 中添加:

devices {
        device {
                vendor                 "LIO-ORG"
                product                "TCMU device"
                hardware_handler       "1 alua"
                path_grouping_policy   "failover"
                path_selector          "queue-length 0"
                failback               60
                path_checker           tur
                prio                   alua
                prio_args              exclusive_pref_bit
                fast_io_fail_tmo       25
                no_path_retry          queue
        }
}
systemctl reload multipathd

iSCSI Discovery and Setup: 在 /etc/iscsi/iscsid.conf 中启用 CHAP 认证:

node.session.auth.authmethod = CHAP
node.session.auth.username = myusername
node.session.auth.password = mypassword

(双向认证再加:)

node.session.auth.username_in = mytgtusername
node.session.auth.password_in = mytgtpassword
iscsiadm -m discovery -t st -p 192.168.56.101
192.168.56.101:3260,1 iqn.2003-01.org.linux-iscsi.rheln1
192.168.56.102:3260,2 iqn.2003-01.org.linux-iscsi.rheln1
iscsiadm -m node -T iqn.2003-01.org.linux-iscsi.rheln1 -l     # 登录

Multipath IO Setup:

multipath -ll
mpathbt (360014059ca317516a69465c883a29603) dm-1 LIO-ORG ,IBLOCK
size=1.0G features='0' hwhandler='1 alua' wp=rw
|-+- policy='queue-length 0' prio=50 status=active
| `- 28:0:0:1 sde  8:64  active ready running
`-+- policy='queue-length 0' prio=10 status=enabled
  `- 29:0:0:1 sdc  8:32  active ready running

注意每条路径都被放到了各自的 priority group 中(failover 配置)。现在就可以像使用普通 multipath iSCSI 磁盘一样使用该 RBD image 了。

iscsiadm -m node -T iqn.2003-01.org.linux-iscsi.rheln1 -u     # 登出

13.7.2 Microsoft Windows

引自:iSCSI Initiator for Microsoft Windows

Prerequisite: Microsoft Windows Server 2016 或更新

iSCSI Initiator、Discovery 与 Setup:

  1. 安装 iSCSI initiator 驱动与 MPIO 工具;
  2. 启动 MPIO 程序,点击 "Discover Multi-Paths" 标签页,勾选 "Add support for iSCSI devices",点击 "Add"。这需要重启;
  3. 在 iSCSI Initiator Properties 窗口的 "Discovery" 标签页,添加一个 target portal:输入 Ceph iSCSI gateway 的 IP 地址或 DNS 名称与端口;
  4. 在 "Targets" 标签页,选择 target 并点击 "Connect";
  5. 在 "Connect To Target" 窗口,选择 "Enable multi-path" 选项,然后点击 "Advanced" 按钮;
  6. 在 "Connect using" 部分,选择一个 "Target portal IP"。选择 "Enable CHAP login on" 并输入 Ceph iSCSI Ansible client credentials 段中的 "Name" 与 "Target secret" 值,点击 OK;
  7. 对设置 iSCSI gateway 时定义的每个 target portal 重复步骤 5 和 6。

Multipath IO Setup:

Set-MPIOSetting -NewPDORemovePeriod 120
mpclaim.exe -l -m 1
mpclaim -s -m
MSDSM-wide Load Balance Policy: Fail Over Only
  1. 用 iSCSI Initiator 工具,从 "Targets" 标签页点击 "Devices..." 按钮;
  2. 在 Devices 窗口中选择一个磁盘并点击 "MPIO..." 按钮;
  3. 在 "Device Details" 窗口会显示到每个 target portal 的路径。如果使用 ceph-ansible 设置方式,iSCSI gateway 会用 ALUA 告知 iSCSI initiator 哪个路径与哪个 iSCSI gateway 应作为主路径。必须选择 Load Balancing Policy "Fail Over Only";
mpclaim -s -d $MPIO_DISK_ID

官方 note:使用 ceph-ansible 设置方式时,会有一条 Active/Optimized 路径(通往拥有该 LUN 的 iSCSI gateway 节点),以及每个其他 iSCSI gateway 节点各一条 Active/Unoptimized 路径。

官方 note:建议把 PDORemovePeriod 提高到 120 秒(PowerShell)。该值可能需要根据应用调整。当所有路径都 down 且 120 秒到期后,操作系统会开始让 IO 请求失败。

Tuning(注册表):

项 路径 值
Windows Disk Timeout HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Disk TimeOutValue = 65
Microsoft iSCSI Initiator Driver HKEY_LOCAL_MACHINE\\SYSTEM\CurrentControlSet\Control\Class\{4D36E97B-E325-11CE-BFC1-08002BE10318}\<Instance_Number>\Parameters LinkDownTime = 25、SRBTimeoutDelta = 15

13.7.3 VMware ESX

引自:iSCSI Initiator for VMware ESX

Prerequisite: VMware ESX 6.5 或更新,使用 Virtual Machine compatibility 6.5 与 VMFS 6。

以下步骤使用默认的 vSphere web client 与 esxcli。

  1. 启用 Software iSCSI:从 "Navigator" 点击 "Storage",选择 "Adapters" 标签页,右键 "Configure iSCSI";
  2. 设置 Initiator Name:如果 "Name & alias" 段中的 initiator 名称与 gwcli 设置期间创建客户端时使用的名称(或 ansible client_connections 变量中使用的 initiator 名)不同,则 ssh 到 ESX 主机并执行以下 esxcli 命令修改:
> esxcli iscsi adapter list
> Adapter  Driver     State   UID            Description
> -------  ---------  ------  -------------  ----------------------
> vmhba64  iscsi_vmk  online  iscsi.vmhba64  iSCSI Software Adapter
> esxcli iscsi adapter set -A vmhba64 -n iqn.1994-05.com.redhat:rh7-client
  1. 设置 CHAP:展开 CHAP authentication 段,选择 "Do not use CHAP unless required by target",并输入 gwcli auth 命令或 ansible client_connections credentials 变量中使用的 CHAP 凭据。 Mutual CHAP authentication 段应选择 "Do not use CHAP"。

    web client 有一个 bug:请求的 CHAP 设置有时不会立即生效。在 iSCSI gateway 内核日志中会看到:

    > kernel: CHAP user or password not set for Initiator ACL
    > kernel: Security negotiation failed.
    > kernel: iSCSI Login negotiation failed.
    

    规避方法是用 esxcli 命令设置 CHAP(下面 authname 是用户名,secret 是前面示例中的密码):

> esxcli iscsi adapter auth chap set --direction=uni --authname=myiscsiusername --secret=myiscsipassword --level=discouraged -A vmhba64
  1. 配置 iSCSI 设置:展开 Advanced settings,把 "RecoveryTimeout" 设为 25;

  2. 设置 discovery 地址:在 Dynamic targets 段点击 "Add dynamic target",在 Addresses 下添加一个 gateway IP 地址(gwcli 阶段设置的 gateway IP,或 ansible gateway_ip_list 变量中的某个 IP)。只需添加一个地址,因为 gateway 已设置为在 discovery 期间返回所有 iSCSI portal。最后点击 "Save configuration" 按钮。在 Devices 标签页中应能看到 RBD image。

    LUN 应被自动配置,并使用 ALUA SATP 与 MRU PSP。不得使用其他 SATP 和 PSP。 可用以下命令校验:

> esxcli storage nmp path list -d eui.your_devices_id

13.8 Monitoring

引自:Monitoring Ceph iSCSI gateways

Ceph 提供了一个工具用于监控导出 RBD image 的性能。

gwtop 是一个类 top 的工具,显示通过 iSCSI 导出给客户端的 RBD image 的聚合性能指标。指标来源于 Performance Metrics Domain Agent (PMDA);来自 Linux-IO target (LIO) PMDA 的信息用于列出每个导出的 RBD image、连接的客户端及其关联的 I/O 指标。

Requirements: 一个运行中的 Ceph iSCSI gateway。

Installing(在每个 iSCSI gateway 节点上以 root 执行):

yum install ceph-iscsi-tools
yum install pcp
yum install pcp-pmda-lio

systemctl enable pmcd
systemctl start pmcd

cd /var/lib/pcp/pmdas/lio
./Install
  • 默认情况下,gwtop 假定 iSCSI gateway 配置对象存储在 rbd pool 中一个名为 gateway.conf 的 RADOS 对象里。该配置定义了要联系哪些 iSCSI gateway 以收集性能统计。可通过 -g 或 -c 标志覆盖,详见 gwtop --help;
  • LIO 配置决定从 performance co-pilot 提取哪类性能统计。gwtop 启动时会查看 LIO 配置,如果发现 user-space 磁盘,就自动选择 LIO collector。

示例输出:

gwtop  2/2 Gateways   CPU% MIN:  4 MAX:  5    Network Total In:    2M  Out:    3M   10:20:00
Capacity:   8G    Disks:   8   IOPS:  503   Clients:  1   Ceph: HEALTH_OK          OSDs:   3
Pool.Image       Src    Size     iops     rMB/s     wMB/s   Client
iscsi.t1703             500M        0      0.00      0.00
iscsi.testme1           500M        0      0.00      0.00
iscsi.testme2           500M        0      0.00      0.00
iscsi.testme3           500M        0      0.00      0.00
iscsi.testme5           500M        0      0.00      0.00
rbd.myhost_1      T       4G      504      1.95      0.00   rh460p(CON)
rbd.test_2                1G        0      0.00      0.00
rbd.testme              500M        0      0.00      0.00

在 Client 列中,(CON) 表示该 iSCSI initiator(客户端)当前已登录到 iSCSI gateway。若显示 -multi-,则表示多个客户端映射到了同一个 RBD image。


十四、NVMe-oF

14.1 Overview

引自:Ceph NVMe-oF Gateway

存储管理员可以为 Storage Ceph 集群安装并配置 NVMe over Fabrics (NVMe-oF) gateway。借助 Ceph NVMe-oF gateway,你可以运行一套完全集成的块存储基础设施,具备传统 SAN 的全部特性与优势。

NVMe-oF gateway 把 Storage Ceph 与 NVMe over TCP (NVMe/TCP) 协议集成,提供一个导出 RBD image 的 NVMe/TCP target。NVMe/TCP 协议允许客户端(称为 initiators)通过 IP 网络向存储设备(称为 targets)发送 NVMe-oF 命令。Initiator 可以是 Linux 客户端、VMware 客户端,或两者兼有。对 VMware 客户端,NVMe/TCP 卷表现为 VMFS Datastore;对 Linux 客户端,NVMe/TCP 卷表现为块设备。

                  Cluster Network (optional)
                 +-------------------------------------------+
                 |             |               |             |
             +-------+     +-------+       +-------+     +-------+
             |       |     |       |       |       |     |       |
             | OSD 1 |     | OSD 2 |       | OSD 3 |     | OSD N |
             |    {s}|     |    {s}|       |    {s}|     |    {s}|
             +-------+     +-------+       +-------+     +-------+
                 |             |               |             |
      +--------->|             |  +---------+  |             |<----------+
      :          |             |  |   RBD   |  |             |           :
      |          +----------------|  Image  |----------------+           |
      |           Public Network  |    {d}  |                            |
      |                           +---------+                            |
      |                                                                  |
      |                      +--------------------+                      |
      |   +--------------+   | NVMeoF Initiators  |   +--------------+   |
      |   |  NVMe‐oF GW  |   |    +-----------+   |   | NVMe‐oF GW   |   |
      +-->|  RBD Module  |<--+    | Various   |   +-->|  RBD Module  |<--+
          |              |   |    | Operating |   |   |              |
          +--------------+   |    | Systems   |   |   +--------------+
                             |    +-----------+   |
                             +--------------------+

高可用(HA)与 gateway group

  • **HA(高可用性)**为主机 initiator 提供 I/O 与控制路径冗余,有时也称为 failover 与 failback 支持。这种冗余对于防范一个或多个 gateway 故障至关重要。有了 HA,即使有 gateway 失效,主机也能继续 I/O,最多只是可能有性能延迟,直到故障 gateway 恢复正常;
  • NVMe-oF gateway 被虚拟地分组为 gateway groups,HA 域位于 gateway group 内。一个 NVMe-oF gateway group 目前支持 8 个 gateway。group 中每个 gateway 都可以作为通往该 group 中定义的任一 subsystem 或 namespace 的路径。HA 在 gateway group 中有两个及以上 gateway 时才有效;
  • HA 默认启用。要使用 HA,必须为每个 subsystem 在所有 GW 上定义至少两个 gateway 和两个 listener;
  • 必须在主机与 gateway 之间建立冗余:为获得完全冗余的网络连接,主机应有两个以太网口,通过具备冗余的网络(例如两台网络交换机)连接到 gateway;
  • HA 对每个 namespace 使用 Active/Standby 方式:任意时刻只有该 gateway group 中的一个 NVMe-oF gateway 为某个特定 namespace 提供主机 I/O。为了充分利用所有 NVMe-oF gateway,每个 namespace 被分配到不同的 load-balancing group。load-balancing group 的数量等于 gateway group 中 NVMe-oF gateway 的数量;
  • 如果 group 中一个或多个 GW 失效或无法被 Ceph NVMe-oF monitor 看到,会自动触发 failover,group 中另一个 GW 将接管失效 GW 的 load-balancing group。这意味着 IO 不会中断,因为会有另一个 GW 继续服务这些 namespace。如果失效的 GW 重新出现,会自动触发 failback;
  • 主机上的 NVMe-oF initiator 也会继续尝试连接失效的 GW,持续时间为 connect-all 命令中指定的时间。推荐值为 1800 秒。

Scale-out 上限(官方明确数字)

维度 上限
NVMe-oF gateway groups 4
每个 gateway group 中的 NVMe-oF gateway 8
每个 gateway group 中的 NVMe-oF subsystem 128
每个 NVMe-oF subsystem 的主机数 32
每个 gateway group 的 namespace 数 1024

Discovery

Ceph NVMe-oF gateway 支持 Discovery。每个运行在 Ceph 集群中的 NVMe-oF gateway 同时运行一个 Discovery Controller,该 Discovery Controller 报告 GW group 中的所有 Subsystem。

14.2 Requirements

引自:NVMe-oF Gateway Requirements

项 要求
内存 每个 NVMe-oF gateway 实例至少 8 GB 专用内存
CPU 强烈建议为每个 NVMe-oF gateway 专用至少 4 个 CPU 线程或 vcore。只用 1 个线程/vcore 也能工作,但性能可能低于预期。最好为 NVMe-oF gateway 服务专用服务器,以免与其他 Ceph 服务互相拖累
网络 到 Ceph public network 至少 10 Gb/s 链路。为获得最佳延迟与吞吐,推荐 25 Gb/s 或 100 Gb/s 链路
Bonding 链路绑定(配合适当的 xmit hash policy)对高可用性是理想的。注意:某个 NVMe-oF 客户端的吞吐不会高于 bond 中单条链路的吞吐。 因此,如果 gateway 节点上绑定了 4 条 10 Gb/s 链路,单个客户端实现的吞吐不会超过 10 Gb/s。注意 Ceph NVMe-oF gateway 同时还要通过 public network 与后端 OSD 通信,这与客户端到 gateway 的流量相互争用。网络资源务必预留充足,避免拥塞与饱和
高可用 在一个 gateway group 中、在独立的 Ceph 集群节点上至少配置 2 个 NVMe-oF gateway,以构成高可用的 Ceph NVMe/TCP 方案
代理 Ceph NVMe-oF gateway 容器由多个相互通信的组件构成。如果运行这些容器的节点需要 HTTP/HTTPS 代理配置才能访问容器注册表或其他外部资源,这些设置可能干扰容器内部通信。如果在供给 NVMe-oF gateway 时遇到 gRPC 或其他错误,可能需要调整代理配置

14.3 安装与配置 NVMe-oF Target

引自:Installing and Configuring NVMe-oF Targets

Prerequisites:

  • 一个可用的 Ceph Tentacle 或更新存储集群,使用 cephadm 部署;
  • NVMe-oF gateway,可与 OSD 节点共存或使用专用节点;
  • 独立的网络子网用于 NVMe-oF 前端流量与 Ceph 后端流量。

Ceph NVMe-oF gateway 既是 NVMe-oF target 也是 Ceph 客户端——可以把它看作 Ceph RBD 接口与 NVMe-oF 协议之间的"翻译器"。

Installation:

# 1) 创建用于管理 gateway 配置的 pool
ceph osd pool create NVME-OF_POOL_NAME

# 2) 在该 pool 上启用 RBD
rbd pool init NVME-OF_POOL_NAME

# 3) 在指定节点集合上部署 NVMe-oF gateway 守护进程
ceph orch apply nvmeof NVME-OF_POOL_NAME --placement="host01, host02"

Configuration:

首次使用前先下载 nvmeof-cli 容器:

podman pull quay.io/ceph/nvmeof-cli:latest
# 1) 创建 NVMe subsystem(subsystem NQN 是用户自定义字符串,例如 nqn.2016-06.io.spdk:cnode1)
podman run -it --rm quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 subsystem add --subsystem SUSYSTEM_NQN

# 2a) 在安装节点上获取 NVMe-oF Gateway 名称
ceph orch ps | grep nvme

# 2b) 定义 gateway 上处理 NVMe/TCP 命令与 I/O 的 IP 端口
podman run -it --rm quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 listener add --subsystem SUBSYSTEM_NQN --host-name HOST_NAME --traddr GATEWAY_IP --trsvcid 4420

# 3) 获取每台主机的 host NQN(NVMe Qualified Name)
cat /etc/nvme/hostnqn          # Linux
esxcli nvme info get           # ESX

# 4) 允许 initiator 主机连接到新建的 NVMe subsystem
podman run -it --rm quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 host add --subsystem SUBSYSTEM_NQN --host "HOST_NQN1 HOST_NQN2"

# 5) 列出 gateway 中配置的所有 subsystem
podman run -it --rm quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 subsystem list

# 6) 创建新的 NVMe namespace
podman run -it quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 namespace add --subsystem SUBSYSTEM_NQN --rbd-pool POOL_NAME --rbd-image IMAGE_NAME

# 7) 列出 subsystem 中的所有 namespace
podman run -it quay.io/ceph/nvmeof-cli:latest --server-address GATEWAY_IP --server-port GATEWAY_PORT 5500 namespace list --subsystem SUBSYSTEM_NQN

💡 补充:源文中 subsystem add 与 namespace add 的模板写作 SUSYSTEM_NQN(typo,应为 SUBSYSTEM_NQN),使用时按 SUBSYSTEM_NQN 理解。此外 --server-port GATEWAY_PORT 5500 这种写法在源文中同时出现了 GATEWAY_PORT 与 5500 两个记号;5500 是 rbd-target-api 风格的 API 端口写法,4420 是 NVMe/TCP 数据端口、8009 是 Discovery controller 端口(见 14.4)。请以实际环境的 gateway API 端口为准。

14.4 Initiators

引自:Configuring the NVMe-oF Initiators

支持两类 initiator:Linux 与 VMware ESX。

14.4.1 Linux

引自:NVMe/TCP Initiator for Linux

Prerequisites:

项 要求
内核 5.0 或更新
RHEL 9.2 或更新
Ubuntu 24.04 或更新
SLES 15 SP3 或更新
# 1) 安装 nvme-cli
yum install nvme-cli

# 2) 加载 NVMe-oF 模块
modprobe nvme-fabrics

# 3) 验证 NVMe/TCP target 可达
nvme discover -t tcp -a GATEWAY_IP -s 8009

# 4) 连接到 NVMe/TCP target。为高可用,使用 connect-all 命令
nvme connect-all --traddr GATEWAY_IP --transport tcp -l 1800 -s 8009
  • -l 1800:推荐值,允许 initiator 在 1800 秒内持续尝试连接 GW。这在 GW 因任何原因临时不可用时很有帮助;
  • -s 8009:Discovery controller 的端口地址。connect-all 命令会先连接到 DC,然后用其返回的信息连接各个 GW。

验证:

# 1) 验证 initiator 已连接到 gateway group 中的所有 NVMe-oF gateway 与 subsystem
nvme list-subsys
nvme-subsys<X> - NQN=<NQN>
\
    +- nvmeX tcp traddr=<GW IP>,trsvcid=4420 live
    +- nvmeY tcp traddr=<GW IP>,trsvcid=4420 live
    +- nvmeZ tcp traddr=<GW IP>,trsvcid=4420 live
    +- nvmeW tcp traddr=<GW IP>,trsvcid=4420 live
# 2) 列出 NVMe 块设备
nvme list

# 3) 在目标设备上创建文件系统
mkfs.ext4 NVME_NODE_PATH

# 4) 挂载文件系统
mkdir /mnt/nvmeof
mount NVME_NODE_PATH /mnt/nvmeof

# 5) 列出 NVMe-oF 文件
ls /mnt/nvmeof

# 6) 在 /mnt/nvmeof 目录创建一个文本文件
echo "Hello NVME-oF" > /mnt/nvmeof/hello.text

# 7) 验证文件可访问
cat /mnt/nvmeof/hello.text

14.4.2 VMware ESX

引自:NVMe/TCP Initiator for VMware ESX

Prerequisites:

  • 运行 VMware vSphere Hypervisor (ESXi) 7.0U3 或更新的 VMware ESXi 主机;
  • 已部署的 Ceph NVMe-oF gateway;
  • 带有 NVMe-oF 配置的 Ceph 集群;
  • gateway 中已定义的 subsystem。

Configuration(vSphere web client + esxcli):

# 1) 在 NIC 上启用 NVMe/TCP(把 N 换成 NIC 编号)
esxcli nvme fabric enable --protocol TCP --device vmnicN

# 2) 给 VMKernel NIC 打标签以允许 NVMe/TCP 流量(把 N 换成 VMkernel 的 ID)
esxcli network uip interface tag add --interface-nme vmkN --tagname NVMeTCP

# 3a) 列出 NVMe-oF adapter
esxcli nvme adapter list

# 3b) 可选:发现 NVMe-oF subsystem
esxcli nvme fabric discover -a NVME_TCP_ADAPTER -i GATEWAY_IP -p 4420

# 3c) 连接到 NVMe-oF gateway subsystem
esxcli nvme fabrics discover -a NVME_TCP_ADAPTER -i GATEWAY_IP -p 8009 -c

-c 会发现 gateway group 中的 NVMe-oF gateway,然后连接到这些 gateway 以提供 multipath 访问。

# 3d) 列出 NVMe/TCP controller
esxcli nvme controller list

# 3e) 列出 subsystem 中的 NVMe-oF namespace
esxcli nvme namespace list

验证: 从 vSphere client 进入 ESXi 主机 → 在 Storage 页面进入 Devices 标签页 → 确认表中列出了 NVMe/TCP 磁盘。

14.5 iSCSI 与 NVMe-oF 对比

💡 补充:官方未直接给出两者的对比表,下表基于本册两个章节的源文事实整理。

维度 iSCSI Gateway NVMe-oF Gateway
维护状态 自 2022-11 起 maintenance,不再新增特性 活跃开发
协议 iSCSI over TCP/IP(SCSI 语义) NVMe/TCP(NVMe 语义)
内核子系统 LIO + TCMU(userspace passthrough)→ librbd NVMe-oF gateway → RBD Module(容器化)
部署方式 ceph-ansible 或 CLI(gwcli) cephadm + nvmeof-cli 容器
集群版本要求 Luminous+ Tentacle+
OS 要求 RHEL/CentOS 7.5+,kernel v4.16+ 客户端:Linux kernel 5.0+ / RHEL 9.2+ / Ubuntu 24.04+ / SLES 15 SP3+ / ESXi 7.0U3+
网络建议 前后端独立子网;端口 3260(数据)/ 5000(API) 至少 10 Gb/s,推荐 25/100 Gb/s;前后端独立子网;端口 4420(数据)/ 8009(Discovery)
网关数量建议 2–4 个节点 gateway group 内最多 8 个,group 最多 4 个
HA 机制 ALUA + 客户端 multipath(Linux multipathd、Windows MPIO、ESXi SATP/PSP) 内建 Active/Standby per namespace + 自动 failover/failback
认证 必须配置 CHAP(否则拒绝登录),支持双向 CHAP 源文未提及 CHAP(nvmeof-* 页面);以 subsystem/host NQN 控制访问
监控 gwtop(PCP + LIO PMDA) 源文未提及专门监控工具
限制 不支持 SCSI PGR / SCSI-2 reservations(多 gateway 导出同一 RBD image 时) 单客户端吞吐受限于 bond 单链路;命名空间按 gateway 数分组负载均衡
客户端形态 Linux / Windows Server 2016+ / ESXi 6.5+ Linux / ESXi 7.0U3+(Windows 未在 nvmeof-* 源文中出现)

十五、其他

15.1 RBD Replay

引自:RBD Replay

RBD Replay 是一套用于捕获(capture)与回放(replay)RADOS Block Device (RBD) 工作负载的工具。

版本要求:

动作 要求
捕获工作负载 客户端必须安装 lttng-tools;客户端的 librbd 必须是 v0.87 (Giant) 或更新
回放工作负载 客户端的 librbd 必须是 Giant 或更新

三步流程:

# 1) 捕获 trace。务必捕获 pthread_id 上下文
mkdir -p traces
lttng create -o traces librbd
lttng enable-event -u 'librbd:*'
lttng add-context -u -t pthread_id
lttng start
# run RBD workload here
lttng stop
# 2) 用 rbd-replay-prep 处理 trace
rbd-replay-prep traces/ust/uid/*/* replay.bin
# 3) 用 rbd-replay 回放 trace。在确定它确实在做你想做的事之前,使用 read-only
rbd-replay --read-only replay.bin

⚠️ 官方 important:rbd-replay 默认会破坏数据。 不要对希望保留的 image 使用,除非加上 --read-only 选项。

  • 回放的工作负载不必针对与捕获时相同的 RBD image,甚至不必是同一个集群;
  • 为了适配差异,可能需要使用 rbd-replay 的 --pool 与 --map-image 选项。

相关 man page: rbd-replay(8)、rbd-replay-prep(8)、rbd-replay-many(8)(见 15.3)。

15.2 rbd-ko(内核模块操作)

引自:Kernel Module Operations

📌 说明:任务书把 rbd-ko.rst 描述为"对象映射内核对象",但该页实际内容是"内核模块操作"(rbd device map / list / unmap),并不涉及 object map 的内核对象结构。本章按其真实内容定位,完整内容见第四章「内核模块与客户端」。

操作 命令
获取 image 列表 rbd list
map sudo rbd device map {pool-name}/{image-name} --id {user-name}(可加 --keyring 或 --keyfile)
显示已映射 rbd device list
unmap sudo rbd device unmap /dev/rbd/{poolname}/{imagename}

15.3 RBD man pages 清单

引自:Ceph Block Device Manpages

man page 说明(本册推断,页面本身只列出条目)
rbd(8) RBD – Manage RADOS Block Device (RBD) Images
rbd-fuse(8) 以 FUSE 方式挂载 RBD
rbd-nbd(8) 通过 NBD 映射 RBD image(加密 image 的自动 encryption load 即通过它)
rbd-ggate(8) FreeBSD 侧的 RBD 映射
rbd-map(8) / rbdmap rbdmap 服务相关
ceph-rbdnamer(8) RBD 设备命名(udev)
rbd-replay-prep(8) 见 15.1
rbd-replay(8) 见 15.1
rbd-replay-many(8) 批量回放

十六、命令速查表

16.1 image 生命周期

命令 用途
rbd pool init {pool-name} 初始化 pool 供 RBD 使用
rbd create --size {MB} {pool}/{image} 创建 image
rbd ls / rbd ls {pool} 列出 image
rbd info {image} / rbd info {pool}/{image} 查看 image 信息
rbd resize --size {MB} {image} 扩容
rbd resize --size {MB} {image} --allow-shrink 缩容
rbd rm {image} / rbd rm {pool}/{image} 删除 image
rbd trash mv {pool}/{image} 移入 trash(延迟删除)
rbd trash ls [--all] / rbd trash ls {pool} 列出 trash
rbd trash rm {pool}/{image-id} 从 trash 彻底删除(可加 --force、--expires-at)
rbd trash restore {image-id} / rbd trash restore {pool}/{image-id} [--image new-name] 恢复
rbd du 计算实际数据用量(依赖 fast-diff 加速)
rbd status {pool}/{image} 查看 watchers、PWL cache 状态、live-migration 状态

16.2 快照与克隆

命令 用途
rbd snap create {pool}/{image}@{snap} 创建快照
rbd snap ls {pool}/{image} 列出快照
rbd snap rollback {pool}/{image}@{snap} 回滚(慢,建议改用克隆)
rbd snap rm {pool}/{image}@{snap} 删除快照(异步 snaptrim 回收空间)
rbd snap purge {pool}/{image} 清除所有快照
rbd snap protect {pool}/{image}@{snap} 保护快照(克隆前必需)
rbd snap unprotect {pool}/{image}@{snap} 取消保护(需先 flatten/删除所有克隆)
rbd clone {pool}/{parent}@{snap} {pool}/{child} 从快照克隆(可跨 pool)
rbd children {pool}/{image}@{snap} 列出快照的子 image
rbd flatten {pool}/{image} 展平克隆(去除对父快照的依赖)

16.3 feature 与配置

命令 用途
rbd feature enable {pool}/{image} {feature-name} 启用 image feature(如 journaling)
rbd feature disable {pool}/{image} {feature-name} 禁用 image feature
rbd config ...(参见 rbd(8) 的 commands 段) per-pool / per-image 配置覆盖

16.4 内核映射(krbd)与 NBD / Windows

命令 用途
rbd list 列出 image(准备映射)
sudo rbd device map {pool}/{image} --id {user} 映射 image(krbd)
sudo rbd device map {pool}/{image} --id {user} --keyring {path} 带 keyring 映射
sudo rbd device map {pool}/{image} --id {user} --keyfile {path} 带 keyfile 映射
rbd device map --exclusive ... 以 RBD_LOCK_MODE_EXCLUSIVE 映射(禁止自动锁转移)
rbd device list 列出已映射设备
sudo rbd device unmap /dev/rbd/{pool}/{image} 取消映射
rbd device map -t nbd -o encryption-passphrase-file={file} {image-spec} 以 NBD 映射并自动加载加密
rbd device map {image} -onon-persistent(Windows) 非持久映射
rbd-wnbd show {image} --format=json(Windows) 查看映射详情(含 disk_number)

16.5 缓存

命令 用途
rbd status {pool}/{image} 查看 PWL cache 状态
rbd persistent-cache flush {pool}/{image} 刷写 PWL 缓存到 OSD
rbd persistent-cache invalidate {pool}/{image} 使 PWL 缓存失效并删除本地缓存文件
systemctl enable ceph-immutable-object-cache@{id} 启用只读父镜像缓存守护进程
ceph-immutable-object-cache -f --log-file={path} 前台运行该守护进程

16.6 加密

命令 用途
rbd encryption format [--cipher-alg aes-128|aes-256] {image-spec} {luks1|luks2} {passphrase-file} 格式化 image 为 LUKS
rbd resize --size {G} --encryption-passphrase-file {file} {image-spec} 按有效大小 resize(补偿 header 开销)
rbd device map -t nbd -o encryption-passphrase-file={file} {image-spec} 映射加密 image

16.7 Live migration

命令 用途
rbd migration prepare {source} [{target}] 准备迁移(可加 layout 选项)
rbd migration prepare --import-only --source-spec "{JSON}" {target} 准备 import-only 迁移
rbd migration prepare --import-only --source-spec-path {file|-} {target} 从文件/STDIN 读 source-spec
rbd migration execute {target} 执行块深度拷贝
rbd migration commit {target} [--force] 提交迁移(删除源)
rbd migration abort {target} 中止迁移(删除目标、恢复源)
rbd status {target} 查看迁移状态与进度

16.8 Mirroring

(详见第十章 10.11 表格,此处不重复)

16.9 QEMU / libvirt

命令 用途
qemu-img create -f raw rbd:{pool}/{image} {size} 用 QEMU 创建 image
qemu-img resize rbd:{pool}/{image} {size} 用 QEMU 调整大小
qemu-img info rbd:{pool}/{image} 用 QEMU 查看信息
qemu-img convert -f qcow2 -O raw {src} rbd:{pool}/{image} 转换并导入
qemu -m 1024 -drive format=raw,file=rbd:{pool}/{image} 从 RBD image 启动 VM
qemu -m 1024 -drive format=rbd,file=rbd:{pool}/{image},cache=writeback 启用 librbd writeback 缓存
virsh secret-define --file secret.xml 定义 Ceph secret
virsh secret-set-value --secret {uuid} --base64 {key} 设置 secret 值
virsh secret-undefine {uuid} 删除 secret(重置前必需)
virsh edit {vm-domain-name} 编辑 VM 配置(必须用 virsh,不能直接用编辑器)
virsh dumpxml {vm-domain-name} 校验配置一致性
virsh qemu-monitor-command --hmp {domain} 'info block' 检查 VM 是否在与 Ceph 通信
virsh domblklist {domain} --details 检查设备是否存在
ceph daemon /var/run/ceph/ceph-client.cinder.*.asok help 访问 hypervisor 上的 client admin socket

16.10 iSCSI / NVMe-oF

命令 用途
gwcli / gwcli ls iSCSI gateway 命令行(交互式)
systemctl <start|stop|restart|reload> rbd-target-api 管理 iSCSI API 服务
ansible-playbook site.yml --limit iscsigws Ansible 部署 iSCSI gateway
ansible-playbook purge_gateways.yml 清除 iSCSI gateway 配置(all / lio / abort)
iscsiadm -m discovery -t st -p {IP} iSCSI 发现
iscsiadm -m node -T {iqn} -l / -u iSCSI 登录 / 登出
multipath -ll 查看 multipath 拓扑
mpathconf --enable --with_multipathd y 生成 multipath.conf
gwtop 监控导出的 RBD image 性能
ceph orch apply nvmeof {POOL} --placement="host01, host02" 部署 NVMe-oF gateway
ceph orch ps | grep nvme 查看 NVMe-oF gateway 名称
podman pull quay.io/ceph/nvmeof-cli:latest 获取 nvmeof-cli
nvmeof-cli ... subsystem add --subsystem {NQN} 创建 NVMe subsystem
nvmeof-cli ... listener add --subsystem {NQN} --host-name {H} --traddr {IP} --trsvcid 4420 定义 NVMe/TCP listener
nvmeof-cli ... host add --subsystem {NQN} --host "{NQN1} {NQN2}" 授权 initiator 主机
nvmeof-cli ... namespace add --subsystem {NQN} --rbd-pool {P} --rbd-image {I} 创建 namespace
nvme discover -t tcp -a {GW_IP} -s 8009 Linux 发现 NVMe-oF target
nvme connect-all --traddr {GW_IP} --transport tcp -l 1800 -s 8009 Linux 连接(HA)
nvme list-subsys / nvme list 查看已连接的 subsystem / 块设备
esxcli nvme fabric enable --protocol TCP --device vmnicN ESXi 启用 NVMe/TCP
esxcli nvme fabrics discover -a {ADAPTER} -i {GW_IP} -p 8009 -c ESXi 发现并连接(multipath)
esxcli nvme controller list / esxcli nvme namespace list ESXi 查看 controller / namespace
esxcli iscsi adapter list / esxcli iscsi adapter set -A {A} -n {IQN} ESXi 查看/设置 iSCSI initiator 名
esxcli iscsi adapter auth chap set --direction=uni --authname {u} --secret {p} --level=discouraged -A {A} ESXi 设置 CHAP

16.11 工作负载录制回放

命令 用途
lttng create -o traces librbd / lttng enable-event -u 'librbd:*' / lttng add-context -u -t pthread_id / lttng start / lttng stop 捕获 RBD 工作负载 trace
rbd-replay-prep traces/ust/uid/*/* replay.bin 处理 trace
rbd-replay --read-only replay.bin 回放(默认会破坏数据,务必先加 --read-only)
rbd-replay --pool {P} --map-image {A}={B} replay.bin 回放时适配不同 pool / image 名

文档未明确项

以下条目在 Ceph Tentacle v20.2.4 的 doc/rbd/ 源文与 src/common/options/{rbd,rbd-mirror,immutable-object-cache,global,mon}.yaml.in 中均未给出明确答案,本册不做臆测:

# 项目 说明
1 krbd 的通用最低内核版本 rbd-ko.rst 未给出;仅能从 rbd-config-ref.rst 的 image feature 表推出各 feature 所需版本(见 4.5)
2 单 image 快照数量上限与性能影响的量化数据 rbd-snapshot.rst 只说明 snaptrim 异步回收,无任何数字
3 Operations(256)、Migrating(512)、Non-primary(1024)三个 feature 的默认值 rbd-config-ref.rst 的 feature 表对这三项未写 :Default:
4 Operations、Migrating、Non-primary 的 CLI 值 三项均未给出 :CLI value:(rbd_default_features 的 long_desc 也只列到 +128 data-pool)
5 rbd_default_data_pool 的默认值 rbd.yaml.in 中无 default: 字段;validator 仅说明非法值时重置为空串
6 rbd_default_map_options 的默认值 rbd.yaml.in 中无 default: 字段
7 rbd_plugins 的默认值 rbd.yaml.in 中无 default: 字段
8 rbd_qos_exclude_ops 的默认值 rbd.yaml.in 中无 default: 字段
9 rbd_journal_pool 的默认值 rbd.yaml.in 中无 default: 字段
10 rbd_default_stripe_count = 0 与 rbd_default_stripe_unit = 0 的精确语义 源文只写 "default stripe count/width",未定义 0 的行为
11 iscsigws.yml 中 seed_monitor、cluster_name、gateway_keyring、deploy_settings、perform_system_checks、trusted_ip_list 的默认值 iscsi-target-ansible.rst 的表格只写含义,未写默认值(仅 api_user/api_password/api_port/api_secure 有默认值)
12 iSCSI gateway 场景下各 Ansible 变量在 iscsigws.yml.sample 中的完整清单 源文只列出 10 个变量,并说"完整清单见 sample 文件"
13 NVMe-oF 的认证机制(是否存在 CHAP 等价物) nvmeof-* 全部页面均未提及认证配置;只描述了 host NQN 授权
14 NVMe-oF gateway 的监控工具 nvmeof-* 页面未提及(iSCSI 有 gwtop)
15 NVMe-oF 对 Windows initiator 的支持 nvmeof-initiators.rst 只列出 Linux 与 VMware ESX 两个子页面
16 Kubernetes 侧快照 / 克隆的 VolumeSnapshot 与 PVC dataSource 官方示例 rbd-kubernetes.rst 未提供(属于 ceph-csi 项目文档范围)
17 ceph-csi ConfigMap 中 ceph.conf 的完整可用配置项清单 源文只给了 3 个 auth_*_required 示例
18 iscsi-gateway.cfg 中 api_port 的默认值究竟是 5000 还是 5001 iscsi-target-cli.rst 注释写 # api_port = 5001,而 iscsi-target-ansible.rst 表格写 default 为 5000;源文存在不一致,本册不做裁决
19 nvmeof-cli 命令中 --server-port 的准确取值(源文同时出现 GATEWAY_PORT 与 5500) 源文写法含混,见 14.3 的补充说明
20 RBD image 命名 userid/<uuid> 这类约定的具体用途与冲突场景 rados-rbd-cmds.rst 只提示"可能造成混淆",未展开
21 rbd_default_snapshot_quiesce_mode = required 时若无 agent 的具体失败行为 源文只给了枚举值含义,未描述失败模式
22 PWL cache 在 ssd 模式下是否需要 DAX 源文只说 rwl 模式"必须"启用 DAX,未说明 ssd 模式
23 immutable_object_cache_max_inflight_ops 与 immutable_object_cache_client_dedicated_thread_num 在 rbd-persistent-read-only-cache.rst 中未列出 本册从 immutable-object-cache.yaml.in @ v20.2.4 补齐,已标注来源
24 osd_client_watch_timeout 15 是否必须配合 iSCSI gateway iscsi-requirements.rst 在"在每个 OSD 节点上更新运行态"示例中给出该命令,但未说明它与其他两项的关系