一个机房断电,里面所有节点同时宕机——如果数据只存在这个机房,业务直接停摆。RAID 能扛住单盘故障,但扛不住整机柜、整个机房的故障。跨节点的数据冗余和调度,是分布式文件系统要解决的核心问题。
Ceph 用 CRUSH 算法计算数据应该放在哪个节点,不需要查中心化元数据表;Gluster 用 DHT 哈希把文件映射到节点,无元数据服务器架构消除了单点。两者思路不同,但目标一致:跨节点组织数据,让存储容量和性能随节点数线性扩展。
一、分布式文件系统概述
1.1 为什么需要分布式文件系统
| 需求 | 单机方案 | 分布式方案 |
|---|---|---|
| 容量 > 100TB | 加磁盘 | 加节点 |
| IOPS > 10万 | 加 SSD | 加节点 |
| 高可用 | RAID | 多副本/纠删码 |
| 弹性扩展 | 换大机器 | 加节点 |
1.2 分布式文件系统的核心挑战
| 挑战 | 说明 | 解决方案 |
|---|---|---|
| 元数据管理 | 文件→块的映射 | 中心化 vs 去中心化 |
| 数据放置 | 数据放在哪个节点 | 一致性哈希 vs CRUSH |
| 一致性 | 多副本间数据一致 | Quorum + Paxos/Raft |
| 故障恢复 | 节点故障后数据恢复 | 自动重平衡 |
| 扩展性 | 加节点后数据均衡 | 动态重分布 |
二、Ceph 架构
2.1 Ceph 整体架构
2.2 RADOS 架构
RADOS(Reliable Autonomic Distributed Object Store)是 Ceph 的核心:
| 组件 | 说明 | 数量 |
|---|---|---|
| Monitor (MON) | 维护集群映射表,提供一致性保证 | 3 或 5 个(奇数) |
| OSD (Object Storage Daemon) | 存储数据,处理读写,做副本复制 | 每块磁盘一个 |
| MDS (Metadata Server) | 管理文件系统元数据(CephFS 专用) | 1+ 个 |
2.3 数据映射流程
# Ceph 数据映射流程def ceph_map_object(object_id, pool_id, cluster_map): """将对象映射到 OSD""" # 1. 计算归置组(PG) pg_id = hash(object_id) % pg_num
# 2. 使用 CRUSH 算法计算 OSD 集合 osd_set = crush_algorithm(pg_id, cluster_map, pool_rules)
# 3. Primary OSD 处理读写 primary_osd = osd_set[0]
return { "object_id": object_id, "pg_id": pg_id, "osd_set": osd_set, "primary": primary_osd, }三、CRUSH 算法
3.1 CRUSH 的设计目标
CRUSH(Controlled Replication Under Scalable Hashing)是 Ceph 的核心数据放置算法:
| 目标 | 说明 | 为什么 |
|---|---|---|
| 确定性 | 相同输入总是得到相同输出 | 客户端可独立计算数据位置 |
| 均衡性 | 数据均匀分布在所有 OSD 上 | 避免热点 |
| 故障域感知 | 副本分布在不同故障域 | 容忍故障域故障 |
| 最小迁移 | 增减 OSD 时最小化数据迁移 | 减少恢复开销 |
3.2 CRUSH 算法流程
# CRUSH 算法简化实现def crush_choose(pgid, cluster_map, rule, num_replicas): """CRUSH 选择 OSD""" result = []
for replica in range(num_replicas): # 1. 从故障域的根开始 current = cluster_map.root
# 2. 沿故障域层级向下选择 for failure_domain in rule.failure_domains: # host → rack → row → room children = get_children(current, failure_domain)
# 3. 使用 straw 桶算法选择子节点 selected = straw_bucket_select( children, pgid, replica )
current = selected
# 4. 检查是否已在结果中(避免同一 OSD) if current.osd not in result: result.append(current.osd)
return result
# Straw 桶算法def straw_bucket_select(items, pgid, replica): """Straw 桶选择算法""" # 为每个 item 计算一个 straw 长度 # 长度与 item 的权重成正比 max_straw = -1 selected = None
for item in items: # hash(pgid, replica, item_id) → straw 长度 straw = hash(pgid, replica, item.id) & item.weight
if straw > max_straw: max_straw = straw selected = item
return selected3.3 CRUSH vs 一致性哈希
| 维度 | 一致性哈希 | CRUSH |
|---|---|---|
| 数据放置 | 环形哈希 | 层级化选择 |
| 故障域感知 | 无 | 有 |
| 权重支持 | 虚节点 | 原生支持 |
| 元数据依赖 | 无 | 无(可计算) |
| 迁移量 | 最小 | 较小 |
| 适用场景 | 缓存/简单 KV | 分布式存储 |
四、Ceph 数据一致性
4.1 副本写入流程
4.2 Quorum 写入
| 副本数 | 写入 Quorum | 读 Quorum | 说明 |
|---|---|---|---|
| 2 | 2 | 1 | 不推荐(无法处理脑裂) |
| 3 | 2 | 1 | 默认,容忍 1 节点故障 |
| 4 | 3 | 2 | 容忍 1 节点故障 |
| 5 | 3 | 2 | 容忍 2 节点故障 |
五、Gluster 架构
5.1 Gluster 整体架构
5.2 Gluster 卷类型
| 卷类型 | 说明 | 空间利用率 | 容错 | 适用场景 |
|---|---|---|---|---|
| Distribute | 分布式(哈希分片) | 100% | 无 | 大容量 |
| Replicate | 镜像(副本) | 50% | 有 | 高可用 |
| Disperse | 纠删码 | (k)/(k+m) | 有 | 大容量+高可用 |
| Distribute-Replicate | 分布式+镜像 | 50% | 有 | 通用 |
5.3 Gluster 卷管理实战
# 创建分布式副本卷(2 副本,跨 3 节点)gluster volume create gv0 replica 2 \ node1:/data/brick1 node2:/data/brick1 \ node3:/data/brick1 node1:/data/brick2 \ node2:/data/brick2 node3:/data/brick2
# 启动卷gluster volume start gv0
# 查看卷信息gluster volume info gv0# Volume Name: gv0# Type: Distributed-Replicate# Number of Bricks: 3 x 2 = 6# Status: Started
# 在线扩容:添加 Brickgluster volume add-brick gv0 node4:/data/brick1 node5:/data/brick1
# 触发重平衡gluster volume rebalance gv0 start5.4 Ceph vs Gluster 对比
| 维度 | Ceph | Gluster |
|---|---|---|
| 架构 | 中心化元数据(MON) | 去中心化(DHT) |
| 数据放置 | CRUSH 算法 | DHT 哈希 |
| 接口 | 块/文件/对象 | 文件/对象 |
| 元数据 | MDS(CephFS) | DHT + 扩展属性 |
| 一致性 | 强一致 | 最终一致 |
| 小文件 | 一般 | 较好 |
| 大文件 | 优秀 | 优秀 |
| 运维复杂度 | 高 | 中 |
| 社区活跃度 | 高 | 中 |
六、数据均衡与恢复
6.1 Ceph 数据重平衡
# Ceph 集群状态ceph status# cluster:# id: xxx# health: HEALTH_OK# services:# mon: 3 daemons# osd: 12 osds: 12 up, 12 in# data:# pools: 3 pools, 576 pgs# objects: 1.2M objects, 450 GB# usage: 12 TiB used, 108 TiB / 120 TiB avail# pgs: 576 active+clean
# 添加新 OSD 后的自动重平衡ceph osd tree# ID CLASS WEIGHT TYPE NAME# -1 0.52340 root default# -3 0.17446 host node1# 0 hdd 0.08723 osd.0 up 1.00000# 1 hdd 0.08723 osd.1 up 1.00000# -5 0.17446 host node2# 2 hdd 0.08723 osd.2 up 1.00000# 3 hdd 0.08723 osd.3 up 1.00000
# 重平衡进度ceph -w# pgmap v1234: 576 pgs: 576 active+clean; 450 GB data, 12 TiB used6.2 重平衡策略
| 参数 | 默认值 | 说明 |
|---|---|---|
osd_max_backfills | 1 | 同时恢复的 PG 数量 |
osd_recovery_max_active | 3 | 每个 OSD 同时活跃的恢复操作 |
osd_recovery_op_priority | 3 | 恢复操作优先级(1–63) |
osd_recovery_sleep | 0 | 恢复操作间睡眠时间 |
重平衡期间,恢复操作会占用大量 I/O 和网络带宽,可能影响前台读写性能。Ceph 通过限制恢复速率来减轻影响,但恢复时间会相应延长。
七、实战:Ceph 部署与操作
7.1 Ceph 集群部署
# 使用 cephadm 部署 Ceph# 1. 安装 cephadmcurl --silent --remote-name \ https://download.ceph.com/rpm-18.2.0/el9/noarch/cephadmchmod +x cephadm
# 2. 引导集群./cephadm bootstrap --mon-ip 192.168.1.1
# 3. 添加节点ceph orch host add node2 192.168.1.2ceph orch host add node3 192.168.1.3
# 4. 添加 OSDceph orch daemon add osd node1:/dev/sdbceph orch daemon add osd node2:/dev/sdbceph orch daemon add osd node3:/dev/sdb
# 5. 创建池ceph osd pool create mypool 128 128 replicatedceph osd pool set mypool size 3ceph osd pool set mypool min_size 27.2 Ceph 性能测试
# 使用 rados bench 测试性能# 写入测试rados bench -p mypool 60 write --no-cleanup
# 读取测试rados bench -p mypool 60 seq
# 随机读取测试rados bench -p mypool 60 rand
# 使用 fio 测试 RBD 性能fio --name=ceph-rbd --ioengine=rbd --pool=mypool \ --rbdname=test-volume --size=10G \ --rw=randwrite --bs=4k --iodepth=32 --numjobs=4八、CephFS 元数据管理
8.1 MDS 与元数据池
CephFS 的元数据由 MDS(Metadata Server)管理,元数据和数据存储在不同的 RADOS 池中:
| 组件 | 说明 | 存储位置 |
|---|---|---|
| MDS Journal | 元数据变更的 WAL,保证崩溃可恢复 | 元数据池(独立对象) |
| Capabilities | 客户端缓存授权,控制缓存一致性 | MDS 内存 → 客户端内存 |
| 元数据池 | inode、目录项(dentry)、扩展属性 | RADOS 池(通常用副本池) |
| 数据池 | 文件内容,按对象条带化存储 | RADOS 池(可用副本池或 EC 池) |
元数据池和数据池分离的设计,使得元数据可以放在高性能 SSD 副本池上,而数据放在大容量 HDD EC 池上——不同类型的数据获得最合适的存储介质和冗余策略。
8.2 Capabilities 机制
Capabilities 是 MDS 颁发给客户端的缓存授权令牌。客户端持有 Cap 时,可以在本地缓存元数据和文件数据,减少与 MDS 的交互。当其他客户端需要访问同一文件时,MDS 会召回(revoke)前一个客户端的 Cap,确保缓存一致性。这种设计在单客户端大量顺序读的场景下效果最好——MDS 只需颁发一次 Cap,后续读取全部命中本地缓存。
九、Ceph 纠删码池
9.1 EC 池配置
Ceph 支持纠删码池,用更低的存储开销提供与副本池同等的容错能力:
# 创建纠删码 Profileceph osd erasure-code-profile set my-ec-profile \ k=6 m=3 \ crush-failure-domain=host \ plugin=jerasure \ technique=reed_sol_van
# 基于该 Profile 创建池ceph osd pool create ec-pool 128 128 erasure my-ec-profile
# 查看池信息ceph osd pool ls detail | grep ec-pool# pool 4 'ec-pool' erasure profile my-ec-profile size 9 min_size 7| 配置 | k (数据块) | m (校验块) | 容忍故障数 | 空间利用率 | 最小写入 OSD |
|---|---|---|---|---|---|
| 2+1 | 2 | 1 | 1 | 67% | 3 |
| 4+2 | 4 | 2 | 2 | 67% | 6 |
| 6+3 | 6 | 3 | 3 | 67% | 9 |
| 8+4 | 8 | 4 | 4 | 67% | 12 |
| 12+4 | 12 | 4 | 4 | 75% | 16 |
9.2 EC 池的覆盖写问题
纠删码池原生不支持覆盖写(overwrite)。对已有对象的随机修改需要读取所有 k 个数据块、重新计算校验块、再写回——即”读-改-写”(read-modify-write),开销远大于副本池的原地更新。Ceph 从 Luminous 版本开始支持 EC 池的覆盖写(需开启 allow_ec_overwrites),但性能仍不如副本池。
部分写(partial write)的惩罚尤其严重:修改 4KB 数据可能需要读取 6×64KB=384KB 的数据块,计算校验后再写回 9 个 OSD。对于随机写入密集的工作负载,应使用副本池而非 EC 池。EC 池更适合对象存储、冷数据归档等追加写入场景。
十、PG 状态与恢复流程
10.1 PG 状态机
PG(Placement Group)是 Ceph 数据管理的基本单位。每个 PG 有自己的状态机:
| PG 状态 | 含义 | 客户端影响 |
|---|---|---|
| Creating | PG 正在创建 | 不可读写 |
| Peering | OSD 间协商 PG 一致性 | 不可读写 |
| Active | PG 可提供服务 | 可读写 |
| Clean | 所有副本完全同步 | 可读写 |
| Degraded | 部分副本缺失 | 可读写(降级) |
| Recovering | 正在恢复数据 | 可读写(可能变慢) |
| Backfilling | 新 OSD 回填数据 | 可读写(可能变慢) |
| Down | Primary OSD 不可达 | 不可读写 |
10.2 Peering 流程
Peering 是 PG 从不一致到一致的核心过程:Primary OSD 收集所有 Acting Set 中 OSD 的 PG 日志,通过比较日志确定权威日志(authority log),计算出缺失的对象列表,然后进入 Active 状态。Peering 期间 PG 不可读写,通常在秒级完成。
十一、Ceph RBD 详解
11.1 RBD 条带化与分层
RBD(RADOS Block Device)将块设备镜像条带化为对象存储在 RADOS 池中:
# RBD 操作# 创建镜像rbd create mypool/myimage --size 1T --image-feature layering,exclusive-lock
# 创建快照rbd snap create mypool/myimage@snap1
# 保护快照(克隆前必须保护)rbd snap protect mypool/myimage@snap1
# 基于快照克隆rbd clone mypool/myimage@snap1 mypool/clone1
# 查看克隆关系rbd children mypool/myimage@snap1
# 合并克隆(flatten)rbd flatten mypool/clone1RBD 的分层克隆采用 CoW(Copy-on-Write)策略:克隆镜像初始不占空间,读取时直接读父镜像,写入时将修改的对象复制到克隆镜像。这种设计使得从同一个基础镜像创建数百个虚拟机只需极少的额外存储。
十二、故障恢复场景
12.1 OSD 故障恢复流程
当一个 OSD 故障时,Ceph 的恢复流程如下:
OSD 故障后的恢复时间取决于数据量和恢复速率配置。默认 osd_max_backfills=1 限制每个 OSD 同时只做 1 个 PG 的回填,这是为了保护前台 I/O 性能。如果需要加速恢复,可以临时调大该值,但会显著影响客户端读写延迟。在Ch9 WAL 与崩溃恢复中讨论了单机崩溃恢复,这里的 PG Peering + Backfill 是分布式层面的恢复机制。
十三、总结
| 主题 | 核心要点 | 关键词 |
|---|---|---|
| Ceph 架构 | RADOS + MON + OSD + MDS,块/文件/对象三种接口 | RADOS, CRUSH |
| CRUSH 算法 | 确定性、均衡、故障域感知的数据放置 | Straw 桶, 层级选择 |
| 数据一致性 | Quorum 写入保证强一致性 | 2/3 写, 1/3 读 |
| Gluster | 去中心化架构,DHT 哈希放置 | DHT, 弹性卷 |
| CephFS 元数据 | MDS Journal + Capabilities,元数据与数据池分离 | Cap, Journal |
| 纠删码池 | 低存储开销容错,但不适合随机写入 | EC, 读-改-写 |
| PG 状态机 | Peering → Active → Clean,故障触发 Recovering/Backfilling | Peering, Backfill |
| RBD | 条带化 + 分层克隆 + 快照,CoW 策略 | Layering, Clone |
| 故障恢复 | OSD down → PG Peering → Backfill → Active+Clean | 恢复速率 |
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






