3. Ceph 数据存储原理:CRUSH 算法、PG 与 PGP、数据分布与故障域
Ceph 之所以能够实现无中心化的高可靠、高可扩展存储,其核心在于 CRUSH 算法(Controlled Replication Under Scalable Hashing)与 PG(Placement Group)的巧妙设计。本章将深入剖析数据从客户端写入到最终落盘的完整路径,并解释 PG 与 PGP 在数据分布中的关键作用,以及如何通过故障域配置保障数据安全。
3.1 数据写入路径与 CRUSH 算法
当客户端向 Ceph 集群写入一个对象时,数据并不直接发送到 OSD,而是经过以下计算路径:
- 对象名 → PG:客户端使用对象名、存储池 ID 和哈希函数计算出该对象所属的 PG ID。
- PG → OSD 列表:CRUSH 算法根据集群拓扑(Cluster Map)和存储池的副本策略,计算出该 PG 应该分布在哪一组 OSD 上(例如 3 副本即返回 3 个 OSD)。
- 数据写入:客户端直接与主 OSD 通信,由主 OSD 负责将数据同步到从 OSD。
3.2 PG 与 PGP 的概念与作用
PG(Placement Group) 是 Ceph 中数据管理的基本单位,它是一个逻辑容器,用于将大量对象聚合为更小的管理单元。每个 PG 包含一组对象,并负责将这些对象的副本分布到指定的 OSD 上。
PGP(Placement Group for Placement) 是用于控制 PG 在 OSD 上分布粒度的参数。PGP 的值决定了 CRUSH 算法在计算 PG 分布时使用的 PG 数量。
| 参数 | 全称 | 作用 | 典型值 |
|---|---|---|---|
| PG | Placement Group | 对象的逻辑分组,决定数据副本的分布范围 | 每个 OSD 100~200 个 PG |
| PGP | Placement Group for Placement | 控制 PG 在 OSD 上的分布粒度,PGP ≤ PG | 通常与 PG 相等 |
3.3 数据分布与故障域
Ceph 通过 故障域(Failure Domain) 来保证数据的高可用性。故障域定义了数据副本在物理或逻辑上的隔离层级,常见的故障域包括:
- OSD 级别:副本分布在不同的 OSD 上,防止单个磁盘故障。
- 主机级别:副本分布在不同的主机上,防止单台服务器故障。
- 机架级别:副本分布在不同的机架上,防止机架级故障。
- 数据中心级别:副本分布在不同的数据中心,防止区域性灾难。
CRUSH 算法在计算 PG 的 OSD 集合时,会严格遵循故障域约束。例如,当故障域设置为 host 时,CRUSH 会确保同一个 PG 的多个副本不会落在同一台主机上。
3.4 PG 与 PGP 的调整实践
在 Rook Ceph 环境中,可以通过修改存储池的 pg_num 和 pgp_num 参数来调整 PG 数量。以下是一个典型的调整流程:
# 查看当前存储池的 PG 和 PGP 数量
ceph osd pool get <pool-name> pg_num
ceph osd pool get <pool-name> pgp_num
# 增加 PG 数量(例如从 32 增加到 64)
ceph osd pool set <pool-name> pg_num 64
# 增加 PGP 数量,触发数据重新分布
ceph osd pool set <pool-name> pgp_num 64
# 监控数据迁移状态
ceph -w
- PG 总数建议按公式计算:
PG 总数 = (OSD 总数 × 100) / 副本数,并向上取整到 2 的幂次。 - 每次调整 PG 数量时,建议以 2 的幂次递增(如 32 → 64 → 128),避免数据分布不均。
- 调整 PGP 会触发数据迁移,建议在业务低峰期操作。
3.5 故障域配置示例
在 Rook Ceph 中,故障域通过 CRUSH map 的 crush-root 和 crush-failure-domain 参数控制。以下是一个将故障域设置为 host 的存储池配置示例:
# 创建存储池时指定故障域
ceph osd pool create mypool 128 128 replicated
ceph osd pool set mypool crush_rule replicated_rule
# 查看当前 CRUSH 规则
ceph osd crush rule dump replicated_rule
# 输出示例(关键字段):
# {
# "rule_id": 0,
# "rule_name": "replicated_rule",
# "ruleset": 0,
# "type": 1,
# "min_size": 1,
# "max_size": 10,
# "steps": [
# {"op": "take", "item": "default", "item_name": "default"},
# {"op": "chooseleaf_firstn", "num": 0, "type": "host"},
# {"op": "emit"}
# ]
# }
在 chooseleaf_firstn 步骤中,type 字段指定了故障域层级。常见的故障域层级包括:
| 故障域层级 | type 值 | 说明 |
|---|---|---|
| OSD | osd | 副本分布在不同的 OSD 上 |
| 主机 | host | 副本分布在不同的主机上 |
| 机架 | rack | 副本分布在不同的机架上 |
| 数据中心 | datacenter | 副本分布在不同的数据中心 |
CephCluster CRD 中,可以通过 spec.storage.nodes 下的 topologyKey 字段定义故障域层级。例如,设置 topologyKey: "topology.kubernetes.io/zone" 可以实现跨可用区的数据分布。
3.6 数据分布均衡性
CRUSH 算法虽然能保证确定性的数据分布,但在实际运行中,由于 OSD 的容量差异、PG 的创建和删除、OSD 的增减等因素,可能会导致数据分布不均。Ceph 提供了 reweight 和 rebalance 机制来调整数据分布:
- reweight:手动调整 OSD 的权重,影响 CRUSH 算法选择该 OSD 的概率。
- rebalance:自动触发 PG 在 OSD 之间的迁移,使数据分布趋于均衡。
# 查看 OSD 的使用率
ceph osd df
# 手动调整 OSD 权重(例如将 osd.5 的权重从 1.0 调整为 0.8)
ceph osd crush reweight osd.5 0.8
# 触发自动 rebalance(通常由集群自动执行)
ceph balancer mode upmap
ceph balancer on