Files
wiki/存储/Ceph/知识库/01-架构篇.md
T
2026-09-18 13:49:11 +08:00

2554 lines
174 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 基于 Ceph 官方文档整理 | 文档版本:**Tentacle v20.2.4** | 抓取日期:2026-09-17 | 源文:ceph/ceph@v20.2.4 `doc/architecture.rst`
> 官网页面:https://docs.ceph.com/en/tentacle/architecture/
# 第 01 册 · Ceph 架构篇
---
## 本册导读
本册是整个《Ceph Tentacle v20.2.4 中文学习手册》的骨架分册。它回答三个最根本的问题:**Ceph 由什么组成**、**数据怎么放进去**、**为什么这样设计能撑到 EB 级**。后续各册(部署运维、RBD、RGW、CephFS、故障排查等)都是在本册建立的概念坐标系上展开的。
### 本册覆盖页面
| 源文页面 | 本册对应章节 |
| --- | --- |
| `doc/architecture.rst`(主源,约 76KB / 1713 行,已完整读完) | 第一 ~ 第十章 |
| `doc/glossary.rst`(术语表,约 26KB) | 第十一章(术语表) |
| `doc/rados/operations/crush-map.rst`(同版本本地源文,补充 CRUSH 结构) | 第四章 |
| `doc/dev/crush-msr.rst`(同版本本地源文,补充 CRUSH rule 语法) | 第四章 |
| `doc/rados/operations/data-placement.rst`、`pg-concepts.rst`、`pg-states.rst` | 第三、五、八章 |
| `doc/rados/configuration/mon-osd-interaction.rst` | 第二、八章(心跳与故障检测) |
| `doc/mgr/index.rst`、`doc/mgr/administrator.rst` | 第二章(MGR) |
> 💡 补充:第四章(CRUSH map 结构)与第八章(心跳数值、PG 状态机)在 `architecture.rst` 中只有一句带过,本册从同版本(Tentacle v20.2.4)的其它官方源文补齐,并在对应小节另行标注来源;凡官方文档**没有**给出的机制(例如 CRUSH 内部哈希选择算法的具体实现),一律按写作规范标注为「文档未明确」,不做推测性编造。
### 建议阅读顺序
1. **第一遍(建立骨架)**:第一章 架构总览 → 第二章 四大守护进程 → 第三章 RADOS 对象/Pool/PG。读完这三章,你应该能画出「一个文件是怎么进 Ceph 的」的粗线条。
2. **第二遍(理解核心机制)**:第四章 CRUSH → 第五章 完整寻址链路。这是 Ceph 的心脏,也是面试/排障最常问的部分。第五章末尾的「端到端示例」建议自己在纸上推一遍。
3. **第三遍(理解可靠性)**:第六章 智能守护进程与集群映射传播 → 第七章 一致性与纠删码 → 第八章 高可用与故障恢复。
4. **第四遍(按需要查)**:第九章 扩展性与性能 → 第十章 协议与接口层 → 第十一章 术语表(当字典用)。
### 阅读约定
- 命令、参数名、配置项、默认值、算法术语(CRUSH、PG、OSD、MON、MGR、MDS、Paxos、bucket type、`acting set` 等)**一律保留英文原文**,只在必要处加中文注释。
- 原文中的 Sphinx 指令(`.. ditaa::`、`.. note::`、`.. image::`、`.. topic::`)不原样输出,其中图形一律用 ASCII / 表格重画,并注明「原图为 XX 示意图」。
- 凡本册作者(而非官方文档)补充的分析,均以 `> 💡 补充:` 开头标注。
---
## 一、Ceph 架构总览
### 1.1 Ceph 是什么
官方开篇对 Ceph 的定位是:**Ceph 在一个统一的系统中同时提供 object(对象)、block(块)、file(文件)三种存储能力**——这一点是 Ceph 区别于绝大多数存储系统的地方。
原文关键描述(逐条对应源文 `architecture.rst` 开头):
- Ceph 高度可靠、易于管理,并且是**免费**的(free)。
- Ceph 提供**非凡的可扩展性**:数千个客户端并发访问 **PB 到 EB 级**数据(thousands of clients accessing petabytes to exabytes of data)。
- 一个 **Ceph Node** 利用**商用硬件**(commodity hardware)与**智能守护进程**(intelligent daemons)。
- 一个 **Ceph Storage Cluster** 容纳大量节点,节点之间互相通信,以**动态地复制(replicate)和再分布(redistribute)**数据。
三个关键词值得单独拎出来:
| 关键词 | 含义 | 为什么重要 |
| --- | --- | --- |
| unified(统一) | 一套底层 RADOS 集群,同时对外提供对象、块、文件三种接口 | 不必为不同业务形态部署三套存储,运维面收敛 |
| commodity hardware(商用硬件) | 不依赖专有硬件,用普通服务器 + 普通硬盘 | 成本与供应链可控;但也意味着**必须靠软件做可靠性**,这正是 CRUSH / 副本 / scrub 存在的理由 |
| intelligent daemons(智能守护进程) | OSD 自身具备计算能力(会做 CRUSH 计算、心跳、peering、scrub、复制) | 消除中心化组件,把负载分摊到全集群 CPU/RAM |
### 1.2 RADOS:一切的底座
Ceph Storage Cluster 建立在 **RADOS**(**R**eliable **A**utonomic **D**istributed **O**bject **S**tore,可靠的、自治的、分布式对象存储)之上。官方对 RADOS 的定义是:
> 一个可靠的、分布式的存储服务,它利用**每个节点自身的智能**来保护它所存储的数据,并把数据提供给客户端。
拆解 RADOS 这个名字,它就是 Ceph 的设计宣言:
- **Reliable(可靠)**:数据靠副本/纠删码 + 心跳 + peering + scrub 保证,而不是靠 RAID 卡或高端盘阵。
- **Autonomic(自治)**:OSD 自己检测故障、自己协商状态、自己完成恢复与再平衡,不需要一个中心控制器指挥。
- **Distributed(分布式)**:没有中心查找表,位置由算法计算得出。
- **Object Store(对象存储)**:底层唯一的存储语义就是「对象」,块和文件都是在其上构造出来的。
> 💡 补充:RADOS 的 "Autonomic" 是理解 Ceph 与「传统集中式存储」差异的关键。传统阵列的控制器是大脑、磁盘是四肢;Ceph 里每一个 OSD 都既是四肢也是(部分的)大脑。因此 Ceph 集群规模越大,可用的"总脑力"(CPU/RAM)也越大——这是它能线性扩展的根本原因,也是它对 OSD 节点 CPU/内存配置比传统存储更敏感的原因。
官方在文中给出两篇延伸阅读(本册不展开,仅登记出处):
- Sage Weil 的博客 *The RADOS Object Store*(初级介绍)
- 论文 *RADOS - A Scalable, Reliable Storage Service for Petabyte-scale Storage Clusters*(详尽说明)
### 1.3 Ceph 存储集群由哪些守护进程组成
Ceph Storage Cluster 由**多种类型的守护进程**组成,官方明确列出四类:
| 守护进程 | 英文/进程名 | 官方一句话职责 |
| --- | --- | --- |
| **Ceph Monitor** | `ceph-mon`,常简称 **MON** / mon | 维护 cluster map 的**主副本(master copy)**,并把它提供给 Ceph 客户端 |
| **Ceph OSD Daemon** | `ceph-osd` | 检查**自身状态与其他 OSD 的状态**,并回报给 Monitor |
| **Ceph Manager** | `ceph-mgr`,简称 **MGR** | 作为**监控(monitoring)、编排(orchestration)与插件模块(plug-in modules)**的端点 |
| **Ceph Metadata Server** | `ceph-mds`,简称 **MDS** | 当使用 CephFS 提供文件服务时,**管理文件元数据** |
三句必须记住的补充事实(原文):
1. **多个 monitor 的存在**保证了「某个 monitor 守护进程或其主机故障」时集群仍然可用。
2. 存储集群客户端和 OSD 守护进程**都用 CRUSH 算法**计算数据位置;正因为用了 CRUSH,客户端和 OSD **不会被中心查找表(central lookup table)卡住脖子**。
3. Ceph 的高级特性包括:通过 `librados` 提供的**原生接口**,以及**构建在 `librados` 之上的一系列服务接口**。
### 1.4 总体栈图(原图为 Ceph 存储栈示意图 `images/stack.png`)
`architecture.rst` 在开篇处用 `.. image:: images/stack.png` 引入了一张**Ceph 存储栈示意图**。本册用 ASCII 重画如下(内容取自源文后面的官方客户端分层图,见第十章):
```text
+------------+ +----------------+ +-------------+
| RBD | | RGW (对象) | | CephFS | <- 三种服务接口
| (块设备) | | S3 / Swift API | | (POSIX 文件) |
+------------+ +----------------+ +-------------+
| librbd | | librgw | | libcephfs | <- 服务层库
+------------+ +----------------+ +-------------+
+-------------------------------------------------+
| Ceph Storage Cluster Protocol (librados) | <- 原生协议/统一入口
+-------------------------------------------------+
+---------------+ +---------------+ +---------------+
| OSDs | | MDSs | | Monitors | <- 集群守护进程
+---------------+ +---------------+ +---------------+
| RADOS (Ceph Storage Cluster) | <- 底座
+-------------------------------------------------+
```
要点:
- `librados` 是**唯一**的原生入口;RBD / RGW / CephFS 都是"上层建筑",不是并列的三套存储引擎。
- 只有 **CephFS** 需要 MDS;RBD 与 RGW **不需要** MDS。
- MON 不在数据路径上(它只提供集群映射与认证),数据在客户端与 OSD 之间直接流动。
### 1.5 数据是怎么存进去的(一句话版本)
1. Ceph Storage Cluster 从 **Ceph Client** 接收数据——来源可能是 **Ceph Block Device**、**Ceph Object Storage**、**Ceph File System**,或者是你用 `librados` 自己写的实现。
2. 收到的数据**一律以 RADOS object 的形式存储**。
3. 每个 object 存放在一个 **Object Storage Device**(也就是 OSD)上。
4. **Ceph OSD 控制存储驱动器上的读、写与复制操作**。
5. 默认的 **BlueStore** 后端以「**整体的、类数据库的方式**」(monolithic, database-like fashion)存储对象。
> 💡 补充:第 5 点是理解 BlueStore 与旧 FileStore 差异的钥匙。BlueStore 直接管理裸块设备,不再经过一层本地文件系统,因此避免了「对象语义 → 文件语义」的双重映射(以及随之而来的双写放大与元数据开销)。代价是 BlueStore 自己必须实现空间分配、校验、压缩等原本由文件系统承担的职责。
### 1.6 可扩展性与高可用性:Ceph 到底消除了什么
官方用一个对比来立论:
**传统架构**:客户端要跟一个**中心化组件**通信——它可能是 gateway、broker、API 或 facade。这个中心化组件是通往复杂子系统的**单一入口**。依赖这种组件的架构存在:
- **单点故障**(single point of failure)
- **性能与可扩展性的上限**(limits to performance and scalability)
- 中心化组件一挂,**整个系统不可用**
**Ceph 的做法**:消除这个中心化组件。
- 客户端**直接与 Ceph OSD 交互**。
- Ceph OSD 在**其他 Ceph Node** 上创建对象副本,保证数据安全与高可用。
- Ceph 用**一个 monitor 集群**保证高可用。
- 为了消除中心化,Ceph 使用了名为 **CRUSH**(**C**ontrolled **R**eplication **U**nder **S**calable **H**ashing)的算法。
> 💡 补充:注意官方用词是"消除中心化组件",而不是"消除中心化管理"。Ceph 依然有 MON 集群做**权威状态管理**,但它**不在数据路径上**。这是"控制面中心化、数据面去中心化"的经典设计:控制面要强一致(所以 MON 用 Paxos),数据面要去中心(所以客户端和 OSD 都用 CRUSH 自己算位置)。很多 Ceph 排障思路都源于这个二分法——比如"集群能 `ceph -s` 但业务 IO 卡住",通常是数据面(OSD/PG)问题,而"业务 IO 正常但看不到状态"通常是控制面(MON/MGR)问题。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)
---
## 二、Ceph 存储集群组件
本章按 MON → MGR → OSD → MDS 的顺序展开。每个组件都按「是什么 / 为什么这么设计 / 内部原理 / 举例 / 注意事项 / 与其他概念的关系」六段式说明。
### 2.1 Ceph Monitor(MON,`ceph-mon`)
#### 是什么
Ceph Monitor 维护 **cluster map 的主副本(master copy)**,并将其提供给 Ceph 客户端。Ceph 用一个 **monitor 集群**(而非单个 monitor)来提升可靠性与容错能力。
#### 为什么这么设计
- **客户端必须先拿到 cluster map 才能干活**:一个 Ceph Client 必须联系 Ceph Monitor 并取得当前的 cluster map 副本,才能从集群读数据或向集群写数据。
- 单个 monitor 可以正常工作,但**存在单点故障**:一旦它挂了,客户端就无法读写。
- 多个 monitor 时,可能因为**延迟或其他故障**导致部分 monitor 落后(fall behind)。Ceph 的缓解办法是:**要求多数 monitor 实例就集群状态达成一致**。
#### 内部原理:Paxos 与多数派
Ceph 使用 **Paxos** 算法与「**多数派 monitor**」在 monitor 之间建立关于集群状态的一致性(consensus)。官方给出的多数派数量表:
| monitor 总数 | 需要的多数派数量 |
| --- | --- |
| 1 | 1 |
| 3 | 2 |
| 5 | 3 |
| 6 | 4 |
| … | 依此类推(and so on) |
> 💡 补充:这张表揭示了一个重要的运维事实——**偶数个 monitor 不会带来额外容错能力**。3 个 MON 允许坏 1 个,4 个 MON 也只允许坏 1 个(需要 3 个存活,原文未列 4 个的情况,但按「多数派」规则即 3),却多消耗一台机器。因此生产环境普遍部署 **3 或 5 个** MON。
术语表(Glossary)对 **Quorum** 的定义是:集群中**多数 Monitor 处于 `up` 状态**时所处的状态;**集群中必须至少存在 3 个 Monitor,Quorum 才可能成立**。
术语表对 **Ceph Monitor** 的补充:一个 Ceph 集群**必须至少包含 3 个运行中的 monitor**,才能同时具备冗余(redundant)与高可用(highly-available)。Monitor 及其所在节点常被称作 "mon"。
#### 注意事项
- Monitor **不参与数据路径**。它不在客户端与 OSD 之间转发数据。
- Monitor 尽量做**轻量级进程**:故障检测的主要工作由 OSD 互相之间完成(见 2.3 节与第八章)。
- Monitor 的持久化存储称为 **Monitor Store**,指 Monitor 使用的持久化存储,包括 Monitor 的 RocksDB 以及 `/var/lib/ceph` 下的相关文件。
#### 与其他概念的关系
- MON ← → **Cluster Map**:MON 持有主副本;客户端和 OSD 持有副本。
- MON ← → **CephX**:CephX 认证中,客户端与 MON **各持有一份客户端的密钥**;每个 MON 都能**独立**认证用户并分发票据(见第六章)。
- MON ← → **MDS**:CephFS 中 active MDS 故障切换由 `ceph-mon` 自动触发(见 2.4)。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)
### 2.2 Ceph Manager(MGR,`ceph-mgr`)
#### 是什么
术语表定义:**Ceph Manager** 守护进程(`ceph-mgr`)与 monitor 守护进程**并排运行**(runs alongside),提供额外的监控能力,以及对接外部监控与管理系统的接口。它也常被描述为「把整个集群的全部状态收集到一处」的软件(MGR:collects all the state from the whole cluster in one place)。
`architecture.rst` 的定位更偏功能面:**Ceph Manager 是 monitoring、orchestration 与 plug-in modules 的端点(endpoint)**。
#### 为什么这么设计
在 Luminous 之前,`ceph -s` 里的很多状态要靠 monitor 或外部脚本去聚合;随着集群规模增长,这类聚合逻辑越来越不适合塞进 Paxos 强一致状态的 MON 里。MGR 的引入把「**读多写少、允许重建、允许扩展**」的集群视图类工作从 MON 剥离出来,做成一个**插件化**的守护进程。
关键事实(官方):
- 自 **Luminous(12.x)** 起,**没有运行中的 `ceph-mgr`,Ceph 集群无法正常工作**。在 11.x(Kraken)中它是可选组件。
- 默认情况下,manager 守护进程**除了"确保它在运行"之外不需要额外配置**。
- 如果没有 mgr 在跑:会看到一条健康警告;`ceph status` 输出中的**部分信息会缺失或过期(stale)**。
- 通常应在**每个运行 `ceph-mon` 的主机上**部署一个 `ceph-mgr`,以获得同等可用性;并不强制与 mon 同机,但「几乎总是明智的」(almost always sensible)。
#### 内部原理:模块(module)机制
MGR 的能力几乎全部由**模块**提供。官方 `mgr/index.rst` 列出的模块(部分):
| 模块 | 作用(按官方标题) |
| --- | --- |
| `dashboard` | 内建的 Web 管理与监控应用 |
| `prometheus` | 提供 Prometheus exporter,把 `ceph-mgr` 收集点的性能计数器传给 Prometheus |
| `alerts` | 告警 |
| `diskprediction` | 磁盘预测 |
| `localpool` | 本地池 |
| `influx` / `telegraf` | 对接时序数据库 |
| `hello` | 示例模块 |
| `telemetry` | 遥测 |
| `iostat` | I/O 统计 |
| `crash` | 崩溃信息收集 |
| `insights` | 洞察 |
| `orchestrator` | 编排(如对接 cephadm / rook) |
| `rook` | Rook 集成 |
| `rgw` | RGW 管理 |
| `mds_autoscaler` | MDS 自动伸缩 |
| `nfs` / `smb` | NFS / SMB 网关管理 |
| `progress` | 进度跟踪 |
| `cli_api` | CLI API 命令 |
MGR 的三种守护进程状态(官方 `mgr/administrator.rst`,在 `ceph status` 中可见):
| 状态 | 含义 |
| --- | --- |
| **active** | 该 Manager 已完全初始化,可以接收并执行命令。**同一时刻只有一个** Manager 处于此状态 |
| **active (starting)** | 已被选为 active 但尚未完成初始化。此时管理员仍可下发命令,命令会被**暂存**,待其变为 active 后执行。**同一时刻只有一个** |
| **standby** | 当前不接收/执行命令,但随时可接管。可用 `ceph mgr fail <mgr name>` 手动把某个 standby 提升为 active。所有非 active / active (starting) 的 Manager 都处于此状态 |
MGR 的高可用机制(官方):
- 默认情况下,**最先启动的那个 ceph-mgr 实例会被 monitor 置为 active,其余为 standby**。
- **MGR 之间不需要 quorum**。
- 如果 active 守护进程**超过 `mon_mgr_beacon_grace` 未向 monitor 发送 beacon**,它将被一个 standby 取代。
- 可以显式执行 `ceph mgr fail <mgr name>` 抢占式切换。
- 性能与可扩展性:所有 mgr 模块共享一个缓存,可用 `ceph config set mgr mgr_ttl_cache_expire_seconds <seconds>` 开启。官方建议在 **500+ OSD 或 10k+ PG** 时启用 **10 秒 TTL** 的缓存(举例:1000 个 OSD 的 OSDMap 约 4MiB;在 3000 OSD 集群上有过 1.5 倍提升)。
**Ceph Dashboard** 是最常用的模块:它是**内建的、基于 Web 的 Ceph 管理与监控应用**,用于查看和管理集群内各类资源,实现为一个 **ceph-mgr 模块**。
#### 举例
`ceph status` 输出中的相关行(官方示例):
```text
services:
mon: 3 daemons, quorum a,b,c (age 47m) [leader: a]
mgr: x(active, starting, since 3s)
mds: 1/1 daemons up, 2 standby
osd: 4 osds: 4 up (since 47m), 4 in (since 47m)
```
手工创建 mgr 的认证 key(官方):
```bash
ceph auth get-or-create mgr.$name mon 'allow profile mgr' osd 'allow *' mds 'allow *'
```
#### 注意事项
- **MGR 挂了不会立刻导致数据不可用**,但会丢监控视图与依赖 mgr 的功能(如 dashboard、`ceph -s` 中部分字段、部分 CLI),且会报健康警告。
- 老集群升级后,若客户端 CephX capabilities 没有 `mgr allow *`,调用某些集群命令会收到 **EACCES** 错误。
#### 与其他概念的关系
- MGR ← → **MON**:MGR 由 monitor 决定谁是 active;MGR 通过 beacon 向 monitor 报活。
- MGR ← → **CephX**:MGR 是 CephX 中的一类服务(AUTH、MON、MGR、OSD、MDS 之一)。
- MGR ← → **Prometheus / Dashboard**:通过模块把集群状态导出给外部系统。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)、[Ceph Manager Daemon](https://docs.ceph.com/en/tentacle/mgr/)、[ceph-mgr administrator's guide](https://docs.ceph.com/en/tentacle/mgr/administrator/)
### 2.3 Ceph OSD Daemon(`ceph-osd`)
OSD 是整个 Ceph 里**工作量最重**的守护进程。本节覆盖:对象存储、状态上报、心跳、peering、scrub、复制。
#### 2.3.1 是什么
- **Ceph OSD Daemon** 检查**自身状态以及其他 OSD 的状态**,并回报给 monitor。
- Ceph OSD 控制存储驱动器上的 **read / write / replication** 操作。
- 每个 RADOS object 存放在一个 **Object Storage Device**(也叫做 "OSD")上。
> 💡 术语坑:术语表专门解释了 "OSD" 的歧义。约 2013 年,"研究界与工业界"曾试图坚持 OSD 只表示 "**Object Storage Device**"(物理或逻辑存储单元,例如 LUN);但 **Ceph 社区始终用它表示 "Object Storage Daemon"**。Sage Weil 本人在 2022 年 11 月确认 "**Daemon is more accurate for how Ceph is built**"。因此在本手册中,**OSD 默认指 Ceph OSD Daemon**(即 `ceph-osd` 软件进程);当指物理/逻辑磁盘时,官方用 **Object Storage Device** 全称。
#### 2.3.2 数据以对象形式存储在扁平命名空间
Ceph OSD 守护进程把数据以对象形式存放在**扁平命名空间(flat namespace)**中。这意味着:**对象不是按目录层级存放的**。
一个对象的组成(原图为 RADOS 对象结构示意图 `.. ditaa::`):
```text
/------+------------------------------+----------------\
| ID | Binary Data | Metadata |
+------+------------------------------+----------------+
| 1234 | 0101010101010100110101010010 | name1 = value1 |
| | 0101100001010100110101010010 | name2 = value2 |
| | 0101100001010100110101010010 | nameN = valueN |
\------+------------------------------+----------------/
```
即:**标识符(ID)+ 二进制数据(Binary Data)+ 由 name/value 对组成的元数据(Metadata)**。
**对象 ID 在整个集群范围内唯一**,而不仅仅是在本地文件系统内唯一(原文 note)。
对象的语义由 **Ceph Client** 决定:例如 **CephFS 用元数据来存储文件属性**,如文件属主(file owner)、创建日期(created date)、最后修改日期(last modified date)。
对象与 OSD、驱动器的关系(原图为 object → OSD → drive 关系示意图 `.. ditaa::`):
```text
/------\ +-----+ +-----+
| obj |------>| {d} |------>| {s} |
\------/ +-----+ +-----+
Object OSD Drive
```
#### 2.3.3 OSD 成员身份与状态(Membership and Status)
OSD 加入集群时会上报自己的状态。
- **最低层级的状态是 `up` 或 `down`**,反映该 OSD 守护进程**是否在运行、是否能够服务 Ceph Client 请求**。
- 若一个 OSD 是 **`down` 且 `in`** 集群,这个状态可能指示该 OSD 守护进程发生了故障。
- 若 OSD 因**崩溃**而未运行,它**无法**通知 Monitor 自己是 `down`。
- 因此 OSD 会**周期性**向 Monitor 发送消息:
- **Luminous 之前**:通过 `MPGStats`
- **从 Luminous 起**:通过 `MOSDBeacon`
- 如果 Monitor 在**一段可配置的时间**内没有收到这类消息,就把该 OSD 标记为 `down`。
- 但官方强调:**这个机制是一种兜底(failsafe)**。正常情况是:**OSD 自己判断邻居 OSD 是否 `down`,并上报给 Monitor**——这让 Monitor 保持为轻量级进程。
心跳数值(引自 `mon-osd-interaction.rst`,官方默认值):
| 行为 | 官方数值 |
| --- | --- |
| OSD 检查其他 OSD 心跳的间隔 | **随机间隔,小于每 6 秒** |
| 邻居 OSD 未显示心跳的宽限期(grace period) | **20 秒**;超过后可将邻居视为 `down` 并上报 Monitor |
| OSD 无法 peering 时向 Monitor 索取最新 cluster map 的间隔 | **每 30 秒**(`osd mon heartbeat interval`) |
| OSD 在可上报事件(故障、PG stats 变化、`up_thru` 变化、或启动时)后上报的时限 | **5 秒内** |
| 无论有无变化,OSD 向 Monitor 上报的周期 | **每 120 秒** |
| Monitor 判定 OSD `down` 的超时 | `mon osd report timeout` 超时后判定 |
#### 2.3.4 数据清洗(Data Scrubbing)
为维持数据一致性,Ceph OSD 守护进程会 scrub RADOS 对象:
- OSD 把**本地对象的元数据**与**存放在其他 OSD 上的副本的元数据**做比较。
- Scrubbing **以 PG 为单位**进行(per-Placement-Group basis)。
- 它能发现**对象大小不匹配(mismatches in object size)**与**元数据不匹配(metadata mismatches)**。
- 通常**每天**执行(usually performed daily)。
- **深度清洗(deeper scrubbing)**:逐位比较对象数据与校验和,能发现**轻度 scrub 检测不出的坏扇区**(bad sectors)。
术语表对 **scrubs** 的补充定义:
| 类型 | 频率(官方) | 做什么 |
| --- | --- | --- |
| **light scrubbing**(也称 shallow scrubbing) | **每天** | 仅确认对象存在、元数据正确 |
| **deep scrubbing** | **每周** | 读取数据并用 checksums 确保数据完整性 |
Scrub 的过程(术语表):Ceph 先**生成一个 PG 中所有对象的目录(catalog)**,然后**把每个主对象与存放在其他 OSD 上的副本做比较**,确保没有对象丢失或不匹配。任何被判定为「某副本与其它副本不同」或「整个缺失」的 PG 会被标记为 **`inconsistent`**。
#### 2.3.5 复制(Replication)
数据复制是 **Ceph Client 与 Ceph OSD Daemon 协作**完成的:
1. Ceph 客户端用 **CRUSH** 算法确定对象的存储位置 → 对象被映射到 **pool** 和 **placement group** → 客户端查阅 **CRUSH map** 找出该 PG 的 **primary OSD**。
2. 客户端把对象写入该 PG 的 **primary OSD**。
3. Primary OSD 查阅**自己的 CRUSH map 副本**,找出 **secondary OSD**。
4. Primary OSD 把对象复制到这些 secondary OSD 上的 PG。
5. Primary OSD 确认对象已成功存入 secondary OSD。
6. Primary OSD 向客户端报告对象写入成功。
这些复制操作被称为 **`subops`**。
写入时序(原图为客户端写入复制时序示意图 `.. ditaa::`):
```text
+----------+
| Client |
+----------+
* ^
Write (1) | | Ack (6)
v *
+-------------+
| Primary OSD |
+-------------+
* ^ ^ *
Write (2) | | | | Write (3)
+------+ | | +------+
| +------+ +------+ |
| | Ack (4) Ack (5)| |
v * * v
+---------------+ +----------------+
| Secondary OSD | | Secondary OSD |
+---------------+ +----------------+
```
官方的总结:**通过执行这种数据复制,Ceph OSD 守护进程把客户端(及其网卡)从复制数据的负担中解放了出来**。
> 💡 补充:注意时序编号——客户端只发**一次**写(Write 1),剩下的复制由 primary OSD 承担。这与「客户端并行扇出写多副本」的设计形成对比:Ceph 的选择让**客户端的上行带宽只消耗 1 份数据**,代价是 primary OSD 承担了额外的一跳转发(这也是为什么 Ceph 集群网络与公共网络的规划很重要,见第十章网络相关分册)。
#### 2.3.6 默认后端:BlueStore
- 默认后端是 **BlueStore**,它以「**整体的、类数据库的方式**」存储对象。
- 术语表 **BlueStore**:OSD 的存储后端,专为 Ceph 设计;**Kraken 版本引入**,**Luminous 版本成为默认后端**(取代 FileStore);**自 Reef 版本起,FileStore 不再可用**作为存储后端。
- BlueStore **直接把对象存放在裸块设备或分区上**,**不与已挂载的文件系统交互**。
- BlueStore 使用 **RocksDB 的 key/value 数据库**把对象名映射到磁盘上的块位置(block locations)。
#### 2.3.7 下一代 OSD:Crimson(术语表)
**Crimson** 是下一代 OSD 架构,目标是**降低跨核通信带来的延迟开销**。通过重新设计 OSD,**减少数据路径上 shard 之间的通信**以降低锁竞争;并通过**消除对线程池(thread pools)的依赖**来提升 classic Ceph OSD 的性能。
#### 2.3.8 注意事项与相关概念
| 注意事项 | 说明 |
| --- | --- |
| 一个对象 ≠ 一块盘 | 对象是逻辑单元,由 OSD 落盘;BlueStore 负责对象 → 块位置的映射 |
| `down` 不等于 `out` | `down` 是守护进程不服务;`out` 涉及 CRUSH 是否还把它算作放置目标(见第八章) |
| flapping OSD | 术语表:**反复被迅速交替标记为 `up` 和 `down`** 的 OSD |
| OSD id / fsid / uuid | **OSD ID** 是每个 OSD 唯一的整数,由 monitor 在 OSD 创建时生成;**OSD FSID** 是 OSD 的唯一标识符,存于 OSD 路径下的 `osd_fsid` 文件;FSID 与 UUID 两个术语**可互换使用** |
| hybrid OSD | 术语表:同时拥有 HDD 和 SSD 驱动器的 OSD |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)、[Configuring Monitor/OSD Interaction](https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/)
### 2.4 Ceph Metadata Server(MDS,`ceph-mds`)
#### 是什么
- `architecture.rst`:**A Ceph Metadata Server (MDS) 在使用 CephFS 提供文件服务时管理文件元数据**。
- 术语表 **MDS**:**M**eta**D**ata **S**erver 守护进程,也称 `ceph-mds`。**任何运行 CephFS 文件系统的 Ceph 集群都必须运行 MDS**。MDS 存储**全部文件系统元数据**。客户端与**单个 MDS 或一组 MDS** 协同维护 CephFS 所需的**分布式元数据缓存**。
#### 为什么这么设计:把元数据从数据里分离出来
MDS 存在的原因是:像**列目录(`ls`)或切目录(`cd`)**这类简单的文件系统操作,**会不必要地消耗 Ceph OSD 守护进程的资源**。因此:**把元数据与数据分离,意味着 Ceph File System 可以提供高性能服务,而不会拖累 Ceph Storage Cluster**。
具体分工:
- **MDS**:存放**全部文件系统元数据**(目录、文件属主、访问模式等 directories, file ownership, access modes),元数据**驻留在内存中**(resides in memory)。
- **Ceph Storage Cluster(OSD)**:存放**文件数据**,一个文件被映射为**一个或多个对象**。
#### 内部原理:单个 / 高可用 / 可扩展
`ceph-mds` 可以**作为单个进程运行**,也可以**分布到多台物理机上**——要么为了高可用,要么为了可扩展性。
| 模式 | 机制(官方原文) |
| --- | --- |
| **High Availability** | 额外的 `ceph-mds` 实例可以是 **`standby`**,随时接管任何失败的 **`active`** `ceph-mds`。这很容易做到,因为**所有数据(包括 journal)都存放在 RADOS 上**。切换由 **`ceph-mon` 自动触发** |
| **Scalability** | 多个 `ceph-mds` 实例可以是 **`active`**,它们会把**目录树切分成子树(subtrees)**(以及对**单个繁忙目录**做分片 shards),从而在所有 `active` 服务器之间**有效地均衡负载** |
`standby` 与 `active` **可以组合使用**,例如:运行 **3 个 `active`** `ceph-mds` 实例做扩展,外加 **1 个 `standby`** 实例做高可用。
#### 注意事项
- **只有 CephFS 需要 MDS**。RBD 与 RGW 不需要 MDS——这是初学者最常见的误解之一。
- MDS 的元数据最终落在 RADOS 上(包括 journal),所以 MDS 本身是**可重建**的;这也是 standby 接管能"很容易"实现的原因。
- `ceph status` 中 MDS 行例如 `mds: 1/1 daemons up, 2 standby`。
#### 与其他概念的关系
- MDS ← → **CephFS**:CephFS 是构建在 RADOS 之上的 POSIX 兼容文件系统(见第十章)。
- MDS ← → **MON**:active/standby 切换由 `ceph-mon` 触发。
- MDS ← → **CephX**:MDS 是 CephX 五类服务之一;官方举例"rotating service keys 会安全分发给每类服务,例如 CephFS 的 MDS"。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)
### 2.5 四大组件对照表
| 组件 | 是否必须在数据路径上 | 不在会怎样 | 状态一致性机制 | 是否可水平扩展 |
| --- | --- | --- | --- | --- |
| **MON** | 否(只提供 cluster map + 认证) | 客户端无法取得/更新 cluster map,无法读写 | **Paxos + 多数派**(3 节点需 2、5 节点需 3、6 节点需 4) | 是(部署 3/5 个,但不扩吞吐) |
| **MGR** | 否(监控/编排/插件端点) | 健康警告;`ceph -s` 部分字段缺失或 stale;dashboard 等模块失效 | 无 quorum 要求;monitor 选主 + beacon(`mon_mgr_beacon_grace`) | 是(模块可扩展) |
| **OSD** | **是** | 数据不可读写 | peering(PG 内 OSD 协商)+ Monitor 持有的权威 OSDMap | **是**(加 OSD 即扩容并触发 rebalance) |
| **MDS** | **是(仅 CephFS)** | CephFS 不可用;RBD/RGW 不受影响 | active/standby,元数据与 journal 存于 RADOS;由 `ceph-mon` 触发切换 | 是(多 active 按目录子树分片) |
> 💡 补充:这张表最有用的一列是"是否必须在数据路径上"。排障时的快速判据:MON/MGR 异常 → 通常是"看得见但管不了/管得慢";OSD 异常 → 直接是"读写卡/慢/报错";MDS 异常 → 只有 CephFS 受影响,RBD/RGW 完全无感。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)
---
## 三、RADOS 对象存储层:object / pool / PG 的关系
### 3.1 三层抽象:object → pool → PG
```text
RADOS object ──属于──> pool(逻辑分区) ──划分为──> PG(放置组) ──CRUSH──> OSD
```
官方的三个定义:
| 概念 | 官方定义 | 关键点 |
| --- | --- | --- |
| **Pool** | 「用于存储对象的**逻辑分区**(logical partition)」 | 池是**逻辑**概念,不是物理设备划分 |
| **Placement Group (PG)** | 「每个逻辑 Ceph pool 的**子集**(subsets)」;PG 的功能是**把对象(作为一个组)放置到 OSD 中** | Ceph **在内部以 PG 粒度管理数据** |
| **RADOS object** | 集群存储的唯一数据语义;有 ID + 二进制数据 + name/value 元数据 | ID 全集群唯一 |
### 3.2 为什么需要 PG 这一层:可扩展性的关键
官方给了两条理由:
1. **PG 粒度管理比逐对象管理扩展性更好**。原文:Ceph 在内部以 placement-group 粒度管理数据,"**这比管理一个个(因而数量多得多的)RADOS 对象扩展性更好**"(this scales better than would managing individual (and therefore more numerous) RADOS objects)。
2. **PG 数量越多,均衡性越好**。原文:一个拥有**较多 PG(例如每 OSD 100 个)**的集群,比其它条件相同但 PG 数更少的集群**均衡得更好**。
`data-placement.rst` 的补充:**PG 减少了 Ceph 在 OSD 中存储数据所需的 per-object 元数据量**(Placement groups reduce the amount of per-object metadata that is necessary for Ceph to store the data in OSDs)。并给出同样的经验值对比:**每 OSD 100 个 PG 比每 OSD 50 个 PG 均衡得更好**。
**归属关系(必须记牢的两句原文)**:
- Ceph 的**内部 RADOS 对象**每个都被映射到**一个特定的 placement group**。
- **每个 placement group 恰好属于一个 Ceph pool**(each placement group belongs to exactly one Ceph pool)。
> 💡 补充:把 PG 理解为"**对象的哈希桶 + 一致性单元**"最贴切。它同时承担三种角色:
> 1. **寻址中间层**:把"数亿个对象"收敛成"数万个 PG",让元数据(PG 的状态、日志、统计)可控;
> 2. **复制单元**:副本/EC 条带是以 PG 为单位组织到 OSD 的(acting set 是 PG 的属性,不是对象的属性);
> 3. **恢复与均衡单元**:recovery、backfill、scrub、rebalance 全部以 PG 为调度粒度。
> 这也是为什么"PG 数量"是 Ceph 里最难改的参数之一——它同时决定了元数据开销与均衡粒度。
### 3.3 Pool:它到底设置了什么
`architecture.rst`「About Pools」给出的关键点:
- Ceph 客户端从 Monitor **取得 Cluster Map**,然后**把 RADOS 对象写入 pool**。
- **数据如何放置在 pool 中**由三样东西决定:
1. pool 的 **`size`**(副本数量)
2. **CRUSH rule**
3. pool 中的 **PG 数量**
- Pool **至少**设置以下参数:
- **Ownership/Access to Objects**(对象的归属/访问)
- **The Number of Placement Groups**(PG 数量)
- **The CRUSH Rule to Use**(使用的 CRUSH 规则)
官方的 pool → CRUSH rule 关系图(原图为 pool 选择 CRUSH rule 示意图 `.. ditaa::`):
```text
+--------+ Retrieves +---------------+
| Client |------------>| Cluster Map |
+--------+ +---------------+
|
v Writes
/-----\
| obj |
\-----/
| To
v
+--------+ +---------------+
| Pool |---------->| CRUSH Rule |
+--------+ Selects +---------------+
```
`data-placement.rst` 对 pool 的补充定义:Ceph 把数据存放在 pool 内,**pool 是用于存储对象的逻辑组**;pool **管理 placement group 的数量、副本的数量、以及该 pool 的 CRUSH rule**。要往 pool 里存数据,必须是**对该 pool 有权限的已认证用户**。Ceph **可以对 pool 做快照**。
> 💡 补充:`architecture.rst` 里"pool 至少设置三个参数"是**最少集合**的说法;实际生产中 pool 还涉及 `pg_num`/`pgp_num`、`min_size`、EC profile、`target_size_ratio` 等。本册严格按架构页展开,这些运维参数的完整说明属于运维分册。
### 3.4 PG 数量:数量关系与经验公式
**架构页本身给出的数量关系**:
- 一个 pool 内有**若干 PG**。
- **CRUSH 动态地把 PG 映射到 OSD**。
- 客户端存对象时,**CRUSH 把每个 RADOS 对象映射到一个 PG**。
- 官方举例:一个**每 OSD 100 个 PG** 的集群比 PG 更少的同配置集群**均衡得更好**。
**补充公式与数值**(引自同版本 `rados/operations/placement-groups.rst`,非架构页内容,单独标注):
- 参数 **`mon_target_pg_per_osd`**(**默认值 100**),可用 `ceph config set global mon_target_pg_per_osd 100` 调整。
- 官方建议:**如果 OSD 超过 50 个,推荐每 OSD 大约 100–250 个 PG 副本**。
- 计算公式:**Total PGs = OSDs × 100 / pool size**。
- 官方算例:**200 × 100 / 3 = 6667**,**向上取整到最近的 2 的幂:8192**。
- 另一条规则:**默认情况下每个 pool 应有 50–100 个 PG**(taking into account the replication)。
> 💡 补充:公式里的 "100" 来自 `mon_target_pg_per_osd` 默认值,而"除以 pool size"是因为公式统计的是 **PG 副本数(PG replicas)**而非 PG 数——一个 3 副本的 pool,1 个 PG 会占用 3 个 OSD 上的各一份副本。混淆"PG 数"和"PG 副本数"是 PG 规划中最常见的错误。
### 3.5 object → PG → OSD 的间接层:为什么要加一层
官方的核心论述(「Mapping PGs to OSDs」):
- 每个 pool 内有若干 PG,**CRUSH 动态地把 PG 映射到 OSD**。
- 把 RADOS 对象映射到 PG,在 **Ceph OSD Daemon 与 Ceph Client 之间实现了一层抽象与间接(abstraction and indirection layer)**。
- Ceph Storage Cluster 必须能够在**内部拓扑变化时自适应地增长(或收缩)并重新分布数据**。
- 如果客户端"知道"哪些 OSD 存了哪些对象,客户端与 OSD 之间就会存在**紧耦合(tight coupling)**。Ceph 避免了这种紧耦合:CRUSH 先把对象映射到 PG,再把 PG 映射到一个或多个 OSD。
- 这层间接让 Ceph 能够**在新 OSD 及其底层设备上线时动态再平衡**。
对象 → PG → OSD 映射(原图为 object/PG/OSD 映射关系示意图 `.. ditaa::`):
```text
/-----\ /-----\ /-----\ /-----\ /-----\
| obj | | obj | | obj | | obj | | obj |
\-----/ \-----/ \-----/ \-----/ \-----/
| | | | |
+--------+--------+ +---+----+
| |
v v
+-----------------------+ +-----------------------+
| Placement Group #1 | | Placement Group #2 |
+-----------------------+ +-----------------------+
| |
| +-----------------------+---+
+------+------+-------------+ |
| | | |
v v v v
/----------\ /----------\ /----------\ /----------\
| OSD #1 | | OSD #2 | | OSD #3 | | OSD #4 |
\----------/ \----------/ \----------/ \----------/
```
> 💡 补充:注意图里**一个 PG 指向多个 OSD**——这就是副本(或 EC 分片)。同一个 PG 的多个副本分散在不同 OSD 上,构成一个 acting set(见第五章与第八章)。而**多个对象可以映射到同一个 PG**,这正是"PG 是对象的哈希桶"的直观体现。
### 3.6 三层概念对照与数量关系速查
| 层级 | 数量级(典型) | 由谁决定 | 是否可变 | 变化后果 |
| --- | --- | --- | --- | --- |
| **object** | 亿级 ~ 十亿级 | 客户端写入 | 随时增删改 | 无(最细粒度) |
| **PG** | 千级 ~ 万级(每 pool) | pool 的 `pg_num` / autoscaler | 可调整但有代价 | 触发 PG 分裂与数据迁移 |
| **pool** | 个级 ~ 十级 | 管理员创建 | 可增删 | 影响 CRUSH rule、配额、权限 |
| **OSD** | 十级 ~ 千级 | 硬件与部署 | 可扩缩 | 触发 rebalance |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)、[Data Placement Overview](https://docs.ceph.com/en/tentacle/rados/operations/data-placement/)
---
## 四、CRUSH 算法
> 说明:`architecture.rst` 在本章范围内只给了「CRUSH Introduction」与 cluster map 中 CRUSH Map 的一句话定义。为了让本册把 CRUSH 讲透,本章从**同版本(Tentacle v20.2.4)**的 `rados/operations/crush-map.rst` 与 `dev/crush-msr.rst` 补充结构、bucket 类型、rule 语法与权重机制,并逐节标注来源。凡官方未给出的内部实现细节,标注「文档未明确」。
### 4.1 为什么需要 CRUSH
#### 问题:中心查找表的三重罪
`architecture.rst` 的论证是:
- 传统上,客户端与 OSD 依赖**中心查找表(central lookup table)**来确定数据位置。
- 依赖中心查找表会让客户端与 OSD **被中心查找表卡住脖子(bottlenecked)**。
- 而 CRUSH 让 **Ceph Client 与 Ceph OSD Daemon 都通过算法计算对象位置信息**,而不是查表。
`crush-map.rst` 的补充论证(三重收益):
> CRUSH 让 Ceph 客户端**直接与 OSD 通信**,而不是通过一个中心化的服务器或 broker。通过这种**由算法决定**的存取方法,Ceph 得以避免**单点故障**、**性能瓶颈**以及**可扩展性的物理上限**。
#### 收益:把工作分摊到全集群
`architecture.rst`:CRUSH 提供了比旧方法更好的数据管理机制,并且**通过把工作分发给集群中所有的 OSD 守护进程以及所有与它们通信的客户端,CRUSH 实现了大规模(massive scale)**。
此外,CRUSH 使用**智能的数据复制(intelligent data replication)**来确保韧性(resiliency),这**更适合超大规模存储(hyper-scale storage)**。
### 4.2 CRUSH 与传统哈希表的对比
| 维度 | 传统中心查找表 / 朴素哈希 | CRUSH |
| --- | --- | --- |
| 位置获取方式 | 查询中心表(一次网络往返) | **本地计算**(无网络往返) |
| 单点故障 | 有(表所在的中心组件) | **无**(每个客户端/OSD 都有 CRUSH map 副本) |
| 性能瓶颈 | 中心组件是所有请求的汇聚点 | 计算分摊到所有客户端与 OSD |
| 可扩展性上限 | 受中心组件容量限制 | 无物理上限(文档表述:avoids a physical limit to its scalability) |
| 故障域感知 | 需要额外机制 | **内建**:CRUSH map 编码了物理拓扑与故障域 |
| 拓扑变化时的数据迁移 | 朴素哈希(`hash mod N`)会导致**几乎全部**数据重排 | **稳定**:大量 PG 保持原位(见 4.7 重平衡) |
| 权重/容量异构 | 通常不支持 | 支持(weight,见 4.6) |
官方原文对"计算 vs 查询"的论述(「Calculating PG IDs」):**计算对象位置要比通过一次啰嗦的会话(chatty session)去查询对象位置快得多**。
> 💡 补充:CRUSH 常被类比为**一致性哈希**的一种工程化、层次化实现,二者的共同目标是"节点变化时最小化数据搬迁"。但 CRUSH 比朴素一致性哈希多了三样东西:**(1) 层次化的物理拓扑(bucket 树)**、**(2) 每节点权重(支持异构容量)**、**(3) 可声明的放置规则与故障域约束**。这三点是它能在真实数据中心里落地的原因。此处的"一致性哈希"类比是笔者的解释性补充,官方文档本身只说 CRUSH 是 "Controlled Replication Under Scalable Hashing",未使用一致性哈希的表述。
### 4.3 CRUSH map 的组成
`crush-map.rst` 的定义(原文):**CRUSH map 由 (1) 描述集群物理拓扑的层级结构(hierarchy)与 (2) 一组定义数据放置策略的规则(rules)组成。**
`architecture.rst` 在 cluster map 中对 CRUSH Map 的定义:**包含存储设备列表、故障域层级(failure domain hierarchy,例如 `device`、`host`、`rack`、`row`、`room`),以及在存储数据时遍历该层级的规则(rules)**。
查看 CRUSH map 的官方命令(`architecture.rst`):
```bash
ceph osd getcrushmap -o {filename}
crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename}
# 之后用文本编辑器或 cat 查看反编译后的 map
```
### 4.4 Devices(设备)
- **Devices 是存放数据的单个 OSD**(通常每个存储驱动器一个 device)。
- 由 **`id`**(非负整数)与 **`name`**(通常是 `osd.N`,其中 `N` 是该设备的 `id`)标识。
- **自 Luminous 起**,OSD 可以被赋予 **device class**(例如 `hdd`、`ssd`、`nvme`),从而**被 CRUSH rule 定向选择**。在同一 host 内混插不同设备类型时,device class 尤其有用。
### 4.5 Types and Buckets(类型与桶)
**Bucket 的定义**:在 CRUSH 语境中,"bucket" 是层级中**任何内部节点**的统称:hosts、racks、rows 等等。CRUSH map 定义了一系列 **types** 用于标识这些节点。
**默认类型(官方列表,共 12 个)**:
| # | type | 说明(本册注释) |
| --- | --- | --- |
| 1 | `osd`(或 `device`) | 叶子节点 |
| 2 | `host` | 主机 |
| 3 | `chassis` | 机箱 |
| 4 | `rack` | 机柜 |
| 5 | `row` | 机列 |
| 6 | `pdu` | 电源分配单元 |
| 7 | `pod` | 交付单元/资源池单元 |
| 8 | `room` | 机房 |
| 9 | `datacenter` | 数据中心 |
| 10 | `zone` | 可用区 |
| 11 | `region` | 地域 |
| 12 | `root` | 根节点 |
官方说明:**大多数集群只使用其中少数几个类型,其它类型可以按需定义**。
**层级结构**:叶子是 device(通常 type 为 `osd`),内部节点是非 device 类型,**根节点类型为 `root`**。
CRUSH 层级示意(原图为 CRUSH 层级结构示意图 `.. ditaa::`,取自 `crush-map.rst`):
```text
+-----------------+
|{o}root default |
+--------+--------+
|
+---------------+---------------+
| |
+------+------+ +------+------+
|{o}host foo | |{o}host bar |
+------+------+ +------+------+
| |
+-------+-------+ +-------+-------+
| | | |
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
| osd.0 | | osd.1 | | osd.2 | | osd.3 |
+-----------+ +-----------+ +-----------+ +-----------+
```
**OSD 如何进入层级(CRUSH Location)**:
- OSD 部署时,会**被自动加入 CRUSH map 中以该 OSD 所在节点命名的 `host` bucket 之下**。结合所配置的 **CRUSH 故障域**,这确保了**副本或纠删码分片跨主机分布**,并且**单个主机故障(或其它类型故障)不会影响可用性**。
- OSD 在 CRUSH map 层级中的位置称为其 **CRUSH location**,用一组 key-value 对描述。例如一个 OSD 位于某个 row、rack、chassis、host 中,并且属于 `default` CRUSH root(大多数集群如此),其 CRUSH location 可这样指定:
```text
root=default row=a rack=a2 chassis=a2a host=a2a1
```
- 可在 `ceph.conf` 中用 `crush_location` 选项设置。设置后,**OSD 每次启动都会校验自己是否位于 CRUSH map 中的正确位置,若不在则自行移动**。
- 若未显式设置 `crush_location`,OSD 使用默认值 **`root=default host=HOSTNAME`**,其中 hostname 由 `hostname -s` 命令的输出决定。
- 可用 custom location hook 在启动时生成更完整的 CRUSH location(例如跨多个数据中心部署同一份配置时)。
### 4.6 故障域(Failure Domain)
**为什么需要**:CRUSH 通过**反映部署环境的底层物理组织**,可以**建模(并因此应对)相关设备故障的可能性(correlated device failures)**。与 CRUSH 层级相关的因素包括:**chassis、racks、物理邻近性、共享电源、共享网络、以及故障域**。
**怎么做**:把这些信息编码进 CRUSH map 后,CRUSH 放置策略就能**在维持期望分布的同时,把对象副本分布到不同的故障域上**。官方举的动机性例子:为应对并发故障,可能希望确保**数据副本位于不同 shelves / racks / power supplies / controllers / physical locations**。
**故障域的选择**:
- OSD 部署时自动归入 `host` bucket + 所配置的故障域 → 保证副本/EC 分片**跨主机**分布。
- 官方提示:**更大规模的集群,管理员必须仔细考虑故障域的选择**。例如**跨 rack 分布副本在中大型集群中是典型的做法**。
> 💡 补充:故障域的选择本质是"容错能力 vs 资源效率 vs 放置成功率"的三方权衡:故障域越粗(`host` → `rack` → `datacenter`),抗相关故障能力越强,但**要求该层级下的域数量 ≥ 副本数**,否则 CRUSH 选不出足够的独立目标,PG 会 `undersized` / `incomplete`。例如 `size=3` 且故障域设为 `rack`,集群至少需要 3 个 rack。
### 4.7 Rules(放置规则)
#### 是什么
- **CRUSH rules 定义策略,规定数据如何分布到层级中的设备上**。
- 规则定义了 **placement** 以及 **replication strategies / distribution policies**,让你能**精确指定 CRUSH 如何放置数据副本**。
- 官方举的三种规则形态:
1. 选择一对目标做**双路镜像**(two-way mirroring)
2. 选择**两个不同数据中心中的三个目标**做三副本
3. **跨六个存储设备做纠删码**
#### 怎么创建
- CRUSH 规则**可以通过命令行创建**,需指定:
- **pool type**(`replicated` 或 `erasure coded`)
- **failure domain**
- 可选地指定 **device class**
- **极少数情况下**必须手工编辑 CRUSH map 来创建规则。
查看命令:
```bash
ceph osd crush rule ls # 列出集群中定义的规则
ceph osd crush rule dump # 查看规则内容
```
#### rule 语法(引自同版本 `dev/crush-msr.rst`,官方示例)
一个典型的三副本规则(跨 host):
```text
rule replicated_rule_1 {
...
step take default class hdd
step chooseleaf firstn 3 type host
step emit
}
```
拆成 choose + choose 的等价写法:
```text
rule replicated_rule_1 {
...
step take default class hdd
step choose firstn 3 type host
step choose firstn 1 type osd
step emit
}
```
EC 场景(把 8+6 编码分摊到 4 个 host):
```text
rule ecpool-86 {
...
step take default class hdd
step choose indep 4 type host
step choose indep 4 type osd
step emit
}
```
| step 关键字 | 含义(据官方示例与说明) |
| --- | --- |
| `step take <root> [class <class>]` | 从层级中取出一个子树作为选择起点(如 `default`,可加 `class hdd` 限定设备类) |
| `step chooseleaf firstn N type <type>` | 选出 N 个 `<type>` 类型的 bucket,并**在各自内部再下探到叶子(OSD)** |
| `step choose firstn N type <type>` | 只选出 N 个 `<type>` 类型的 bucket,不自动下探 |
| `step choose indep N type <type>` | `indep` 变体,用于 EC 等需要"位置稳定"的场景 |
| `step emit` | 输出结果 |
| `step choosemsr N type <type>` / `type msr_indep` | CRUSH MSR(Multi-step Retry)规则专用,见下文 |
**`firstn` 与 `indep` 的行为差异**、**`chooseleaf` 与 `choose` 的差异**(官方说明):
- 当 **N 个 OSD 需要分布到 N 个故障域**时,用 **`chooseleaf`** 即可。
- 但如果某个 host 上的**所有 OSD 都被标记 `out`**,用 `choose firstn 3 type host` + `choose firstn 1 type osd` 的写法时,映射到该 OSD 的 PG 会被重映射到**同一 host 上的其它 OSD**,导致**这些 OSD 相对其它 host 的 OSD 被过度使用**;若一个 host 上**所有 OSD 都 out**,命中该 host 的映射会**失败**,导致 **undersized PG**。
- 而 `chooseleaf firstn 3 type host` 在这种情况下会把映射**重映射到其它 host**(只要有足够的 host)。官方还给出定量描述:如果某 host 上 **1/8 的 OSD 被标记 out**,**大约 1/8 映射到该 host 的 PG** 会被重映射到别的 host,**保持整体每 OSD 利用率均衡**。
- `chooseleaf` 的局限:**它不支持在指定 type 之下映射多于一个叶子**(does not support mapping more than one leaf below the specified type),所以上面的 8+6 跨 4 host 场景不能用它。
#### CRUSH MSR(Multi-step Retry)
- 经典 CRUSH 的局限:**包含多个 `choose` step 的规则,在遇到 `out` OSD 时无法回退重试前面的 step**。
- MSR 规则用**不同的下探算法**解决这个问题:**命中 out OSD 时重试所有 step**。
- **经典 CRUSH 是广度优先**(breadth first:对每一个 step,先把结果向量填满,再进入下一个 step);**MSR 规则是深度优先**(depth first:对每一次选择,**递归地走完所有 step**,再进行下一次选择)。
- MSR 示例:
```text
rule ecpool-86 {
type msr_indep
...
step take default class hdd
step choosemsr 4 type host
step choosemsr 4 type osd
step emit
}
```
> 💡 补充:`chooseleaf` vs `choose` 的这段官方说明是 CRUSH 排障里非常实用的一条:当集群出现"OSD out 之后剩余 OSD 利用率不均"或"PG undersized"时,优先检查 CRUSH rule 是否用了 `choose ... choose ...` 的多段写法,而不是 `chooseleaf`。
> 关于 **ruleset**:本册抓取的 Tentacle v20.2.4 源文(含 `architecture.rst`、`crush-map.rst`、`crush-msr.rst` 等)中**未出现 `ruleset` 这一术语**;当前文档统一使用 **CRUSH rule / rules** 表述,并通过 `ceph osd crush rule ...` 命令族管理。因此本册不对 `ruleset` 给出定义——详见文末「文档未明确项」。
### 4.8 Device classes(设备类)
- **每个 device 可以可选地拥有一个 class**。默认情况下,OSD 在启动时**根据其底层设备类型自动设置为 `hdd`、`ssd` 或 `nvme`**。
- 显式设置:`ceph osd crush set-device-class <class> <osd-name> [...]`
- **一旦设置了 device class,必须先 unset 旧 class 才能改成另一个 class**;移除:`ceph osd crush rm-device-class <osd-name> [...]`
- 这条限制让管理员可以设置**不会被 OSD 重启或脚本改掉**的 device class。
- 创建面向特定 device class 的放置规则:`ceph osd crush rule create-replicated <rule-name> <root> <failure-domain> <class>`
- 应用到 pool:`ceph osd pool set <pool-name> crush_rule <rule-name>`
- **实现机制**:device class 通过创建一个或多个 **"shadow" CRUSH 层级**实现——**每种在用的 device class 都有一个只包含该类设备的 shadow 层级**,CRUSH 规则随后在相应的 shadow 层级上分布数据。这种做法**对老版本 Ceph 客户端完全向后兼容**。
- 查看带 shadow 项的层级:`ceph osd crush tree --show-shadow`
- Luminous 之前创建的老集群可能依赖手工 CRUSH map 来维护按设备类型的层级,官方提供 **reclassify** 工具帮助它们迁移到 device class 而**不触发不必要的数据迁移**。
### 4.9 权重(Weight)与重平衡
#### 权重的定义
- 层级中的**每个节点(device 或 bucket)都有一个 weight**,表示**该设备或该子树应当存储的数据占全部数据的相对比例**。
- **权重设置在叶子节点上**,表示**设备的大小**。
- 这些权重**自动沿"树上"方向求和**:即 **`root` 节点的权重等于其下所有 device 权重之和**。
- **权重通常以 tebibytes(TiB)为单位**。
- 查看:`ceph osd tree`
#### 为什么有权重还不够:weight sets
`crush-map.rst` 的解释很直白:**CRUSH 是一个概率性的伪随机放置过程,因此总会与理想分布存在一定偏差**(就像掷骰子 60 次,很可能不会恰好得到 10 个 1 和 10 个 6)。**Weight sets 允许集群基于你的集群具体情况(层级、pool 等)做数值优化,以达成均衡分布**。
两种 weight set:
| 类型 | 特点(官方) |
| --- | --- |
| **compat** weight set | 为集群中**每个 device 和每个 node** 提供**单一的一套替代权重**。不能指望它纠正所有异常(例如不同 pool 的 PG 大小与负载可能不同,但 balancer 大多一视同仁)。**主要优势是向后兼容**——即使 weight set 是 Luminous v12.2.z 引入的,使用 compat weight set 时**老客户端(例如 Firefly)仍能连上集群** |
| **per-pool** weight set | 更灵活:允许**针对每个 data pool 优化放置**;并且**可以针对放置的每个位置(position)调整权重**,使优化器能够纠正"数据向权重相对较小的设备倾斜"的细微偏差(这种效应通常只在超大规模集群中明显,但会导致均衡问题) |
使用 weight set 时,各节点权重会在 `ceph osd tree` 输出中的**单独一列**显示(列标签为 `(compat)` 或 pool 名称)。
#### 重平衡(Rebalancing)
`architecture.rst` 的论述:
- 当你**向集群添加一个 Ceph OSD Daemon** 时,**cluster map 会更新为包含这个新 OSD**。
- 这**改变了 cluster map**,因而**改变了对象放置,因为它改变了计算的一个输入**。
- 关键性质:**即使在重平衡时,CRUSH 也是稳定的(CRUSH is stable)**。
- **许多 PG 保持原有配置不变**;
- **每个 OSD 都获得一部分新增容量**;
- 因此**重平衡完成后新 OSD 上不会出现负载尖峰(no load spikes)**。
- 官方也坦承下面这张图**相当粗糙**(albeit rather crudely),因为在**大规模集群中影响要小得多**。
重平衡前后(原图为 rebalancing 过程示意图 `.. ditaa::`):
```text
+--------+ +--------+
Before | OSD 1 | | OSD 2 |
+--------+ +--------+
| PG #1 | | PG #6 |
| PG #2 | | PG #7 |
| PG #3 | | PG #8 |
| PG #4 | | PG #9 |
| PG #5 | | PG #10 |
+--------+ +--------+
+--------+ +--------+ +--------+
After | OSD 1 | | OSD 2 | | OSD 3 |
+--------+ +--------+ +--------+
| PG #1 | | PG #7 | | PG #3 |
| PG #2 | | PG #8 | | PG #6 |
| PG #4 | | PG #10 | | PG #9 |
| PG #5 | | | | |
| | | | | |
+--------+ +--------+ +--------+
```
> 💡 补充:把两张图对照看,可以验证官方说的"CRUSH 是稳定的":10 个 PG 中只有 **PG #3、PG #6、PG #9** 三个迁移到了新 OSD(约 30%),其余 7 个**原地不动**。这正是层次化 CRUSH 相对 `hash mod N` 的核心优势——朴素取模在 N 从 2 变 3 时几乎会导致**全部**数据重排。也解释了为什么"扩 OSD 后集群会很长时间处于 `recovery`/`backfill` 状态但并不会把网络打满"。
### 4.10 CRUSH 查找过程(Descent)
**官方文档给出的部分**:
1. 输入:CRUSH 需要 **cluster map(CRUSH map)**、**pool 的 CRUSH rule**、以及 **PG** 作为放置单位。
2. 规则从 `step take <root>`(可带 `class`)开始,确定选择起点。
3. 按 step 依次下探:`chooseleaf firstn N type X`(一步到位下探到叶子)或 `choose ... choose ...`(分段下探)。
4. 输出:`step emit`,得到 N 个 OSD 组成的结果向量(对应 acting set 的目标)。
5. 经典 CRUSH 是**广度优先**(逐步填满结果向量);MSR 是**深度优先**并在遇到 `out` OSD 时**重试所有 step**。
**文档未明确的部分**(本册不推测):
- CRUSH 在每一层**如何从兄弟节点中选出一个**(例如 straw / straw2 这类 bucket 选择算法的具体实现、随机种子 `r` 的取值与递增规则、冲突/失败时的重试次数上限)——**文档未明确**。
- CRUSH 输入中除 PG 之外的其他参数(如副本序号 `r`)的**完整取值规则**——**文档未明确**。
- `firstn` 与 `indep` 在**算法层面**的精确差异(官方只给出用途与行为描述,未给出实现细节)——**文档未明确**。
> 💡 补充(概念性描述,非官方原文):可以把它理解为"**带约束的、确定性的伪随机下探**"——给定 (PG id, 层级, 权重, 规则, 副本序号),任何持有相同 CRUSH map 的一方(客户端或 OSD)都会算出**完全相同**的结果。这个"确定性"是 Ceph 去中心化的前提:客户端不需要问任何中心组件,只要大家手上的 CRUSH map 版本一致(或能够通过 epoch 对齐),就能独立算出一致的位置。
### 4.11 CRUSH 关键命令速查
| 目的 | 命令 |
| --- | --- |
| 导出 CRUSH map | `ceph osd getcrushmap -o {filename}` |
| 反编译 CRUSH map | `crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename}` |
| 查看层级与权重 | `ceph osd tree` |
| 查看(含 shadow 层级) | `ceph osd crush tree --show-shadow` |
| 列出规则 | `ceph osd crush rule ls` |
| 查看规则内容 | `ceph osd crush rule dump` |
| 创建副本规则(带 device class) | `ceph osd crush rule create-replicated <rule-name> <root> <failure-domain> <class>` |
| 给 pool 应用规则 | `ceph osd pool set <pool-name> crush_rule <rule-name>` |
| 设置 device class | `ceph osd crush set-device-class <class> <osd-name> [...]` |
| 移除 device class | `ceph osd crush rm-device-class <osd-name> [...]` |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[CRUSH Maps](https://docs.ceph.com/en/tentacle/rados/operations/crush-map/)、[CRUSH MSR (Multi-step Retry)](https://docs.ceph.com/en/tentacle/dev/crush-msr/)
---
## 五、数据放置与寻址完整链路:file → object → pool → PG → OSD
本章把前面几章的零件组装成一条完整链路,并给出可手工验算的示例。
### 5.1 全链路总览
```text
┌──────────────────────────────────────────────────────────────────────┐
│ 客户端侧(Ceph Client:RBD / RGW / CephFS / librados 自定义实现) │
└──────────────────────────────────────────────────────────────────────┘
│
│ ① 把用户数据切成对象(RBD/RGW/CephFS 各自负责条带化与命名)
v
┌──────────────┐
│ object │ name: "john" / "paul" / ... (ID 全集群唯一)
└──────────────┘
│
│ ② 选定 pool(客户端写到哪个 pool)
v
┌──────────────┐
│ pool │ name: "liverpool" → pool ID: 4
└──────────────┘
│
│ ③ 计算 PG ID:hash(object ID) mod pg_num → PG ID;再前置 pool ID
v
┌──────────────┐
│ PG │ PG ID: 4.58
└──────────────┘
│
│ ④ CRUSH(用 cluster map + pool 的 CRUSH rule):PG → OSD 集合
v
┌──────────────────────────────────────────────┐
│ Acting Set: [Primary, Secondary, ...] │
│ 例:osd.25(Primary), osd.32, osd.61 │
└──────────────────────────────────────────────┘
│
│ ⑤ 客户端直连 Primary OSD 写入;Primary 通过 subops 复制到 Secondary
v
┌──────────────────────────────────────────────┐
│ OSD → BlueStore → Drive │
└──────────────────────────────────────────────┘
```
### 5.2 第 ① 步:file → object(客户端侧的数据表示转换)
官方说明(「Data Striping」):Ceph 提供三类客户端(**Ceph Block Device、Ceph File System、Ceph Object Storage**)。**Ceph 客户端把它提供给用户的数据表示格式**(块设备镜像、RESTful 对象、CephFS 文件系统目录)**转换成对象**,以便存入 Ceph Storage Cluster。
各客户端的切分方式:
| 客户端 | 用户看到的形态 | 转换成 |
| --- | --- | --- |
| **RBD** | 块设备镜像(block device image) | 镜像被**条带化到多个对象**上(详见第九章) |
| **RGW** | RESTful 对象(S3 / Swift object) | 映射到 Ceph 存储集群对象;**S3/Swift 对象与集群对象不一定 1:1**,**一个 S3 或 Swift 对象可能映射到多个 Ceph 对象** |
| **CephFS** | 文件系统目录与文件 | **CephFS 文件被映射为 Ceph 存放在存储集群中的对象** |
### 5.3 第 ② 步:选定 pool
- Ceph **把数据存放在命名(named)的 pool 中**(例如 `"liverpool"`)。
- pool 是逻辑分区;pool 至少设置了 **对象归属/访问、PG 数量、CRUSH rule**。
- 客户端**只写单个 pool**,因此**所有条带化出来的对象都会被映射到同一个 pool 中的 PG**,从而**使用同一个 CRUSH map 和相同的访问控制**(原文 note)。
### 5.4 第 ③④ 步:计算 PG ID(官方五步法)
**前提(极其重要,原文加粗强调)**:
- 当 Ceph Client **绑定(binds)到一个 Ceph Monitor** 时,它取得**最新版本的 Cluster Map**。
- 拿到 cluster map 副本后,客户端**就知道了集群中所有的 monitor、OSD 和 metadata server**。
- **但是,即便手握最新版 cluster map,客户端对对象位置一无所知**(the client doesn't know anything about object locations)。
- **对象位置必须被计算出来**(Object locations must be computed)。
- 客户端**只需要对象 ID 和 pool 名**,就能计算出对象位置。
**官方五步(Calculating PG IDs)**:
| 步骤 | 官方描述 | 官方示例 |
| --- | --- | --- |
| 1 | 客户端输入 **pool 名**和**对象 ID** | pool = `"liverpool"`,object-id = `"john"` |
| 2 | Ceph **对对象 ID 做哈希**(hashes the object ID) | — |
| 3 | Ceph 用**哈希值对 PG 数量取模**,得到 **PG ID** | 假设结果为 **58** |
| 4 | Ceph 用 **pool 名取出 pool ID** | `"liverpool"` = **4** |
| 5 | Ceph 把 **pool ID 前置到 PG ID 之前** | **4.58** |
原文在描述这一计算时还提到使用的要素是:**object name、a hash code、pool 中 PG 的数量、以及 pool 名**。
> 💡 补充:PG ID 写成 `4.58` 这种「`pool_id.pg_id`」的形式,是你在 `ceph pg dump`、`ceph -s` 里看到的 PG 编号格式(如 `4.3f`)。注意 **PG ID 中的后半部分通常用十六进制显示**,而上面步骤里的 `58` 是官方给出的十进制示例——两者是同一个数的不同表示。理解"PG 编号 = pool ID + 池内 PG 序号"这一点,对读懂 `ceph pg map 4.58` 之类的输出很关键。
### 5.5 第 ④ 步:PG → OSD(CRUSH 与 Acting Set / Up Set)
- **CRUSH 动态地把 PG 映射到 OSD**。
- 客户端**使用自己的 cluster map 副本与 CRUSH 算法,精确计算出读写某个对象时要使用哪个 OSD**。
- 客户端用 CRUSH 确定对象存储位置后,对象被映射到 pool 和 PG,然后客户端**查阅 CRUSH map 找出该 PG 的 primary OSD**。
**Acting Set 与 Up Set(官方定义)**:
| 概念 | 定义(原文) |
| --- | --- |
| **Acting Set** | 负责某个 placement group 的 **OSD 集合**。该术语**既可以指当前负责该 PG 的 OSD,也可以指在某个 epoch 时曾负责该 PG 的 OSD** |
| **Up Set** | Acting Set 中的 OSD **并不总是 `up`**。当 Acting Set 中的某个 OSD 是 `up` 时,**它就是 Up Set 的一部分**。Up Set 是一个重要区分,因为**当某个 OSD 失败时,Ceph 可以把 PG 重映射到其它 OSD** |
| **Primary** | **按惯例是 Acting Set 中的第一个 OSD**,负责**为它作为 Primary 的每个 PG 编排 peering 过程**。**Primary 是给定 PG 中唯一接受客户端发起的写操作的 OSD** |
官方的 Acting Set / Up Set 示例:假设某个 PG 的 Acting Set 包含 **`osd.25`、`osd.32`、`osd.61`**。**第一个 OSD(`osd.25`)是 Primary**。如果它失败,**Secondary(`osd.32`)成为 Primary,而 `osd.25` 被移出 Up Set**。
`pg-concepts.rst` 的补充定义(更精确):
| 术语 | 定义 |
| --- | --- |
| **Acting Set** | 负责特定 PG 的 **OSD 有序列表**(或某个 epoch 时的列表) |
| **Up Set** | 按 CRUSH,特定 epoch 下负责特定 PG 的 **OSD 有序列表**。**通常与 Acting Set 相同,除非 Acting Set 被 OSD Map 中的 `pg_temp` 显式覆盖** |
| **Primary** | Acting Set 的**成员(按惯例是第一个)**,负责协调 peering,且**是唯一接受客户端对该 PG 中对象发起写操作的 OSD** |
| **Replica** | Acting Set 中的**非 primary OSD**(且已被 primary 认可并 *activated*) |
| **Stray** | **不是**当前 Acting Set 成员,但**尚未被告知可以删除**其某个 PG 副本的 OSD |
| **Current Interval / Past Interval** | Acting Set 与 Up Set **不发生变化**的一段连续 OSD map epoch 序列 |
| **Epoch** | (单调递增的)**OSD map 版本号** |
### 5.6 第 ⑤ 步:写入 Primary 并复制(subops)
见 2.3.5 节的六步流程与时序图。补充两条原文要点:
- **Ceph OSD Daemon 使用 CRUSH 算法确定对象副本的存储位置**(即 primary 侧也是算出来的,不是查表)。
- 官方把这类复制操作称为 **`subops`**。
### 5.7 端到端示例(严格使用官方给定的数值)
场景:客户端要把一个名为 `john` 的对象写入名为 `liverpool` 的 pool。
```text
输入:
pool name = "liverpool"
object id = "john"
pool 的 PG 数量(pg_num)= 假设值(官方未给出具体 pg_num,仅以结果示例)
① hash("john") -> 得到哈希值(官方未给出具体哈希函数与数值)
② hash % pg_num -> 58 (官方示例值)
③ pool name -> pool ID -> "liverpool" = 4 (官方示例值)
④ 拼接 -> PG ID = 4.58 (官方示例值)
⑤ CRUSH(4.58, CRUSH map, pool 的 CRUSH rule) -> Acting Set
例如 [osd.25, osd.32, osd.61]
⑥ Primary = Acting Set 的第一个 = osd.25
⑦ 客户端直连 osd.25 写入
⑧ osd.25 用自己的 CRUSH map 副本算出 secondary(osd.32、osd.61),
通过 subops 复制,收集 Ack(4)/Ack(5)
⑨ osd.25 返回 Ack(6) 给客户端
```
**验证一遍"客户端为什么不需要问任何人"**:
1. 客户端从 MON 拿到 cluster map(含 CRUSH map、OSD map 中的 pool 列表与 pool ID、PG 数量等)→ 知道了 `pool "liverpool" = 4` 与 `pg_num`。
2. 客户端本地 hash + 取模 → 得到 `4.58`。**全程无网络交互。**
3. 客户端本地跑 CRUSH → 得到 `osd.25`。**全程无网络交互。**
4. 客户端才第一次真正发起数据 I/O:直连 `osd.25`。
> 💡 补充:这就是为什么 Ceph 客户端的 CPU 开销里有一部分是 CRUSH 计算,也是为什么"客户端持有过期的 cluster map"会导致**读到旧位置/写错 OSD**——Ceph 的做法是让 OSD 在收到请求时**比对 epoch**,发现客户端 map 过旧就回送更新,客户端重算重试。这个"乐观计算 + 版本校验 + 重试"的模式,是去中心化寻址的标准解法。
### 5.8 寻址链路中的注意事项
| 注意事项 | 说明 |
| --- | --- |
| 计算优于查询 | 原文:**计算对象位置比通过一次 chatty session 查询对象位置快得多** |
| 客户端的 cluster map 可能过期 | 客户端只持有副本;epoch 变化时需要更新(属 OSD map 传播,见第六章) |
| Primary 是唯一写入口 | **Primary 是给定 PG 中唯一接受客户端发起写操作的 OSD** |
| 一个 pool 一套 CRUSH rule | 客户端写单个 pool,条带出来的对象同属该 pool,使用同一 CRUSH map 与访问控制 |
| PG 数量变化会改变取模结果 | `pg_num` 是第 ③ 步的除数,改变它会**重新分配对象到 PG**(这正是 PG 扩容要谨慎的原因) |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Placement Group Concepts](https://docs.ceph.com/en/tentacle/rados/operations/pg-concepts/)
---
## 六、智能守护进程与分布式集群管理
### 6.1 核心思想:OSD 与客户端都是"集群感知"的
官方论述(「Smart Daemons Enable Hyperscale」):
- 很多存储集群的特征是有一个**中心化接口(centralized interface)**,它记录客户端被允许访问哪些节点。这类中心化架构通过**双次分发(double dispatch)**向客户端提供服务。**在 PB 到 EB 规模上,这种双次分发是重大瓶颈**。
- **Ceph 消除了这个瓶颈:Ceph 的 OSD Daemon 与 Ceph Client 都是集群感知的(cluster-aware)**。
- 像客户端一样,**每个 Ceph OSD Daemon 都知道集群中其他 Ceph OSD Daemon**。这使得 OSD 能**直接与其他 OSD 交互、直接与 Monitor 交互**。
- 因为**客户端、monitor、OSD 三者彼此直接交互**,OSD 守护进程**可以利用集群节点的聚合 CPU 与 RAM 资源**。这意味着 Ceph 集群可以轻松完成带中心接口的集群难以完成的任务。
### 6.2 四大收益(官方列举)
| # | 收益 | 机制 |
| --- | --- | --- |
| 1 | **OSDs Service Clients Directly** | **网络设备只能支持有限数量的并发连接**。因为客户端**无需先连接中心接口**就直接联系 OSD,Ceph 相对包含中心接口的存储冗余策略**拥有更好的性能与更大的系统容量**。客户端**只在需要时保持会话**,而且**只与特定的 OSD 保持会话**,而不是与中心接口保持 |
| 2 | **OSD Membership and Status** | 见 2.3.3:OSD 上报状态;`up`/`down`;邻居判定 + Monitor 兜底;`MOSDBeacon`(Luminous+)/ `MPGStats`(Luminous 前) |
| 3 | **Data Scrubbing** | 见 2.3.4:以 PG 为单位比对元数据;每日;深度 scrub 逐位比对校验和 |
| 4 | **Replication** | 见 2.3.5:客户端算 PG/primary,primary 用 `subops` 复制到 secondary 并 ack |
### 6.3 Cluster Map:分布式管理的"共享真相"
#### 五张 map(官方定义,逐条)
| map | 内容(官方原文) | 查看命令 |
| --- | --- | --- |
| **Monitor Map** | 集群 **`fsid`**、**每个 monitor 的 position、name、address 与 TCP port**;monitor map 还规定了**当前 epoch、monitor map 的创建时间、以及最后一次修改的时间** | `ceph mon dump` |
| **OSD Map** | 集群 **`fsid`**、OSD map 的**创建时间**、**最后修改时间**、**pool 列表**、**副本 size 列表**、**PG 数量列表**、以及 **OSD 及其状态(例如 `up`、`in`)的列表** | `ceph osd dump` |
| **PG Map** | **PG version**、其 **time stamp**、**最近的 OSD map epoch**、**full ratios**,以及**每个 placement group 的细节**。细节包括:**PG ID、Up Set、Acting Set、PG 的状态(例如 `active + clean`)、以及每个 pool 的数据使用统计** | —(文档未给出专门命令) |
| **CRUSH Map** | **存储设备列表**、**故障域层级(例如 `device`、`host`、`rack`、`row`、`room`)**、以及**存储数据时遍历该层级的规则** | `ceph osd getcrushmap -o {filename}` 然后 `crushtool -d {comp-crushmap-filename} -o {decomp-crushmap-filename}` |
| **MDS Map** | **当前 MDS map epoch**、map **创建时间**、**最后变更时间**;还包括**存放元数据的 pool**、**metadata server 列表**、以及**哪些 metadata server 是 `up` 和 `in`** | `ceph fs dump` |
#### 关键性质
- **每一张 map 都维护其运行状态变更的历史**(Each map maintains a history of changes to its operating state)。
- **Ceph Monitor 维护 cluster map 的主副本(master copy)**。这份主副本包括:**集群成员、集群状态、集群变更、以及记录 Ceph Storage Cluster 整体健康情况的信息**。
#### 术语表对 Cluster Map 的定义
Cluster Map = 由 **monitor map、OSD map、PG map、MDS map 和 CRUSH map** 组成的集合,**共同报告 Ceph 集群的状态**。
> 💡 补充(文档内部差异,值得注意):`architecture.rst` 的 "Ceph Monitor" 相关表述与术语表的 "Ceph Monitor" 条目在**列举 monitor 维护的 map**时并不完全一致——术语表的 **Ceph Monitor** 条目说 cluster state 包括 **monitor map、manager map、OSD map 和 CRUSH map**(把 manager map 列入,未列 PG map 与 MDS map);而 `architecture.rst` 与术语表的 **Cluster Map** 条目都明确是**五张 map**(monitor / OSD / PG / MDS / CRUSH)。本册以 `architecture.rst` 的"五张 map"为准,此差异已登记在文末「文档未明确项」。
### 6.4 集群映射如何传播(Propagation)
**官方给出的传播环节**(散见各节,本册归纳):
| 环节 | 官方描述 |
| --- | --- |
| 客户端首次获取 | 客户端**绑定到一个 Ceph Monitor**,取得**最新版本的 Cluster Map**;**必须**先取得当前 cluster map 副本才能读写数据 |
| 客户端持有什么 | 取得 cluster map 副本后,客户端**知道集群中所有的 monitor、OSD 与 metadata server**,但**不知道对象位置** |
| OSD 侧 | OSD **周期性地向 Monitor 发送消息**(Luminous 前 `MPGStats`,Luminous 起 `MOSDBeacon`) |
| OSD 拉取更新 | 若 OSD **无法与集群 map 中定义的任何 OSD 完成 peering**,它会**每 30 秒 ping 一次 Monitor 索取最新的 cluster map 副本**(`osd mon heartbeat interval`) |
| MON 侧兜底 | 若 OSD 未上报,Monitor 在 **`mon osd report timeout`** 超时后将其判定为 `down` |
| 一致性保证 | Monitor 之间用 **Paxos** 就集群状态达成一致,并要求**多数派** |
> 💡 补充:Ceph 的映射传播是**惰性 + 按需**的,不是广播推送。这个设计决定了两类常见现象:(1) 集群拓扑刚变化时,部分客户端/OSD 可能短暂持有旧 epoch,需要一次"被拒 → 更新 → 重试"才能收敛;(2) 集群规模越大,map 更新传播到全部参与者的尾部延迟越长。这也解释了为什么文档反复强调 Monitor 要"轻量"——它只服务于少量控制面请求,而非每个 I/O。
### 6.5 librados:智能客户端的载体
- Ceph 客户端使用**原生协议(native protocol)**与 Ceph Storage Cluster 交互。Ceph 把这一功能**打包进 `librados` 库**,因此**你可以创建自己的 Ceph Client**。
- 术语表 **librados**:一个 API,**可用于创建到 Ceph 存储集群的自定义接口**;`librados` 使得**与 Ceph Monitor 和 OSD 交互**成为可能。
- 原生协议提供**直接、并行的对象访问能力**(direct, parallel access to objects throughout the cluster),并具备**异步通信能力**(asynchronous communication capability)。
> 💡 补充:`librados` 之所以能叫"智能客户端",是因为它把本该由中心组件承担的 **CRUSH 计算、连接池管理、故障重试、OSD 会话维护**全部下沉到了客户端库里。代价是客户端升级要与集群能力对齐(虽然协议有版本协商),收益是数据路径上没有任何中心瓶颈。
### 6.6 高可用认证:CephX
#### 是什么
- **CephX** 是 Ceph 的认证协议,**用于认证用户与守护进程,并防范中间人攻击(man-in-the-middle attacks)**。
- 同时,CephX 提供一种机制来**授予客户端使用 Ceph 服务的授权(authorizations)**。这些授权被编码为 **CephX Capabilities**,由相应服务解释其赋予了客户端什么权限。
- 授权的形式是**有时限的票据(time-limited tickets)**,**必须由 Ceph 客户端周期性刷新**。
#### 重要边界(官方 note)
> **CephX 协议不处理传输中的数据加密(例如 SSL/TLS)或静态数据加密(encryption at rest)。**
#### 内部原理
**共享密钥**:CephX 使用**共享密钥(shared secret keys)**进行认证。这意味着**客户端与 Monitor 各保存一份客户端密钥的副本**。
**与 Kerberos 的相似性**(原文列举):
| 特征 | CephX(官方描述) |
| --- | --- |
| 总体类比 | CephX 协议的运行方式**类似于 Kerberos** |
| 无单点 | 像 Kerberos 一样,**每个 Monitor 都能独立认证用户并向客户端分发票据**。因此使用 CephX 时**不存在单点故障,也不存在瓶颈** |
| 票据内容 | 票据里包含:**客户端身份(entity name)、客户端的全局标识符或 incarnation、票据创建时间与过期时间、以及客户端拥有的所有 capabilities(授权)** |
| 密钥分发 | CephX 使用 **rotating service keys**,它们被**安全地分发给每一类服务**(例如 CephFS 的 MDS) |
| 票据数量 | 对**每一类 Ceph 服务(AUTH、MON、MGR、OSD、MDS)**,客户端**可能为每一个 rotating service key 收到一张票据**(**通常为 3 个**) |
| 过期 | 像 Kerberos 票据一样,**CephX 票据会过期**。攻击者**不能使用**已偷偷获得的过期票据或会话密钥 |
**安全性论证(原文)**:这种认证形式可以阻止**能够接触通信介质的攻击者以他人身份伪造消息**,也能阻止攻击者**篡改他人的合法消息**——**前提是该用户的密钥在过期前未被泄露**。
**密钥轮转(2026+ 的预期)**:官方明确写到——**预计在 2026 年以后的 Ceph 安装中,服务守护进程的密钥(例如 `mgr.x`)可能在守护进程被迁移甚至重启时轮转,这将成为常规操作**。同样的机制可用于任何实体类型(包括客户端)。最后,**当密码套件(ciphers)升级以提升安全性时,该机制还能用于实施密钥类型升级**。
#### 建立用户(官方示例)
- 要使用 CephX,**管理员必须提前建立每一个 user/credential**。
- 新装 Ceph 时**通常创建的第一个 credential 是 `client.admin` key**,可用于管理集群。
- 下图中,`client.admin` 用户可从命令行调用 **`ceph auth get-or-create-key`** 来生成新实体与密钥。Ceph 的 **`auth` 子系统生成用户名与密钥,在所有 Monitor 上保存一份副本,并把新实体的密钥返回给管理员**。
- 官方 note:**`client.admin` 用户必须以安全的方式把 user ID 与密钥提供给使用者**。
创建用户(原图为创建用户流程示意图 `.. ditaa::`):
```text
+---------+ +---------+
| Client | | Monitor |
+---------+ +---------+
| request to |
| create a user |
|-------------->|----------+ create user
| | | and
|<--------------|<---------+ store key
| transmit key |
| |
```
#### 认证与票据获取(官方逐步描述)
**第一步:认证,取得 auth session key**
1. 客户端以实体名 `client.username` 进行认证,**把用户名传给 Monitor**。
2. Monitor **生成一个 auth session key**,并用与 `client.username` 关联的**密钥**对其加密。
3. Monitor 把**加密后的 `auth` 票据**传给客户端。
4. 客户端**用其 principal 的密钥解密载荷**。
5. 新建立的 **auth session key 现在就代表了该用户的身份**,并将**在其 incarnation 期间持续有效**。
**第二步:请求服务票据,取得 service session key**
1. 客户端请求服务票据时,Monitor **用所请求服务类型的最新 rotating service secret 生成新票据**。
2. 每张票据内含一个**全新的、仅供该票据使用的 service session key**。
3. 这个 **service session key 被加密了两次**:
- 一次**用 auth session key** 加密;
- 第二次**在加密的服务票据内部**(该票据**只能被服务守护进程解密**)。
4. 这个新的 service session key 因此**让客户端与服务守护进程在建立会话时能够互相认证(mutually authenticate)**。
两步流程(原图为认证与票据获取示意图 `.. ditaa::`):
```text
+---------+ +---------+
| Client | | Monitor |
+---------+ +---------+
| authenticate |
|-------------->|----------+ generate and
| | | encrypt
|<--------------|<---------+ session key
| transmit |
| encrypted |
| session key |
| |
|-----+ decrypt |
| | session |
|<----+ key |
| |
| req. ticket |
|-------------->|----------+ generate and
| | | encrypt
|<--------------|<---------+ ticket
| recv. ticket |
| |
|-----+ decrypt |
| | ticket |
|<----+ |
```
#### 会话期间的持续保护
- CephX 协议**认证客户端与 Ceph 守护进程之间正在进行的通信**。
- **初始认证之后**,客户端与守护进程之间发送的**每条消息都用一个票据签名**,该票据**可被 Monitor、OSD 与 metadata daemon 验证**。
- 验证使用**客户端与守护进程之间共享的密钥**。
全流程(原图为 CephX 端到端认证示意图 `.. ditaa::`):
```text
+---------+ +---------+ +-------+ +-------+
| Client | | Monitor | | MDS | | OSD |
+---------+ +---------+ +-------+ +-------+
| request to | | |
| create a user | | |
|-------------->| mon and | |
|<--------------| client share | |
| receive | a secret. | |
| shared secret | | |
| |<------------>| |
| |<-------------+------------>|
| | mon, mds, | |
| authenticate | and osd | |
|-------------->| share | |
|<--------------| a secret | |
| session key | | |
| | | |
| req. ticket | | |
|-------------->| | |
|<--------------| | |
| recv. ticket | | |
| | | |
| make request (CephFS only) | |
|----------------------------->| |
|<-----------------------------| |
| receive response (CephFS only) |
| |
| make request |
|------------------------------------------->|
|<-------------------------------------------|
receive response
```
#### 重要限制(官方明确)
> **这种认证只保护 Ceph 客户端与 Ceph 守护进程之间的连接。认证不延伸到 Ceph 客户端之外。** 如果用户从远程主机访问 Ceph 客户端,**CephX 认证不会应用到用户主机与客户端主机之间的连接上**。
#### 为什么 CephX 是"高可用认证"
官方把 CephX 放在「High Availability Authentication」标题下,理由在文中很清楚:
- Ceph **在客户端与 Ceph 对象存储之间没有任何中心化接口**(引用「Scalability and High Availability」一节)。
- 但**正因如此,客户端必须直接与 OSD 交互**,而**客户端与 OSD 的直接交互需要已认证的连接**。
- **CephX 认证系统建立并维持这些已认证连接**。
- 又因为**每个 Monitor 都能独立认证与发票据**,所以 CephX **既无单点故障也无瓶颈**。
#### 与其他概念的关系
- CephX ← → **MON**:MON 是票据颁发者,并保存客户端密钥副本。
- CephX ← → **OSD / MDS / MGR**:它们是票据的验证方(服务方)。
- CephX ← → **Capabilities**:授权(authorization)与认证(authentication)是两个不同的关注点;官方另有一份 *A Detailed Description of the CephX Authentication Protocol* 专门讲这两者的区分。
- CephX ← → **加密**:**不覆盖**传输加密与静态加密。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)
---
## 七、数据一致性、Scrubbing、纠删码与副本对比
### 7.1 数据一致性:为什么需要 scrub
**根本问题**:`architecture.rst` 说得很直白——Ceph 依赖**商用硬件**,硬件会静默损坏(bit rot / bad sectors)。副本机制能保证"数据不丢",但**不能保证"副本之间内容一致"**,也不能发现**硬盘上的坏块**。
**Ceph 的对策**(官方两条):
| 机制 | 做什么 | 频率 |
| --- | --- | --- |
| **Scrubbing(light / shallow)** | OSD 把**本地对象的元数据**与**其他 OSD 上副本的元数据**做比较;**以 PG 为单位**;发现**对象大小不匹配**与**元数据不匹配**;**确认对象存在且元数据正确** | **每日**(daily) |
| **Deep scrubbing** | **逐位(bit-for-bit)**比较对象内的数据与其校验和(checksums);能发现**轻度 scrub 检测不出的坏块/坏扇区** | **每周**(weekly,by default performed weekly) |
**Scrub 的完整过程**(术语表):
1. Ceph **生成一个 PG 中所有对象的目录(catalog)**;
2. 把**每个主对象(primary object)**与存放在其它 OSD 上的**副本**做比较;
3. 确保**没有对象缺失或不匹配**;
4. 任何 PG 若被发现**某个副本与其他副本不同**,或**对象整个缺失**,该 PG 被标记为 **`inconsistent`**。
**官方对 scrub 价值的定位**:scrubbing **能捕获 OSD bug 或文件系统错误**(often as a result of hardware issues);deep scrubbing **能发现驱动器上轻度 scrub 时未显现的坏块**。
相关 PG 状态(`pg-states.rst`):
| 状态 | 含义 |
| --- | --- |
| `scrubbing` | Ceph 正在检查该 PG 的元数据以寻找不一致 |
| `deep` | Ceph 正在对照存储的校验和检查该 PG 的数据 |
| `inconsistent` | Ceph 检测到该 PG 中一个或多个对象副本存在不一致(例如对象大小错误、recovery 结束后某副本仍缺对象等) |
| `repair` | Ceph 正在检查该 PG 并**修复**它能修复的不一致 |
> 💡 补充:注意 `inconsistent` 与 `degraded` 的区别——`degraded` 是"副本数不够"(可用性问题,通常能自愈),`inconsistent` 是"副本内容不一致"(正确性问题,**Ceph 不会自动判定谁对**,需要人工介入 `ceph pg repair`)。这是运维上非常关键的区分。
### 7.2 Peering:一致性的协商阶段
**定义**:peering 是**让存放某个 PG 的所有 OSD 就该 PG 中所有 RADOS 对象(及其元数据)的状态达成一致**的过程。
**关键性质**:
- OSD 守护进程**互相检查心跳并向 Monitor 上报**,此外它们还会 **peer**。
- Ceph OSD 守护进程会**向 Monitor 上报 Peering Failure**。
- **Peering 问题通常会自行解决**;但如果问题持续存在,需要参考官方的 *Troubleshooting Peering Failure* 节。
- **重要 note**:**对集群状态达成一致的 PG,并不必然已经拥有最新数据**(PGs that agree on the state of the cluster do not necessarily have the current data yet)。
**副本数量基线(原文,含警告)**:
| 配置 | 官方说法 |
| --- | --- |
| `size = 2` | Ceph Storage Cluster **被设计为至少存储对象的两份副本(即 `size = 2`),这是数据安全的最低要求** |
| `size = 3` 且 `min size = 2` | 为了高可用,集群**应存储多于两份副本**,这样它**可以在维持数据安全的同时以 `degraded` 状态继续运行** |
| **警告(warning)** | 虽然这里说 R2(两副本复制)是数据安全的最低要求,**但推荐使用 R3(三副本)**。**在足够长的时间线上,采用 R2 策略存储的数据将会丢失** |
**Peering 相关概念(`pg-concepts.rst`)**:
| 术语 | 定义 |
| --- | --- |
| **Peering** | 让存放某 PG 的所有 OSD 就该 PG 中所有对象(及其元数据)的状态达成一致的过程。**注意:对状态达成一致并不意味着它们都拥有最新内容** |
| **PG Info** | 关于 PG 的**创建 epoch**、**最近一次写入该 PG 的 version**、***last epoch started*、*last epoch clean***、以及 ***current interval* 起点**的基本元数据。**任何 OSD 间关于 PG 的通信都包含 PG Info**,因此任何知道某 PG 存在(或曾经存在)的 OSD 也都拥有 *last epoch clean* / *last epoch started* 的下界 |
| **PG Log** | 该 PG 中对象的**最近更新列表**。当 Acting Set 中所有 OSD 都已确认到某一点之后,**这些日志可以被截断** |
| **Missing Set** | 每个 OSD 记录更新日志条目,**若这些条目意味着对象内容有更新,就把该对象加入一个"待更新"列表**,该列表称为该 `<OSD,PG>` 的 **Missing Set** |
| **Authoritative History** | 一个**完整的、完全有序的操作集合**,若执行这些操作,就能把某 OSD 的 PG 副本更新到最新 |
| **Last Epoch Start** | Acting Set 中所有节点就 **Authoritative History** 达成一致的**最后一个 epoch**。此时认为 **Peering 成功** |
| **Last Epoch Clean** | Acting Set 中所有节点**完全最新(PG 日志与对象内容均如此)**的**最后一个 Epoch**。此时认为 **recovery 完成** |
| **up_thru** | 在 Primary 能成功完成 peering 之前,**它必须通过一个存活的 monitor 在 OSD map 中设置自己的 `up_thru`**,告知 monitor 自己存活到当前 OSD map epoch。这有助于 peering **忽略那些 peering 从未完成的先前 Acting Set** |
`up_thru` 要处理的故障序列(官方示例):
```text
- acting set = [A,B]
- acting set = [A]
- acting set = [] 极短时间之后(例如同时故障,但检测有先后)
- acting set = [B] (B 重启,A 没有)
```
> 💡 补充:`Last Epoch Start`(peering 成功)与 `Last Epoch Clean`(recovery 完成)的区分,解释了为什么一个 PG 可以**先变成 `active`(能服务 I/O)再进入 `recovering`**——只要 peering 完成、Primary 确定,就可以开始接受写;数据补齐在后台进行。这正是官方说的"Once Peering has been performed, the Primary can start accepting write operations, and Recovery can proceed in the background"。
### 7.3 纠删码(Erasure Coding)
#### 基本概念
- 一个**纠删码池(erasure coded pool)把每个对象存储为 `K+M` 个 chunk**。
- 它被划分为 **`K` 个数据 chunk(data chunks)**与 **`M` 个编码 chunk(coding chunks)**。
- 该 pool 的 **`size` 被配置为 `K+M`**,这样**每个 chunk 存放在 acting set 中的一个 OSD 上**。
- **chunk 的 rank(序号)作为对象的一个属性(attribute)存储**。
#### 举例(官方)
- 可以创建一个使用**五个 OSD(`K+M = 5`)**并能**承受其中两个丢失(`M = 2`)**的纠删码池。
- **在 `(K+1)` 个 shard 恢复之前,数据可能不可用**(Data may be unavailable until (`K+1`) shards are restored)。
#### 写入:编码过程(官方 NYAN 例子)
对象 **NYAN**,内容为 **`ABCDEFGHI`**,写入一个 `K=3, M=2` 的池:
1. 纠删编码函数**把内容切成三个数据 chunk**,方法是**简单地三等分内容**:第一个含 `ABC`,第二个 `DEF`,最后一个 `GHI`。
2. **如果内容长度不是 `K` 的倍数,内容会被填充(padded)**。
3. 该函数**再创建两个编码 chunk**:第四个是 `YXY`,第五个是 `QGC`。
4. **每个 chunk 存放在 acting set 中的一个 OSD 上**。
5. 这些 chunk **存放在名字相同的对象(都叫 **NYAN**)中,但位于不同的 OSD 上**。
6. **chunk 被创建的顺序必须被保留**,它**作为对象的一个属性(`shard_t`)**存储,此外还有对象的名字。
7. 具体落位(官方示例):**chunk 1 含 `ABC`,存放在 OSD5**;**chunk 4 含 `YXY`,存放在 OSD3**。
编码示意(原图为纠删码编码示意图 `.. ditaa::`):
```text
+-------------------+
name | NYAN |
+-------------------+
content | ABCDEFGHI |
+--------+----------+
|
v
+------+------+
+---------------+ encode(3,2) +-----------+
| +--+--+---+---+ |
| | | | |
| +-------+ | +-----+ |
| | | | |
+--v---+ +--v---+ +--v---+ +--v---+ +--v---+
name | NYAN | | NYAN | | NYAN | | NYAN | | NYAN |
+------+ +------+ +------+ +------+ +------+
shard | 1 | | 2 | | 3 | | 4 | | 5 |
+------+ +------+ +------+ +------+ +------+
content | ABC | | DEF | | GHI | | YXY | | QGC |
+--+---+ +--+---+ +--+---+ +--+---+ +--+---+
| | | | |
v v v v v
+------+ +------+ +------+ +------+ +------+
| OSD5 | | OSD2 | | OSD1 | | OSD3 | | OSD4 |
+------+ +------+ +------+ +------+ +------+
```
(上表落位按官方文字描述绘制:chunk1→OSD5、chunk2→OSD2、chunk3→OSD1、chunk4→OSD3、chunk5→OSD4;原文图示中 OSD 与 chunk 的连线以源文 ditaa 图为准。)
#### 读取:解码过程(官方 NYAN 例子)
1. 解码函数**读取三个 chunk**:**chunk 1(含 `ABC`)、chunk 3(含 `GHI`)、chunk 4(含 `YXY`)**。
2. 然后**重建出对象的原始内容 `ABCDEFGHI`**。
3. 解码函数**被告知 chunk 2 和 chunk 5 缺失**(它们被称为 **'erasures'**)。
4. **chunk 5 读不到,因为 OSD4 处于 out(不在)状态**。
5. **只要读到三个 chunk,解码函数就可以被调用**:**OSD2 是最慢的,它的 chunk 未被计入**。
解码示意(原图为纠删码解码示意图 `.. ditaa::`):
```text
+-------------------+
name | NYAN |
+-------------------+
content | ABCDEFGHI |
+---------+---------+
^
|
+-------+-------+
| decode(3,2) |
+------------->+ | erasures 2,5 | <-+
| +-------+-------+ |
| ^ |
+--+---+ +------+ +---+--+ +---+--+
name | NYAN | | NYAN | | NYAN | | NYAN |
+------+ +------+ +------+ +------+
shard | 1 | | 2 | | 3 | | 4 |
+------+ +------+ +------+ +------+
content | ABC | | DEF | | GHI | | YXY |
+--+---+ +--+---+ +--+---+ +--+---+
^ . ^ ^
| TOO . | |
| SLOW . +--+---+ |
| ^ | OSD1 | |
| | +------+ |
| +-------| OSD2 | |
| +------+ |
| +------+ |
| | OSD3 |--------+
| +------+
| +------+
| | OSD4 | OUT
| +------+
| +------+
+----------------| OSD5 |
+------+
```
#### 中断的全量写(Interrupted Full Writes)
这是 EC 最微妙的正确性场景,官方用了大篇幅说明。
**初始状态**:一个 EC PG,`K = 2, M = 1`,由**三个 OSD** 支撑(两个给 `K`,一个给 `M`)。Acting Set 为 **OSD 1、OSD 2、OSD 3**。一个对象编码后:`D1v1`(数据 chunk 1,版本 1)在 **OSD 1**,`D2v1` 在 **OSD 2**,`C1v1`(编码 chunk 1,版本 1)在 **OSD 3**。每个 OSD 上的 PG 日志**完全相同**(即 epoch 1、version 1,记为 `1,1`)。
```text
Primary OSD
+-------------+
| OSD 1 | +-------------+
| log | Write Full | |
| +----+ |<------------+ Ceph Client |
| |D1v1| 1,1 | v1 | |
| +----+ | +-------------+
+------+------+
|
| +-------------+
| | OSD 2 |
| | log |
+--------->+ +----+ |
| | |D2v1| 1,1 |
| | +----+ |
| +-------------+
|
| +-------------+
| | OSD 3 |
| | log |
+--------->| +----+ |
| |C1v1| 1,1 |
| +----+ |
+-------------+
```
**写入 v2(WRITE FULL)**:
- **OSD 1 是 primary,收到客户端的 WRITE FULL**,意思是**载荷要整体替换对象,而不是覆盖其中一部分**。
- 创建**版本 2(v2)**来覆盖版本 1(v1)。
- **OSD 1 把载荷编码成三个 chunk**:`D1v2` 在 OSD 1、`D2v2` 在 OSD 2、`C1v2` 在 OSD 3。
- **每个 chunk 都被发送到目标 OSD**,**包括 primary OSD 自己**——primary **除了处理写操作、维护 PG 日志的权威版本之外,也负责存储 chunk**。
- **当 OSD 收到写 chunk 的指令时,它同时在 PG 日志中新建一条记录**。例如 **OSD 3 一存下 `C1v2`,就往日志里加入条目 `1,2`(epoch 1、version 2)**。
- **因为 OSD 是异步工作的**,某些 chunk 可能**仍在传输中**(例如 `D2v2`),而其它 chunk 已被确认并持久化到存储驱动器。
```text
Primary OSD
+-------------+
| OSD 1 |
| log |
| +----+ | +-------------+
| |D1v2| 1,2 | Write Full | |
| +----+ +<------------+ Ceph Client |
| | v2 | |
| +----+ | +-------------+
| |D1v1| 1,1 |
| +----+ |
+------+------+
|
| +------+------+
| | OSD 2 |
| +------+ | log |
+->| D2v2 | | +----+ | <- D2v2 仍在传输中
| +------+ | |D2v1| 1,1 |
| | +----+ |
| +-------------+
|
| +-------------+
| | OSD 3 |
| | log |
| | +----+ |
| | |C1v2| 1,2 | <- 已持久化
+---------->+ +----+ |
| |
| +----+ |
| |C1v1| 1,1 |
| +----+ |
+-------------+
```
**顺利收尾**:如果一切顺利,chunk 在 acting set 的每个 OSD 上都被确认,**日志的 `last_complete` 指针可以从 `1,1` 移动到 `1,2`**;然后**存放上一版本 chunk 的文件可以被删除**(OSD 1 上的 `D1v1`、OSD 2 上的 `D2v1`、OSD 3 上的 `C1v1`)。
```text
Primary OSD
+-------------+
| OSD 4 |
| log |
| +----+ |
| |D1v1| 1,1 | <- 由 decode 在 scrubbing 期间重建
| +----+ |
+------+------+
^
| +-------------+
| | OSD 2 |
+----------+ +----+ |
| | |D2v1| 1,1 |
| +-------------+
|
| +-------------+
| | OSD 3 |
+----------| +----+ |
| |C1v1| 1,1 |
+-------------+
+-------------+
| OSD 1 |
| (down) |
| c333 |
+-------------+
```
**事故场景**:如果 **`D2v2` 仍在传输中时 OSD 1 宕机**,则**对象的版本 2 被部分写入**:
- **OSD 3 有一个 chunk,但不足以恢复**。它丢了两个 chunk:`D1v2` 和 `D2v2`;而纠删码参数 **`K = 2`、`M = 1` 要求至少有两个 chunk 可用才能重建第三个**。
- **OSD 4 成为新的 primary**,它发现 **`last_complete` 日志条目(即:该条目之前的所有对象在先前的 acting set 中所有 OSD 上都已知可用)是 `1,1`**,于是 **`1,1` 将成为新的权威日志的头**。
- **OSD 3 上的日志条目 `1,2` 与 OSD 4 提供的新权威日志分歧(divergent)**:它被**丢弃**,**包含 `C1v2` chunk 的文件被删除**。
- **`D1v1` chunk 在 scrubbing 期间用纠删码库的 `decode` 函数重建,并存放在新的 primary OSD 4 上**。
| 阶段 | OSD 1 (primary) | OSD 2 | OSD 3 | OSD 4 |
| --- | --- | --- | --- | --- |
| 初始 v1 | `D1v1` log `1,1` | `D2v1` log `1,1` | `C1v1` log `1,1` | — |
| 写 v2 中 | `D1v2` log `1,2`(后 down) | `D2v2` 在途 | `C1v2` log `1,2` | — |
| OSD 1 down 后 | (down) | `D2v1` log `1,1` | `C1v2` 被判定分歧 → 删除;回退到 `C1v1` | 新 primary;权威日志头 = `1,1` |
| scrub 后 | (down) | `D2v1` | `C1v1` | `D1v1`(decode 重建) |
> 💡 补充:这个例子揭示了 **EC 池不支持"部分覆盖写"的原子性保证**——一旦写入过程中 primary 挂掉且数据不足以重建,系统**回退到上一个 `last_complete` 版本**,而不是"猜"一个结果。这正是为什么 EC 池对 `allow_ec_overwrites`、对 RBD/RGW/CephFS 的使用方式有额外限制(详见纠删码分册)。同时注意原文的 `K=2, M=1` 只能容忍 **1 个** chunk 丢失,而丢失 2 个就无法重建——这与前面"K+M=5、M=2 可承受 2 个丢失"的规则一致:**可容忍丢失数 = M**。
### 7.4 副本 vs 纠删码:对比
| 维度 | 副本(Replicated) | 纠删码(Erasure Coded) |
| --- | --- | --- |
| 存储开销 | `size` 份完整数据(R3 = 3x) | `K+M` 份分片,开销 **`(K+M)/K`**(3+2 = 1.67x) |
| pool 的 `size` 语义 | `size` = 副本数 | `size` = **`K+M`**;官方特别说明:**EC 池会"报告"一个等于 K+M 的 size,但该值不能被直接 set** |
| 容忍丢失 | R3 可容忍 `size-1` 份丢失(仍可读);可服务的最小副本由 `min_size` 控制 | 可容忍 **M** 个 chunk 丢失 |
| 读取路径 | 直接从 primary(或副本)读 | **需要至少 K 个 chunk**;官方示例:**读到 K 个即可解码**,慢的那个(OSD2)**不纳入考虑** |
| 写入路径 | primary 复制整对象到 secondary | **primary 负责把载荷编码成 K+M 个 chunk 并发送给其它 OSD**;primary **也存储 chunk**、**并维护 PG 日志的权威版本** |
| 部分写 / 覆盖写 | 天然支持 | **受限制**;中断的全量写需要靠 `last_complete` 日志回退(见 7.3) |
| omap(object map) | 支持 | **术语表明确:纠删码池不能存储 RADOS omap 数据结构** |
| 恢复期可用性 | 剩 1 份完整副本即可读 | 官方:**在 `(K+1)` 个 shard 恢复之前,数据可能不可用** |
| 恢复成本 | 拷贝整对象 | 需要 **decode 重建**(可能要读 K 个 chunk 才能重建 1 个) |
| 适用场景(官方未直接给出,💡 补充) | 热数据、小块随机写、需要 omap(如 RGW bucket index)、RBD | 冷/温数据、大对象顺序写、归档 |
> 💡 补充:术语表中那句"**Erasure-coded pools cannot store RADOS omap data structures**"是选型的硬约束——因为 **RGW 的 bucket index 就是存成 omap 的**,所以 RGW 的 index / 元数据池**不能用 EC 池**,只有 data 池可以用 EC。这是生产上最常见的 EC 落地形态(元数据池副本、数据池 EC)。
### 7.5 一致性机制小结
| 层次 | 机制 | 保证什么 |
| --- | --- | --- |
| 写路径 | primary 串行化 + `subops` 复制到 secondary + ack | 副本**被写入**并被确认 |
| PG 内协商 | **peering** + **PG Log** + **Authoritative History** + `up_thru` | 各副本**对"发生了哪些操作"达成一致** |
| 状态推进 | `last_epoch_start`(peering 成功)→ `last_epoch_clean`(recovery 完成) | 区分"可以开始服务"与"数据已完全补齐" |
| 数据完整性 | **light scrub(每日)** + **deep scrub(每周)** | 发现**静默损坏**、对象缺失、大小/元数据不一致 |
| 故障恢复 | recovery / backfill(见第八章) | 把副本补回期望份数 |
| 认证 | **CephX** | 防止伪造/篡改消息(**但不提供加密**) |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)、[Placement Group Concepts](https://docs.ceph.com/en/tentacle/rados/operations/pg-concepts/)、[Placement Group States](https://docs.ceph.com/en/tentacle/rados/operations/pg-states/)
---
## 八、高可用与故障恢复
### 8.1 故障检测:三层机制
```text
① OSD ↔ OSD 心跳(peer 检测,主力)
OSD 以 <6s 的随机间隔检查邻居心跳;20s 宽限期内无心跳 → 判定邻居 down
│
v
② OSD → MON 上报(down 报告 + 状态报告)
默认需要来自不同 host 的 2 个 OSD 报告,MON 才确认某 OSD down
│
v
③ MON 兜底(beacon 超时)
OSD 周期性发 MOSDBeacon;mon osd report timeout 超时 → MON 标记 OSD down
```
#### ① OSD 检查心跳(OSDs Check Heartbeats)
- 每个 OSD 守护进程**以随机间隔(小于每 6 秒)**检查其他 OSD 的心跳。
- 如果邻居 OSD **在 20 秒宽限期内未显示心跳**,该 OSD **可以认为邻居 `down` 并上报给 Monitor**,Monitor 随之更新 Cluster Map。
- 可用 `osd heartbeat grace` 修改(`[mon]` 与 `[osd]` 或 `[global]` 段,或运行时设置)。
#### ② OSD 上报 down 的 OSD(OSDs Report Down OSDs)
- **默认情况下,必须有来自不同 host 的两个 OSD 向 Monitor 报告某个 OSD `down`,Monitor 才会确认**。
- **为什么需要两个**:官方解释——有可能所有上报故障的 OSD **都位于一个带有故障交换机的 rack 中**,该交换机与另一个 OSD 的连接有问题。**为避免这类误报,我们把上报故障的 peer 视为整体集群中一个潜在"子集群(subcluster)"的代理**,这个子集群同样滞后(laggy)。官方坦承"**这显然并非在所有情况下都成立,但有时能帮助我们把这个宽限修正定位到系统中出问题的子集上**"。
- **`mon osd reporter subtree level`** 用于**按 peer 在 CRUSH map 中的共同祖先类型把它们分组为"subcluster"**。
- **默认情况下,只需要来自不同 subtree 的两份报告**即可上报某个 OSD `down`。
- 可用 **`mon osd min down reporters`** 与 **`mon osd reporter subtree level`** 修改(`[mon]` 段或运行时)。
#### ③ OSD 上报自身状态(OSDs Report Their Status)
- 若 OSD 未向 Monitor 上报,**Monitor 在 `mon osd report timeout` 超时后会认为该 OSD `down`**。
- OSD 在**可上报事件**发生时会上报:例如**故障、PG stats 变化、`up_thru` 变化、或启动时(5 秒内)**。可用 `osd mon report interval` 修改。
- **无论是否有显著变化,OSD 每 120 秒上报一次**。可用 `osd mon report interval max` 修改。
#### ④ Peering 失败时的 map 拉取
- 若 OSD **无法与配置/集群 map 中定义的任何 OSD 完成 peering**,它会**每 30 秒 ping Monitor 索取最新的集群 map 副本**。可用 `osd mon heartbeat interval` 修改(`[osd]` 段)。
#### 心跳数值速查表
| 项目 | 默认值 | 可调参数 |
| --- | --- | --- |
| OSD 间心跳检查间隔 | **< 6 秒**(随机) | — |
| 邻居无心跳宽限期 | **20 秒** | `osd heartbeat grace` |
| 确认 OSD down 所需上报者 | **来自不同 host 的 2 个 OSD**(默认只需来自不同 subtree 的 2 份报告) | `mon osd min down reporters`、`mon osd reporter subtree level` |
| peering 失败后拉取 map 间隔 | **30 秒** | `osd mon heartbeat interval` |
| 可上报事件后上报时限 | **5 秒** | `osd mon report interval` |
| 无条件上报周期 | **120 秒** | `osd mon report interval max` |
| MON 判定 OSD down | `mon osd report timeout` 超时 | `mon osd report timeout` |
> 💡 补充:官方文档特意提醒——**修改心跳设置时应把它们放在配置文件的 `[global]` 段**。这是因为心跳参数在 mon 与 osd 两侧都会被读取,放在单侧会导致行为不一致。
### 8.2 OSD 状态与集群可用性
| 状态组合 | 含义(官方) |
| --- | --- |
| `up` | OSD 守护进程**正在运行且能够服务 Ceph Client 请求** |
| `down` | 守护进程未运行/不能服务 |
| **`down` 且 `in`** | **可能指示该 OSD 守护进程发生了故障** |
| **flapping OSD** | **被反复、快速地交替标记为 `up` 和 `down`** 的 OSD(术语表) |
相关配置项(`mon-osd-interaction.rst` 的 Monitor Settings 列表):`mon_osd_min_up_ratio`、`mon_osd_min_in_ratio`、`mon_osd_laggy_halflife`、`mon_osd_laggy_weight`、`mon_osd_laggy_max_interval`、`mon_osd_adjust_heartbeat_grace`、`mon_osd_adjust_down_out_interval`、`mon_osd_auto_mark_in`、`mon_osd_auto_mark_auto_out_in`、`mon_osd_auto_mark_new_in`、`mon_osd_down_out_interval`、`mon_osd_down_out_subtree_limit`。
> 💡 补充:架构页没有展开 `down` → `out` 的转换(由 `mon_osd_down_out_interval` 控制,默认在其它分册给出)。本册只登记参数名,**不给出默认值**(架构页与本节引用的页面未给出具体默认秒数)。这是本册「不编造」原则的要求——具体数值请查阅运维/配置分册。
### 8.3 Recovery 与 Backfill
**Recovery**(`pg-concepts.rst`):
> **确保某个 PG 中所有对象的副本都存在于 Acting Set 的所有 OSD 上。一旦 Peering 完成,Primary 就可以开始接受写操作,而 Recovery 可以在后台进行。**
**Backfill**(`pg-states.rst`):
> **Ceph 扫描并同步一个 PG 的**全部内容**,而不是从最近操作的日志中推断需要同步哪些内容。Backfill 是 recovery 的一个特例(Backfill is a special case of recovery)。**
两者的对比(💡 补充归纳,基于上述官方定义):
| 维度 | Recovery | Backfill |
| --- | --- | --- |
| 依据 | **PG Log / Missing Set**(能推断出缺哪些对象) | **扫描 PG 的全部内容** |
| 触发 | 短暂离线后回到 acting set(日志还在) | 日志不足以推断(例如新加入的 OSD、PG 整体迁移) |
| 代价 | 只补缺失对象 | 全量比对,代价更高 |
| 关系 | — | **是 recovery 的特例** |
相关的 PG 状态(`pg-states.rst`):
| 状态 | 含义 |
| --- | --- |
| `recovering` | Ceph 正在迁移/同步对象及其副本 |
| `forced_recovery` | 用户强制该 PG 的高恢复优先级 |
| `recovery_wait` | 该 PG 排队等待开始 recover |
| `recovery_toofull` | 恢复操作等待中,因为**目标 OSD 超过了它的 full ratio** |
| `recovery_unfound` | 由于 **unfound objects**,恢复停止 |
| `backfilling` | Ceph 正在扫描并同步一个 PG 的全部内容 |
| `forced_backfill` | 用户强制该 PG 的高 backfill 优先级 |
| `backfill_wait` | 该 PG 排队等待开始 backfill |
| `backfill_toofull` | backfill 等待中,因为**目标 OSD 超过了 backfillfull ratio** |
| `backfill_unfound` | 由于 unfound objects,backfill 停止 |
### 8.4 PG 状态机(完整清单,引自 `pg-states.rst`)
官方说明:**检查集群状态时,一个 PG 会有一个或多个状态;placement group map 中 PG 的最优状态是 `active + clean`**。
| 状态 | 官方含义 |
| --- | --- |
| `creating` | Ceph 仍在创建该 PG |
| `activating` | 该 PG 已 peered 但尚未 active |
| `active` | Ceph 会处理对该 PG 的请求 |
| `clean` | Ceph 已把该 PG 中所有对象**按正确的份数完成复制** |
| `down` | **一个持有必要数据的副本处于 down**,因此该 PG 离线 |
| `laggy` | 某个副本**未及时确认来自 primary 的新 lease**;**IO 被临时暂停** |
| `wait` | 该 PG 的 OSD 集合刚发生变化,**IO 被临时暂停**,直到前一 interval 的 lease 过期 |
| `scrubbing` | Ceph 正在检查该 PG 的元数据以寻找不一致 |
| `deep` | Ceph 正在对照存储的校验和检查该 PG 的数据 |
| `degraded` | Ceph **尚未**把该 PG 中某些对象复制到正确份数 |
| `inconsistent` | Ceph 检测到该 PG 中**一个或多个对象副本存在不一致**(例如对象大小错误、recovery 结束后某副本仍缺对象等) |
| `peering` | 该 PG 正在进行 peering 过程 |
| `repair` | Ceph 正在检查该 PG 并**修复**发现的(可能的)不一致 |
| `recovering` | Ceph 正在迁移/同步对象及其副本 |
| `forced_recovery` | 用户强制高恢复优先级 |
| `recovery_wait` | 排队等待开始 recover |
| `recovery_toofull` | 恢复等待中,目标 OSD 超过 full ratio |
| `recovery_unfound` | 因 unfound objects 恢复停止 |
| `backfilling` | 扫描并同步整个 PG 内容(recovery 的特例) |
| `forced_backfill` | 用户强制高 backfill 优先级 |
| `backfill_wait` | 排队等待开始 backfill |
| `backfill_toofull` | 目标 OSD 超过 backfillfull ratio |
| `backfill_unfound` | 因 unfound objects backfill 停止 |
| **`incomplete`** | Ceph 检测到某 PG **缺失关于"可能已发生的写"的信息**,或**没有任何健康副本**。若看到此状态,**尝试启动任何可能含有所需信息的失败 OSD**。在纠删码池的情况下,**临时降低 `min_size` 可能允许恢复** |
| **`stale`** | 该 PG 处于**未知状态**——**自从该 PG 的映射发生变化后,monitor 一直没有收到关于它的更新** |
| `remapped` | 该 PG **被临时映射到一组与 CRUSH 指定的不同的 OSD** |
| `undersized` | 该 PG 的**副本数少于配置的 pool 复制级别** |
| `peered` | 该 PG 已 peered,但**由于副本数不足以达到 pool 配置的 `min_size`,无法服务客户端 IO**。此状态下**可能发生 recovery**,PG 有可能最终恢复到 `min_size` |
| `snaptrim` | 正在裁剪快照(Trimming snaps) |
| `snaptrim_wait` | 排队等待裁剪快照 |
| `snaptrim_error` | 裁剪快照出错而停止 |
| `unknown` | 自 mgr 启动以来,**ceph-mgr 尚未从任何 OSD 收到关于该 PG 状态的信息** |
> 💡 补充:`stale` 与 `incomplete` 是最容易混淆的两个状态,也是本册范围内最值得记住的两个:
> - **`stale` = 不知道**(信息过期):monitor 在 PG 映射变化后没收到更新。常见诱因是**所有持有该 PG 的 OSD 都不可达**或 monitor 自身问题。它说的是"**我(MON)没有最新消息**"。
> - **`incomplete` = 知道了,但不完整**:Ceph 明确知道"缺少可能已发生的写的信息"或"没有健康副本"。它说的是"**数据可能已经不全了**",处理方式是**先去救 OSD**(而不是直接放弃)。
> 处理路径也因此不同:`stale` 先排查 OSD 可达性 / monitor;`incomplete` 先尝试拉起失败的 OSD,EC 池可考虑临时调低 `min_size`(官方明确给出的建议)。
### 8.5 故障恢复的典型流程(综合官方描述)
```text
OSD 故障
│
├─ 邻居 OSD 在 20s 宽限期内未收到心跳 → 判定 down
├─ 需来自不同 host/subtree 的 2 个 OSD 上报 → MON 确认 down
└─ (兜底)OSD beacon 超时(mon osd report timeout)→ MON 标记 down
│
v
MON 更新 OSD Map(epoch 递增)
│
v
PG 的 Acting Set 变化(原 primary 被移出 Up Set,secondary 成为 primary)
│ 例:Acting Set [osd.25, osd.32, osd.61] → osd.25 失败
│ → osd.32 成为 primary,osd.25 移出 Up Set
v
Peering(新 primary 编排;需 MON 设置 up_thru)
│ 成功 → last_epoch_start;PG 可进入 active
v
Recovery / Backfill(后台)
│ 依据 PG Log / Missing Set(recovery)或全量扫描(backfill)
v
last_epoch_clean → PG 回到 active + clean
│
v
(EC 池特例)若版本不完整:以 last_complete 为新权威日志,丢弃分歧日志,
缺失 chunk 在 scrubbing 期间用 decode 重建
```
### 8.6 高可用的其他支柱
| 支柱 | 机制 | 官方要点 |
| --- | --- | --- |
| **MON 高可用** | monitor 集群 + **Paxos** + 多数派 | 至少 **3 个** monitor 才能既冗余又高可用;Quorum = 多数 monitor `up` |
| **MGR 高可用** | active / standby + beacon | 最先启动者为 active,其余 standby;**不需要 quorum**;beacon 超时(`mon_mgr_beacon_grace`)则被 standby 取代;可用 `ceph mgr fail` 抢占切换 |
| **MDS 高可用** | `active` / `standby` 实例 | 额外实例为 `standby`,可接管任何失败的 `active`;**因为所有数据(含 journal)都在 RADOS 上,所以这很容易**;切换由 **`ceph-mon` 自动触发** |
| **数据高可用** | 副本 / EC + CRUSH 故障域 | 推荐 **R3**(`size = 3`、`min size = 2`);**R2 在足够长的时间线上必然丢数据** |
| **连接高可用** | CephX | 每个 MON 都能独立认证与发票据,**无单点故障、无瓶颈** |
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Configuring Monitor/OSD Interaction](https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/)、[Placement Group States](https://docs.ceph.com/en/tentacle/rados/operations/pg-states/)、[Placement Group Concepts](https://docs.ceph.com/en/tentacle/rados/operations/pg-concepts/)、[ceph-mgr administrator's guide](https://docs.ceph.com/en/tentacle/mgr/administrator/)
---
## 九、扩展性与性能特性
### 9.1 数据条带化(Data Striping)
#### 为什么需要
**存储设备存在吞吐限制,这会影响性能与可扩展性。** 因此存储系统通常支持 **striping**——把**顺序的信息片段存放到多个存储设备上**——以提升吞吐与性能。最常见的条带化来自 **RAID**;**与 Ceph 条带化最相似的 RAID 类型是 RAID 0(即 'striped volume')**。
官方对 Ceph 条带化的定位:**Ceph 的条带化提供了 RAID 0 条带化的吞吐、n 路 RAID 镜像(mirroring)的可靠性,以及更快的恢复**。
#### 关键前提(官方 tip,非常重要)
> **Ceph 存放在 Ceph Storage Cluster 中的对象本身不是条带化的(are not striped)。** 是 **Ceph Object Storage、Ceph Block Device 和 Ceph File System 把它们的数据条带化到多个 Ceph Storage Cluster 对象上**。而**直接通过 `librados` 写入存储集群的客户端,必须自己完成条带化(以及并行 I/O),才能获得这些收益**。
#### 最简单的条带化(stripe count = 1 object)
客户端**把 stripe unit 写到一个对象中,直到该对象达到最大容量,然后创建另一个对象**继续写入。官方说:这种简单形式**对小型块设备镜像、S3 或 Swift 对象、以及 CephFS 文件可能已经足够**。但它**没有最大化利用 Ceph 把数据分布到多个 PG 的能力,因而对性能提升有限**。
(原图为最简单的条带化示意图 `.. ditaa::`)
```text
+---------------+
| Client Data |
| Format |
| cCCC |
+---------------+
|
+--------+-------+
| |
v v
/-----------\ /-----------\
| Begin cCCC| | Begin cCCC|
| Object 0 | | Object 1 |
+-----------+ +-----------+
| stripe | | stripe |
| unit 1 | | unit 5 |
+-----------+ +-----------+
| stripe | | stripe |
| unit 2 | | unit 6 |
+-----------+ +-----------+
| stripe | | stripe |
| unit 3 | | unit 7 |
+-----------+ +-----------+
| stripe | | stripe |
| unit 4 | | unit 8 |
+-----------+ +-----------+
| End cCCC | | End cCCC |
| Object 0 | | Object 1 |
\-----------/ \-----------/
```
#### 跨对象集(object set)的条带化
**适用场景**:官方说,如果你预期会有**很大的镜像、很大的 S3/Swift 对象(例如视频)、或很大的 CephFS 目录**,把客户端数据**条带化到一个 object set 中的多个对象上**,可能会带来**可观的读写性能提升**。
**为什么快(官方论证)**:
- **当客户端把 stripe unit 并行写入各自对应的对象时,会出现显著的写性能提升**。
- 因为**对象被映射到不同的 PG,进而映射到不同的 OSD,每次写入都以最大写速度并行发生**。
- **写入单个驱动器会受限于磁头移动(例如每次寻道 6ms)和该设备的带宽(例如 100MB/s)**。
- **通过把写入分散到多个对象(它们映射到不同 PG 与 OSD),Ceph 可以减少每块驱动器的寻道次数,并合并多个驱动器的吞吐,从而实现快得多的写(或读)速度**。
**object set 的推进规则**:客户端**把 stripe unit 依次写入由 stripe count 决定的一系列对象**,这一系列对象称为 **object set**。**写到 object set 中最后一个对象之后,客户端回到该 object set 的第一个对象**。若 object set 已满,则**创建一个新的 object set**。
(原图为跨 object set 的条带化示意图 `.. ditaa::`)
```text
+---------------+
| Client Data |
| Format |
| cCCC |
+---------------+
|
+-----------------+--------+--------+-----------------+
v v v v
/-----------\ /-----------\ /-----------\ /-----------\
| Begin cCCC| | Begin cCCC| | Begin cCCC| | Begin cCCC|
| Object 0 | | Object 1 | | Object 2 | | Object 3 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 0 | | unit 1 | | unit 2 | | unit 3 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 4 | | unit 5 | | unit 6 | | unit 7 | Object
+-----------+ +-----------+ +-----------+ +-----------+ Set 1
| stripe | | stripe | | stripe | | stripe |
| unit 8 | | unit 9 | | unit 10 | | unit 11 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 12 | | unit 13 | | unit 14 | | unit 15 |
+-----------+ +-----------+ +-----------+ +-----------+
| End cCCC | | End cCCC | | End cCCC | | End cCCC |
| Object 0 | | Object 1 | | Object 2 | | Object 3 |
\-----------/ \-----------/ \-----------/ \-----------/
/-----------\ /-----------\ /-----------\ /-----------\
| Begin cCCC| | Begin cCCC| | Begin cCCC| | Begin cCCC|
| Object 4 | | Object 5 | | Object 6 | | Object 7 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 16 | | unit 17 | | unit 18 | | unit 19 |
+-----------+ +-----------+ +-----------+ +-----------+ Object
| stripe | | stripe | | stripe | | stripe | Set 2
| unit 20 | | unit 21 | | unit 22 | | unit 23 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 24 | | unit 25 | | unit 26 | | unit 27 |
+-----------+ +-----------+ +-----------+ +-----------+
| stripe | | stripe | | stripe | | stripe |
| unit 28 | | unit 29 | | unit 30 | | unit 31 |
+-----------+ +-----------+ +-----------+ +-----------+
| End cCCC | | End cCCC | | End cCCC | | End cCCC |
| Object 4 | | Object 5 | | Object 6 | | Object 7 |
\-----------/ \-----------/ \-----------/ \-----------/
```
#### 三个决定条带化的变量(官方定义)
| 变量 | 官方定义 | 官方建议 |
| --- | --- | --- |
| **Object Size** | Ceph Storage Cluster 中的对象有**可配置的最大尺寸**(例如 **2MB、4MB** 等) | 对象大小应**足够大以容纳许多 stripe unit**,并且**应为 stripe unit 的倍数** |
| **Stripe Width** | stripe 有**可配置的 unit 大小**(例如 **64kb**)。客户端把要写入对象的数据**划分成等大的 stripe unit,最后一个除外** | stripe width **应为 Object Size 的一个分数(fraction)**,这样一个对象可以包含许多 stripe unit |
| **Stripe Count** | 客户端**把一串 stripe unit 写入由 stripe count 决定的一系列对象**,这一系列对象称为 **object set**。写到最后一个对象后,**回到第一个对象** | — |
#### 注意事项(官方 important / note)
- **重要(important)**:**在把集群投入生产之前,先测试你的条带化配置的性能。一旦你把数据条带化并写入对象之后,就【不能】再更改这些条带化参数了。**
- **注意(note)**:**条带化独立于对象副本**。因为 **CRUSH 会跨 OSD 复制对象,所以 stripe 会被自动复制**。
- **注意(note)**:因为**客户端只写单个 pool**,所以**所有条带化出来的对象都被映射到同一个 pool 中的 PG**,因此它们**使用同一个 CRUSH map 与相同的访问控制**。
> 💡 补充:"条带化参数不可更改"这条限制的实际含义是:**条带化参数(object size / stripe unit / stripe count)是"数据布局格式"的一部分**,改了就等于改变了已有数据的解释方式。所以在 Ceph 里调整这些参数的正确做法是——**新建一个不同参数的 pool/image,把数据迁移过去**,而不是原地改。这与"PG 数量可以改但代价大"是不同量级的问题。
### 9.2 缓存分层(Cache Tiering)
#### 官方状态(重要)
> **注意:Cache tiering 在 Reef 版本中已被弃用(deprecated)。**
> 并且官方在末尾补充:**Cache Tiers 可能很棘手(can be tricky),现在已不鼓励使用它们**。
本册完整记录其设计,但**读者应以"已弃用、不鼓励使用"为前提看待**。
#### 是什么
- **缓存层(cache tier)为 Ceph 客户端提供对"存放在后备存储层中的一部分数据"的更好 I/O 性能**。
- 做法是:创建一个**由相对快速/昂贵存储设备(例如固态硬盘)组成的 pool 作为 cache tier**,以及一个**后备 pool**(可以是纠删码的,或由相对较慢/便宜的设备组成)作为**经济的存储层**。
- **Ceph objecter 决定把对象放在哪里**,**分层代理(tiering agent)决定何时把对象从缓存刷(flush)到后备存储层**。
- 因此,**缓存层与后备存储层对 Ceph 客户端是完全透明的**。
(原图为缓存分层示意图 `.. ditaa::`)
```text
+-------------+
| Ceph Client |
+------+------+
^
Tiering is |
Transparent | Faster I/O
to Ceph | +---------------+
Client Ops | | |
| +----->+ Cache Tier |
| | | |
| | +-----+---+-----+
| | | ^
v v | | Active Data in Cache Tier
+------+----+--+ | |
| Objecter | | |
+-----------+--+ | |
^ | | Inactive Data in Storage Tier
| v |
| +-----+---+-----+
| | |
+----->| Storage Tier |
| |
+---------------+
Slower I/O
```
### 9.3 快照与写时复制克隆(Snapshots and Copy-on-write Cloning)
`architecture.rst` 在列出 `librados` 原生协议能力时,明确包含:
- **Snapshots and Copy-on-write Cloning**(快照与写时复制克隆)
以及在客户端章节对 RBD 的描述:**Ceph Block Device 提供可调整大小(resizable)、精简置备(thin-provisioned)、可快照(snapshotted)与可克隆(cloned)的块设备**。
PG 状态中也有快照相关状态(`pg-states.rst`):
| 状态 | 含义 |
| --- | --- |
| `snaptrim` | 正在裁剪快照 |
| `snaptrim_wait` | 排队等待裁剪快照 |
| `snaptrim_error` | 裁剪快照时出错停止 |
> 💡 补充:写时复制(copy-on-write)是 Ceph 快照的底层机制——创建快照时**不复制数据**,只记录"这些对象当前版本被这个快照引用";此后对对象的写入**产生新版本**,旧版本由快照保留。这解释了两个运维现象:(1) 快照创建本身极快,但**删除快照会触发 `snaptrim`**(因为要清理不再被引用的旧版本);(2) **长期保留大量快照会持续占用空间并放大写放大**。
### 9.4 扩展 Ceph:Ceph Classes(对象类)
#### 是什么
- 你可以通过创建**共享对象类(shared object classes)**——称为 **'Ceph Classes'**——来扩展 Ceph。
- Ceph **动态加载**存放在 **`osd class dir`** 目录中的 **`.so` 类**(**默认为 `$libdir/rados-classes`**)。
- 实现一个类时,你可以**创建新的对象方法**,这些方法**能够调用 Ceph Object Store 的原生方法**,或**你通过库引入、或自己创建的其它类方法**。
#### 读写两侧
| 方向 | 能力(官方) |
| --- | --- |
| **写(On writes)** | Ceph Classes 可以**调用原生或类方法**,**对入站数据执行任意一系列操作**,并**生成一个结果性的写事务(write transaction),由 Ceph 原子地应用(applied atomically)** |
| **读(On reads)** | Ceph Classes 可以**调用原生或类方法**,**对出站数据执行任意一系列操作**,并**把数据返回给客户端** |
> 💡 补充:"原子地应用"是 Ceph Classes 最有价值的一点——它把"读-改-写"这一整段逻辑**下推到 OSD 侧并在单个事务内完成**,从而避免了客户端侧的多次往返与并发竞争。本质上这是**存储端计算(computation near storage)**的一种实现。
#### 官方示例(topic)
一个用于内容管理系统的 Ceph class,**呈现特定尺寸与宽高比的图片**:它可以**接收一张入站位图,裁剪到特定宽高比,调整尺寸,并嵌入不可见的版权或水印**以帮助保护知识产权;然后**把结果位图保存到对象存储中**。
#### 参考实现
官方指向:`src/objclass/objclass.h`、`src/fooclass.cc`、`src/barclass`。
### 9.5 Object Watch/Notify(对象作为同步通道)
#### 是什么
- 客户端可以**对一个对象注册一个持久的兴趣(persistent interest)**,并**保持与 primary OSD 的会话打开**。
- 客户端可以**向所有 watcher 发送一条通知消息(notification message)与载荷(payload)**,并**在 watchers 收到通知时收到通知**。
- 这使得客户端**可以把任何对象当作同步/通信通道(synchronization/communication channel)**。
#### 交互流程
(原图为 Object Watch/Notify 交互示意图 `.. ditaa::`)
```text
+----------+ +----------+ +----------+ +---------------+
| Client 1 | | Client 2 | | Client 3 | | OSD:Object ID |
+----------+ +----------+ +----------+ +---------------+
| | | |
| Watch Object | | |
|--------------------------------------------------->|
| Ack/Commit |
|<---------------------------------------------------|
| | Watch Object |
| |---------------------------------->|
| | Ack/Commit |
| |<----------------------------------|
| | | Watch Object |
| | |----------------->|
| | | Ack/Commit |
| |<-----------------------------------|
| Notify | | |
|--------------------------------------------------->|
| Notify | | |
|--------------------------------------------------->|
| | Notify | |
| |---------------------------------->|
| | | Notify |
| | |----------------->|
| Ack | | |
|--------------------------------------------------->|
| | Ack | |
| |---------------------------------->|
| | | Ack |
| | |----------------->|
|<---------------+----------------+------------------|
| Complete
```
> 💡 补充:Watch/Notify 是 Ceph 提供的**轻量级分布式协调原语**,常用于:多客户端之间的配置/锁同步(例如 RBD exclusive-lock 场景下的通知)、缓存失效广播、集群内事件通知。它的关键优势是**复用已有的对象存储与 OSD 会话**,不需要额外部署 ZooKeeper/etcd 之类的协调服务;局限是**通知的可靠性语义由客户端协议保证**(watcher 掉线后 watch 会随会话过期),不适合作为强一致的分布式锁的唯一依据。
### 9.6 扩展性特性的横向对比
| 特性 | 解决什么问题 | 作用位置 | 官方状态 |
| --- | --- | --- | --- |
| **条带化** | 单设备吞吐上限 | **客户端侧**(RBD/RGW/CephFS),或 `librados` 用户自行实现 | 推荐(但参数不可改) |
| **CRUSH 权重 / weight set** | 数据分布不均 | CRUSH map | 推荐 |
| **Balancer** | 自动优化 PG 跨设备分布 | 集群(`data-placement.rst` 列出的四大概念之一) | 推荐 |
| **缓存分层** | 热数据 I/O 性能 | pool 之间(cache tier + storage tier) | **Reef 起已弃用,不鼓励使用** |
| **快照 / COW 克隆** | 时间点副本与快速克隆 | `librados` 原生能力 | 支持 |
| **Ceph Classes** | 把计算下推到 OSD | OSD 侧 `.so` 插件 | 支持 |
| **Watch/Notify** | 客户端间同步/通信 | 对象 + primary OSD | 支持 |
| **Crimson** | 跨核通信延迟与锁竞争 | 下一代 OSD 架构 | 术语表登记 |
| **多 active MDS** | CephFS 元数据扩展性 | MDS 目录子树分片 | 支持 |
> 💡 补充:关于 **Balancer**——`data-placement.rst` 把它列为数据放置规划的四大概念之一,官方定义是:**自动优化 PG 在设备间的分布,以达成均衡的数据分布、最大化集群可存储的数据量、并在 OSD 之间均匀分摊工作负载**。它是对"CRUSH 概率性分布必然有偏差"这一事实的**运行时补偿**,与 weight set(静态优化)形成互补。
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Data Placement Overview](https://docs.ceph.com/en/tentacle/rados/operations/data-placement/)、[Placement Group States](https://docs.ceph.com/en/tentacle/rados/operations/pg-states/)
---
## 十、Ceph 协议与接口层
### 10.1 Ceph Protocol 与 librados
#### 定位
- **Ceph 客户端使用原生协议(native protocol)与 Ceph Storage Cluster 交互**。
- Ceph 把这一功能**打包进 `librados` 库**,因此**你可以创建自己的 Ceph Client**。
(原图为 Ceph 协议基本架构示意图 `.. ditaa::`)
```text
+---------------------------------+
| Ceph Storage Cluster Protocol |
| (librados) |
+---------------------------------+
+---------------+ +---------------+
| OSDs | | Monitors |
+---------------+ +---------------+
```
#### 设计动机
官方原话:**现代应用需要一个带有异步通信能力的简单对象存储接口。** Ceph Storage Cluster 提供的正是这样一个接口。该接口提供**对整个集群中对象的直接、并行访问(direct, parallel access)**。
#### librados 原生接口的能力清单(官方逐条)
| 能力 | 细分 |
| --- | --- |
| **Pool Operations** | 池操作 |
| **Snapshots and Copy-on-write Cloning** | 快照与写时复制克隆 |
| **Read/Write Objects** | **Create or Remove**;**Entire Object or Byte Range**(整对象或字节范围);**Append or Truncate** |
| **Create/Set/Get/Remove XATTRs** | 扩展属性 |
| **Create/Set/Get/Remove Key/Value Pairs** | 键值对(omap 相关) |
| **Compound operations and dual-ack semantics** | **复合操作**与 **dual-ack 语义** |
| **Object Classes** | 对象类(见 9.4) |
> 💡 补充:这一清单里最容易被人忽略的是 **Compound operations(复合操作)** 与 **dual-ack semantics(双确认语义)**。前者允许把多个操作打包成一个事务提交(原子性);后者区分"**已提交到内存/日志**"与"**已持久化到磁盘**"两种确认级别——这直接决定了性能与持久性的权衡,也是理解 Ceph 写延迟构成的关键。官方此处**未给出两者的具体语义与参数**,本册不展开(详见 librados / 运维分册)。
### 10.2 三类客户端的分层
Ceph 提供**三类客户端**:**Ceph Block Device、Ceph File System、Ceph Object Storage**。
(原图为 Ceph 客户端高层架构示意图 `.. ditaa::`)
```text
+--------------+ +----------------+ +-------------+
| Block Device | | Object Storage | | CephFS |
+--------------+ +----------------+ +-------------+
+--------------+ +----------------+ +-------------+
| librbd | | librgw | | libcephfs |
+--------------+ +----------------+ +-------------+
+---------------------------------------------------+
| Ceph Storage Cluster Protocol (librados) |
+---------------------------------------------------+
+---------------+ +---------------+ +---------------+
| OSDs | | MDSs | | Monitors |
+---------------+ +---------------+ +---------------+
```
官方还说明:Ceph **可以运行额外的 OSD、MDS 与 monitor 实例**,以实现**可扩展性与高可用性**。
### 10.3 各层定位速查
| 层 | 组件 | 定位(官方) | 是否需要 MDS |
| --- | --- | --- | --- |
| **原生层** | `librados` | 唯一原生协议;**可用于创建自定义接口**;**可与 Ceph Monitor 与 OSD 交互** | 否 |
| **块** | **Ceph Block Device / RBD**(`librbd`) | 提供**可调整大小、精简置备、可快照与克隆的块设备**;**Ceph 把块设备条带化到整个集群以获得高性能**;同时支持**内核对象(KO)**与**直接使用 `librbd` 的 QEMU hypervisor**(避免虚拟化场景下的内核对象开销) | **否** |
| **对象** | **Ceph Object Storage / RGW**(`librgw`,守护进程 `radosgw`) | 提供 **RESTful API**,接口**兼容 Amazon S3 与 OpenStack Swift** | **否** |
| **文件** | **Ceph File System / CephFS**(`libcephfs`) | 提供 **POSIX 兼容的文件系统**,可以以 `mount` 方式或**用户态文件系统(FUSE)**方式使用 | **是** |
### 10.4 Ceph Object Storage(RGW)
#### 是什么
- **Ceph Object Storage 守护进程 `radosgw` 是一个 FastCGI 服务,提供 RESTful HTTP API 来存储对象与元数据**。
- 它**层叠在 Ceph Storage Cluster 之上,带有自己的数据格式**,并**维护自己的用户数据库、认证与访问控制**。
- RADOS Gateway 使用**统一的命名空间(unified namespace)**,这意味着**你既可以使用 OpenStack Swift 兼容 API,也可以使用 Amazon S3 兼容 API**。例如:**你可以用 S3 兼容 API 写入数据,然后用 Swift 兼容 API 读出数据**。
#### S3/Swift 对象 ≠ Ceph 存储集群对象(官方 topic)
官方专门澄清这个易混点:
- Ceph 的 Object Storage 也用 *object* 这个词描述它存储的数据。
- **S3 与 Swift 的对象,与 Ceph 写入 Ceph Storage Cluster 的对象不是同一回事**。
- **Ceph Object Storage 的对象被映射到 Ceph Storage Cluster 的对象**。
- **S3 与 Swift 对象与存储集群中的对象不一定 1:1 对应**。
- **一个 S3 或 Swift 对象有可能映射到多个 Ceph 对象**。
#### 相关术语
- **Ceph Object Gateway / RGW / RADOS Gateway**:构建在 `librados` 之上的对象存储接口,**在应用与 Ceph 存储集群之间提供 RESTful 网关**。
- **Ceph Object Store**:由 **Ceph Storage Cluster + Ceph Object Gateway (RGW)** 组成。
- **Bucket(RGW 语境)**:一组对象。**用文件系统的类比:对象是文件,bucket 就是目录**。可以对 bucket 设置 **Multisite sync policies**,以精细控制数据从一个 zone 到另一个 zone 的流动。bucket 概念**取自 AWS S3**;**OpenStack Swift 把 RGW 与 AWS 称为 "buckets" 的东西称为 "containers"**。
- **Zone(RGW 语境)**:由**一个或多个 RGW 实例**组成的逻辑组。一个 zone 的配置状态存放在 **period** 中。
- **Period(RGW 语境)**:**Realm 的配置状态**。period 存储**多站点配置的配置状态**。**当 period 被更新时,就称 "epoch" 发生了变化**。
- **Realm(RGW 语境)**:全局唯一的命名空间,由**一个或多个 zonegroup** 组成。
- **S3 / OpenStack Swift**:Ceph Object Store 支持的**两套 API**。
### 10.5 Ceph Block Device(RBD)
#### 是什么
- **Ceph Block Device 把一个块设备镜像条带化到 Ceph Storage Cluster 中的多个对象上**;**每个对象被映射到一个 PG 并被分布出去**,而这些 **PG 被分散到整个集群中彼此独立的 `ceph-osd` 守护进程上**。
- 官方 important:**条带化使 RBD 块设备的性能优于单台服务器所能达到的性能!**
#### 部署形态
| 场景 | 官方说明 |
| --- | --- |
| **QEMU/KVM 虚拟机** | 人们通常用 **QEMU/KVM 中的 `rbd` 网络存储驱动**部署 Ceph Block Device,**宿主机使用 `librbd` 向 guest 提供块设备服务**。许多云计算栈使用 **`libvirt`** 与 hypervisor 集成 |
| **云栈集成** | 可以把精简置备的 Ceph Block Device 与 QEMU 和 `libvirt` 一起使用,以支撑 **OpenStack、OpenNebula、CloudStack** 等方案 |
| **其它 hypervisor** | 官方明确:**目前我们不为其它的 hypervisor 提供 `librbd` 支持**;但你也可以使用 **Ceph Block Device 内核对象**向客户端提供块设备。**Xen 等其它虚拟化技术可以访问 Ceph Block Device 内核对象**,这是通过命令行工具 **`rbd`** 完成的 |
#### 相关术语
- **Ceph Block Device**:也称 "RADOS Block Device" 与 **RBD**。它**把基于块的应用数据切分成 "chunks"**,**RADOS 把这些 chunk 存为对象**,Ceph Block Device **编排这些对象在整个存储集群中的存放**。
- **Ceph Block Storage**:Ceph 支持的三种存储之一(另两种是对象存储与文件存储)。它是块存储的"产品"形态,指与 **`librbd`**(提供类文件方式访问 RBD 镜像的 python 模块)+ **hypervisor(如 QEMU 或 Xen)** + **hypervisor 抽象层(如 `libvirt`)** 配合使用时的块存储相关服务与能力。
### 10.6 Ceph File System(CephFS)
#### 是什么
- **CephFS 提供 POSIX 兼容的文件系统,作为层叠在基于对象的 Ceph Storage Cluster 之上的服务**。
- **CephFS 文件被映射为 Ceph 存放在 Ceph Storage Cluster 中的对象**。
- **Ceph 客户端把 CephFS 文件系统挂载为内核对象,或作为用户态文件系统(FUSE)使用**。
(原图为 CephFS 架构示意图 `.. ditaa::`)
```text
+-----------------------+ +------------------------+
| CephFS Kernel Object | | CephFS FUSE |
+-----------------------+ +------------------------+
+---------------------------------------------------+
| CephFS Library (libcephfs) |
+---------------------------------------------------+
+---------------------------------------------------+
| Ceph Storage Cluster Protocol (librados) |
+---------------------------------------------------+
+---------------+ +---------------+ +---------------+
| OSDs | | MDSs | | Monitors |
+---------------+ +---------------+ +---------------+
```
#### 元数据与数据分离(架构动机)
见 2.4 节。核心两句原文:
- **MDS 的目的是把全部文件系统元数据(目录、文件属主、访问模式等)存放在高可用的 Ceph Metadata Server 中,元数据驻留在内存里**。
- **MDS 存在的理由是:像 `ls`、`cd` 这样简单的文件系统操作会不必要地消耗 Ceph OSD Daemon。所以把元数据与数据分离,意味着 Ceph File System 可以提供高性能服务而不拖累 Ceph Storage Cluster。**
#### 相关术语
- **CephFS**:**Ceph F**ile **S**ystem,构建在 Ceph 的分布式对象存储 RADOS 之上的 POSIX 兼容文件系统。
- **ceph-fuse**:CephFS 的 FUSE("**F**ilesystem in **USE**rspace")客户端,**把 Ceph FS 挂载到指定的挂载点**。
- **Ceph Kernel Modules**:可用于与 Ceph Cluster 交互的内核模块集合(例如 `ceph.ko`、`rbd.ko`)。
- **Ceph Client Libraries**:可用于与 Ceph Cluster 各组件交互的库的集合。
### 10.7 协议栈总结(原图为 Ceph 协议栈示意图)
```text
+------------------+ +------------------+ +------------------+
| RBD (块) | | RGW (对象) | | CephFS (文件) |
| 可调整大小/精简置备 | | S3 & Swift 兼容 | | POSIX 兼容 |
| 可快照/可克隆 | | 统一命名空间 | | mount 或 FUSE |
+------------------+ +------------------+ +------------------+
| librbd | | librgw | | libcephfs |
+------------------+ +------------------+ +------------------+
| | |
+----------------------+----------------------+
|
+---------------------------------------------------------------+
| librados(原生协议) |
| Pool Ops / Snapshots & COW Cloning / Read-Write Objects / |
| XATTRs / Key-Value Pairs / Compound Ops & dual-ack / |
| Object Classes / Watch-Notify |
+---------------------------------------------------------------+
|
+------------------+ +------------------+ +------------------+
| OSDs | | Monitors | | MDSs (CephFS) |
| 数据存储/复制/ | | cluster map 主副本 | | 文件元数据 |
| peering/scrub | | Paxos / CephX | | active/standby |
+------------------+ +------------------+ +------------------+
| RADOS |
+---------------------------------------------------------------+
```
> 引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/)、[Glossary](https://docs.ceph.com/en/tentacle/glossary/)
---
## 十一、术语表
> 来源:`doc/glossary.rst`(Ceph Glossary)。下表覆盖该页的**全部条目**(含 `Releases` 下的 5 个子条目),三列分别为:英文术语 | 中文解释 | 关联概念。少数条目在原文中是交叉引用(如 "See :term:`CephFS`"),本册在"中文解释"中给出指向,在"关联概念"中给出被指向的术语。
### 11.1 核心概念
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Ceph** | 一个分布式网络存储与文件系统,具有分布式元数据管理与 POSIX 语义 | RADOS、CephFS |
| **Ceph Platform** | Ceph 的全部软件,包括托管在 `https://github.com/ceph` 上的任何代码 | Ceph Project |
| **Ceph Project** | Ceph 的**人员、软件、使命与基础设施**的统称 | Ceph Platform、Ceph Stack |
| **Ceph Stack** | 两个或多个 Ceph 组件的集合 | Ceph Platform |
| **Ceph Node** | Ceph 集群中的一个单元,与其他节点通信以复制和再分布数据。所有节点合称 Ceph Storage Cluster。Ceph 节点包括 OSD、Ceph Monitor、Ceph Manager 与 MDS。文档中 "node" 通常等同于 "host"。可用 `ceph node ls all` 列出所有节点 | Host、Ceph Storage Cluster |
| **Host** | Ceph 集群中的任何单台机器或服务器 | Ceph Node |
| **Node** | 见 Ceph Node | Ceph Node |
| **Ceph Storage Cluster** | 由 Ceph Monitor、Ceph Manager、Ceph Metadata Server 与 OSD 组成的集合,它们协同工作,为应用、Ceph 用户与 Ceph Client 存储和复制数据。**Ceph Storage Cluster 从 Ceph Client 接收数据** | RADOS、Ceph Node |
| **RADOS** | **R**eliable **A**utonomic **D**istributed **O**bject **S**tore。为可变大小对象提供可扩展服务的对象存储,**是 Ceph 集群的核心组件** | Ceph Storage Cluster、RADOS Cluster、librados |
| **RADOS Cluster** | Ceph Cluster 的一个真子集,由 **OSD、Ceph Monitor 与 Ceph Manager** 组成 | RADOS、Ceph Storage Cluster |
| **Reliable Autonomic Distributed Object Store** | 存储用户数据的核心存储软件集合(**MON + OSD**) | RADOS、MON、OSD |
| **librados** | 一个 API,**可用于创建到 Ceph 存储集群的自定义接口**。它使得与 **Ceph Monitor 和 OSD** 交互成为可能 | RADOS、Ceph Protocol |
| **Ceph Client** | 任何能够访问 Ceph Storage Cluster 的 Ceph 组件。包括 **Ceph Object Gateway、Ceph Block Device、Ceph File System** 及其对应库,也包括**内核模块与 FUSE** | Ceph Client Libraries、Application |
| **Ceph Client Libraries** | 可用于与 Ceph Cluster 各组件交互的库的集合 | Ceph Client、librados |
| **Ceph Kernel Modules** | 可用于与 Ceph Cluster 交互的内核模块集合(例如 `ceph.ko`、`rbd.ko`) | Ceph Client |
| **Application** | 更确切地说是 **client**,指 Ceph 之外任何使用 Ceph Cluster 来存储和复制数据的程序 | Client、Ceph Client |
| **Client** | Ceph 之外任何使用 Ceph Cluster 来存储和复制数据的程序 | Application、Ceph Client |
| **User** | 使用 Ceph 客户端与 Ceph Storage Cluster 交互的个人或系统参与者(例如一个应用) | Ceph Client、CephX |
| **Cloud Platforms / Cloud Stacks** | 第三方云供应平台,例如 **OpenStack、CloudStack、OpenNebula、Proxmox VE** | Ceph Block Device、libvirt |
### 11.2 集群映射与守护进程
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Cluster Map** | 由 **monitor map、OSD map、PG map、MDS map 与 CRUSH map** 组成的集合,共同报告 Ceph 集群的状态 | Monitor Map、OSD Map、PG Map、MDS Map、CRUSH Map |
| **Ceph Cluster Map** | 见 Cluster Map | Cluster Map |
| **Ceph Monitor** | 维护集群状态映射的守护进程。"cluster state" 包括 **monitor map、manager map、OSD map 与 CRUSH map**。**一个 Ceph 集群必须至少包含 3 个运行中的 monitor,才能既冗余又高可用**。monitor 及其所在节点常被称作 "mon" | Quorum、Paxos、Monitor Store、MON |
| **MON** | Ceph monitor 软件 | Ceph Monitor、Quorum |
| **Monitor Store** | Monitor 使用的持久化存储,包括 Monitor 的 **RocksDB** 以及 `/var/lib/ceph` 下的全部相关文件 | Ceph Monitor |
| **Quorum** | 集群中**多数 Monitor 处于 `up`** 时存在的状态。**集群中必须至少存在 3 个 Monitor,Quorum 才可能成立** | Ceph Monitor、Paxos |
| **Ceph Manager** | `ceph-mgr` 守护进程,**与 monitor 守护进程并排运行**,提供额外的监控以及对接外部监控与管理系统的接口。**自 Luminous(12.x)起,集群必须有运行中的 ceph-mgr 才能正常工作** | MGR、Ceph Dashboard |
| **MGR** | Ceph manager 软件,**把整个集群的全部状态收集到一处** | Ceph Manager、Dashboard |
| **Ceph OSD** | **O**bject **S**torage **D**aemon。与逻辑磁盘(OSD)交互的 Ceph OSD 软件。约 2013 年有"研究界与工业界"试图坚持 OSD 只表示 "Object Storage Device",但 Ceph 社区始终用它表示 "Object Storage Daemon";Sage Weil 在 2022-11 确认 "**Daemon 是更准确的说法**" | OSD、Ceph OSD Daemon、Object Storage Device |
| **Ceph OSD Daemon** | 见 Ceph OSD | Ceph OSD、OSD |
| **Ceph OSD Daemons** | 见 Ceph OSD | Ceph OSD |
| **OSD** | **可能是 Ceph OSD,但不一定**。有时(尤其是较旧的通信中,以及非专为 Ceph 写的文档中)"OSD" 表示 "**O**bject **S**torage **D**evice",指**物理或逻辑存储单元(例如 LUN)**。Ceph 社区始终用 OSD 指 Ceph OSD Daemon,尽管 2010 年代中期业界曾推动把它限定为 "Object Storage Device",因此**必须分辨是哪一种含义** | Ceph OSD、Object Storage Device |
| **Object Storage Device** | 见 OSD | OSD、Ceph OSD |
| **OSD ID** | 每个 OSD 唯一的整数(每个 OSD 有唯一的 OSD ID)。**每个 OSD id 由 monitor 在创建其关联 OSD 时生成** | OSD FSID、OSD UUID |
| **OSD FSID** | 用于标识 OSD 的唯一标识符,位于 OSD 路径下名为 `osd_fsid` 的文件中。**FSID 与 UUID 两个术语可互换使用** | OSD UUID、OSD ID |
| **OSD UUID** | OSD 的唯一标识符。该术语与 **FSID** 互换使用 | OSD FSID |
| **Flapping OSD** | **被反复、快速地交替标记为 `up` 和 `down`** 的 OSD | OSD, flapping |
| **OSD, flapping** | 见 Flapping OSD | Flapping OSD |
| **Hybrid OSD** | 同时拥有 HDD 和 SSD 驱动器的 OSD | device class |
| **MDS** | Ceph **M**eta**D**ata **S**erver 守护进程,也称 `ceph-mds`。**任何运行 CephFS 文件系统的 Ceph 集群都必须运行 MDS**。**MDS 存储全部文件系统元数据**。客户端与单个 MDS 或一组 MDS 协同维护 CephFS 所需的**分布式元数据缓存** | Ceph Metadata Server、CephFS |
| **Ceph Metadata Server** | 见 MDS | MDS、CephFS |
| **CephX** | Ceph 的认证协议。**认证用户与守护进程**。运行方式**类似 Kerberos,但没有单点故障**,并允许**对一类服务**进行认证 | Client、User、secrets |
| **secrets** | 每当特权用户必须访问需要认证的系统时,用于执行数字认证的凭据。可以是**密码、API key、token、SSH key、私有证书或加密密钥** | CephX |
### 11.3 存储组织与放置
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Pool** | 用于存储对象的**逻辑分区** | Pools、Placement Groups、CRUSH rule、pg_num |
| **Pools** | 见 pool | Pool |
| **Placement Groups (PGs)** | 每个逻辑 Ceph pool 的**子集**。PG 的功能是**把对象(作为一个组)放置到 OSD 中**。Ceph **在内部以 PG 粒度管理数据**——这比管理一个个(数量多得多的)RADOS 对象扩展性更好。**PG 数量较多的集群(例如每 OSD 100 个)比 PG 较少的同配置集群均衡得更好**。Ceph 的内部 RADOS 对象**每个都被映射到一个特定的 PG**,而**每个 PG 恰好属于一个 Ceph pool** | Pool、OSD、CRUSH |
| **CRUSH** | **C**ontrolled **R**eplication **U**nder **S**calable **H**ashing。**Ceph 用来计算对象存储位置的算法** | CRUSH rule、Cluster Map、bucket |
| **CRUSH rule** | 应用于**某个或某些特定 pool** 的 CRUSH 数据放置规则 | CRUSH、Pool、failure domain |
| **Primary Affinity** | OSD 的一个属性,**决定某个 OSD 被选为 acting set 中 primary OSD(或 "lead OSD")的可能性**。自 **Firefly (v. 0.80)** 引入 | OSD、Acting Set、Primary |
| **Object Storage** | Ceph 相关的三种存储之一(另两种是文件存储与块存储)。**对象存储是 Ceph 最基础的存储类别** | RADOS、Ceph Object Storage |
| **omap** | "object map"。一个 **key-value 存储(数据库)**,用于**减少从 Ceph 集群读写数据所需的时间**。**RGW 的 bucket index 以 omap 形式存储**。**纠删码池不能存储 RADOS omap 数据结构**。可用 `ceph osd df` 查看 omap | RGW、Bucket、Erasure Coding |
| **BlueStore** | OSD 使用的存储后端,**专为 Ceph 设计**。**Kraken 引入**;**Luminous 提升为默认 OSD 后端**(取代 FileStore);**自 Reef 起,FileStore 不再可用**。**BlueStore 把对象直接存放在裸块设备或分区上,不与已挂载的文件系统交互**;使用 **RocksDB 的 key/value 数据库把对象名映射到磁盘上的块位置** | OSD、RocksDB |
| **Crimson** | **下一代 OSD 架构**,目标是**降低跨核通信带来的延迟开销**。重新设计 OSD 以**减少数据路径上 shard 之间的通信**从而降低锁竞争;通过**消除对线程池的依赖**提升 classic Ceph OSD 的性能 | OSD |
| **DAS** | **D**irect-**A**ttached **S**torage。直接连接到访问它的计算机、不经过网络的存储。与 NAS 和 SAN 相对 | SDS |
| **SDS** | **S**oftware-**d**efined **S**torage(软件定义存储) | Ceph、DAS |
| **scrubs** | Ceph 用来**确保数据完整性**的过程。scrubbing 时,Ceph **生成一个 PG 中所有对象的目录**,然后**把每个主对象与存放在其他 OSD 上的副本做比较**,确保没有对象缺失或不匹配。任何 PG 若被发现副本内容不同或对象整个缺失,就被标记为 **`inconsistent`**。有两种:**light scrubbing**(也称 **shallow scrubbing**,**每天**执行,仅确认对象存在且元数据正确)与 **deep scrubbing**(**每周**执行,读取数据并用 checksums 确保完整性) | PG、deep scrubbing、inconsistent |
| **LVM tags** | **L**ogical **V**olume **M**anager 标签。LVM 卷与卷组的**可扩展元数据**,用于**存储 Ceph 关于设备及其与 OSD 关系的特定信息** | OSD、ceph-volume |
| **PLP** | **P**ower **L**oss **P**rotection。一种**用电容延长把数据从 DRAM 缓存转移到 SSD 永久存储器的可用时间**,从而保护固态硬盘数据的技术。**消费级 SSD 很少配备 PLP** | SSD、BlueStore |
### 11.4 对象/块/文件服务
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Ceph Block Device** | 也称 "RADOS Block Device" 与 **RBD**。一个软件构件,用于**编排基于块的数据在 Ceph 中的存储**。**Ceph Block Device 把基于块的应用数据切分成 "chunks"**,**RADOS 把这些 chunk 存为对象**,Ceph Block Device **编排这些对象在整个存储集群中的存放** | RBD、Ceph Block Storage、librbd |
| **RBD** | **R**ADOS **B**lock **D**evice。见 Ceph Block Device | Ceph Block Device、librbd |
| **Ceph Block Storage** | Ceph 支持的三种存储之一(另两种是对象存储与文件存储)。它是块存储"产品",指与 (1) **`librbd`**(提供类文件方式访问 RBD 镜像的 python 模块)、(2) **hypervisor(如 QEMU 或 Xen)**、(3) **hypervisor 抽象层(如 `libvirt`)** 配合使用时的块存储相关服务与能力 | Ceph Block Device、libvirt |
| **Ceph Object Gateway** | 构建在 `librados` 之上的对象存储接口。**在应用与 Ceph 存储集群之间提供 RESTful 网关** | RGW、Ceph Object Store、librados |
| **RGW** | **R**ADOS **G**ate**w**ay。也称 "Ceph Object Gateway"。**提供到 Amazon S3 RESTful API 与 OpenStack Swift API 的网关**的 Ceph 组件 | Ceph Object Gateway、S3、Swift |
| **RADOS Gateway** | 见 RGW | RGW |
| **Ceph Object Storage** | 见 Ceph Object Store | Ceph Object Store、RGW |
| **Ceph Object Store** | 由 **Ceph Storage Cluster + Ceph Object Gateway (RGW)** 组成 | RGW、Ceph Storage Cluster |
| **Bucket** | 在 **RGW** 语境中,bucket 是一组对象。用文件系统的类比:**对象对应文件,bucket 对应目录**。可以对 bucket 设置 **Multisite sync policies**,以精细控制数据从一个 zone 流向另一个 zone。该概念**取自 AWS S3**;**OpenStack Swift 把 RGW/AWS 称为 "buckets" 的东西称为 "containers"** | RGW、Zone、Realm |
| **CephFS** | **Ceph F**ile **S**ystem,构建在 Ceph 的分布式对象存储 RADOS 之上的 **POSIX 兼容文件系统** | Ceph File System、MDS、libcephfs |
| **Ceph File System** | 见 CephFS | CephFS |
| **ceph-fuse** | CephFS 的 FUSE("**F**ilesystem in **USE**rspace")客户端。**把 Ceph FS 挂载到指定的挂载点** | CephFS、Ceph Client |
| **S3** | 在 Ceph 语境中,**Ceph Object Store 支持的两套 API 之一**(另一套是 OpenStack Swift) | RGW、Bucket |
| **Swift / OpenStack Swift** | 在 Ceph 语境中,OpenStack Swift 是 **Ceph Object Store 支持的两套 API 之一**(另一套是 S3)。它把 RGW/AWS 称为 "buckets" 的东西称为 "**containers**" | RGW、Bucket |
| **Zone** | 在 **RGW** 语境中,zone 是由**一个或多个 RGW 实例**组成的逻辑组。一个 zone 的配置状态存放在 **period** 中 | RGW、Period、Realm |
| **Period** | 在 **RGW** 语境中,period 是 **Realm 的配置状态**。period 存储**多站点(multi-site)配置的配置状态**。**当 period 被更新时,就称 "epoch" 发生了变化** | RGW、Realm、Zone |
| **Realm** | 在 **RADOS Gateway (RGW)** 语境中,realm 是**全局唯一的命名空间**,由**一个或多个 zonegroup** 组成 | RGW、Zone、Period |
### 11.5 管理与运维相关
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Ceph Dashboard** | **内建的、基于 Web 的 Ceph 管理与监控应用**,通过它可以查看和管理集群内的各种资源。**它实现为一个 ceph-mgr 模块** | Dashboard、Ceph Manager |
| **Dashboard** | 内建的、基于 Web 的 Ceph 管理与监控应用,用于管理集群的各个方面与对象。**dashboard 实现为一个 Ceph Manager 模块** | Ceph Dashboard、Dashboard Module |
| **Dashboard Module** | Dashboard 的另一种叫法 | Dashboard |
| **Dashboard Plugin** | **Mimic 时代**的一个 Web 应用,**用一个由 Ceph Manager 托管的 web server 可视化 Ceph 集群的信息与统计** | Dashboard、Ceph Manager |
| **Ceph Manager Dashboard** | 见 Ceph Dashboard | Ceph Dashboard |
| **Prometheus** | 一个开源的监控与告警工具包。Ceph 提供 **"Prometheus module"**,它提供一个 **Prometheus exporter**,**把 `ceph-mgr` 中一个收集点的性能计数器传递给 Prometheus** | Ceph Manager、Dashboard |
| **ceph-ansible** | 一个 GitHub 仓库,**从 Jewel 到 Quincy 版本受支持**,用于**简化 Ceph 集群的安装** | Cloud Platforms |
| **Teuthology** | 对 Ceph 执行**脚本化测试**的软件集合 | Ceph Platform |
| **systemd oneshot** | 一种 systemd `type`,其命令定义在 `ExecStart` 中,**完成后即退出**(它不打算 daemonize) | — |
| **FQDN** | **F**ully **Q**ualified **D**omain **N**ame。应用于网络中某个节点、并**指定该节点在 DNS 树状层级中确切位置**的域名。在 Ceph 集群管理中,FQDN 常用于主机。文档中主要用 FQDN 来**区分它与相对简单的 hostname**(后者不指定主机在 DNS 树中的确切位置,只是给主机起个名字) | Host、Ceph Node |
| **DC** | **D**ata **C**enter(数据中心) | CRUSH bucket type `datacenter` |
### 11.6 Releases(发布类型,含 5 个子条目)
| 英文术语 | 中文解释 | 关联概念 |
| --- | --- | --- |
| **Releases** | 发布类型的总条目,包含下列 5 个子条目 | Ceph Release |
| **Ceph Release** | Ceph 的**任何不同的编号版本** | Ceph Point Release、Ceph Stable Release |
| **Ceph Interim Release** | **尚未经过质量保证(QA)测试**的 Ceph 版本。**可能包含新特性** | Ceph Stable Release |
| **Ceph Release Candidate** | 已**经过初步质量保证测试、可供 beta 测试者使用**的 Ceph 主版本 | Ceph Interim Release |
| **Ceph Stable Release** | 一个 Ceph 主版本,其中**来自前面各 interim release 的全部特性都已成功通过质量保证测试** | Ceph Interim Release |
| **Ceph Point Release** | **任何只包含 bug 修复与安全修复**的临时(ad hoc)发布 | Ceph Stable Release |
| **Ceph Stable Release** | 见上 | — |
> 引自:[Glossary](https://docs.ceph.com/en/tentacle/glossary/)
---
## 文档未明确项
本册在撰写过程中,遇到以下**官方文档未给出**或**文档内部表述不一致**之处。按写作规范登记,**不做推测性填充**:
1. **CRUSH 内部选择算法**:`architecture.rst` 与 `crush-map.rst` **均未给出** CRUSH 在每一层如何从兄弟节点中选出一个(bucket 选择算法、随机种子的取值与递增规则、冲突/失败时的重试次数上限)。本册已在 4.10 节标注。
2. **`firstn` 与 `indep` 的算法层面差异**:官方仅给出用途与行为描述(见 4.7),**未给出实现细节**。
3. **`ruleset` 术语**:本册抓取的 Tentacle v20.2.4 全部源文中**未出现 `ruleset` 字样**;当前文档统一使用 **CRUSH rule / rules**。因此本册不定义 `ruleset`,也不把它当作现行概念使用。
4. **CRUSH 查找的完整输入参数**:官方未列出 CRUSH 计算所需的**全部**输入(例如副本序号等参数的具体取值规则)。
5. **PG Map 的查看命令**:`architecture.rst` 为 monitor / OSD / CRUSH / MDS 四张 map 都给出了查看命令,唯独 **PG Map 没有给出专门命令**。
6. **默认 `size` 的出处差异**:`architecture.rst` 只给出设计基线(`size = 2` 为最低要求,推荐 `size = 3` 且 `min size = 2`),**未使用"默认值"这一措辞**。本册因此不声称"默认 size = 3"来自架构页。
7. **Cluster Map 的组成存在文档内部差异**:`architecture.rst` 与术语表 **Cluster Map** 条目均为**五张 map**(monitor / OSD / PG / MDS / CRUSH);但术语表 **Ceph Monitor** 条目把 cluster state 列为 **monitor map、manager map、OSD map、CRUSH map**(未列 PG map 与 MDS map,却列入了 manager map)。本册以 `architecture.rst` 的五张 map 为准(见 6.3)。
8. **`mon_osd_down_out_interval` 等参数的默认数值**:本册引用的 `mon-osd-interaction.rst` 只列出参数名,**未在本册阅读范围内给出具体默认秒数**,故本册只登记参数名。
9. **`compound operations` 与 `dual-ack semantics` 的具体语义**:`architecture.rst` 在 librados 能力清单中列出二者,但**未给出定义、参数或行为说明**(见 10.1)。
10. **条带化参数(object size / stripe width / stripe count)的默认值**:官方只给了示例值(object size 例如 2MB、4MB;stripe width 例如 64kb),**未给出默认值**。
11. **CephX rotating service keys 的具体数量规则**:官方表述为"**通常为 3 个**"(usually 3),未说明其配置方式与取值规则。
12. **PG ID 中后半部分的进制**:官方示例给出十进制 `58`,而实际 `ceph` 输出中 PG 编号后半部分通常显示为十六进制。**官方未说明这一表示差异**,第 5.4 节的说明为笔者的 💡 补充。
---
> 本册完 · 第 01 册《Ceph 架构篇》
> 主要引自:[Architecture](https://docs.ceph.com/en/tentacle/architecture/) | [Glossary](https://docs.ceph.com/en/tentacle/glossary/)