跳到主要内容
约 12 分钟5 张卡

Ceph

Ceph 是分布式存储系统。它把数据存入底层的 RADOS 对象存储集群,再向应用提供块、对象或文件接口。虚拟机可以把 RBD 当作磁盘使用,应用也可以通过对象网关保存文件内容,但两者面对的接口与并发约束并不相同。

讨论 Ceph 项目,先说清楚使用了哪一层,再解释具体操作会改变哪个对象。例如,从快照克隆出一个新磁盘,与把原磁盘回滚到快照,是两种不同的恢复方式。本文以 Ceph Reef 18.2.4 为基线,重点讨论 RBD 与 NBD 访问路径。

1. 面试核心​

先记住这几条,再读正文
  • RADOS 是底层存储服务,RBD、对象网关和 CephFS 在它之上提供不同接口。RBD 镜像不是一个 S3 对象,RBD 的读写接口也不自动提供文件系统语义。卡 · RADOS 与访问接口
  • RBD 快照保存某一时刻的镜像状态;clone 创建新镜像,rollback 修改原镜像的当前内容。克隆的未覆盖区域仍可能依赖父快照,不能直接当作独立备份。卡 · 快照、克隆与回滚
  • 存储快照不自动成为应用一致性快照。应用缓存、文件系统与数据库状态需要配合停写、刷盘或恢复协议。卡 · 快照的一致性范围
  • NBD 并发要分别检查请求关联、数据范围、顺序与持久化、关闭与回滚的生命周期。能并发提交多个请求,不代表重叠写和业务读改写没有冲突。卡 · NBD 与并发访问
  • RBD 的 exclusive-lock 默认支持客户端之间的协作交接,保护镜像内部状态不等于独占业务使用权,更不能让普通文件系统自动变成多主机文件系统。卡 · exclusive-lock 与应用所有权

2. RADOS 与访问接口​

RADOS(Reliable Autonomic Distributed Object Store)是 Ceph 底层的分布式对象存储服务。这里的对象是 Ceph 内部存储和复制的数据单位,并不意味着每个上层应用都必须通过 S3 API 访问。

接口应用看到的对象主要用途
librados对象、对象数据及元数据直接使用 RADOS 接口开发应用
RBD(RADOS Block Device)镜像及其中的字节范围虚拟机磁盘、需要块设备的工作负载
RADOS Gateway(RGW)桶、对象键和对象 API提供 S3、Swift 风格访问
CephFS文件、目录及文件系统操作共享文件访问

RBD 的镜像(image)是一块逻辑磁盘的存储表示。它的内容按布局分散到多个 RADOS 对象中,客户端把镜像偏移转换成相应对象的访问。CephFS 还需要元数据服务器处理目录和文件元数据;普通 RBD 读写不经过这层文件系统元数据服务。

块、对象和文件的通用差异见对象存储。在项目介绍中,应明确说出“通过 RBD 读写虚拟磁盘”,不能只说“用了 Ceph,所以是对象存储项目”。

2.1 集群中的职责​

组件主要职责
Monitor(MON)维护集群映射与成员状态,通过监视器法定人数协调相关状态
OSD存储对象,处理数据读写,并参与复制、恢复等工作
Manager(MGR)提供监控、管理模块等能力
Metadata Server(MDS)为 CephFS 管理文件系统元数据

放置组(Placement Group,PG)是组织对象放置、复制与恢复的逻辑分组。对象先映射到 PG,再结合集群映射与 CRUSH 规则确定相关 OSD。CRUSH 根据拓扑、权重和放置规则计算位置,可以表达把副本分散到不同主机等故障域的要求。

客户端取得必要的集群信息后直接与相应 OSD 交互,MON 不是所有数据字节必须经过的转发节点。CRUSH 也不是另一个数据副本:副本数、纠删码参数、故障域及恢复状态共同决定数据保护能力,不能仅凭“用了分布式存储”就承诺某个容错数量。

3. 快照、克隆与回滚​

快照是镜像在某个时刻的只读逻辑状态。假设 base 在创建快照 s1 时保存内容 A,之后当前镜像写入内容 B,那么 base 的当前状态与 base@s1 已经不同。

3.1 clone 创建新的镜像​

克隆从父快照创建一个新的可写镜像。把 base@s1 克隆成 child 后,child 最初看到 A,原来的 base 仍然是 B。

这种克隆通过分层和写时复制共享未改变的数据,不需要先完整复制整块磁盘。child 读取尚未在自己这一层保存的范围时,可以回到父快照读取;对 child 的修改写入子镜像,不改写父快照。

共享也带来了依赖。flatten 会把解除父快照依赖所需的数据补到子镜像,使其不再通过这层父引用读取。它会增加实际存储和 I/O 工作,不能把“创建克隆很快”推广为“所有后续操作都没有复制成本”。克隆也不是自动隔离于原集群故障的备份。

3.2 rollback 恢复原镜像​

回滚把当前镜像恢复到指定快照的内容与大小。对 base 回滚到 s1,改变的是 base 本身,不会另建一个 child。

18.2.4 的实现会协调镜像写入、恢复尺寸并处理需要回滚的对象。这项工作不是简单地把一个全局指针换成旧值,也不能按镜像容量直接推算固定耗时。

对照clonerollback
操作结果新建一个可写镜像修改现有镜像的当前状态
原镜像的当前内容保留被恢复到快照状态
典型场景从模板创建实例、保留当前状态再尝试恢复明确要把原卷恢复到旧状态
需要继续处理的事父快照依赖、后续写入与 flatten 成本停写、现有客户端状态、应用恢复及失败处理

如果上层仍挂载着文件系统并持有缓存,直接改变底层镜像内容会让缓存与磁盘状态不一致。恢复流程应先协调使用者和在途 I/O,再执行恢复,之后按上层系统的方式重新打开或恢复服务。库内部的写入协调不能代替应用级恢复流程。

3.3 快照保护与格式条件​

镜像格式与克隆格式是两个设置。支持分层克隆的 RBD 镜像使用 image format 2;clone format 则影响如何维护克隆对父快照的依赖。

在 18.2.4 中,clone format 1 要求父快照先被保护,有依赖的子镜像时不能直接解除保护并删除父快照。clone format 2 的依赖管理不同,允许克隆未保护快照,并处理仍被子镜像使用的父快照删除;它有相应的客户端兼容要求。删除名称也不意味着仍被引用的数据立刻释放。

因此,“克隆前必须 snap protect”需要带上格式条件。下面的示例明确使用 clone format 1,让快照保护与解除依赖的过程可见,不依赖集群的默认选择。

3.4 对照示例​

下面使用 18.2.4 的 Python rados、rbd 绑定。前提是已经存在专用实验池 coding101-lab,其中没有 base 或 child;sys.argv[1] 是实验配置文件。两个镜像没有其他读写者或挂载者,示例只写入合成数据。

import sys
import rados
import rbd

with rados.Rados(conffile=sys.argv[1],
conf={"rbd_default_clone_format": "1"}) as cluster:
with cluster.open_ioctx("coding101-lab") as ioctx:
api = rbd.RBD()
api.create(ioctx, "base", 8 * 1024 * 1024,
old_format=False, features=rbd.RBD_FEATURE_LAYERING)
with rbd.Image(ioctx, "base") as base:
base.write(b"A" * 4096, 0)
base.flush()
base.create_snap("s1")
base.protect_snap("s1")
base.write(b"B" * 4096, 0)
base.flush()
api.clone(ioctx, "base", "s1", ioctx, "child",
features=rbd.RBD_FEATURE_LAYERING)
with rbd.Image(ioctx, "child") as child:
print(base.read(0, 1), child.read(0, 1))
child.write(b"C" * 4096, 0)
child.flush()
base.rollback_to_snap("s1")
print(base.read(0, 1), child.read(0, 1))
child.flatten()
base.unprotect_snap("s1")
base.remove_snap("s1")
api.remove(ioctx, "child")
api.remove(ioctx, "base")

第一行对照的是当前镜像 B 与从快照克隆出的 A;第二行对照回滚后的 A 与独立修改后的子镜像 C。flatten 后再解除保护和删除快照,避免把子镜像仍然需要的父状态直接当作可回收数据。

4. 快照的一致性范围​

崩溃一致性意味着保存的状态需要按类似故障重启的方式恢复;应用一致性还要求上层应用的逻辑状态已经协调好。RBD 不理解镜像中的文件系统、数据库事务或业务订单,不能只靠一次底层快照替它们完成协调。

例如数据库的一部分修改可能已经写入磁盘,另一部分还在内存中。快照恢复后,数据库可能需要重放日志;如果应用协议没有保证可恢复性,也不能仅凭块设备快照就认定数据完整。

需要应用一致性时,使用对应系统支持的暂停写入、文件系统冻结、缓存刷新或备份接口。多个镜像之间还要明确是否需要共同的时间点及相应协调。恢复验证要检查应用能否正确打开和继续工作,不能只检查块设备能够读取。

5. NBD 与并发访问​

NBD(Network Block Device)把块读写请求交给另一端处理。Linux 可以通过 NBD 设备呈现块接口;Ceph 的 rbd-nbd 则在用户态把这条路径连接到 librbd。NBD 本身不是 Ceph 的副本协议,也不是文件系统锁。

讨论“NBD 设备的并发调用”,应先明确:是同一进程的多个线程读写一个设备、多个进程共用设备,还是多台机器写同一 RBD 镜像;操作是否重叠,是否还包含快照、回滚、缩容与卸载。这些条件会改变所需的协调方式。

5.1 请求关联与完成顺序​

NBD 允许多个请求在途,完成和回复顺序不必与提交顺序一致。回复携带原请求的标识,客户端据此关联结果,不能用“下一条回复属于最早请求”来分配缓冲区。

18.2.4 的 rbd-nbd 为请求保留独立上下文与数据,通过 librbd 异步提交读写,完成回调把请求移到完成队列,再发送相应回复。这里的队列锁保护请求管理状态,不代表所有磁盘内容的业务修改都被串行化。

自己封装异步接口时,需要让请求标识、缓冲区和完成对象存活到操作结束。发出取消、超时返回或开始卸载,不自动意味着后端已经停止使用它们。

5.2 重叠范围与业务操作​

情况需要确认或协调的内容
独立范围读写后端的并发契约、句柄与缓冲区是否可共享、是否存在上层元数据依赖
重叠范围写入覆盖顺序及写入原子性范围;有先后要求时显式建立依赖
读出计数、修改、写回整个读改写过程需要协调,不能因单次读写成功就排除丢失更新
回滚、缩容、关闭或卸载停止接收新任务,处理在途请求,协调上层缓存及引用后再切换生命周期

锁的范围应对应需要保护的状态。一个进程内的互斥锁可以保护该进程中的共享请求表,却不能约束另一台机器上的写入者。需要多主机访问时,上层文件系统或应用本身也必须支持相应模式;给普通文件系统增加一个块设备访问入口,不会自动增加分布式缓存一致性。

5.3 持久化与顺序​

请求处理完成与数据到达非易失存储是不同条件。NBD 的 FLUSH 和 FUA 能表达相应要求,但必须按双方支持并协商的能力使用。

协议允许重排普通请求;不能把“先发出写入,再发出 FLUSH”当作任意多连接场景下都覆盖那些写入。多连接客户端需要先收到希望被刷新操作覆盖的写入回复,再发起相应 FLUSH,并核对多连接缓存一致性能力。FUA 则要求带此标志的相应操作满足持久化条件后才能成功回复。

在 18.2.4 的 rbd-nbd 实现中,FLUSH 走 librbd 的异步刷新路径;该实现设置的设备能力没有包含 NBD_FLAG_SEND_FUA,不能因为 NBD 协议定义了 FUA 就断言这条访问路径支持它。实际持久化结论仍要结合客户端缓存、OSD 后端和底层设备保证;单机内存型实验不能证明断电安全。

6. exclusive-lock 与应用所有权​

RBD 的 exclusive-lock 特性协调客户端对镜像内部结构的写入,例如对象映射、日志与相关缓存状态。默认模式允许客户端协作交接:原持有者停止相关写入、刷新缓存并释放,另一个客户端随后取得锁。

因此,启用这个特性并不意味着其他客户端永远不能打开并写入同一个镜像。它们可以交替取得写入资格,应用层仍可能发生“一个写入覆盖另一个写入”。--exclusive 等明确的独占模式有不同要求,不能和默认特性混称。

异常接管还需要处理旧客户端恢复后继续访问。Ceph 的相关流程可以通过 blocklist 让 OSD 拒绝旧客户端,再完成锁接管;它依赖对应身份、权限和集群映射更新。不要只删除一条锁记录,就认为已经隔离旧写入者。外部资源保护的通用问题见分布式锁。

7 · 面试题卡片​

本页题目与参考回答

7.1 Ceph 同时支持对象、块和文件存储,它们是什么关系?​

考察重点:RADOS 与上层接口、OSD 与 MDS 的职责。

相关内容:RADOS 与访问接口。

参考回答

RADOS 提供底层分布式对象存储,RBD、对象网关和 CephFS 在它上面提供不同访问语义。 RBD 面向块镜像,RGW 提供对象 API,CephFS 提供文件和目录接口。

OSD 处理底层对象存储与复制;CephFS 还使用 MDS 管理文件系统元数据。应根据项目实际调用的接口说明能力,不能把内部以对象存储数据等同于应用直接使用 S3。

7.2 Ceph RBD 的 clone 和 rollback 有什么区别?​

考察重点:操作对象、父快照依赖、恢复代价与使用者协调。

相关内容:快照、克隆与回滚。

参考回答

clone 从快照创建新的可写镜像,rollback 把现有镜像恢复到快照状态。 前者保留原镜像当前内容,适合从模板创建实例或保留现场;后者改变原镜像,需要明确暂停和恢复现有使用者。

克隆通过分层共享未变化的数据,写入子镜像不修改父快照。解除父依赖需要 flatten 等处理,快照保护规则也要按 clone format 说明。回滚会恢复镜像大小和对象状态,不是给原镜像换一个名字;内部协调不代替文件系统、缓存和业务的恢复协议。

7.3 怎样分析 NBD 设备的并发调用?​

考察重点:调用者范围、请求关联、重叠访问、持久化与生命周期。

相关内容:NBD 与并发访问、exclusive-lock 与应用所有权。

参考回答

先明确调用者与操作范围,再分别处理请求本身和共享数据的冲突。 同进程多线程、多个进程和多主机不是同一个条件;独立范围读写也不同于重叠写或回滚整个镜像。

  • 请求可以异步完成,应通过标识关联回复,正确管理请求上下文、缓冲区和完成通知。
  • 重叠写需要明确顺序与原子性范围;读改写需要保护整个过程,进程内锁不能约束远端写入者。
  • 持久化要按 FLUSH、FUA、缓存及后端契约判断,不能仅看请求发送顺序。
  • 关闭、卸载、缩容和回滚要协调新请求、在途请求及上层缓存。取消请求不等于 I/O 已经停止。

使用 RBD 时还应说明 exclusive-lock 的模式与上层文件系统支持的并发访问方式,不能只回答“给设备加一把锁”。

7.4 创建 RBD 快照,是否就得到可以直接恢复的应用备份?​

考察重点:块状态、应用缓存、崩溃恢复与故障隔离。

相关内容:快照的一致性范围、clone 创建新的镜像。

参考回答

不能直接这样判断。RBD 快照不了解文件系统和数据库的业务状态,没有上层协调时不能自动承诺应用一致性。 应按系统要求暂停写入、冻结或刷新,并验证恢复后的应用行为。

克隆还可能依赖同一集群中的父快照。是否构成备份,要继续检查独立故障域、保留与删除策略以及恢复过程,不能只看镜像名称是否不同。

7.5 开启 RBD exclusive-lock,就能禁止其他应用写同一个镜像吗?​

考察重点:默认协作交接、显式独占、镜像内部状态与应用语义。

相关内容:exclusive-lock 与应用所有权。

参考回答

默认的 exclusive-lock 允许客户端协作交接,不等于永远只允许一个应用使用镜像。 它保护镜像内部结构,多个客户端仍可能轮流写入,覆盖业务内容。

需要禁止其他写入者时,应核对显式独占模式、异常接管与 blocklist 条件。即使存储层限制了写入资格,上层文件系统和应用仍要满足自己的缓存及一致性要求。

8. 参考资料​

来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明