2554 lines
174 KiB
Markdown
2554 lines
174 KiB
Markdown
> 基于 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/)
|
||
|
||
|
||
|