第 7/20 天
引言:CRUSH Map 在 Ceph 生产环境中的核心地位
在前 6 天的连载中,我们已经完成了 Ceph Squid v19 集群在 Ubuntu 24.04 LTS 上的引导初始化、Monitor/Manager 节点部署以及基于 BlueStore 的 OSD 存储节点上线。集群已经可以写入数据,但你是否想过:当客户端写入一个对象时,Ceph 如何决定这个对象最终落在哪几个 OSD 上? 这个问题的答案就是 CRUSH——Ceph 分布式存储的灵魂算法。
CRUSH(Controlled Replication Under Scalable Hashing,可控可扩展哈希复制)是 Ceph 区别于传统集中式元数据存储系统的核心机制。传统分布式存储(如 HDFS、Ceph 的早期设计思路)依赖一个中心化的元数据服务器来记录”哪个数据放在哪个节点”,这个元数据服务器会成为单点瓶颈和性能天花板。CRUSH 则通过一个可计算的哈希算法,让任何客户端或 OSD 都能独立计算出任意对象的位置,无需查询中心节点。
而承载 CRUSH 算法运行所需的全部拓扑信息的数据结构,就是 CRUSH Map。它定义了集群的物理拓扑结构(机房、机架、主机、OSD)、故障域层级、以及数据分布规则(CRUSH Rule)。在生产环境中,CRUSH Map 的规划质量直接决定了集群的数据可靠性、故障隔离能力和扩容时的数据迁移效率。本篇将深入解析 CRUSH Map 的架构原理,并通过完整实战演示如何定制 CRUSH Map 与故障域规划。

架构与原理:CRUSH 算法与 CRUSH Map 数据结构
CRUSH 算法核心思想
CRUSH 算法的核心流程可以用一句话概括:给定一个对象 ID(x)和集群拓扑(CRUSH Map),通过伪随机哈希函数 + 节点权重,确定性地计算出该对象的所有副本应放置在哪些 OSD 上。
整个计算过程不需要任何中心化元数据查询,关键特性如下:
| 特性 | 说明 | 生产价值 |
|---|---|---|
| 确定性 | 同样的输入永远得到同样的输出 | 所有节点计算结果一致,无需协调 |
| 去中心化 | 任何节点都能独立计算 | 消除元数据服务器单点瓶颈 |
| 稳定性 | 拓扑变化时仅迁移最小数据量 | 扩容/缩容时数据迁移可控 |
| 权重感知 | 按 OSD 容量比例分布数据 | 大容量盘承载更多数据 |
| 故障域感知 | 按 Bucket 层级隔离副本 | 避免单机架/机房故障丢数据 |
CRUSH Map 的四大组成部分
CRUSH Map 是一个文本/二进制结构,包含四个关键部分:
1. Devices(设备列表):列出集群中所有 OSD,每条形如 device 0 osd.0,编号从 0 开始。这是 CRUSH 计算的”叶子节点”。
2. Bucket Types(Bucket 类型):定义拓扑层级结构。Ceph 内置了从粗到细的层级类型:
root → 整个集群或集群逻辑分区的根
datacenter → 数据中心级别
room → 机房房间
row → 机房行
pod → 机房Pod(一组机架)
rack → 机架
host → 物理主机
chassis → 机箱(刀片服务器场景)
osd → 单个OSD设备(叶子,不可再分)
这些类型形成树形继承关系,每个子节点的故障不会影响父节点其他分支,这就是故障域隔离的基础。
3. Buckets(Bucket 实例):按照 Bucket Types 定义的层级,将实际的 OSD 组织成树。例如:
root default {
id -1
alg straw2
hash 0
item rack rack-1 weight 10.0
item rack rack-2 weight 10.0
}
rack rack-1 {
id -3
alg straw2
hash 0
item host node-1 weight 5.0
item host node-2 weight 5.0
}
host node-1 {
id -5
alg straw2
hash 0
item osd.0 weight 1.0
item osd.1 weight 1.0
}
这里的关键参数 alg straw2 是 Ceph 推荐的 Bucket 算法,相比早期的 uniform/list/tree 算法,straw2 在节点增删时数据迁移量最小,是生产环境的标准选择。
4. Rules(CRUSH 规则):定义数据如何从 CRUSH Map 树中选取副本位置。一条典型规则如下:
rule replicated_rule {
id 0
name "replicated_rule"
steps [
step take root default
step chooseleaf firstn 0 type host
step emit
]
}
规则的核心是 take → choose/chooseleaf → emit 三步:
– take:从某个 Bucket 开始选取
– chooseleaf firstn 0 type host:选取 N 个 host 类型的子树,并在每个 host 下递归选一个叶子 OSD(即 chooseleaf 的 leaf 含义)
– type host:这是故障域类型——意味着同一对象的多个副本不会落在同一台 host 上

故障域规划的设计哲学
故障域是 CRUSH Map 规划中最重要的生产概念。故障域 = 你能容忍同时发生故障而不丢数据的最大物理边界。 如果 CRUSH Rule 的 type host 设为 host,那么同主机 OSD 全部损坏时,只要副本数足够,数据不丢。如果设为 rack,那么同机架断电也不丢数据。
生产环境的常见选择:
| 副本数 | 推荐 chooseleaf type | 故障域含义 | 适用场景 |
|---|---|---|---|
| 3 副本 | host | 单机故障不丢数据 | 单机房、节点数≥3 |
| 3 副本 | rack | 单机架故障不丢数据 | 多机架、机架数≥3 |
| 3 副本 | datacenter | 单机房故障不丢数据 | 双机房/三机房灾备 |
| 纠删码 4+2 | rack | 1-2 个机架故障可恢复 | 大容量冷数据 |
部署实战:CRUSH Map 导出、定制与故障域配置
本节在已部署好的 Squid v19 集群上演示完整的 CRUSH Map 定制流程。所有操作在部署节点(拥有 cephadm 的 admin 节点)上执行。
1. 环境准备:查看当前 CRUSH Map 与集群拓扑
首先确认集群状态和当前默认 CRUSH Map:
# 确认集群健康状态
ceph -s
# 查看当前 CRUSH 树结构(人类可读层级视图)
ceph osd tree
# 查看当前所有 CRUSH Rule
ceph osd crush rule ls
# 输出示例:
# replicated_rule
# erasure-code
# 查看某条 rule 的详细步骤
ceph osd crush dump | python3 -m json.tool | head -80
典型 ceph osd tree 输出展示了 root → host → osd 的层级和权重:
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT AFFINITY
-1 27.00000 root default
-7 9.00000 host ceph-node1
1 hdd 9.00000 osd.1 up 1.00000 1.00000
-5 9.00000 host ceph-node2
2 hdd 9.00000 osd.2 up 1.00000 1.00000
-3 9.00000 host ceph-node3
0 hdd 9.00000 osd.0 up 1.00000 1.00000
2. 导出 CRUSH Map 为可编辑文本
要定制 CRUSH Map,需要先导出反编译的文本格式:
# 获取当前编译后的 CRUSH Map 二进制
ceph osd getcrushmap -o /tmp/crushmap-compiled.bin
# 反编译为文本格式(需 ceph-crush-location 或源码 crushtool)
# Squid 版本:直接使用 crushtool
crushtool -d /tmp/crushmap-compiled.bin -o /tmp/crushmap-decompiled.txt
# 查看反编译文本
head -100 /tmp/crushmap-decompiled.txt
反编译文本的头部 tunable 参数控制 CRUSH 算法的行为,例如:
# begin crush map
tunable choose_local_tries 0
tunable choose_local_depth_to_fallback 0
tunable choose_try_fall_tree 1
tunable choose_leaf_stable 1
# devices
device 0 osd.0 class hdd
device 1 osd.1 class hdd
device 2 osd.2 class hdd
# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 zone
type 10 region
type 11 root
# buckets
host ceph-node1 { … }
host ceph-node2 { … }
host ceph-node3 { … }
root default { … }
3. 定制 CRUSH Map:引入机架故障域
假设我们的 3 台 OSD 主机分布在 3 个独立机架(rack-a / rack-b / rack-c),希望副本按机架隔离。下面直接通过 ceph CLI 添加 rack 层级,而不手动改文本(手动编译注入风险高,CLI 是生产推荐方式):
# 1) 创建 3 个 rack bucket
ceph osd crush add-bucket rack-a
ceph osd crush add-bucket rack-b
ceph osd crush add-bucket rack-c
# 2) 将每个 host 移动到对应的 rack 下(注意权重单位为TB)
ceph osd crush move ceph-node1 root=default rack=rack-a
ceph osd crush move ceph-node2 root=default rack=rack-b
ceph osd crush move ceph-node3 root=default rack=rack-c
# 3) 验证新的层级结构
ceph osd tree
# 现在应看到 default -> rack-x -> host -> osd 的 4 层结构
4. 创建基于机架故障域的 CRUSH Rule
# 创建新规则:replicated_rule_rack,副本按 rack 故障域分布
ceph osd crush rule create-replicated
replicated_rule_rack
default
rack
hdd
# 查看规则是否生效
ceph osd crush rule ls
# replicated_rule
# replicated_rule_rack
# 查看规则详细步骤
ceph osd crush rule dump replicated_rule_rack
5. 验证 CRUSH 分布正确性
最关键的一步:模拟数据分布,验证副本确实分布在不同的 rack 上:
# 生成测试 PG 分布报告(num réplicas = 3)
ceph osd pool create test-crush 8 8 replicated_rule_rack
# 写入测试对象并查看实际落点
rados -p test-crush put testobj /etc/hosts
ceph osd map test-crush testobj
# 输出形如:
# test-crush object testobj -> pg 3.xxx (3.0) -> up [2,1,0] acting [2,1,0]
# 这里的 [2,1,0] 应来自 3 个不同的 rack
# 用 crushtool 做大规模模拟(更直观的分布验证)
crushtool -i /tmp/crushmap-compiled.bin –test –num-rep 3
–min-x 0 –max-x 1024 –output /tmp/crush-test-out.json 2>/dev/null
# 输出 json 中每个 OSD 承载的对象数应接近均衡
# 清理测试池
ceph osd pool delete test-crush test-crush –yes-i-really-really-mean-it
配置参数详解:CRUSH 相关关键参数
CRUSH 行为受多个全局 tunable 和 per-pool 参数控制。下面列出生产环境最需要关注的几项:
| 参数 | 默认值 | 说明 | 生产建议 |
|---|---|---|---|
osd_pool_default_crush_replicated_ruleset |
0 | 新建副本池默认使用的 rule id | 指向自定义 rack/DC rule |
osd_crush_choose_leaf_type |
1 (host) | chooseleaf 默认故障域类型 | 跨机架设为 3 (rack) |
osd_crush_update_on_start |
true | OSD 启动时自动更新 CRUSH 权重 | 保持 true,但注意其会重置自定义权重 |
mon_max_mdsmap_epochs |
500 | 与 CRUSH 无直接关系,但影响重平衡速度 | — |
osd_pool_default_size |
3 | 默认副本数 | 生产最低 3 |
osd_pool_default_min_size |
2 | 降级写入下限 | 推荐 size-1 |
mon_cluster_def_pool_default_crush_rule |
0 | 全集群默认 rule | 跨机架部署建议改为 rack rule |
设置示例:
# 设置全集群默认使用基于 rack 的副本规则(避免新池误用 host 故障域)
ceph config set global osd_pool_default_crush_replicated_ruleset 1
# 确认
ceph config get mon osd_pool_default_crush_replicated_ruleset
# 输出: 1
此外,CRUSH Bucket 的 alg(算法)和 hash(哈希函数)也需关注。straw2 是 Squid 的默认且推荐算法,相比 straw 修正了权重变化时的数据迁移问题,生产环境切勿改回旧算法。hash 0 表示使用 rjenkins 哈希,这是唯一支持的值。
生产环境注意事项与踩坑提示
1. 切勿在生产环境直接手动编译注入 CRUSH Map
手动 crushtool -c 编译 + ceph osd setcrushmap 注入会绕过所有安全检查,一旦层级写错会导致大规模数据迁移甚至 PG 不可用。生产推荐使用 ceph osd crush 系列 CLI 子命令逐步调整,每步都可回滚。
2. 权重单位是 TiB,不是 GB
ceph osd crush add 和 move 的 weight 参数单位是 TiB(1024 GiB)。常见错误是把 9TB 盘写成 9.0 实际应是 0.008(9TiB = 9.0 即可,但若误用 GB 会差 10%)。生产建议始终使用 ceph osd crush add ... 由 cephadm 自动计算,不手动指定。
3. 跨机架故障域必须保证机架数 ≥ 副本数
如果设置了 chooseleaf type rack 但只有 2 个机架,3 副本将无法分布,Ceph 会触发 pool has insufficient devices 告警并拒绝创建。扩容到足够机架前,先用 host 故障域。
4. CRUSH Map 变更会触发数据重平衡
任何层级调整(add-bucket / move)都会让 CRUSH 重新计算所有 PG 的目标位置,触发数据迁移。生产环境务必在业务低峰期执行,并配合 osd_max_backfills、osd_recovery_max_active 限速:
# 临时降低重平衡速度,保护业务 IOPS
ceph config set osd osd_max_backfills 1
ceph config set osd osd_recovery_max_active 1
# 完成后恢复(默认 3 / 3)
ceph config rm osd osd_max_backfills
ceph config rm osd osd_recovery_max_active
5. 设备类(device class)与 CRUSH 层级的关系
Squid 版本默认启用 device class(hdd/ssd/nvme 自动分类)。device class 会在 CRUSH Map 中自动创建 shadow root(如 ~hdd),与自定义 rack/host 层级是正交的。如同时使用两者,确保 pool 的 rule 选择正确,否则可能副本全落同一物理盘类型。
6. 安全考量
CRUSH Map 描述了集群物理拓扑,包含主机名、机架编号等基础设施信息。生产环境应限制 ceph osd crush dump 的访问权限,仅授予运维 admin 角色,避免通过普通应用客户端枚举集群拓扑后进行定向攻击。

常见问题(FAQ)
Q1:修改 CRUSH Map 后,已有数据需要手动迁移吗?
A:不需要。CRUSH 是惰性计算的,PG 的 up/acting 集合在下次 PG peering 时按新 Map 重新计算。Ceph 会自动触发数据迁移(reweight/backfill)使实际分布与新规则一致。但要注意迁移过程占用网络和磁盘 IO,需限速。
Q2:跨机房灾备时,CRUSH Rule 应该如何配置?
A:在多机房场景下,先在 CRUSH Map 中建立 datacenter 层级,把每个机房的主机归到对应 DC 下,然后创建规则 chooseleaf type datacenter。3 副本跨 3 机房时,单个机房整体故障仍可读写(前提是 min_size 设为 2)。若要支持机房级读亲和性,可配合 CRUSH chooseleaf 的 locality tunable 或使用 stretch cluster 特性。
Q3:straw2 和 straw 有什么区别,能否混用?
A:straw2 修正了 straw 在节点权重变化时数据迁移量过大的缺陷,是 Squid 的默认且唯一推荐算法。同一 CRUSH Map 中所有 bucket 应统一使用 straw2,混用会导致分布不均和不可预期的迁移。旧版升级到 Squid 时会自动迁移,无需手动干预。
总结
本篇深入解析了 Ceph CRUSH Map 的架构原理与生产环境故障域规划实战。核心要点回顾:
- CRUSH 是去中心化的伪随机分布算法,让客户端无需元数据服务器即可定位对象位置
- CRUSH Map 四大组成:devices / bucket types / buckets / rules,构成完整拓扑与规则
- 故障域类型决定数据可靠性边界,生产环境应至少做到 host 级别,多机架/机房部署应做到 rack/datacenter 级别
- 生产环境操作 CRUSH Map 必须用 CLI 子命令,避免手动编译注入;任何变更都伴随数据迁移,需限速并选低峰期
- straw2 是推荐算法,全集群应统一使用,切勿回退旧算法
理解并正确规划 CRUSH Map,是构建高可靠 Ceph 生产集群的关键基础。下一篇我们将进入存储池(Pool)的创建与 PG 数量规划,把 CRUSH 故障域策略落实到具体的存储池配置上。
下期预告
Ceph 生产环境搭建 | 第 8 天:存储池(Pool)创建与 PG 数量规划 —— 将讲解 PG/PGP 的计算原理、pool 配置参数与生产环境的 PG 数量规划公式。
系列目录
- ✅ 第 1 天:架构概述与生产环境选型指南
- ✅ 第 2 天:Ubuntu 系统准备:内核调优与磁盘规划
- ✅ 第 3 天:Cephadm 部署工具详解与集群引导初始化
- ✅ 第 4 天:Monitor 节点部署与高可用配置
- ✅ 第 5 天:Manager 节点部署与模块启用
- ✅ 第 6 天:OSD 存储节点部署:BlueStore 配置与磁盘管理
- 🔥 第 7 天:CRUSH Map 架构解析与故障域规划(本文)
- ⏳ 第 8 天:存储池(Pool)创建与 PG 数量规划(即将发布)
- ⏳ 第 9 天:副本与纠删码存储池策略对比实战
- ⏳ 第 10 天:Ceph 块存储 RBD 配置与生产实践
- ⏳ 第 11 天:Ceph 对象存储 RGW 网关部署与 S3 兼容
- ⏳ 第 12 天:CephFS 分布式文件系统部署与挂载
- ⏳ 第 13 天:Ceph 网络架构:集群网络与公共网络分离
- ⏳ 第 14 天:Ceph 认证体系 cephx 与用户权限管理
- ⏳ 第 15 天:Ceph 集群监控:Prometheus + Grafana + 内置仪表盘
- ⏳ 第 16 天:Ceph 性能调优:OSD 参数与缓存分层
- ⏳ 第 17 天:Ceph 集群扩容实战:OSD 动态添加与 CRUSH 重平衡
- ⏳ 第 18 天:Ceph 高可用与容灾设计:多副本、跨机房与异地灾备
- ⏳ 第 19 天:Ceph 故障排查与数据恢复实战
- ⏳ 第 20 天:Ceph 生产环境升级维护与版本迭代策略

















暂无评论内容