> 基于 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 ` 手动把某个 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 模块共享一个缓存,可用 `ceph config set mgr mgr_ttl_cache_expire_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 [class ]` | 从层级中取出一个子树作为选择起点(如 `default`,可加 `class hdd` 限定设备类) | | `step chooseleaf firstn N type ` | 选出 N 个 `` 类型的 bucket,并**在各自内部再下探到叶子(OSD)** | | `step choose firstn N type ` | 只选出 N 个 `` 类型的 bucket,不自动下探 | | `step choose indep N type ` | `indep` 变体,用于 EC 等需要"位置稳定"的场景 | | `step emit` | 输出结果 | | `step choosemsr N 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 [...]` - **一旦设置了 device class,必须先 unset 旧 class 才能改成另一个 class**;移除:`ceph osd crush rm-device-class [...]` - 这条限制让管理员可以设置**不会被 OSD 重启或脚本改掉**的 device class。 - 创建面向特定 device class 的放置规则:`ceph osd crush rule create-replicated ` - 应用到 pool:`ceph osd pool set crush_rule ` - **实现机制**: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 `(可带 `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 ` | | 给 pool 应用规则 | `ceph osd pool set crush_rule ` | | 设置 device class | `ceph osd crush set-device-class [...]` | | 移除 device class | `ceph osd crush rm-device-class [...]` | > 引自:[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 记录更新日志条目,**若这些条目意味着对象内容有更新,就把该对象加入一个"待更新"列表**,该列表称为该 `` 的 **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/)