TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

Rook-Ceph 解析 1:Ceph 如何在何处存放数据

状态:草稿(2026-10-29),基于官方文档与论文,无实际测量。 解释仅基于打开并确认过的 Ceph v20.2 (Tentacle) 官方文档 (docs.ceph.com/en/tentacle) 和 Ceph 原始论文。所有默认值均为该文档中记录的值。在 TT Lab 实践集群中 ceph status 通过类似输出进行确认的内容将在本系列的第 4 篇(收集到原始数据后编写)中进行。编写规则:以实测数据为基础,无法测量的标记为「未实测」,在文档中未找到的标记为「未确认」。

目录

1. 一句话概括:没有记录数据位置的中央索引

在 Kubernetes 中,如果 Pod 请求磁盘(PVC),就必须由某人提供实际的磁盘空间。如果使用单台节点的磁盘,当该节点挂掉时,数据也会随之绑定丢失。将多台节点的磁盘组合起来使其看起来像一个存储库,并保留多份数据以保证即使单台节点挂掉也能继续读写,这就是 分布式存储。Ceph 是其中的代表性开源软件,而 Rook 是在 Kubernetes 上运行 Ceph 的 operator。本系列将依次阅读 Ceph 的架构(第 1 篇)、Rook operator 做的事情(第 2 篇)以及 Helm Chart 的选项(第 3 篇)。

许多存储系统会将「文件 X 的片段在 3 号服务器磁盘的什么地方」这样的列表(元数据服务器、分配表)集中存放在一个地方。由于每次读写时都必须查询该列表,因此列表会成为瓶颈和单点故障。首次介绍 Ceph 的论文(Weil 等人,OSDI 2006「Ceph: A Scalable, High-Performance Distributed File System」)将该分配表归纳为 通过名为 CRUSH 的伪随机分发函数进行替代。不记录数据在哪里,而是 计算得出。如果客户端和存储守护进程拥有相同的映射和相同的函数,任何人都能得到相同的答案。

Ceph 의 층: 한 RADOS 위의 세 얼굴 블록·파일·S3 가 모두 RADOS 객체로 내려앉는다 VM·파드의 디스크 여러 파드가 공유 S3 클라이언트 RBD 블록 장치, RWO CephFS 파일, RWX(+MDS) RGW S3·Swift HTTP librados: RADOS 에 객체를 읽고 쓰는 라이브러리 RADOS: 신뢰성 있는 자율 분산 객체 저장소 MON ×3 클러스터 맵 원본, Paxos 과반 MGR 대시보드·지표·모듈 osd.0 BlueStore → 디스크 osd.1 BlueStore → 디스크 osd.2 BlueStore → 디스크 클라이언트는 MON 에서 맵을 받고, 데이터는 OSD 와 직접 주고받는다
图:Ceph 的分层:在单个 RADOS 之上的 RBD、CephFS、RGW

图的最底层是 RADOS(Reliable Autonomic Distributed Object Store,可靠自主分布式对象存储)。Ceph 接收的数据无论是块、文件还是 S3 对象,最终都会变成 RADOS 对象(名称、字节、名称-值元数据的集合)进行存储。对象存放在没有目录的扁平命名空间中。其上的 RBD、CephFS、RGW 是让 RADOS 表现为块设备、文件系统和 S3 的翻译层,这三者与 RADOS 对话的公共库是 librados(官方文档:Architecture).

2. 构成 RADOS 的守护进程网络

Ceph 集群由四种角色各异的守护进程(后台进程)组成。在 Rook 中,一个守护进程通常就是一个 Pod。

守护进程 职责 数量 Rook 的 Pod 名称
MON(监视器) 维护集群映射的原始版本并分发给客户端。还负责认证(CephX) 通常为 3 个,建议奇数 rook-ceph-mon-a 等
MGR(管理器) 监控、编排、插件模块的端点。仪表盘和指标由此产生 1(加上 1 个备用则为 2) rook-ceph-mgr-a 等
OSD(对象存储守护进程) 在磁盘上存储对象,并直接进行复制、恢复和清理(scrub) 通常一块磁盘对应一个 rook-ceph-osd-0 等
MDS(元数据服务器) 管理 CephFS 的目录、所有者、权限等文件元数据 仅在使用 CephFS 时 每个文件系统至少有一对活动和备用节点

MON:集群映射与 Paxos 仲裁

客户端若要在 Ceph 中读取或写入任何内容,首先必须连接到 MON 并获取最新的 集群映射。官方文档解释称,集群映射实际上是五个映射的集合。

映射 包含内容 查看命令
Monitor 映射 集群 fsid,各 MON 的名称、地址、端口 ceph mon dump
OSD 映射 存储池列表、副本数、PG 数、各 OSD 的状态(up/down, in/out) ceph osd dump
PG 映射 每个 PG 的状态(例如 active+clean)、Up Set、Acting Set、使用量 (文档中无示例)
CRUSH 映射 存储设备列表、故障域层级(设备、主机、机架…)、放置规则 ceph osd getcrushmap 后 crushtool -d
MDS 映射 CephFS 元数据池、MDS 列表与状态 ceph fs dump

每个映射在每次更改时都有一个递增的版本号(epoch)。如果只有一个 MON,在该 MON 挂掉的瞬间将没有人能够获取映射,因此通常会部署多台。由于多个 MON 不能给出不同的映射,Ceph 使用名为 Paxos 的共识算法来决定映射的更改,并且必须有 多数派存活才能做出决定。根据文档的例子,1 个 MON 时 1 个算多数,3 个时 2 个,5 个时 3 个,6 个时 4 个。因此,3 个 MON 可以承受 1 个挂掉,5 个可以承受 2 个,而 4 个并不会比 3 个增加容错能力。这就是建议使用奇数个的原因(Monitor Config Reference). MON 将映射的更改记录为一个 Paxos 实例,并将结果写入 RocksDB 键值存储中。

OSD 与 BlueStore

OSD 是管理单个磁盘并在其上存储对象的守护进程。当前的默认存储引擎 BlueStore 不会在磁盘上创建 ext4 或 XFS 等文件系统,而是 直接写入设备(raw) 。对象的元数据保存在内置的 RocksDB 中。设备最多可以分为三部分(BlueStore Configuration Reference).

BlueStore 会为写入的所有数据和元数据附加 校验和(默认 crc32c),并在读取时进行检查。内存由缓存自行调节,并努力不超过 osd_memory_target(默认 4Gi,最小值 896M)。该值将在第 2 篇和第 3 篇中与 Rook 的资源配置再次相遇。

OSD 会与相邻的 OSD 互相交换 心跳(heartbeat) 。默认每 6 秒发送一次 ping(osd_heartbeat_interval),如果在 20 秒内没有收到消息(osd_heartbeat_grace),则通知 MON 该 OSD 已下线。如果下线(down)的 OSD 在 10 分钟内(mon_osd_down_out_interval)没有恢复,MON 会将该 OSD 从数据放置中剔除(out)。从那时起,开始进行重新平衡,由其他 OSD 分担该 OSD 的份额。不过,如果比 mon_osd_down_out_subtree_limit(默认 rack)更大的单元整体下线,则不会自动剔除。这是为了防止因某个机架断电而引发数据的大规模迁移(Configuring Monitor/OSD Interaction).

OSD 还会进行 清理(scrub)。在同一 PG 的副本之间比较元数据的轻量清理通常每天进行一次,而按位比较数据的深度清理默认每周进行一次。深度清理可以找出轻量清理无法捕获的磁盘坏块。

MGR 与 MDS

MGR 负责处理对 MON 而言过于沉重的工作(指标收集、仪表盘、各种模块)。MDS 仅在需要使用 CephFS 时才需要。它将元数据保存在内存中并单独处理,以免诸如列出目录或移动目录(ls, cd)之类的元数据操作干扰 OSD。文件的数据本身不经过 MDS,而是直接走向 RADOS 对象。这二者将在第 7 节中再次介绍。

3. 对象寻找 OSD 的两次计算:存储池、PG、CRUSH

存储池

存储池(pool)是容纳对象的逻辑分区。每个存储池至少决定三件事:保留几个副本(size)、使用什么 CRUSH 规则进行放置,以及将 PG 划分为多少个(pg_num)。它们是根据用途分别创建的,例如用于块存储的池、用于文件系统元数据的池、用于 S3 数据的池。

第一次计算:从对象名称到 PG

PG(placement group,放置组)是将存储池细分后的集合。Ceph 不是按单个对象,而是以 PG 为单位确定位置并进行迁移。直接使用文档中的例子,当向 liverpool 池(池编号 4)写入名为 john 的对象时,客户端会这样计算:

  1. 对对象名称 john 进行哈希运算。
  2. 用哈希值除以存储池的 PG 数量,将余数用作 PG 编号(例如 58)。
  3. 在前面附加池编号以创建 PG ID: 4.58.

第二次计算:从 PG 到 OSD 列表

接下来, CRUSH(Controlled Replication Under Scalable Hashing,可扩展散列下的受控复制)算法接收 PG ID、CRUSH 映射和存储池规则,返回「容纳该 PG 的 OSD 列表」。列表中的第一个 OSD 是 primary,其余的是 secondary(Architecture: Calculating PG IDs, Peering and Sets).

객체가 OSD 를 찾아가는 두 번의 계산 문서의 예: liverpool 풀(번호 4)의 객체 john. OSD 번호는 설명용 ① 해시로 PG 를 정한다(클라이언트 혼자 계산) 객체 john hash(john) mod pg_num → 58 PG 4.58 ② CRUSH 로 OSD 목록을 정한다 CRUSH(PG 4.58, CRUSH 맵, 풀의 규칙) [osd.1, osd.5, osd.7] 첫째 osd.1 이 primary ③ 장애 단위 host: 사본마다 다른 호스트 host A osd.0 osd.1 host B osd.4 osd.5 host C osd.6 osd.7 호스트 하나가 통째로 죽어도 남은 두 사본은 다른 호스트에 있다
图:对象名称经过 PG 到达三个 OSD 的两次计算

CRUSH 映射包含存储设备(OSD)列表,以及将这些设备组合在一起的 桶(bucket)中包含层级。默认的桶类型从下到上依次 osd, host, chassis, rack, row, pdu, pod, room, datacenter, zone, region, root 为。当部署新的 OSD 时,它会自动进入该节点名称的 host 桶下面。由于存储池的规则中记入了 failure domain(故障域),如果将其设为 host ,CRUSH 就会为每个副本挑选不同的主机。这意味着即使一整台主机挂掉,同一个 PG 的其他副本也会保留在其他主机上。在规模较大的环境中,有时会将故障域提升到 rack (CRUSH Maps).

将计算分为两步的原因是 间接层。如果客户端直接知道“对象 X 在 OSD 3 上”,那么每次添加或移除 OSD 时,都必须重新通知所有对象的位置。如果将对象固定在 PG 中,而仅让 PG 在 OSD 之间迁移,那么当集群扩容或缩容时,发生移动的单位就不是数十亿个对象,而是数千个 PG。CRUSH 本身最初在 Weil 等人的 SC 2006 论文CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data”中被介绍。

容纳 PG 的 OSD 们就“该 PG 的对象状态完全一致”达成共识的过程称为 peering 。当前负责该 PG 的 OSD 集合称为 Acting Set,其中存活的称为 Up Set。如果 primary 挂掉,下一个 OSD 将接替 primary 的角色。

多少个 PG 才合适:PG 自动调节器

如果 PG 太少,数据将无法在 OSD 之间均匀分布;如果太多,peering 流量和内存就会增加。过去是由人工计算来确定 pg_num ,但现在 PG 自动调节器会根据每个存储池的使用情况来决定Placement Groups).

设置 默认值 含义
osd_pool_default_pg_autoscale_mode on 新存储池的自动调节模式。 on 如果是 pg_num ,则未确定
mon_target_pg_per_osd 100 的新存储池将从 1 个 PG 开始
调整阈值 3倍 仅当推荐值与当前值相差超过 3 倍时才进行更改(调增时,如果推荐值为 512 或 1024,则降至 1.0,如果是 2048 则降至 2.0)
bulk 存储池显示 关闭 开启后,会被视为“大型存储池”,从一开始就分配较多的 PG

ceph osd pool autoscale-status 会显示每个存储池的推荐值(NEW PG_NUM)。我们在第 3 篇中将看到的 Rook Chart 的对象存储数据池会 bulk: "true" 保持开启状态。

4. 复制与纠删码

保护数据的方法有两种。

复制(replication)将同一个对象完整地保留多份。 osd_pool_default_size 的默认值是 3。 min_size 指的是“必须写入至少多少个副本才能向客户端报告成功”,默认值 0 意味着 size - (size / 2) (因为是整数除法,所以如果 size 为 3,则为 2)。因此,size 为 3 的存储池即使丢失一个副本也能继续读写,但如果丢失两个,该 PG 就会停止 I/OPool, PG and CRUSH Config Reference).

纠删码(erasure coding, EC)将对象切割为 K 个数据块,并计算出 M 个编码块,将总共 K+M 个块放置在不同的 OSD 上。只要剩下 K 个块就能恢复出原始内容。空间成本为 (K+M)/KErasure Code).

方式 可承受的同时故障数 1 GiB 原始数据所需的空间 官方文档评价
复制 size 3 2 3.0 GiB 默认值
EC K=2, M=1 1 1.5 GiB 最简单的 EC。强烈建议生产数据不要使用 M=1
EC K=2, M=2 2 2.0 GiB EC 存储池的默认配置文件
EC K=4, M=2 2 1.5 GiB 如果难以选择配置文件,建议选择此项

文档指出,不要把 EC 误认为是免费的午餐。特别是在使用 HDD 以及恢复/回填(backfill)过程中,性能损失非常大。官方警告称,如果 M=1,在维护期间将无法写入数据,若发生复合故障则会丢失数据。此外,由于 EC 存储池不支持 omap(附加到对象的键值数据),RBD 和 CephFS 必须将元数据放在复制池中,而将数据放在 EC 池中,并且必须在 EC 池中启用 allow_ec_overwrites 。EC 池的 min_size 建议设为 K+1 或更高。配置文件在创建存储池后无法更改,若要更改,必须创建新存储池并迁移数据。

还有一点,如果故障域的数量少于副本数,CRUSH 将无法满足规则。如果故障域是 host 且只有三台主机,那么复制 size 3 会在每台主机上放置一个副本,K+M=3 的 EC 2+1 也能完美契合。但相反,如果一台主机挂掉,由于没有第四台主机来迁移并存放副本,因此在该主机恢复之前,PG 将一直处于副本不足(degraded)的状态。

5. 写入路径:为什么客户端要直接与 OSD 对话

복제 풀(size 3)에 객체 하나를 쓰는 여섯 걸음 MON 클러스터 맵 원본 ① 인증하고 맵 받기 클라이언트 ② 혼자 계산 primary OSD osd.1 (PG 4.58) ③ 쓰기 ⑥ 성공 ④ 복제 ⑤ 확인 osd.5 secondary osd.7 secondary MON 은 데이터 경로 밖 복제는 클라이언트가 아니라 primary 가 한다 클라이언트는 한 번만 보낸다
图:向复制池写入一个对象的六个步骤

官方文档中关于向多副本存储池(Replicated Pool)写入对象的流程如下:

  1. 客户端连接到 MON,通过 CephX 进行认证,并获取最新的集群映射(Cluster Map)。
  2. 客户端通过第 3 节中的两个计算步骤,自行得出 PG 和 OSD 列表。此计算过程不需要与任何人通信。
  3. 客户端将对象发送给 PG 的 primary OSD 。
  4. primary 使用自身的 CRUSH 映射副本找到 secondary OSD 并复制对象(文档中将此复制称为 subop)。
  5. 各个 secondary 完成存储后,向 primary 发送确认信息。
  6. primary 向客户端通知写入成功。

由于复制操作是由 primary 而不是客户端完成的,因此客户端的网络只需发送一次数据。而且,由于数据不经过中央网关或元数据服务器,中央组件不会成为瓶颈或单点故障。MON 仅分发映射,不参与数据路径。这就是第 1 节中提到的“以计算代替列表”的实际效果。

在纠删码存储池(EC Pool)中,primary 接收对象并计算分片,然后将分片分发写入 Acting Set 中的各个 OSD。当所有分片都得到确认后,便会向客户端通告成功。

映射是会发生变化的。当 OSD 宕机或有新 OSD 加入时,OSD 映射的 epoch 会增加,即使是同一个 PG,CRUSH 计算的结果也会不同。文档指出,比起向服务器询问对象位置的交互,由客户端直接计算要快得多,并且 OSD 的故障通常由相邻 OSD 先行察觉并报告给 MON,因此 MON 可以保持为一个轻量级进程。关于获取旧映射的客户端与 OSD 究竟通过哪些消息来同步映射,本文未作探讨(未确认)。

6. 其上的三个面孔:RBD、CephFS、RGW

名称 呈现形态 核心运作机制 在 Kubernetes 中的应用
RBD(RADOS Block Device) 块设备(磁盘) 将磁盘镜像条带化(striping)并分散存储到多个 RADOS 对象中。支持精简配置(Thin Provisioning)和快照 单节点挂载的 RWO 卷、虚拟机磁盘
CephFS POSIX 文件系统 元数据由 MDS 管理,文件数据存储为 RADOS 对象。通过内核客户端或 FUSE 挂载 多 Pod 共享的 RWX 卷
RGW(RADOS Gateway) HTTP 对象存储 兼容 S3 和 Swift 的 REST API。拥有自带的用户数据库、认证与访问控制机制 对象存储桶申领(ObjectBucketClaim)、S3 客户端

有几个要点值得一提。

7. 总结:下一篇预告

概念 一句话概括
RADOS 块、文件、S3 都构建其上的对象存储
MON 集群映射的源头,通过 Paxos 多数表决决定。3 个节点时允许坏 1 个
OSD + BlueStore 一块磁盘对应一个守护进程,不通过文件系统而直接操作裸设备,支持校验和
存储池 (Pool) → PG → OSD 通过哈希计算 PG,通过 CRUSH 计算 OSD 列表。列表首位即为 primary
故障域 (Failure Domain) 将副本分散存放的隔离级别(如主机、机架)
多副本 vs 纠删码 (EC) 3.0 倍空间容忍 2 个故障,或 (K+M)/K 倍空间容忍 M 个故障
写入路径 客户端 → primary → secondary → primary → 客户端。MON 不在数据路径上

以上讲述的都是 Ceph 本身的内容。在 Kubernetes 之上由谁来拉起这些守护进程、由谁来选择磁盘、当 MON 宕机时由谁来重建,这些正是 第 2 篇的主题。而如何通过各项参数来调整这些行为,则将在 第 3 篇中通过逐行阅读 values.yaml 的 Chart 来进行探讨。

小企鹅寄语: Ceph 里可没有用来询问“这个文件在哪里?”的前台服务。取而代之的是,每个人都随身带着同一份地图(集群映射)和同一个计算法则(CRUSH)。所以,前台服务既不会拥堵,也不会瘫痪。只要地图是最新的就行!

参考资料

所有文档均于 2026-10-29 查阅。Ceph 文档版本为 Tentacle (v20)。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论