4104 lines
238 KiB
Markdown
4104 lines
238 KiB
Markdown
> 基于 Ceph 官方文档整理 | 文档版本:**Tentacle v20.2.4** | 抓取日期:2026-09-17 | 源文:ceph/ceph@v20.2.4 `doc/rados/`
|
||
> 官网入口:https://docs.ceph.com/en/tentacle/rados/
|
||
|
||
---
|
||
|
||
# 第 02 册 · RADOS 核心篇(上):数据寻址、CRUSH、PG 与存储引擎
|
||
|
||
## 本册导读
|
||
|
||
本册覆盖 Ceph 官方文档 `doc/rados/` 目录下与「数据是怎么落盘的」直接相关的 **25 个官方页面**(全部为 Tentacle v20.2.4 的 rst 源文):
|
||
|
||
| # | 官方页面(逻辑页名) | 本册章节 |
|
||
|---|---|---|
|
||
| 1 | [rados/index](https://docs.ceph.com/en/tentacle/rados/) | 导言 |
|
||
| 2 | [rados/operations/index](https://docs.ceph.com/en/tentacle/rados/operations/) | 导言 |
|
||
| 3 | [rados/operations/data-placement](https://docs.ceph.com/en/tentacle/rados/operations/data-placement/) | 一 |
|
||
| 4 | [rados/operations/crush-map](https://docs.ceph.com/en/tentacle/rados/operations/crush-map/) | 二 |
|
||
| 5 | [rados/operations/crush-map-edits](https://docs.ceph.com/en/tentacle/rados/operations/crush-map-edits/) | 二、三 |
|
||
| 6 | [rados/operations/placement-groups](https://docs.ceph.com/en/tentacle/rados/operations/placement-groups/) | 四 |
|
||
| 7 | [rados/operations/pg-concepts](https://docs.ceph.com/en/tentacle/rados/operations/pg-concepts/) | 五 |
|
||
| 8 | [rados/operations/pg-states](https://docs.ceph.com/en/tentacle/rados/operations/pg-states/) | 五 |
|
||
| 9 | [rados/operations/pools](https://docs.ceph.com/en/tentacle/rados/operations/pools/) | 六 |
|
||
| 10 | [rados/operations/upmap](https://docs.ceph.com/en/tentacle/rados/operations/upmap/) | 十 |
|
||
| 11 | [rados/operations/erasure-code](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code/) | 七 |
|
||
| 12 | [rados/operations/erasure-code-profile](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-profile/) | 七 |
|
||
| 13 | [rados/operations/erasure-code-jerasure](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-jerasure/) | 七 |
|
||
| 14 | [rados/operations/erasure-code-isa](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-isa/) | 七 |
|
||
| 15 | [rados/operations/erasure-code-lrc](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-lrc/) | 七 |
|
||
| 16 | [rados/operations/erasure-code-shec](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-shec/) | 七 |
|
||
| 17 | [rados/operations/erasure-code-clay](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-clay/) | 七 |
|
||
| 18 | [rados/operations/pgcalc/index](https://docs.ceph.com/en/tentacle/rados/operations/pgcalc/) | 四 |
|
||
| 19 | [rados/configuration/index](https://docs.ceph.com/en/tentacle/rados/configuration/) | 导言 |
|
||
| 20 | [rados/configuration/bluestore-config-ref](https://docs.ceph.com/en/tentacle/rados/configuration/bluestore-config-ref/) | 八 |
|
||
| 21 | [rados/configuration/filestore-config-ref](https://docs.ceph.com/en/tentacle/rados/configuration/filestore-config-ref/) | 八 |
|
||
| 22 | [rados/configuration/journal-ref](https://docs.ceph.com/en/tentacle/rados/configuration/journal-ref/) | 八 |
|
||
| 23 | [rados/configuration/storage-devices](https://docs.ceph.com/en/tentacle/rados/configuration/storage-devices/) | 九 |
|
||
| 24 | [rados/configuration/pool-pg-config-ref](https://docs.ceph.com/en/tentacle/rados/configuration/pool-pg-config-ref/) | 四、六 |
|
||
| 25 | [rados/configuration/mon-osd-interaction](https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/) | 十一 |
|
||
|
||
### 建议阅读顺序
|
||
|
||
1. **第一遍(建立心智模型)**:一(数据放置)→ 二(CRUSH map)→ 四(PG)→ 六(Pool)。这四章构成「一个字节怎么找到它的家」的完整故事。
|
||
2. **第二遍(稳态运维)**:五(PG 状态机,日常 `ceph -s` 看到的就是它)→ 十(upmap 均衡)→ 十一(MON/OSD 心跳与故障判定)。
|
||
3. **第三遍(容量与介质选型)**:七(纠删码)→ 八(BlueStore)→ 九(设备选型)。
|
||
4. **第四遍(动手改)**:三(CRUSH map 编辑实战)—— 这一章是唯一会「改坏集群」的章节,建议先在与生产同构的测试集群上跑一遍 `crushtool --compare`。
|
||
|
||
### 阅读前须知:本文档中「文档未明确」的含义
|
||
|
||
> ⚠️ **重要说明(关于配置项表格)**:Ceph 官方文档的配置参考页使用 Sphinx 的 `.. confval:: <option_name>` 指令,其 **type / default / description 字段是在文档构建期从 Ceph 选项数据库(源码 `src/common/options/*.yaml.in`)拉取渲染的**,rst 源文本身**不包含这些字段的值**。本手册严格基于 rst 源文撰写,因此:
|
||
>
|
||
> - 凡是源文**散文中明确写出**的默认值/数值(例如 `mon_target_pg_per_osd` default 100、`pg_num` default 32、心跳 6 秒 / 20 秒 grace),本手册照录并给出出处;
|
||
> - 凡是源文只有 `.. confval::` 指令、散文未给出数值的,本手册在「类型」「默认值」列一律写 **「文档未明确」**,**不做任何推测或填充**;
|
||
> - 「含义」列取自该配置项所在章节的**散文描述**(这是官方文档对该选项的权威解释);
|
||
> - 「调参建议」列属于本手册的补充,均带 **💡 补充** 标记(若该行无标记,说明建议本身出自原文)。
|
||
>
|
||
> 所有确实无法从本册 25 个源文中得到答案的问题,统一汇总在文末的 **## 文档未明确项** 一节。
|
||
|
||
---
|
||
|
||
## 一、数据放置(Data Placement Overview)
|
||
|
||
### 1.1 Ceph 存储集群的构成
|
||
|
||
Ceph Storage Cluster 是基于 **RADOS(Reliable Autonomic Distributed Object Store)** 的基础设施,由三类守护进程构成:
|
||
|
||
| 守护进程 | 职责(原文表述) |
|
||
|---|---|
|
||
| **Ceph OSD Daemon(OSD)** | stores data as objects on a storage node — 在存储节点上以对象形式存储数据 |
|
||
| **Ceph Monitor(MON)** | maintains a master copy of the cluster map — 维护集群 map 的主副本 |
|
||
| **Ceph Manager(MGR)** | manager daemon |
|
||
|
||
规模上:一个 Ceph Storage Cluster 可以包含数千个存储节点;**最小系统至少需要一个 Ceph Monitor 和两个 Ceph OSD Daemon`(用于数据复制)**。Ceph File System、Ceph Object Storage、Ceph Block Devices 都是从这个集群读写数据。
|
||
|
||
### 1.2 四个核心的数据放置规划概念
|
||
|
||
官方把 data-placement planning 归纳为四个概念(Pools / Placement Groups / CRUSH Maps / Balancer),逐条对照原文:
|
||
|
||
#### (1)Pools(存储池)
|
||
|
||
> Ceph stores data within pools, which are logical groups used for storing objects.
|
||
|
||
Pools 是**存储对象的逻辑分组**。一个 Pool 负责管理三件事:
|
||
|
||
- **placement groups 的数量**
|
||
- **replicas 的数量**
|
||
- **该 pool 使用的 CRUSH rule**
|
||
|
||
此外:向 pool 写数据必须是**对该 pool 有权限的已认证用户**;Ceph 可以对 pool 做快照(snapshot)。
|
||
|
||
#### (2)Placement Groups(PG,归置组)
|
||
|
||
> Ceph maps objects to placement groups. Placement groups (PGs) are shards or fragments of a logical object pool that place objects as a group into OSDs.
|
||
|
||
关键点:
|
||
|
||
- PG 是**逻辑 pool 的分片/片段**,把对象**成组地**放进 OSD。
|
||
- PG 存在的首要意义:**减少 per-object metadata**。Ceph 以 PG 粒度而非对象粒度管理数据("this scales better than would managing individual RADOS objects")。
|
||
- **PG 越多越均衡**:原文明确举例 —— 每 OSD 100 个 PG 比每 OSD 50 个 PG 均衡性更好;另一处(pools.rst)给出典型配置目标是**每 OSD 约 150 个 PG**。
|
||
- 每个 RADOS object 映射到**唯一一个 PG**,每个 PG **恰好属于一个 pool**。
|
||
|
||
#### (3)CRUSH Maps
|
||
|
||
CRUSH(**Controlled Replication Under Scalable Hashing**)让 Ceph 在扩展时避免三类陷阱:**performance bottlenecks(性能瓶颈)、limitations to scalability(可扩展性限制)、single points of failure(单点故障)**。
|
||
|
||
CRUSH map 向 CRUSH 算法提供**集群的物理拓扑**,使算法能够决定:
|
||
|
||
1. 一个对象及其副本的数据应该存在哪里;
|
||
2. 如何跨 **failure domains(故障域)** 分布数据以提升数据安全性。
|
||
|
||
CRUSH map 可以通过编码 chassis / rack / 物理邻近性 / 共享电源 / 共享网络 / 故障域 等信息,来建模并应对**相关性设备故障(correlated device failures)**。举例:为了保证不出现并发故障,可能希望副本落在不同的 shelf、rack、power supply、controller 或物理位置。
|
||
|
||
#### (4)Balancer
|
||
|
||
> The balancer is a feature that automatically optimizes the distribution of placement groups across devices...
|
||
|
||
Balancer 自动优化 PG 在设备间的分布,目的是:(a) 均衡数据分布;(b) 最大化可存储的数据量;(c) 在 OSD 之间均匀分摊负载。
|
||
|
||
### 1.3 使用默认值 vs 定制
|
||
|
||
原文态度很明确:
|
||
|
||
- **测试集群初始搭建**:可以用默认值("Default values are recommended for a test cluster's initial setup")。
|
||
- **规划大型集群**:**必须定制**("when planning a large Ceph cluster, values should be customized"),定制时要参考 pool、PG、CRUSH 各自扮演的不同角色。
|
||
|
||
### 1.4 完整寻址链路:file → object → pool → PG → OSD
|
||
|
||
下面把整条链路拆成 5 跳。**第 0 跳(file → object)发生在 RADOS 之上的接口层(RBD / CephFS / RGW),不在本册 25 个源文覆盖范围内**,因此只在下面做一句说明,不做展开。
|
||
|
||
| 跳 | 环节 | 由谁完成 | 依据(源文表述) |
|
||
|---|---|---|---|
|
||
| 0 | **file / image / S3 object → RADOS object** | 上层接口(RBD / CephFS / RGW) | ❗本册源文未涉及(见「文档未明确项」第 1 条) |
|
||
| 1 | **RADOS object → pool** | 客户端显式指定(pool name / pool id) | "objects are stored within pools";"each placement group belongs to exactly one Ceph pool" |
|
||
| 2 | **object → PG** | **客户端计算**(Ceph client calculates which PG a RADOS object should be in) | "the client hashes the object ID and performs an operation involving both the number of PGs in the specified pool and the pool ID" |
|
||
| 3 | **PG → OSD set( Acting Set / Up Set)** | **CRUSH 算法**(CRUSH computes storage locations) | "CRUSH uses a map of the cluster ... to map data to OSDs";PG 的 OSD 集合由 CRUSH 按 rule + failure domain 算出 |
|
||
| 4 | **客户端 → Primary OSD 直连 I/O** | 客户端 | "CRUSH allows Ceph clients to communicate with OSDs directly rather than through a centralized server or broker" |
|
||
|
||
第 4 跳是 CRUSH 设计的核心价值:**没有中心化的元数据服务器/代理**。客户端拿到了 OSD map + CRUSH map 之后,自己就能算出数据在哪台 OSD 上,直接发起 I/O。这直接消除了单点故障、性能瓶颈和物理扩展上限。
|
||
|
||
#### 第 2 跳(object → PG)的细节
|
||
|
||
源文给出的信息是:
|
||
|
||
> The Ceph client calculates which PG a RADOS object should be in. As part of this calculation, the client hashes the object ID and performs an operation involving both the number of PGs in the specified pool and the pool ID.
|
||
|
||
即:参与运算的三个输入是 **① 对象 ID 的 hash 值 ② 该 pool 的 PG 数量(pg_num) ③ pool ID**。
|
||
|
||
PG 的标识形式可以从官方文档的示例命令中看到(`ceph pg map 1.6c` 返回 `osdmap e13 pg 1.6c (1.6c) -> up [1,0] acting [1,0]`),即 **`<pool_id>.<pg_id_hex>`**。
|
||
|
||
> 💡 补充:下面给出一个**用于建立直观理解的教学演算**,其中的 hash 值是虚构的(真实实现使用 `rjenkins1`,参见 `osd pool ls detail` 输出中的 `object_hash rjenkins`),**不要把它当作可复现的计算结果**:
|
||
>
|
||
> 假定 pool `rbd` 的 `pool_id = 2`,`pg_num = 256`,对象名为 `rb.0.1234.6b8b4567.000000000001`。
|
||
> 1. `h = hash("rb.0.1234.6b8b4567.000000000001")` → 假设得到 `h = 0x9A3F1C07`
|
||
> 2. `h mod pg_num = 0x9A3F1C07 mod 256 = 0x07 = 7`
|
||
> 3. 该对象所属 PG 标识为 `2.7`
|
||
> 4. 客户端再用 CRUSH(以 `2.7` 作为输入,配合 pool 的 CRUSH rule)算出 OSD 集合,例如 `up [1,0] acting [1,0]`
|
||
> 5. 客户端直连 **Primary = OSD 1**(Acting Set 的第一个成员)发起读写
|
||
>
|
||
> 由于 `pg_num` 只取 2 的幂(见第四章),`mod pg_num` 在二进制上等价于**取 hash 的低 log2(pg_num) 位**,这也是为什么把 `pg_num` 调成 2 的幂对 CRUSH 效率有"边际改善"(pgcalc 页面原文:"The nearest power of 2 provides a marginal improvement in efficiency of the CRUSH algorithm")。
|
||
|
||
#### 第 3 跳(PG → OSD)的细节
|
||
|
||
PG 与 OSD 是多对多的关系:
|
||
|
||
- 一个 PG 的副本/分片存放在**一组 OSD** 上。例:size=2 的副本池,每个 PG 把对象存在两个 OSD 上。
|
||
- **一个 OSD 不被某个 PG 独占**,它被同 pool 或其他 pool 的多个 PG 共享。
|
||
- 若某个 OSD 故障,会给它承载的每个 PG 重新分配一个 OSD,并从存活副本回填全部对象。
|
||
- **改变 pg_num 会改变 CRUSH 函数的输出**:新增 PG 后,部分对象会从旧 PG 迁移到新 PG(这就是 split 引发数据迁移的原因)。
|
||
|
||
### 1.5 概念关系图(原文 ditaa 转述)
|
||
|
||
```
|
||
obj obj obj obj obj
|
||
\ / \ /
|
||
v v v v
|
||
+--------------+ +--------------+
|
||
| Placement | | Placement |
|
||
| Group #1 | | Group #2 |
|
||
+--------------+ +--------------+
|
||
\ /
|
||
-------- ---------
|
||
v
|
||
+--------------+
|
||
| Pool |
|
||
+--------------+
|
||
```
|
||
|
||
> 引自:[Data Placement Overview](https://docs.ceph.com/en/tentacle/rados/operations/data-placement/)、[Ceph Storage Cluster](https://docs.ceph.com/en/tentacle/rados/)、[Cluster Operations](https://docs.ceph.com/en/tentacle/rados/operations/)
|
||
|
||
---
|
||
|
||
## 二、CRUSH map 详解
|
||
|
||
### 2.1 CRUSH 是什么
|
||
|
||
**CRUSH = Controlled Replication Under Scalable Hashing**。它**计算存储位置**,从而决定如何存、取数据。CRUSH map 包含:
|
||
|
||
1. 一份 **OSD 列表**;
|
||
2. 一层 **bucket 层级**(`host` / `rack` 等);
|
||
3. 一组 **rules**,规定数据在集群 pool 内如何复制。
|
||
|
||
深入论文:[CRUSH - Controlled, Scalable, Decentralized Placement of Replicated Data](https://ceph.io/assets/pdfs/weil-crush-sc06.pdf)(官方文档在多处引用其 Section 3.2 讲 rule、Section 3.4 讲 bucket 类型)。
|
||
|
||
一条重要的运维事实:
|
||
|
||
> When OSDs are deployed, they are automatically added to the CRUSH map under a `host` bucket that is named for the node on which the OSDs run.
|
||
|
||
即:**OSD 部署时会自动挂到以其所在节点命名的 `host` bucket 下**。这个行为 + 配置的 CRUSH failure domain,共同保证了副本/EC 分片跨主机分布。**更大的集群必须由管理员认真选择 failure domain**(例如中型到大型集群典型地跨 rack 分布副本)。
|
||
|
||
### 2.2 CRUSH Location
|
||
|
||
OSD 在 CRUSH map 层级中的位置称为它的 **CRUSH location**,形式是若干 key-value 对:
|
||
|
||
```text
|
||
root=default row=a rack=a2 chassis=a2a host=a2a1
|
||
```
|
||
|
||
规则:
|
||
|
||
- **key 的顺序不重要**;
|
||
- **key 名(= 左边)必须是合法的 CRUSH type**。默认合法 type 包括:`root`、`datacenter`、`room`、`row`、`pod`、`pdu`、`rack`、`chassis`、`host`。(注:此处列出的 9 个与 2.4 节的默认 type 全集略有出入,见「文档未明确项」第 2 条。)
|
||
|
||
在 `ceph.conf` 中设置:
|
||
|
||
```ini
|
||
crush_location = root=default row=a rack=a2 chassis=a2a host=a2a1
|
||
```
|
||
|
||
设置后,**OSD 每次启动都会校验自己在 CRUSH map 中的位置是否正确,不正确就把自己挪过去**。
|
||
|
||
关闭这个自动管理(在 `[osd]` 段):
|
||
|
||
```ini
|
||
osd_crush_update_on_start = false
|
||
```
|
||
|
||
> 原文评价:Note that this action is unnecessary in most cases.(大多数情况下不需要关)
|
||
|
||
**默认值**:如果 `crush_location` 没有显式设置,OSD 使用 `root=default host=HOSTNAME`,其中 hostname 取自 `hostname -s` 的输出。
|
||
|
||
> ⚠️ 原文警告:从默认值切换到显式设置 `crush_location` 时,**不要忘了带上 `root=default`**,因为已有的 CRUSH rule 会引用它。
|
||
|
||
#### 自定义 location hook
|
||
|
||
当某些 location 字段在写 `ceph.conf` 时还不知道(例如同一份配置要部署到多个 datacenter,`rack`/`datacenter` 字段不同),可以用 hook:
|
||
|
||
```ini
|
||
crush_location_hook = /path/to/customized-ceph-crush-location
|
||
```
|
||
|
||
- hook 是一个**可执行文件**(通常是脚本);
|
||
- 传入参数形如 `--id ID --type TYPE`,`type` 为 `osd` / `mds` / `mgr` / `mon`;
|
||
- hook 向 **stdout 输出单行** CRUSH location 描述;
|
||
- 执行并解析成功后,**hook 的输出会替换掉之前设置的任何 CRUSH location**。
|
||
|
||
示例脚本:
|
||
|
||
```bash
|
||
#!/bin/sh
|
||
echo "root=default rack=$(cat /etc/rack) host=$(hostname -s)"
|
||
```
|
||
|
||
### 2.3 CRUSH 结构:Devices / Types / Buckets / Rules
|
||
|
||
CRUSH map = **① 描述集群物理拓扑的层级(hierarchy) + ② 定义数据放置策略的一组规则(rules)**。层级里**叶子是 devices(OSD)**,内部节点对应物理特征或分组(host / rack / row / datacenter ...)。
|
||
|
||
#### 2.3.1 Devices
|
||
|
||
- Device 就是**存数据的单个 OSD**(通常一个存储盘对应一个 device);
|
||
- 由 **`id`(非负整数)** 和 **`name`(通常 `osd.N`)** 标识;
|
||
- **Luminous 及以后**,OSD 可以有 **device class**(如 `hdd` / `ssd` / `nvme`),从而被 CRUSH rule 定向选择。
|
||
|
||
#### 2.3.2 Types and Buckets
|
||
|
||
"Bucket" 在 CRUSH 语境下指**层级中的任意内部节点**(host / rack / row ...)。CRUSH map 定义一系列 **types** 来标识这些节点。**默认 type 全集(crush-map.rst)**:
|
||
|
||
| # | 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` | 根节点 |
|
||
|
||
> Most clusters use only a handful of these types, and other types can be defined as needed.(大多数集群只用到其中几个;也可以按需自定义)
|
||
|
||
**在反编译后的 CRUSH map 中,type 以数字 ID 声明**(见 3.4 节)。
|
||
|
||
**层级示例**(原文 ditaa 转述):
|
||
|
||
```text
|
||
+-----------------+
|
||
| {o}root default |
|
||
+--------+--------+
|
||
|
|
||
+---------------+---------------+
|
||
| |
|
||
+------+------+ +------+------+
|
||
| {o}host foo | | {o}host bar |
|
||
+------+------+ +------+------+
|
||
| |
|
||
+----+----+ +----+----+
|
||
| | | |
|
||
+--------+ +--------+ +--------+ +--------+
|
||
| osd.0 | | osd.1 | | osd.2 | | osd.3 |
|
||
+--------+ +--------+ +--------+ +--------+
|
||
```
|
||
|
||
查看集群 CRUSH 层级(含权重):
|
||
|
||
```bash
|
||
ceph osd tree
|
||
```
|
||
|
||
#### 2.3.3 权重(Weight)
|
||
|
||
- 层级中**每个节点(device 或 bucket)都有一个 weight**,表示该设备/子树**应存数据的相对比例**;
|
||
- **权重在叶子上设置**,表示设备大小;
|
||
- 权重会**自动向上累加**:`root` 的权重 = 其下所有 device 权重之和;
|
||
- **权重通常以 tebibytes(TiB)为单位**。
|
||
|
||
**Weighting Bucket Items(权重惯例)**:
|
||
|
||
- Ceph 用 **double** 表达 bucket 权重,支持细粒度;
|
||
- 权重是**设备容量之间的相对差异**;
|
||
- **推荐以 `1.00` 作为 1 TB 存储设备的相对权重**:
|
||
- `0.50` ≈ 500 GB
|
||
- `3.00` ≈ 3 TB
|
||
- 层级中越高的 bucket,其权重 = 它聚合的所有叶子项权重之和。
|
||
|
||
#### 2.3.4 Rules
|
||
|
||
Rules 定义数据如何跨层级中的设备分布,既定义 **placement**,也定义 **replication strategy / distribution policy**。原文举了三种 rule:
|
||
|
||
1. 选一对 target 做**两路镜像**;
|
||
2. 在两个不同 datacenter 中选三个 target 做**三副本**;
|
||
3. 跨六个存储设备做**纠删码**。
|
||
|
||
**创建方式**:CRUSH rule 通常可以通过命令行创建,只需指定 **pool type(replicated / erasure coded)**、**failure domain**、可选的 **device class**。**只有在极少数情况下才需要手工编辑 CRUSH map 来创建 rule**("In rare cases, CRUSH rules must be created by manually editing the CRUSH map")。
|
||
|
||
```bash
|
||
# 列出集群已定义的 rule
|
||
ceph osd crush rule ls
|
||
|
||
# 查看 rule 内容
|
||
ceph osd crush rule dump
|
||
```
|
||
|
||
### 2.4 Device classes(设备类)
|
||
|
||
- 每个 device 可以**可选地**有一个 **class**;
|
||
- **默认情况下,OSD 在启动时依据其底层设备类型自动把自己设为 `hdd` / `ssd` / `nvme`**;
|
||
- Luminous 引入,用于**在同一 host 内混插不同设备类型**时被 CRUSH rule 定向。
|
||
|
||
```bash
|
||
# 显式设置一个或多个 OSD 的 device class
|
||
ceph osd crush set-device-class <class> <osd-name> [...]
|
||
|
||
# 移除(unset)旧 class —— 必须先 unset 才能改设成另一个 class
|
||
ceph osd crush rm-device-class <osd-name> [...]
|
||
```
|
||
|
||
> 这个"必须先 unset"的限制意义:**允许管理员设置的 class 不会被 OSD 重启或脚本改掉**。
|
||
|
||
```bash
|
||
# 创建针对特定 device class 的放置规则
|
||
ceph osd crush rule create-replicated <rule-name> <root> <failure-domain> <class>
|
||
|
||
# 把新 rule 应用到指定 pool
|
||
ceph osd pool set <pool-name> crush_rule <rule-name>
|
||
```
|
||
|
||
也可以**自定义 class 名**以做灵活的约束,例如把 OSD 的 device class 设成 `nvme` 以区别于 SATA SSD,或设成像 `ssd-testing` / `ssd-ethel` 这样的任意名字,让 rule 和 pool 可以灵活地"只用"或"避开"某些 OSD 子集。
|
||
|
||
#### 实现原理:shadow CRUSH hierarchy
|
||
|
||
> Device classes are implemented by creating one or more "shadow" CRUSH hierarchies.
|
||
|
||
- **每一个在用的 device class 都对应一棵 shadow hierarchy,其中只包含该 class 的设备**;
|
||
- CRUSH rule 随后在相应的 shadow hierarchy 上分布数据;
|
||
- 这种方式**对老版本 Ceph 客户端完全向后兼容**。
|
||
|
||
```bash
|
||
# 查看带 shadow item 的 CRUSH 层级
|
||
ceph osd crush tree --show-shadow
|
||
```
|
||
|
||
Luminous 之前的老集群依靠**手工编制的 CRUSH map** 来维护 per-device-type 层级,对此有 **reclassify 工具** 可以帮它们迁移到 device class 而不触发不必要的数据移动(详见 3.7 节)。
|
||
|
||
### 2.5 Weight sets(权重集)
|
||
|
||
#### 为什么需要 weight set
|
||
|
||
CRUSH 是一个**概率性伪随机放置过程**,因此实际分布总是对理想分布有偏差。原文比喻:
|
||
|
||
> in the same way that rolling a die sixty times will likely not result in exactly ten ones and ten sixes
|
||
|
||
**weight set 是一组"替代权重"**,让集群能基于本集群的具体情况(层级、pool 等)做**数值优化**,从而达成均衡分布。
|
||
|
||
#### 两种 weight set
|
||
|
||
| 类型 | 特征 | 兼容性 |
|
||
|---|---|---|
|
||
| **compat** | 为**每个 device 和每个节点**提供**单一**的替代权重集 | **向后兼容老版本 Ceph**。虽然 weight set 是 Luminous v12.2.z 引入的,但用 compat weight set 时连 Firefly 这样的老客户端也能连上集群 |
|
||
| **per-pool** | 更灵活:可以**针对每个数据 pool 单独优化**放置;还可以**对放置的每个位置(position)分别调整权重**,从而修正"数据向小权重设备倾斜"的细微偏斜(这种效应通常只在超大规模集群才明显,但会造成均衡问题) | **要求所有服务器和守护进程都运行在 Luminous v12.2.z 或更高版本** |
|
||
|
||
**局限**:compat weight set 不能指望修正所有异常 —— 例如不同 pool 的 PG 大小不同、负载水平不同,但 balancer 大多一视同仁。
|
||
|
||
#### 优先级
|
||
|
||
1. 若该 pool 有自己的 **per-pool** weight set → 用它;
|
||
2. 否则若存在 **compat** weight set → 用它;
|
||
3. 都没有 → 用**普通 CRUSH 权重**。
|
||
|
||
#### 查看
|
||
|
||
使用 weight set 时,层级中每个节点的权重会在下列命令输出的**单独一列**显示(列标题为 `(compat)` 或 pool 名):
|
||
|
||
```bash
|
||
ceph osd tree
|
||
```
|
||
|
||
> 官方建议:虽然可以手工建立和调整 weight set,但**推荐启用 `ceph-mgr` 的 balancer 模块**自动完成(Luminous 及以上)。
|
||
|
||
#### 命令速查
|
||
|
||
```bash
|
||
# ---------- compat weight set ----------
|
||
ceph osd crush weight-set create-compat
|
||
ceph osd crush weight-set reweight-compat {name} {weight}
|
||
ceph osd crush weight-set rm-compat
|
||
|
||
# ---------- per-pool weight set ----------
|
||
ceph osd crush weight-set create {pool-name} {mode} # mode: flat | positional
|
||
ceph osd crush weight-set reweight {pool-name} {item-name} {weight [...]}
|
||
ceph osd crush weight-set ls
|
||
ceph osd crush weight-set rm {pool-name}
|
||
```
|
||
|
||
`mode` 参数释义:
|
||
|
||
| mode | 含义 |
|
||
|---|---|
|
||
| `flat` | 给所有 device 或 bucket 分配**同一个**权重 |
|
||
| `positional` | 对**结果放置映射中的每个位置**都可能有不同的权重。例如 pool 副本数为 3,则 positional weight set 对每个 device 和 bucket 都有**三个**权重 |
|
||
|
||
### 2.6 用 CLI 修改 CRUSH map(不手工编辑)
|
||
|
||
> 注:以下三小节(add/move OSD、调权重、删 OSD)在正常情况下**都不需要手工执行**,因为 OSD 创建时会自动把自己加进 CRUSH map 并带上正确权重。
|
||
|
||
#### 添加/移动一个 OSD
|
||
|
||
```bash
|
||
ceph osd crush set {name} {weight} root={root} [{bucket-type}={bucket-name} ...]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 示例 |
|
||
|---|---|---|---|---|
|
||
| `name` | OSD 全名 | String | 是 | `osd.0` |
|
||
| `weight` | OSD 的 CRUSH 权重,**通常是其大小,以 TB 计** | Double | 是 | `2.0` |
|
||
| `root` | OSD 所在 CRUSH 层级的根节点(通常 `default`) | Key-value pair | 是 | `root=default` |
|
||
| `bucket-type` | OSD 在 CRUSH 层级中的位置 | Key-value pairs | 否 | `datacenter=dc1 room=room1 row=foo rack=bar host=foo-bar-1` |
|
||
|
||
示例:
|
||
|
||
```bash
|
||
ceph osd crush set osd.0 1.0 root=default datacenter=dc1 room=room1 row=foo rack=bar host=foo-bar-1
|
||
```
|
||
|
||
#### 调整 OSD 权重(reweight)
|
||
|
||
```bash
|
||
ceph osd crush reweight {name} {weight}
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 示例 |
|
||
|---|---|---|---|---|
|
||
| `name` | OSD 全名 | String | 是 | `osd.0` |
|
||
| `weight` | OSD 的 CRUSH 权重 | Double | 是 | `2.0` |
|
||
|
||
#### 删除 OSD(从 CRUSH map)
|
||
|
||
```bash
|
||
ceph osd crush remove {name}
|
||
```
|
||
|
||
> 原文注:OSD 通常是作为 `ceph osd purge` 命令的结果被从 CRUSH map 移除的,本命令很少需要。
|
||
|
||
#### Bucket 相关命令
|
||
|
||
```bash
|
||
# 添加 bucket
|
||
ceph osd crush add-bucket {bucket-name} {bucket-type}
|
||
|
||
# 移动 bucket 到不同位置/位置(position)
|
||
ceph osd crush move {bucket-name} {bucket-type}={bucket-name}, [...]
|
||
|
||
# 重命名 bucket(保持在层级中的位置不变)
|
||
ceph osd crush rename-bucket {oldname} {newname}
|
||
|
||
# 删除 bucket
|
||
ceph osd crush remove {bucket-name}
|
||
```
|
||
|
||
参数说明:
|
||
|
||
| 命令 | 参数 | 说明 | 类型 | 必填 | 示例 |
|
||
|---|---|---|---|---|---|
|
||
| `add-bucket` | `bucket-name` | bucket 全名 | String | 是 | `rack12` |
|
||
| `add-bucket` | `bucket-type` | bucket 类型,**该类型必须已存在于 CRUSH 层级中** | String | 是 | `rack` |
|
||
| `move` | `bucket-name` | 要移动的 bucket 名 | String | 是 | `foo-bar-1` |
|
||
| `move` | `bucket-type` | bucket 在 CRUSH 层级中的新位置 | Key-value pairs | 否 | `datacenter=dc1 room=room1 row=foo rack=bar host=foo-bar-1` |
|
||
| `remove` | `bucket-name` | 要删除的 bucket 名 | String | 是 | `rack12` |
|
||
|
||
> ⚠️ **bucket 必须为空才能被删除**:其中不能有 OSD,也不能有其他 CRUSH bucket。
|
||
|
||
**`add-bucket` 的典型用途**(原文给出两个):
|
||
|
||
1. **把一批 host 挪到一个新的 rack 级 bucket**;
|
||
2. **把新的 `host` bucket(OSD 节点)挂到一个 dummy `root` 下**,这样这些 bucket 在准备好接收数据之前不会收到任何数据;等准备好了,再把它们移到 `default` root 或其他 root。
|
||
|
||
示例:
|
||
|
||
```bash
|
||
ceph osd crush add-bucket rack12 rack
|
||
ceph osd crush remove rack12
|
||
```
|
||
|
||
#### 为副本池创建 rule
|
||
|
||
```bash
|
||
ceph osd crush rule create-replicated {name} {root} {failure-domain-type} [{class}]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 示例 |
|
||
|---|---|---|---|---|
|
||
| `name` | rule 名 | String | 是 | `rbd-rule` |
|
||
| `root` | 数据放置所在的 CRUSH 层级节点名 | String | 是 | `default` |
|
||
| `failure-domain-type` | 用于副本故障域的 CRUSH 节点类型 | String | 是 | `rack` |
|
||
| `class` | 数据放置所在 device class | String | 否 | `ssd` |
|
||
|
||
**选择 failure domain 是关键决策**:
|
||
|
||
- `host` → CRUSH 保证每个副本在**不同的 host** 上;
|
||
- `rack` → 每个副本在**不同的 rack** 中;
|
||
- 应根据**集群规模及其 CRUSH 拓扑**来选。
|
||
|
||
**关于 root 的一个易错点**:整个集群层级通常嵌套在名为 `default` 的 root 节点下。如果你自定义了层级,可能想让 rule 嵌套在**其他节点**下 —— **这是可以的**:节点类型不重要,rule **不必**嵌套在 `root` 节点下。
|
||
|
||
#### 为纠删码池创建 rule
|
||
|
||
EC 池同样要决定 failure domain、放置层级节点(通常 `default`)、是否限制 device class。但 **EC 池的创建方式不同**:必须结合所用的 erasure code plugin 精心构造,所以这些决策要**写进 erasure-code profile**,然后由 profile 生成 CRUSH rule(显式生成,或在使用该 profile 创建 pool 时自动生成)。
|
||
|
||
```bash
|
||
# 列出 EC profile
|
||
ceph osd erasure-code-profile ls
|
||
|
||
# 查看某个 profile
|
||
ceph osd erasure-code-profile get {profile-name}
|
||
|
||
# 由 profile 显式创建 CRUSH rule
|
||
ceph osd crush rule create-erasure {name} {profile-name}
|
||
```
|
||
|
||
> 原文注:**正常情况下永远不应该修改 profile**;应该创建新 profile,并用它创建新 pool 或为已有 pool 创建新 rule。
|
||
|
||
**EC profile 中与 CRUSH 相关的 key(`crush-` 前缀)**:
|
||
|
||
| key | 含义 | 默认值 |
|
||
|---|---|---|
|
||
| `crush-root` | 数据放置在哪个 CRUSH 节点下 | `default` |
|
||
| `crush-failure-domain` | 分布 EC 分片所用的 CRUSH bucket 类型 | `host` |
|
||
| `crush-osds-per-failure-domain` | 每个故障域中放置的最大 OSD 数 | `1`(>1 会导致创建 **CRUSH MSR rule**)。**若指定了 `crush-num-failure-domains`,则本项必须指定** |
|
||
| `crush-num-failure-domains` | 要映射的故障域数量。**若指定了 `crush-osds-per-failure-domain`,则本项必须指定**。结果是创建 CRUSH MSR rule | 文档未明确 |
|
||
| `crush-device-class` | 数据放置在哪个 device class 上 | 无(意味着使用所有设备) |
|
||
| `k`、`m`(`lrc` plugin 还有 `l`) | 决定 EC 分片数量,进而影响生成的 CRUSH rule | 见第七章 |
|
||
|
||
> 原文注:创建新 pool 时**不必显式创建 rule**。如果只指定了 erasure-code profile 而省略 rule 参数,Ceph 会自动创建 CRUSH rule。
|
||
|
||
#### CRUSH MSR Rules(Multi-Step Retry)
|
||
|
||
**背景**:普通 CRUSH rule 在遇到 out OSD 时**不能重试之前的步骤**,只能靠 CHOOSELEAF 步骤把 OSD 换到新 host;而 **CHOOSELEAF 不支持每个故障域多于一个 OSD**。
|
||
|
||
**MSR rule**:
|
||
|
||
- **squid 引入**;
|
||
- 通过在遇到 out OSD 时**重试所有先前的步骤**,支持**每个故障域多个 OSD**;
|
||
- 触发条件:`crush-osds-per-failure-domain` > 1 的 EC profile(会自动创建 MSR rule 而非普通 rule);
|
||
- **要求**:OSD 和客户端必须支持 **CRUSH_MSR feature bit**(squid 或更新)。
|
||
|
||
```bash
|
||
# 删除未被任何 pool 使用的 rule
|
||
ceph osd crush rule rm {rule-name}
|
||
```
|
||
|
||
### 2.7 CRUSH map 的编译 / 反编译 / 编辑(crushtool)
|
||
|
||
手工编辑 CRUSH map 的完整五步(详见第三章):
|
||
|
||
```bash
|
||
# 1. 取出(编译格式的)CRUSH map
|
||
ceph osd getcrushmap -o {compiled-crushmap-filename}
|
||
|
||
# 2. 反编译成文本
|
||
crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-filename}
|
||
|
||
# 3. 用文本编辑器编辑 devices / buckets / rules
|
||
|
||
# 4. 重新编译
|
||
crushtool -c {decompiled-crushmap-filename} -o {compiled-crushmap-filename}
|
||
|
||
# 5. 注入集群
|
||
ceph osd setcrushmap -i {compiled-crushmap-filename}
|
||
```
|
||
|
||
### 2.8 CRUSH map 的六大 Section
|
||
|
||
| # | Section | 内容 |
|
||
|---|---|---|
|
||
| 1 | **tunables** | map 顶部的 preamble,描述所有**不属于 legacy CRUSH 行为**的 tunables。这些 tunable 用于修正老 bug、优化、以及多年来为改进 CRUSH 行为所做的其它变更 |
|
||
| 2 | **devices** | 存数据的各个 OSD |
|
||
| 3 | **types** | bucket `types`,定义 CRUSH 层级中使用的 bucket 类型 |
|
||
| 4 | **buckets** | 存储位置(row / rack / chassis / host ...)的层级聚合及其权重。定义完 `types` 后,CRUSH map 为层级中每个节点定义其 type,以及它包含哪些 device 或其他节点 |
|
||
| 5 | **rules** | 定义数据如何跨层级中的设备分布的策略 |
|
||
| 6 | **choose_args** | 与层级关联的、为优化数据放置而调整过的**替代权重**。可以是**一个**用于整个集群的 choose_args map,也可以是**多个**、每个 map 为特定 pool 定制 |
|
||
|
||
### 2.9 Bucket 算法(5 种 bucket type)
|
||
|
||
> 原文建议:If you are unsure of which bucket type to use, use the `straw2` bucket.
|
||
|
||
| Bucket 类型 | 原理 | 适用 / 不适用 |
|
||
|---|---|---|
|
||
| **uniform** | 聚**合权重完全相同**的设备 | 适用:硬件批量采购后整体上线/下线、设备权重**完全一致**的场景。优势:CRUSH 可以**常数时间**把副本映射进 uniform bucket。**不适用**:设备权重不一致 |
|
||
| **list** | 内容以**链表**聚合,行为由 **RUSH<sub>P</sub>** 算法决定。对象要么按适当概率重定位到最新设备,要么留在老设备上 | 优势:往 bucket 里**加** item 时数据迁移最优。**不适用**:会**经常缩容**的场景 —— 从链表中间或尾部删除 item 会导致大量不必要的数据移动 |
|
||
| **tree** | 用**二叉搜索树**,行为由 **RUSH<sub>R</sub>** 算法决定 | 比 list 更擅长处理包含大量 item 的 bucket。放置时间降到 **O(log n)**。适合管理超大规模设备集或嵌套 bucket |
|
||
| **straw** | 所有 item 通过"**抽签**"相互竞争来获得副本放置 | 与 list/tree 的分治策略不同(后者要么给某些 item 优先权、要么可以不必考虑整个子树)。提升放置过程性能,但 bucket 内容因**增删改权重**而变化时,重组织行为可能次优 |
|
||
| **straw2** | 改进 straw:**邻居权重变化时完全避免任何数据移动**。若某 item 权重变化(含加入/移除集群),**只会有数据移入或移出该 item**,不考虑邻居权重 | **新创建的 bucket 的默认类型**(Hammer 引入) |
|
||
|
||
### 2.10 Hash 算法
|
||
|
||
> Each bucket uses a hash algorithm. **As of Reef, Ceph supports the `rjenkins1` algorithm.** To select `rjenkins1` as the hash algorithm, enter `0` as your hash setting.
|
||
|
||
即:**hash `0` = `rjenkins1`**(这也是反编译 CRUSH map 中 `hash 0 # rjenkins1` 注释的来源)。
|
||
|
||
### 2.11 CRUSH rule 语法:逐字段讲解
|
||
|
||
#### 普通 CRUSH rule
|
||
|
||
```text
|
||
rule <rulename> {
|
||
|
||
id [a unique integer ID]
|
||
type [replicated|erasure]
|
||
step take <bucket-name> [class <device-class>]
|
||
step [choose|chooseleaf] [firstn|indep] <N> type <bucket-type>
|
||
step emit
|
||
}
|
||
```
|
||
|
||
#### CRUSH MSR rule
|
||
|
||
```text
|
||
rule <rulename> {
|
||
|
||
id [a unique integer ID]
|
||
type [msr_indep|msr_firstn]
|
||
step take <bucket-name> [class <device-class>]
|
||
step choosemsr <N> type <bucket-type>
|
||
step emit
|
||
}
|
||
```
|
||
|
||
#### 字段表
|
||
|
||
| 字段 | Description | 类型 | 必填 | 默认值 / 合法值 | 备注 |
|
||
|---|---|---|---|---|---|
|
||
| `id` | 标识 rule 的唯一整数;构成 rule mask 的一部分 | Integer | 是 | `0` | |
|
||
| `type` | rule 强制执行的复制策略类型。`msr_firstn` / `msr_indep` 是一种**不同的 descent 算法**,支持在 rule 内重试步骤,从而支持每个故障域多个 OSD | String | 是 | 默认 `replicated`;合法值 `replicated`、`erasure`、`msr_firstn`、`msr_indep` | 构成 rule mask 的一部分 |
|
||
| `step take <bucket-name> [class <device-class>]` | 取一个 bucket 名并**沿树向下迭代**。若指定 `device-class`,该参数必须匹配集群中 OSD 已分配的某个 class,且**只有属于该 class 的设备会被纳入** | — | 是 | 示例 `step take data` | 构成 rule 的一部分 |
|
||
| `step choose firstn {num} type {bucket-type}` | 从当前 bucket 中选出 `num` 个给定类型的 bucket。`{num}` 通常是 pool 的副本数(pool size) | — | — | 示例 `step choose firstn 1 type row` | Prerequisite: 跟在 `step take` 或 `step choose` 之后 |
|
||
| `step chooseleaf firstn {num} type {bucket-type}` | 选出一组给定类型的 bucket,并**从该集合中每个 bucket 的子树里各选一个叶子节点(即一个 OSD)**。集合中 bucket 的数量通常是 pool 的副本数 | — | — | 示例 `step chooseleaf firstn 0 type row` | 用 `chooseleaf` **就不必再单独用一步去选 device**。Prerequisite: 跟在 `step take` 或 `step choose` 之后 |
|
||
| `step emit` | 输出栈顶的当前值并清空栈。通常用在 rule 末尾,但**也可以用来在同一个 rule 中从不同的树中进行选择** | — | — | 示例 `step emit` | Prerequisite: 跟在 `step choose` 之后 |
|
||
| `step choosemsr {num} type {bucket-type}` | 选出 `num` 个 `bucket-type` 类型的 bucket。**`msr_firstn` 和 `msr_indep` 必须用 `choosemsr`,不能用 `choose` 或 `chooseleaf`** | — | — | 示例 `step choosemsr 3 type host` | Prerequisite: 跟在 `step take` 之后、`step emit` 之前 |
|
||
|
||
#### `{num}` 的三种取值语义(choose / chooseleaf / choosemsr 通用)
|
||
|
||
| `{num}` 取值 | 语义 |
|
||
|---|---|
|
||
| `{num} == 0` | 选 `pool-num-replicas` 个 bucket(有多少可用就选多少) |
|
||
| `pool-num-replicas > {num} > 0` | 就选那么多(选 `num` 个) |
|
||
| `{num} < 0` | 选 `pool-num-replicas - {num}` 个 bucket(**注:`choosemsr` 的条目中未列出负值分支**) |
|
||
|
||
#### `firstn` vs `indep`:降级时的替换策略
|
||
|
||
决定当 CRUSH map 中 item(OSD)被标记 `down` 时,CRUSH 使用哪种替换策略:
|
||
|
||
- **副本池用 `firstn`**;
|
||
- **纠删码池用 `indep`**。
|
||
|
||
原文给出的例子:假设一个 PG 存放在 OSD 1,2,3,4,5 上,然后 OSD 3 挂了。
|
||
|
||
| 模式 | 行为 | 最终映射变换 |
|
||
|---|---|---|
|
||
| `firstn` | CRUSH 调整计算:选 1 和 2 → 选到 3 时发现 3 down → 重试选 4 和 5 → 最后再选一个新 OSD:OSD 6 | `1, 2, 3, 4, 5` → `1, 2, 4, 5, 6` |
|
||
| `indep` | CRUSH 选到 3,发现 down,重试选 6 | `1, 2, 3, 4, 5` → `1, 2, 6, 4, 5` |
|
||
|
||
**为什么 EC 必须用 `indep`**:上面的 `firstn` 序列会**改变映射到 OSD 4、5、6 上的数据**(位置发生了平移)。对纠删码池来说,分片位置与数据内容一一对应,位置平移等于数据错乱。`indep` 模式正是为了避免这个后果 —— 它保证"3 号位"仍然落在 3 号位(由 6 顶替),其余位置不变。
|
||
|
||
#### ⚠️ 一条重要约束
|
||
|
||
> A single CRUSH rule can be assigned to multiple pools, but **a single pool cannot have multiple CRUSH rules**.
|
||
|
||
(一条 CRUSH rule 可以分配给多个 pool;但一个 pool 不能有多条 CRUSH rule。)
|
||
|
||
#### 默认 rule 的编号
|
||
|
||
> In most cases, there is no need to modify the default rule. When a new pool is created, by default the rule will be set to the value `0` (which indicates the default CRUSH rule, which has the numeric ID `0`).
|
||
|
||
### 2.12 自定义 rule 示例(含 SSD / HDD 分层)
|
||
|
||
#### 示例 A:混合 SSD + HDD,SSD 做 primary(同一批 host 内混插)
|
||
|
||
有些集群为了平衡成本与性能,在**同一个副本池**里混用 SSD 和 HDD。做法一:把 HDD OSD 的 primary affinity 设为 `0`,让操作被导向每个 acting set 中的 SSD OSD(见 2.14 节)。做法二:**定义一条 CRUSH rule,永远选一个 SSD OSD 作为 primary,其余 OSD 用 HDD**。
|
||
|
||
```text
|
||
rule mixed_replicated_rule {
|
||
id 11
|
||
type replicated
|
||
step take default class ssd
|
||
step chooseleaf firstn 1 type host
|
||
step emit
|
||
step take default class hdd
|
||
step chooseleaf firstn 0 type host
|
||
step emit
|
||
}
|
||
```
|
||
|
||
**关键代价(原文明确)**:
|
||
|
||
> For an `N`-times replicated pool, this rule selects `N+1` OSDs in order to guarantee that `N` copies are on different hosts, because the first SSD OSD might be colocated with any of the `N` HDD OSDs.
|
||
|
||
即:对 N 副本池,这条 rule 会选 **N+1** 个 OSD。因为先选出的那个 SSD OSD **有可能和后面 N 个 HDD OSD 中的某一个在同一台 host 上**,为了保证 N 个副本确实分布在不同 host,只能多选一个。→ **存储开销增加**。
|
||
|
||
> 这正是 `step emit` "也可以用来在同一个 rule 中从不同的树中进行选择" 的实际用例:先 emit 一次 SSD 的选择结果,再 take 一次 hdd 树、选完再 emit。
|
||
|
||
#### 示例 B:SSD 与 HDD 分属不同 host(避免 N+1 开销)
|
||
|
||
```text
|
||
rule mixed_replicated_rule_two {
|
||
id 1
|
||
type replicated
|
||
step take ssd_hosts class ssd
|
||
step chooseleaf firstn 1 type host
|
||
step emit
|
||
step take hdd_hosts class hdd
|
||
step chooseleaf firstn -1 type host
|
||
step emit
|
||
}
|
||
```
|
||
|
||
要点:
|
||
|
||
- CRUSH root `ssd_hosts` 和 `hdd_hosts` **有严格要求:不得包含任何相同的服务器**;
|
||
- `firstn -1` 的含义(按 2.11 的 `{num} < 0` 规则):选 `pool-num-replicas - 1` 个 —— 因为已经选了 1 个 SSD primary;
|
||
- **硬件配套建议(原文)**:因为所有客户端请求都会打到有 SSD 的那批 host,所以**建议给 SSD OSD 配更快的 CPU,给 HDD OSD 配更一般 CPU**(后者正常情况下只做 recovery);
|
||
- ⚠️ **故障降级**:如果 primary SSD OSD 挂了,该 PG 的请求会**临时由较慢的 HDD OSD 提供服务**,直到数据被复制到替换的 primary SSD OSD 上。
|
||
|
||
### 2.13 Tunables(可调参数)
|
||
|
||
#### 为什么有 tunables
|
||
|
||
计算数据放置的 CRUSH 算法**随时间被不断改进**。为了让用户能选择"legacy 还是 optimal 版本的 CRUSH",Ceph 提供了**成组的 tunables**。
|
||
|
||
- 要用更新的 tunables,**所有 Ceph 客户端和守护进程都必须支持新的 CRUSH 主版本**;
|
||
- 因此 tunables 以**引入它的 Ceph 版本**命名成 `profiles`。例:`firefly` tunables 最早由 Firefly 支持,且**不能与更老的客户端(如 Dumpling)一起工作**;
|
||
- 集群的 tunables profile **从 legacy 切到 newer/`optimal` 后**,`ceph-mon` 和 `ceph-osd` 选项会**阻止不支持新 CRUSH 特性的老客户端接入集群**。
|
||
|
||
#### 各 profile 详解
|
||
|
||
##### argonaut(legacy)
|
||
|
||
Argonaut 及更早版本的 legacy CRUSH 行为。**前提是"没有太多 OSD 被标记为 out"**,此时对大多数集群工作良好。
|
||
|
||
##### bobtail(CRUSH_TUNABLES2)
|
||
|
||
解决的问题:
|
||
|
||
1. 对于**叶子 bucket 中设备数较少**的层级,某些 PG 可能映射到**少于期望副本数**的 OSD,产生 `undersized` PG。已知发生在 `host` 节点下 OSD 数很少(1~3 个)的层级。
|
||
2. 对于**大型集群**,一小部分 PG 可能映射到的 OSD 数少于期望。已知发生在使用了多层层级(如 `row`、`rack`、`host`、`osd`)时。
|
||
3. 当一个或多个 OSD 被标记 `out` 时,数据倾向于**重分布到邻近的 OSD**,而不是跨整个层级分布。
|
||
|
||
引入的 tunable:
|
||
|
||
| tunable | 含义 | legacy 值 | optimal 值 |
|
||
|---|---|---|---|
|
||
| `choose_local_tries` | 本地重试次数 | `2` | `0` |
|
||
| `choose_local_fallback_tries` | | `5` | `0` |
|
||
| `choose_total_tries` | 选择某个 item 的总尝试次数 | `19` | `50`(原文:subsequent testing indicates that a value of 50 is more appropriate for typical clusters。对于**极大规模集群**可能需要更大的值) |
|
||
| `chooseleaf_descend_once` | 递归 `chooseleaf` 尝试是否重试(还是只试一次、让原始放置去重试) | `0` | `1` |
|
||
|
||
**迁移影响**:从 `argonaut` 迁到 `bobtail` 会触发**中等量的数据移动**。对已存有大量数据的集群要谨慎。
|
||
|
||
##### firefly(CRUSH_TUNABLES3)
|
||
|
||
**chooseleaf_vary_r**
|
||
|
||
修复 `chooseleaf` CRUSH step 的一个问题:**当很大比例的 OSD 被标记 out 时,会产生 OSD 数过少的 PG 映射**。
|
||
|
||
| tunable | 含义 | legacy 值 | optimal 值 |
|
||
|---|---|---|---|
|
||
| `chooseleaf_vary_r` | 递归 chooseleaf 尝试是否以**非零的 `r` 值**开始(该值由父级已尝试的次数决定) | `0`(此值下 CRUSH 有时**无法找到映射**) | `1`(在计算开销与正确性上最优) |
|
||
|
||
**迁移影响**:对存有大量数据的已有集群,把这个 tunable 从 `0` 改成 `1` 会触发**大量数据迁移**;**取 `4` 或 `5` 既能让 CRUSH 找到合法映射,又能减少数据移动量**。
|
||
|
||
**straw_calc_version**
|
||
|
||
问题:`straw` 算法 bucket 在 CRUSH map 中**计算并存储的内部权重**有问题 —— 当存在 **CRUSH 权重为 0 的 bucket**、或**权重互不相同且唯一的混合 bucket** 时,CRUSH 会**不正确地分布数据**(即不按权重成比例)。
|
||
|
||
| tunable | 含义 |
|
||
|---|---|
|
||
| `straw_calc_version` | `0` = 保留旧的、有问题的内部权重计算;`1` = 修复该问题 |
|
||
|
||
**迁移影响**:改成 `1` 之后再调整 straw bucket(增删改 item 的权重,或用 reweight-all 命令),**如果集群正好命中了上述有问题的条件**,会触发**小到中等量**的数据移动。
|
||
|
||
> 这个 tunable 的特别之处:**对客户端侧的内核版本要求完全没有影响**。
|
||
|
||
##### hammer(CRUSH_V4)
|
||
|
||
- **仅切换 profile 本身不会改变已有 CRUSH map 的映射**;
|
||
- 新增一种 bucket 算法:**`straw2`**。它修复了原始 `straw` 的若干限制。具体来说:**旧的 `straw` bucket 在权重被调整时,会改变一些本不应该改变的映射**;而 `straw2` 达成了最初的目标 —— **只向/从权重发生变化的那个 bucket item 改变映射**;
|
||
- **`straw2` 是所有新建 bucket 的默认类型**。
|
||
|
||
**迁移影响**:把 bucket 类型从 `straw` 改成 `straw2` 会触发**少量数据移动**,移动量取决于 bucket item 的权重差异有多大 —— **权重全一样则无数据移动,权重差异越大移动越多**。
|
||
|
||
##### jewel(CRUSH_TUNABLES5)
|
||
|
||
整体改善 CRUSH 行为:**当一个 OSD 被标记 `out` 时,显著减少发生变化的映射数量 → 显著减少数据移动量**。
|
||
|
||
| tunable | 含义 | legacy 值 | new 值 |
|
||
|---|---|---|---|
|
||
| `chooseleaf_stable` | 递归 chooseleaf 尝试是否为内层循环使用更好的值,从而**大幅减少 OSD 被标记 out 时的映射变更数** | `0` | `1`(使用新方法) |
|
||
|
||
**迁移影响**:在已有集群上改这个值会导致**极大量的数据移动**,因为**几乎每个 PG 映射都可能改变**。
|
||
|
||
#### 各 tunables 所需的客户端版本
|
||
|
||
| Tunables | Ceph 版本 | Linux 内核版本(CephFS / RBD 内核客户端) |
|
||
|---|---|---|
|
||
| CRUSH_TUNABLES2 | v0.55 及以后,含 Bobtail(v0.56.x) | v3.9 及以后 |
|
||
| CRUSH_TUNABLES3 | v0.78(Firefly)及以后 | v3.15 及以后 |
|
||
| CRUSH_V4 | v0.94(Hammer)及以后 | v4.1 及以后 |
|
||
| CRUSH_TUNABLES5 | v10.0.2(Jewel)及以后 | v4.5 及以后 |
|
||
|
||
#### "Non-optimal tunables" 告警
|
||
|
||
**v0.74 及以后**,若当前任何 CRUSH tunable 处于非最优值(即不等于 `default` profile 中的 optimal 值),Ceph 会抛出健康检查:
|
||
|
||
```text
|
||
HEALTH_WARN crush map has non-optimal tunables
|
||
```
|
||
|
||
**两种消除告警的方式**:
|
||
|
||
**方式 1:把 tunables 调整成最优(推荐)**
|
||
|
||
```bash
|
||
ceph osd crush tunables optimal
|
||
```
|
||
|
||
> 会触发**一些数据移动(可能高达 10%)**。一般优于方式 2,但在数据移动会影响性能的场景(如生产集群)要特别小心。
|
||
|
||
可能出现的三类问题(此时应该回退):(a) 新值产生的负载集群扛不住;(b) 新值让集群运行慢到不可接受;(c) **客户端兼容性问题**(用老内核 CephFS/RBD 客户端,或 pre-Bobtail 的 `librados` 客户端时)。
|
||
|
||
回退:
|
||
|
||
```bash
|
||
ceph osd crush tunables legacy
|
||
```
|
||
|
||
**方式 2:不改 CRUSH,仅关闭告警**
|
||
|
||
在 `ceph.conf` 的 `[mon]` 段加入:
|
||
|
||
```ini
|
||
mon_warn_on_legacy_crush_tunables = false
|
||
```
|
||
|
||
生效方式:重启 mon,或在 mon 运行时执行:
|
||
|
||
```bash
|
||
ceph tell mon.\* config set mon_warn_on_legacy_crush_tunables false
|
||
```
|
||
|
||
#### 调整 tunables 的注意事项(原文三条)
|
||
|
||
1. 调整 CRUSH tunables 的值**会导致一个或多个 PG 从某个存储节点迁移到另一个**。若集群已存有大量数据,**准备好迎接显著的数据移动**。
|
||
2. 当 `ceph-osd` 和 `ceph-mon` 守护进程拿到更新后的 map,它们会**立即开始拒绝不支持新特性的客户端的新连接**。但**已连接的客户端实际上被" grandfathered in( grandfathered,既往不咎)"**,其中任何不支持新特性的客户端**会发生故障(malfunction)**。
|
||
3. 若 CRUSH tunables 被设为 newer(非 legacy)值后又回退到 legacy 值,`ceph-osd` 守护进程**不再被要求**支持与 newer 值关联的任何新 CRUSH 特性。**但是,OSD peering 过程需要检查和理解旧的 map。因此,如果集群曾经使用过非 legacy 的 CRUSH 值,就不要运行老版本的 `ceph-osd` 守护进程 —— 即使最新的 map 已经回退为 legacy 默认值。**
|
||
|
||
#### 可用的 profile(Octopus 起)
|
||
|
||
| profile | 含义 |
|
||
|---|---|
|
||
| `legacy` | argonaut 及更早的 legacy 行为 |
|
||
| `argonaut` | argonaut release 支持的 legacy 值 |
|
||
| `bobtail` | bobtail release 支持的值 |
|
||
| `firefly` | firefly release 支持的值 |
|
||
| `hammer` | hammer release 支持的值 |
|
||
| `jewel` | jewel release 支持的值 |
|
||
| `optimal` | 当前 Ceph 版本的最佳值 |
|
||
| `default` | **全新安装集群的默认值**。这些值取决于当前 Ceph 版本,是**硬编码的**,通常**是最优值与 legacy 值的混合**。它们通常对应于**上一个 LTS 版本**的 `optimal` profile,或者"大多数用户被预期已升级到最新客户端"的最近版本的 `optimal` profile |
|
||
|
||
应用 profile:
|
||
|
||
```bash
|
||
ceph osd crush tunables {PROFILE}
|
||
```
|
||
|
||
> ⚠️ 该操作**可能触发大量数据移动**。变更前请查阅 release notes 和文档,并**考虑限制 recovery / backfill 参数**以限制由此产生的 backfill。
|
||
|
||
### 2.14 Primary OSD 选择的调优
|
||
|
||
#### 背景
|
||
|
||
客户端读写时,**首先联系该 PG acting set 中的 primary OSD**。默认 acting set 的**第一个 OSD 就是 primary OSD(也称 "lead OSD")**。例:`[2, 3, 4]` 中 `osd.2` 排第一,因此是 primary。
|
||
|
||
如果某个 OSD 明显不适合当 lead(例如盘慢、控制器慢),为了**避免性能瓶颈(尤其是读操作)**并**最大化硬件利用率**,有两种手段:
|
||
|
||
1. 调整 **primary affinity** 值;
|
||
2. **编制 CRUSH rule**,让它选出更适合当 lead 的 OSD。
|
||
|
||
**对副本池**:这种调优特别有用,因为**默认读操作由每个 PG 的 primary OSD 提供**。
|
||
**对纠删码池**:读速度可以通过启用 **fast read** 来提升(见第六章 `fast_read`)。
|
||
|
||
#### Primary Affinity
|
||
|
||
- primary affinity 是 OSD 的一个属性,决定**该 OSD 在给定 acting set 中被选为 primary(lead OSD)的可能性**;
|
||
- 取值:**`0` 到 `1`(含端点)之间的任意实数**;
|
||
- **默认每个 Ceph OSD 的 primary affinity = `1`**,此时所有 OSD 当 primary 的可能性相等;
|
||
- `0` = 该 OSD **不可**被用作 primary;`1` = **最大可能**被用作 primary;中间值 ≈ 大致的被选概率。
|
||
|
||
```bash
|
||
ceph osd primary-affinity <osd-id> <weight>
|
||
```
|
||
|
||
**原文给出的真实案例**:集群里混插了 1.9 TB 和 3.84 TB 两种 SATA SSD。后者平均被分配到的 PG 数是前者两倍,因此服务的读写也是两倍 —— 更忙。**把 primary affinity 粗略地设成与 OSD 大小成反比**,不是 100% 最优,但**通过更均匀地利用 SATA 接口带宽和 CPU 周期, readily 获得了 15% 的整体读吞吐提升**。原文强调这不是思想实验 —— 这 15% 是在一个真实 Ceph 集群上取得的。
|
||
|
||
> 另一个说明:CRUSH 选 lead OSD 的过程**不是"由相对 affinity 值决定的简单概率"的函数。但即使只用 desirable primary affinity 值的一阶近似,也能取得可测量的效果**。
|
||
|
||
> 引自:[CRUSH Maps](https://docs.ceph.com/en/tentacle/rados/operations/crush-map/)、[Manually editing the CRUSH Map](https://docs.ceph.com/en/tentacle/rados/operations/crush-map-edits/)
|
||
|
||
---
|
||
|
||
## 三、CRUSH map 编辑实战
|
||
|
||
> ⚠️ 原文开宗明义:
|
||
> **Manually editing the CRUSH map is an advanced administrator operation.** For the majority of installations, CRUSH changes can be implemented via the Ceph CLI and do not require manual CRUSH map edits. If you have identified a use case where manual edits *are* necessary with a recent Ceph release, consider contacting the Ceph developers at dev@ceph.io so that future versions of Ceph do not have this problem.
|
||
|
||
### 3.1 标准五步流程
|
||
|
||
| 步骤 | 命令 | 说明 |
|
||
|---|---|---|
|
||
| 1. Get | `ceph osd getcrushmap -o {compiled-crushmap-filename}` | Ceph 输出(`-o`)一个**编译格式**的 CRUSH map |
|
||
| 2. Decompile | `crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-filename}` | 因为取出的 map 是编译过的,编辑前**必须**先反编译 |
|
||
| 3. Edit | 用文本编辑器编辑 `Devices` / `Buckets` / `Rules` 中的至少一个 | |
|
||
| 4. Recompile | `crushtool -c {decompiled-crushmap-filename} -o {compiled-crushmap-filename}` | |
|
||
| 5. Set | `ceph osd setcrushmap -i {compiled-crushmap-filename}` | Ceph 从指定文件加载(`-i`)编译后的 CRUSH map |
|
||
|
||
完整示例:
|
||
|
||
```bash
|
||
# 1. 取
|
||
ceph osd getcrushmap -o /tmp/crush-compiled
|
||
|
||
# 2. 反编译
|
||
crushtool -d /tmp/crush-compiled -o /tmp/crush.txt
|
||
|
||
# 3. 编辑
|
||
vi /tmp/crush.txt
|
||
|
||
# 4. 编译
|
||
crushtool -c /tmp/crush.txt -o /tmp/crush-new
|
||
|
||
# 5. 注入
|
||
ceph osd setcrushmap -i /tmp/crush-new
|
||
```
|
||
|
||
### 3.2 场景一:Devices 段
|
||
|
||
Device 定义通常**每个 OSD 守护进程一条**,由 `id`(非负整数)和 `name`(通常 `osd.N`)标识。
|
||
|
||
**查看 device 列表**:
|
||
|
||
```bash
|
||
ceph device ls
|
||
```
|
||
|
||
输出格式:
|
||
|
||
```text
|
||
device {num} {osd.name} [class {class}]
|
||
```
|
||
|
||
示例输出:
|
||
|
||
```text
|
||
device 0 osd.0 class ssd
|
||
device 1 osd.1 class hdd
|
||
device 2 osd.2
|
||
device 3 osd.3
|
||
```
|
||
|
||
> 注意 `osd.2` / `osd.3` 没有 class —— **device class 是可选的**。
|
||
|
||
**device 与守护进程的对应关系**(原文):大多数情况下每个 device 对应一个 `ceph-osd` 守护进程。这个守护进程可能映射到:
|
||
|
||
- 单个存储设备;
|
||
- 一对设备(例如一个放数据、一个放 journal 或 metadata);
|
||
- 某些情况下是一个小型 RAID 设备,或更大存储设备的一个分区。
|
||
|
||
**手工编辑时的 device 语法**(反编译后形如):
|
||
|
||
```text
|
||
device {num} {osd.name} [class {class}]
|
||
```
|
||
|
||
### 3.3 场景二:Types 段(增加/修改 bucket 类型)
|
||
|
||
在 bucket 类型列表下新增一行:**`type` + 唯一数字 ID + bucket 名**。
|
||
|
||
约定:**有且仅有一个叶子 bucket 类型,且它是 `type 0`**;但叶子 bucket 的**名字可以随便起**(如 `osd`、`disk`、`drive`、`storage`)。
|
||
|
||
```text
|
||
# types
|
||
type {num} {bucket-name}
|
||
```
|
||
|
||
官方给出的完整默认声明示例:
|
||
|
||
```text
|
||
# types
|
||
type 0 osd
|
||
type 1 host
|
||
type 2 chassis
|
||
type 3 rack
|
||
type 4 row
|
||
type 5 pdu
|
||
type 6 pod
|
||
type 7 room
|
||
type 8 datacenter
|
||
type 9 zone
|
||
type 10 region
|
||
type 11 root
|
||
```
|
||
|
||
> 注意:这里的数字 ID 分配(`osd=0, host=1, chassis=2, rack=3, row=4, pdu=5, pod=6, room=7, datacenter=8, zone=9, region=10, root=11`)与 2.3.2 节 crush-map.rst 中列出的**顺序**(osd, host, chassis, rack, row, pdu, pod, room, datacenter, zone, region, root)是**一致的**。
|
||
|
||
### 3.4 场景三:Buckets 段(层级编辑)
|
||
|
||
#### 声明一个 bucket 实例的六要素
|
||
|
||
1. 指定其 **type**;
|
||
2. 给它一个**唯一名字**(alphanumeric string);
|
||
3. 给它一个**唯一 ID,用负整数表示**(**此项可选**);
|
||
4. 给它一个**相对权重**(相对于 bucket 中 item 的总容量/能力);
|
||
5. 给它一个 **bucket 算法**(通常 `straw2`);
|
||
6. 指定 bucket 算法的 **hash**(通常 `0`,即 `rjenkins1`)。
|
||
|
||
#### 语法
|
||
|
||
```text
|
||
[bucket-type] [bucket-name] {
|
||
id [a unique negative numeric ID]
|
||
weight [the relative capacity/capability of the item(s)]
|
||
alg [the bucket type: uniform | list | tree | straw | straw2 ]
|
||
hash [the hash type: 0 by default]
|
||
item [item-name] weight [weight]
|
||
}
|
||
```
|
||
|
||
#### 官方示例(对应 2.3.2 的 host1/host2/rack1 图)
|
||
|
||
```text
|
||
host node1 {
|
||
id -1
|
||
alg straw2
|
||
hash 0
|
||
item osd.0 weight 1.00
|
||
item osd.1 weight 1.00
|
||
}
|
||
|
||
host node2 {
|
||
id -2
|
||
alg straw2
|
||
hash 0
|
||
item osd.2 weight 1.00
|
||
item osd.3 weight 1.00
|
||
}
|
||
|
||
rack rack1 {
|
||
id -3
|
||
alg straw2
|
||
hash 0
|
||
item node1 weight 2.00
|
||
item node2 weight 2.00
|
||
}
|
||
```
|
||
|
||
> 注意:rack bucket **不含任何 OSD**。它包含的是更低层的 host bucket,并且**在 item 条目中计入它们权重的和**(`2.00` = node1 的 `1.00 + 1.00`)。
|
||
|
||
#### 一条重要规则:叶子不需要声明为 bucket 实例
|
||
|
||
> Because leaf nodes reflect storage devices that have already been declared under the `#devices` list at the beginning of the CRUSH map, there is no need to declare them as bucket instances.
|
||
|
||
**层级中第二低的 bucket 类型通常用来聚合 device** —— 通常就是装这些盘的服务器。
|
||
|
||
#### 高密度环境的故障域提示
|
||
|
||
原文特意提示:**高密度环境中,一个 chassis 里有多个 host/node 很常见(blade 或 twin)**。必须预见到 chassis 故障的后果:例如更换 chassis 时,该 chassis 下所有 host 及其 OSD 都会 `down` 而不可用。**因此必须避免把多个副本或数据分片放在同一个 chassis 内** —— 此时 chassis 就是一个 _failure domain_。
|
||
|
||
#### 命名建议(原文)
|
||
|
||
- 让 CRUSH map **适配你偏好的硬件命名规范**;
|
||
- 用**能清楚反映物理硬件**的 bucket 名。清晰的命名在 OSD 或硬件故障、管理员需要接触物理硬件时,能显著降低管理集群和排查问题的难度。
|
||
|
||
### 3.5 场景四:Rules 段(自定义 rule)
|
||
|
||
见 2.11(语法逐字段)和 2.12(SSD/HDD 分层示例)。这里补充几条编辑时的注意:
|
||
|
||
- **默认 CRUSH map 每个 pool 一条 rule**;大集群可能有很多 pool,每个 pool 可能有自己的非默认 CRUSH rule;
|
||
- **大多数情况下不需要修改默认 rule**。新建 pool 时 rule 默认为 `0`;
|
||
- ⚠️ **一条 rule 可以给多个 pool 用;一个 pool 不能有多条 rule**。
|
||
|
||
### 3.6 场景五:调整权重(bucket item 权重)
|
||
|
||
见 2.3.3(权重惯例,1 TB = 1.00)与 2.6(`ceph osd crush reweight`)。手工编辑时直接在 bucket 声明里改 `item ... weight ...`,并**同步更新上层 bucket 的 item weight(其为下层之和)**。
|
||
|
||
> 💡 补充:手工编辑 bucket 时如果忘记同步上层 weight,会造成层级权重不一致。建议优先用 CLI 的 `ceph osd crush reweight`(它会自动处理),只在 CLI 无法表达的场景才手工编辑,并在注入前用 `crushtool --compare`(3.7 节)验证。
|
||
|
||
### 3.7 场景六:从 legacy SSD rule 迁移到 device classes(reclassify)
|
||
|
||
#### 背景
|
||
|
||
Luminous 引入 device class 之前,要写针对特定设备类型(如 SSD)的 rule,**必须手工编辑 CRUSH map 并为每种设备类型维护一套平行的层级**。device class 提供了更透明的方式达成同样目的。
|
||
|
||
**但是**:如果把一个已手工定制过 per-device map 的集群迁移到新的 device class rule,**系统中的所有数据都会被重新洗牌(reshuffled)**。
|
||
|
||
`crushtool` 提供了几条命令,可以**转换 legacy rule 和层级**,从而开始使用新的 device class rule,**同时避免数据移动**。
|
||
|
||
#### 三类转换
|
||
|
||
##### 1. `--reclassify-root <root-name> <device-class>`
|
||
|
||
- 检查层级中 `root-name` 下的所有内容;
|
||
- 把所有引用该 root、形如 `take <root-name>` 的 rule 改写成 `take <root-name> class <device-class>`;
|
||
- **重新编号 bucket,使得旧 ID 被用于指定 class 的 "shadow tree",从而不发生任何数据移动**。
|
||
|
||
示例:
|
||
|
||
```text
|
||
# 原 rule
|
||
rule replicated_rule {
|
||
id 0
|
||
type replicated
|
||
step take default
|
||
step chooseleaf firstn 0 type rack
|
||
step emit
|
||
}
|
||
```
|
||
|
||
把 root `default` reclassify 为 class `hdd` 后:
|
||
|
||
```text
|
||
rule replicated_rule {
|
||
id 0
|
||
type replicated
|
||
step take default class hdd
|
||
step chooseleaf firstn 0 type rack
|
||
step emit
|
||
}
|
||
```
|
||
|
||
##### 2. `--set-subtree-class <bucket-name> <device-class>`
|
||
|
||
- 把以 *bucket-name* 为根的子树中的**每个 device** 都标记上指定的 device class;
|
||
- **通常与 `--reclassify-root` 配合使用**,以确保该 root 下所有 device 都被标上正确的 class;
|
||
- **但**:某些情况下,其中一些 device 正确地属于另一个 class,**不能被重新标记**。此时**应省略 `--set-subtree-class`**;
|
||
- 代价:重映射过程**不会是完美的**(原 rule 会影响多个 class 的设备,而调整后的 rule 只映射到指定 class 的设备)。但**当 outlier 设备不多时,产生的数据移动量通常在可容忍范围内**。
|
||
|
||
##### 3. `--reclassify-bucket <match-pattern> <device-class> <default-parent>`
|
||
|
||
用于**把一套平行的、按类型区分的层级合并进正常层级**。
|
||
|
||
**典型的老式 map**(原文示例):
|
||
|
||
```text
|
||
host node1 {
|
||
id -2 # do not change unnecessarily
|
||
# weight 109.152
|
||
alg straw2
|
||
hash 0 # rjenkins1
|
||
item osd.0 weight 9.096
|
||
item osd.1 weight 9.096
|
||
item osd.2 weight 9.096
|
||
item osd.3 weight 9.096
|
||
item osd.4 weight 9.096
|
||
item osd.5 weight 9.096
|
||
...
|
||
}
|
||
|
||
host node1-ssd {
|
||
id -10 # do not change unnecessarily
|
||
# weight 2.000
|
||
alg straw2
|
||
hash 0 # rjenkins1
|
||
item osd.80 weight 2.000
|
||
...
|
||
}
|
||
|
||
root default {
|
||
id -1 # do not change unnecessarily
|
||
alg straw2
|
||
hash 0 # rjenkins1
|
||
item node1 weight 110.967
|
||
...
|
||
}
|
||
|
||
root ssd {
|
||
id -18 # do not change unnecessarily
|
||
# weight 16.000
|
||
alg straw2
|
||
hash 0 # rjenkins1
|
||
item node1-ssd weight 2.000
|
||
...
|
||
}
|
||
```
|
||
|
||
**pattern 语法**:`%suffix` 或 `prefix%`(`%` 是通配符)。上例用 `%-ssd`。
|
||
|
||
**行为**:
|
||
|
||
- 对**每个匹配的 bucket**,`%` 通配符对应的**名字剩余部分**指定 **base bucket**;
|
||
- 匹配 bucket 中的**所有 device 都被打上指定 device class,然后被移到 base bucket**;
|
||
- 若 base bucket 不存在(例如 `node12-ssd` 存在但 `node12` 不存在),**则创建它,并挂到指定的 *default parent* bucket 下**;
|
||
- **每种情况下都会小心地为新的 shadow bucket 保留旧的 bucket ID,以避免数据移动**;
|
||
- 任何带 `take` 步骤引用了旧 bucket 的 rule 都会相应调整。
|
||
|
||
##### 4. `--reclassify-bucket <bucket-name> <device-class> <base-bucket>`(无通配符形式)
|
||
|
||
同一个命令**不带通配符**时用于映射单个 bucket。例如上例中要把 `ssd` bucket 映射到 `default` bucket:
|
||
|
||
```bash
|
||
ceph osd getcrushmap -o original
|
||
crushtool -i original --reclassify \
|
||
--set-subtree-class default hdd \
|
||
--reclassify-root default hdd \
|
||
--reclassify-bucket %-ssd ssd default \
|
||
--reclassify-bucket ssd ssd default \
|
||
-o adjusted
|
||
```
|
||
|
||
#### `--compare` 校验
|
||
|
||
`--compare` 标志用于**确认上述转换是否正确**:它用**大量输入样本**测试 CRUSH map,并检查输出是否为预期结果。控制这些输入的选项与 `--test` 命令相同。
|
||
|
||
```bash
|
||
crushtool -i original --compare adjusted
|
||
```
|
||
|
||
输出示例:
|
||
|
||
```text
|
||
rule 0 had 0/10240 mismatched mappings (0)
|
||
rule 1 had 0/10240 mismatched mappings (0)
|
||
maps appear equivalent
|
||
```
|
||
|
||
若发现差异,**括号中会报告被重新映射的输入的比例**。
|
||
|
||
确认无误后注入集群:
|
||
|
||
```bash
|
||
ceph osd setcrushmap -i adjusted
|
||
```
|
||
|
||
### 3.8 场景七:手工调整 CRUSH tunables
|
||
|
||
前提:**已确认所有客户端都在运行较新的代码**。
|
||
|
||
```bash
|
||
# 1. 取出最新 CRUSH map
|
||
ceph osd getcrushmap -o /tmp/crush
|
||
|
||
# 2. 调整 tunables(下面这组值是官方测试中"对大集群和小集群都表现最佳"的值)
|
||
# 注意:需要 --enable-unsafe-tunables,且必须 EXTREME CARE
|
||
crushtool -i /tmp/crush --set-choose-local-tries 0 --set-choose-local-fallback-tries 0 --set-choose-total-tries 50 -o /tmp/crush.new
|
||
|
||
# 3. 重新注入
|
||
ceph osd setcrushmap -i /tmp/crush.new
|
||
```
|
||
|
||
> ⚠️ 原文明确要求:该过程**必须**指定 `--enable-unsafe-tunables` 标志,**Use this option with extreme care**。
|
||
|
||
**恢复 legacy 值**:
|
||
|
||
```bash
|
||
crushtool -i /tmp/crush --set-choose-local-tries 2 --set-choose-local-fallback-tries 5 --set-choose-total-tries 19 --set-chooseleaf-descend-once 0 --set-chooseleaf-vary-r 0 -o /tmp/crush.legacy
|
||
```
|
||
|
||
> 同样需要 `--enable-unsafe-tunables`。⚠️ **回退到 legacy 值后运行老版本 `ceph-osd` 守护进程要小心,因为 feature bit 的执行并不完美(not perfectly enforced)**。
|
||
|
||
> 💡 补充:注意 3.8 中的 legacy 值(`choose_total_tries=19`、`choose_local_tries=2`、`choose_local_fallback_tries=5`、`chooseleaf_descend_once=0`、`chooseleaf_vary_r=0`)与 2.13 节表格中的 legacy 值完全一致,可交叉验证。
|
||
|
||
> 引自:[Manually editing the CRUSH Map](https://docs.ceph.com/en/tentacle/rados/operations/crush-map-edits/)
|
||
|
||
---
|
||
|
||
## 四、Placement Group(归置组)
|
||
|
||
### 4.1 PG 的三个基础事实
|
||
|
||
1. **PG 是 pool 的子集**,负责把对象**成组地**放进 OSD;
|
||
2. Ceph **以 PG 粒度**管理内部数据,这比按单个 RADOS object 管理**扩展性更好**;
|
||
3. **PG 数量更多的集群更均衡**(原文举例:每 OSD 150 个 PG 优于每 OSD 更少 PG 的同等集群)。
|
||
|
||
### 4.2 pg_num 与 pgp_num
|
||
|
||
| 概念 | 含义 |
|
||
|---|---|
|
||
| `pg_num` | pool 中 PG 的**总数** |
|
||
| `pgp_num` | **用于计算数据放置的有效 PG 数量**("the effective number of PGs to use when calculating data placement") |
|
||
|
||
**Nautilus 起的关键行为变化**:
|
||
|
||
> Since the Nautilus release, Ceph **automatically and incrementally steps `pgp_num`** for a pool whenever `pg_num` is changed, either by the PG autoscaler or manually. Admins generally do not need to touch `pgp_num` directly, but can monitor progress with `watch ceph osd pool ls detail`.
|
||
|
||
- 改 `pg_num` 时,`pgp_num` 会**被缓慢地步进(stepped slowly)**,这样**分裂或合并 PG 的开销会随时间摊销**,从而最小化性能影响;
|
||
- **增大 `pg_num` 会分裂该 pool 中的一些 PG,但数据不会通过 backfill 迁移到新 PG,直到该 pool 的 `pgp_num` 被增大**;
|
||
- `pgp_num` **应该等于** `pg_num`;
|
||
- 手动设置 `pgp_num` 是**可能的,但很少合适**("It is possible but rarely appropriate to manually set the `pgp_num` parameter");
|
||
- 当 `pg_autoscaler` 未被使用时(Nautilus 起),`pgp_num` 会**自动步进以匹配 `pg_num`**。这个过程表现为**一段时间的 PG remapping 和 backfill**,这是**预期行为**。
|
||
|
||
```bash
|
||
ceph osd pool set {pool-name} pg_num {pg_num}
|
||
ceph osd pool set {pool-name} pgp_num {pgp_num} # 很少需要
|
||
```
|
||
|
||
### 4.3 自动扩缩容(pg-autoscaling)
|
||
|
||
#### 是什么
|
||
|
||
PG 是 Ceph 分发数据的**内部实现细节**。Autoscaling 提供了一种管理 PG(尤其是各 pool 中 PG 数量)的方式。启用后,集群会**依据观测到和预期的 pool 利用率**,对每个 pool 的 PG 数(`pgp_num`)**给出建议或自动调整**。
|
||
|
||
#### 三种模式
|
||
|
||
| `pg_autoscale_mode` | 含义 |
|
||
|---|---|
|
||
| `off` | 关闭该 pool 的自动扩缩容。由管理员为该 pool 选择适当的 `pg_num` |
|
||
| `on` | 启用对该 pool PG 数量的**自动调整** |
|
||
| `warn` | 当 PG 数量需要调整时**抛出健康检查(health checks)** |
|
||
|
||
```bash
|
||
# 设置已有 pool 的模式
|
||
ceph osd pool set <pool-name> pg_autoscale_mode <mode>
|
||
|
||
# 例
|
||
ceph osd pool set foo pg_autoscale_mode on
|
||
```
|
||
|
||
**central config 选项**:还有一个中心配置 `pg_autoscale_mode` 选项,**控制集群初始搭建之后新创建 pool 的 autoscale 模式**:
|
||
|
||
```bash
|
||
ceph config set global osd_pool_default_pg_autoscale_mode <mode>
|
||
```
|
||
|
||
#### 全局开关 `noautoscale`
|
||
|
||
```bash
|
||
ceph osd pool set noautoscale # 对所有 pool 关闭 autoscaler
|
||
ceph osd pool unset noautoscale # 恢复
|
||
ceph osd pool get noautoscale # 查看当前值
|
||
```
|
||
|
||
> **默认 `noautoscale` flag 是 `off`**(即 autoscaler 全局可用)。
|
||
|
||
#### 查看扩缩容建议
|
||
|
||
```bash
|
||
ceph osd pool autoscale-status
|
||
```
|
||
|
||
输出示例:
|
||
|
||
```text
|
||
POOL SIZE TARGET SIZE RATE RAW CAPACITY RATIO TARGET RATIO EFFECTIVE RATIO BIAS PG_NUM NEW PG_NUM AUTOSCALE BULK
|
||
a 12900M 3.0 82431M 0.4695 8 128 warn True
|
||
c 0 3.0 82431M 0.0000 0.2000 0.9884 1.0 1 64 warn True
|
||
b 0 953.6M 3.0 82431M 0.0347 8 warn False
|
||
```
|
||
|
||
**各列含义(逐列对照原文)**:
|
||
|
||
| 列 | 含义 |
|
||
|---|---|
|
||
| **POOL** | pool 名 |
|
||
| **SIZE** | 该 pool 中已存的数据量 |
|
||
| **TARGET SIZE** | (若存在)管理员指定的**预期**存入该 pool 的数据量。系统计算时取 **SIZE 与 TARGET SIZE 中较大者** |
|
||
| **RATE** | 该 pool 的**空间放大因子**,表示给定用户数据量会消耗多少原始存储容量。例:三副本 pool 显示 `3.0`;`k=4 m=2` 的纠删码 pool 为 `1.5` |
|
||
| **RAW CAPACITY** | 该 pool 可用 OSD 上的**原始存储总容量**。注意**多数情况下该容量被多个 pool 共享** |
|
||
| **RATIO** | 该 pool 消耗的存储 / 总原始存储容量。即 `(SIZE * RATE) / RAW CAPACITY`,可理解为**满度百分比** |
|
||
| **TARGET RATIO** | (若存在)该 pool 的预期存储相对于**其它所有设置了 target ratio 的 pool** 的预期存储之比。若 `target_size_bytes` 与 `target_size_ratio` 都指定了,**`target_size_ratio` 优先**。⚠️ **当 BIAS 值不等于 1 时(尤其 CephFS metadata 和 RGW index pool),最好不要动 target ratio**,因为同时调整两者会因 double-dipping 导致不恰当的 `pg_num` |
|
||
| **EFFECTIVE RATIO** | 对 target ratio 做两处调整后的结果:① 减去预期会被设置了 target size 的 pool 使用的容量;② 在设置了 target ratio 的 pool 之间**归一化** target ratio,使它们 collectively 对准集群容量(例:4 个 `target_ratio=1.0` 的 pool,每个 effective ratio = `0.25`)。**系统使用 target ratio 与 effective ratio 中较大者** |
|
||
| **BIAS** | 作为**乘数**手工调整 pool 的 PG,依据是"该 pool 预期应该有多少 PG"的先验信息。对**主要把数据存在 omap 而非 RADOS object 中的 pool**(尤其 RGW index 和 CephFS / RBD EC metadata pool)很重要。**当 pool 设了非 1.0 的 bias 时,建议不要再设 target ratio** |
|
||
| **PG_NUM** | 该 pool 当前的 PG 数;或若正在变更,则为**目标值** |
|
||
| **NEW PG_NUM** | (若存在)系统建议的 `pg_num`。**始终是 2 的幂**,且**仅在建议值与当前值相差超过扩缩容阈值时出现** |
|
||
| **AUTOSCALE** | 该 pool 的 `pg_autoscale_mode`(`on` / `off` / `warn`) |
|
||
| **BULK** | 该 pool 是否为 `bulk`(`True` / `False`) |
|
||
|
||
#### 扩缩容阈值(threshold)
|
||
|
||
- **默认阈值是配置因子 `3`**;
|
||
- **缩容时只用这个配置因子**;
|
||
- **扩容时阈值会动态降低**:
|
||
- 建议 NEW PG_NUM 为 **512 或 1024** → 阈值设为 **1.0**
|
||
- 建议 NEW PG_NUM 为 **2048** → 阈值设为 **2.0**
|
||
|
||
```bash
|
||
ceph osd pool set threshold 2.0 # 修改该倍数(示例改为 2)
|
||
ceph osd pool get threshold # 查看当前值
|
||
```
|
||
|
||
#### 每 OSD 目标 PG 数
|
||
|
||
```bash
|
||
ceph config set global mon_target_pg_per_osd 100
|
||
```
|
||
|
||
| 参数 | 默认值 | 官方建议 |
|
||
|---|---|---|
|
||
| `mon_target_pg_per_osd` | **100** | **除极小型部署外,推荐 200**。**超过 500 可能导致过多的 peering 流量和 RAM 占用** |
|
||
|
||
#### autoscaler 的按子树分析
|
||
|
||
- autoscaler **按 per-subtree 分析 pool 并调整**;
|
||
- 因为每个 pool 可能映射到不同的 CRUSH rule,而每条 rule 又可能把数据分布到**不同且可能重叠的设备集**上,**Ceph 会独立地考虑 CRUSH 层级中每个子树的利用率**;
|
||
- 例:一个映射到 `ssd` class OSD 的 pool 和一个映射到 `hdd` class OSD 的 pool,会各自根据这两种设备类型的 OSD 数量算出各自的 PG 数。
|
||
|
||
⚠️ **跨 root 的 pool(spanning pool)**:
|
||
|
||
- 若一个 pool 使用了**两个及以上 CRUSH root** 下的 OSD(例如同时含 `ssd` 和 `hdd` 设备的 shadow tree),autoscaler 会**在 manager log 中向用户发出 warning**,指出 pool 名和互相重叠的 root 集合;
|
||
- **autoscaler 不会扩缩任何 root 重叠的 pool**(该条件会导致扩缩过程出问题);
|
||
- 建议**把每个 pool 约束到只属于一个 root(即一个设备 OSD class)**,以消除告警并确保扩缩成功。
|
||
|
||
> ⚠️ **排障提示(原文)**:如果 `ceph osd pool autoscale-status` **完全没有输出**,很可能是**至少有一个 pool 跨了多个 CRUSH root**。典型场景:新部署时在 `default` CRUSH root 上自动创建了 `.mgr` pool,之后创建的 pool 又用了约束到特定 shadow CRUSH tree 的 rule。例如:RBD metadata pool 约束到 `deviceclass = ssd`、RBD data pool 约束到 `deviceclass = hdd`,就会遇到这个问题。
|
||
>
|
||
> **修法**:把跨 root 的 pool 也约束到单一 device class。上例中大概率存在一条 `replicated-ssd` CRUSH rule,可以把 `.mgr` pool 约束到 `ssd` 设备:
|
||
>
|
||
> ```bash
|
||
> ceph osd pool set .mgr crush_rule replicated-ssd
|
||
> ```
|
||
>
|
||
> 这会产生**少量 backfill**,但通常不具破坏性且很快完成。
|
||
|
||
#### `bulk` flag
|
||
|
||
| pool 是否 bulk | autoscaler 的起始行为 |
|
||
|---|---|
|
||
| **是**(`bulk`) | 以**完整配额的 PG 数启动**,之后**仅当该 pool 的用量比率不均时才缩减** PG 数 |
|
||
| **否** | 以**最小 PG 数启动**,之后**仅当该 pool 用量增加时才创建更多 PG** |
|
||
|
||
- `bulk` pool 的语义:**预期会很大,因此初始就应有大量 PG,以免性能受损**;
|
||
- 非 `bulk` pool:**预期很小**(例如 `.mgr` pool 或 meta pool);
|
||
- ⚠️ 原文警告:**This flag should be used with care, as it may not have the results one would think.**(该 flag 的结果可能与直觉不符,谨慎使用)
|
||
|
||
```bash
|
||
# 创建时打上 bulk
|
||
ceph osd pool create <pool-name> --bulk
|
||
|
||
# 设置/取消已有 pool 的 bulk
|
||
ceph osd pool set <pool-name> bulk <true/false/1/0>
|
||
|
||
# 查看
|
||
ceph osd pool get <pool-name> bulk
|
||
```
|
||
|
||
#### 指定 pool 的预期大小
|
||
|
||
动机:集群或 pool 刚创建时只消耗总容量的很小一部分,系统据此认为只需要很少 PG。**但管理员往往知道哪些 pool 长期会吃掉大部分容量**。把这一信息告诉 Ceph,就能**从一开始使用更合适的 PG 数**,避免后续改 `pg_num` 及随之而来的数据搬迁开销;同时由于 PG 会被放置到所有可用 OSD 上,性能和数据均匀性也受益。
|
||
|
||
**两种指定方式**:
|
||
|
||
```bash
|
||
# 方式 1:绝对大小(字节)
|
||
ceph osd pool set mypool target_size_bytes 100T
|
||
|
||
# 方式 2:相对于其他设置了 target_size_ratio 的 pool 的权重
|
||
ceph osd pool set mypool target_size_ratio 1.0
|
||
```
|
||
|
||
| 场景 | 结果 |
|
||
|---|---|
|
||
| `mypool` 是集群中唯一 pool,ratio=1.0 | 预期使用 100% 集群总容量 |
|
||
| 集群中还有第二个 pool 也设了 ratio=1.0 | 两个 pool 各预期使用 50% |
|
||
|
||
**创建时指定**:
|
||
|
||
```bash
|
||
ceph osd pool create ... --target-size-bytes <bytes>
|
||
ceph osd pool create ... --target-size-ratio <ratio>
|
||
```
|
||
|
||
**相关健康检查**:
|
||
|
||
| 条件 | 健康检查 |
|
||
|---|---|
|
||
| 指定的 target-size 值不可能成立(例如容量大于集群总量) | `POOL_TARGET_SIZE_BYTES_OVERCOMMITTED` |
|
||
| 同时指定了 `target_size_ratio` 和 `target_size_bytes` | `POOL_HAS_TARGET_SIZE_BYTES_AND_RATIO`(此时 **`target_size_bytes` 会被忽略**,系统计算使用 `target_size_ratio`) |
|
||
|
||
> 官方建议:**大多数情况下不要在同一个 pool 上同时设置非 1.0 的 bias 和 target ratio**。metadata / omap 密集型 pool 用**更高的 bias**;RADOS 数据密集型 pool 用 **target ratio**。
|
||
|
||
#### 限定 PG 数量的上下界
|
||
|
||
```bash
|
||
# 最小值:Ceph 不会(也不会建议你)把 pg_num 降到该值以下。
|
||
# 作用:即使 pool 基本为空,也保证客户端 I/O 的并行度下界。
|
||
ceph osd pool set <pool-name> pg_num_min <num>
|
||
|
||
# 最大值:Ceph 不会(也不会建议你)把 pg_num 增到该值以上。
|
||
ceph osd pool set <pool-name> pg_num_max <num>
|
||
```
|
||
|
||
创建时指定:
|
||
|
||
```bash
|
||
ceph osd pool create ... --pg-num-min <num>
|
||
ceph osd pool create ... --pg-num-max <num>
|
||
```
|
||
|
||
#### 预选 pg_num
|
||
|
||
```bash
|
||
ceph osd pool create {pool-name} [pg_num]
|
||
```
|
||
|
||
- 若不指定 `pg_num`,集群会用 **PG autoscaler** 依据数据量自动配置;
|
||
- ⚠️ **但"是否在创建时指定 pg_num"并不影响该参数之后是否会被自动调优** —— 自动调优由 `ceph osd pool set {pool-name} pg_autoscale_mode (on|off|warn)` 单独控制。
|
||
|
||
**关于每 OSD 的 PG 副本数目标(preselection 段落原文)**:
|
||
|
||
| 条件 | 每 OSD 的 PG 副本数 |
|
||
|---|---|
|
||
| **没有 balancer** 时,对大多数集群**建议(但非默认)**的目标 | 200 |
|
||
| **开启 balancer** 且用默认值 | 初始结果大致 **50 ~ 70** |
|
||
|
||
> 这是 `ceph df` 输出中 **`PGS` 列**报告的数值,**注意它不是"集群总 PG 数 / OSD 数"**。
|
||
|
||
autoscaler 试图满足两个条件:
|
||
|
||
1. **每 OSD 的 PG 副本数应与该 pool 中的数据量成比例**;
|
||
2. **默认情况下每个 pool 应有 50~100 个 PG**(这已把每个 PG 副本跨 OSD 的复制开销或纠删码 fan-out 考虑在内)。
|
||
|
||
### 4.4 影响 pg_num 取舍的三大因素
|
||
|
||
#### (1)Data durability(数据持久性)
|
||
|
||
**PG 数对 durability 几乎无影响**:
|
||
|
||
> In such a cluster, the number of PGs has almost no effect on data durability. Whether there are 128 PGs per OSD or 8192 PGs per OSD, the recovery will be no slower or faster.
|
||
|
||
**但 OSD 数量会显著影响恢复速度**(原文用 10 OSD / 20 OSD / 40 OSD / 200 OSD 的递进例子说明):
|
||
|
||
| 集群规模 | 每 OSD 承载的 PG 数 | 恢复特征 |
|
||
|---|---|---|
|
||
| 10 OSD,512 PG,三副本 | `(512 * 3) / 10 ≈ 150` | 一个 OSD 故障 → 约 150 个 PG 触发恢复,均摊到 9 个剩余 OSD |
|
||
| 20 OSD | ≈ 75 | 19 个 OSD 参与,每个只需复制 50 GB(原 100 GB)→ **若网络原本是瓶颈,恢复快一倍** |
|
||
| 40 OSD | ≈ 38 | 恢复更快(除非遇到别的瓶颈) |
|
||
| **200 OSD** | **≈ 7** | ⚠️ 恢复只能在最多 `≈ 21 = (7 × 3)` 个相关 OSD 间进行,**恢复反而比 40 OSD 时更慢 → 此时应该增加 PG 数** |
|
||
|
||
**级联故障导致数据丢失的推演**(原文完整场景):
|
||
|
||
1. OSD 故障,它承载的所有对象副本丢失。该 PG 中每个对象的副本数从 3 骤降到 2;
|
||
2. Ceph 启动恢复,选一个新 OSD 重建每对象的第三份副本;
|
||
3. 同一 PG 中的**另一个 OSD 在新 OSD 完全填满前又故障** → 部分对象只剩一份;
|
||
4. Ceph 再选一个 OSD 继续复制;
|
||
5. **同一 PG 中第三个 OSD 在恢复完成前又故障**。如果这个 OSD 恰好持有某对象仅存的那份副本 → **该对象永久丢失**。
|
||
|
||
> **这正是 `size=2` 的副本池和 `m=1` 的 EC 池"有风险、通常不推荐"的一个微妙原因。**
|
||
|
||
**概率量化**(原文,假设恢复期间丢失一个 OSD 的概率为 0.0001%):
|
||
|
||
| 集群 | 坏 1 个 OSD 后只剩一份副本的 PG 数 | 坏 3 个 OSD 时数据丢失的概率 |
|
||
|---|---|---|
|
||
| 10 OSD | ≈ 17(≈ 150/9) | ≈ `17 × 10 × 0.0001%` |
|
||
| 20 OSD | ≈ 4(75/19) | ≈ `4 × 20 × 0.0001%` |
|
||
|
||
**结论(原文)**:
|
||
|
||
> In summary, **the greater the number of OSDs, the faster the recovery and the lower the risk of permanently losing a PG due to cascading failures**. As far as data durability is concerned, **in a cluster with fewer than 50 OSDs, it doesn't much matter whether there are 512 or 4096 PGs.**
|
||
|
||
> 补充注:新加入集群的 OSD **可能需要很长时间才被填满分配给它的 PG**。但这**不会导致对象降级或影响数据持久性** —— 因为 Ceph 会**先往新 PG 里填数据,再从旧 PG 里删除**。
|
||
|
||
#### (2)Object distribution within a pool(池内对象分布)
|
||
|
||
- 理想条件下对象在 PG 间均匀分布;
|
||
- **但 CRUSH 只计算每个对象的 PG,它并不知道该 PG 关联的每个 OSD 上已存了多少数据** → **PG 数与 OSD 数的比率对数据分布有显著影响**;
|
||
- 极端例子:三副本池、10 个 OSD、只有 **1 个 PG** → **只会用到 3 个 OSD**(CRUSH 别无选择)。PG 越多,对象越可能均匀分布;
|
||
- **经验法则(原文)**:**只要 PG 数比 OSD 数多一到两个数量级,分布就可能是均匀的**。举例:**3 OSD → 256 PG;10 OSD → 512 PG;10 OSD → 1024 PG**;
|
||
- **分布不均的其它来源**:CRUSH **不考虑 RADOS object 的大小**。少数超大对象就会造成倾斜。原文例子:100 万个 4 KB 对象共 4 GB,均匀分布在 10 OSD 的 1024 个 PG 上 → 每个 OSD 400 MB。此时**再往里加一个 400 MB 的对象**,承载它的那个 PG 所对应的 3 个 OSD 会变成 400+400=800 MB,而其余 7 个 OSD 仍是 400 MB。
|
||
|
||
#### (3)Memory, CPU and network usage(资源占用)
|
||
|
||
- **集群中每个 PG 都会给 OSD 和 Monitor 带来内存、网络和 CPU 开销**;
|
||
- 这些需求**必须随时被满足,并且在恢复期间会增加**;
|
||
- **PG 被发明出来的主要原因之一就是通过把 RADOS 对象聚合成可管理大小的集合来降低这类开销**;
|
||
- → **限制 PG 数量能节省大量资源**。
|
||
|
||
#### 综合建议(原文)
|
||
|
||
> Performance and even data distribution across OSDs weigh in favor of a higher number of PGs. Conserving CPU resources and minimizing memory usage weigh in favor of a lower number of PGs. The latter was more of a concern before Filestore OSDs were deprecated, so **most modern clusters with BlueStore OSDs can favor the former by configuring a value of `200`-`250` for `mon_target_pg_per_osd` and `500` for `mon_max_pg_per_osd`.**
|
||
|
||
> ⚠️ 注意:`mon_max_pg_per_osd` **只是一个 failsafe(保险丝),它本身不参与 `pg_num` 的计算**。
|
||
|
||
### 4.5 手工计算 PG 数量
|
||
|
||
> 原文注:**It is rarely necessary to do the math in this section by hand.** 建议改用 `ceph osd pool autoscale-status` 配合 `target_size_bytes` / `target_size_ratio`。
|
||
|
||
**推荐值**:
|
||
|
||
| 条件 | 建议 |
|
||
|---|---|
|
||
| OSD > 50 | **每 OSD 约 100~250 个 PG 副本**,以平衡资源占用、数据持久性和数据分布 |
|
||
| OSD < 50 | 遵循 preselection 段落(4.3 末)的指导 |
|
||
|
||
**单 pool 基线公式**:
|
||
|
||
```text
|
||
Total PGs = (OSDs × 100) / pool size
|
||
```
|
||
|
||
其中 **pool size**:
|
||
|
||
- 副本池 = **副本数**;
|
||
- 纠删码池 = **K+M 之和**(可用 `ceph osd erasure-code-profile get` 取到)。
|
||
|
||
**三条后续规则**:
|
||
|
||
1. 结果应**向上取整到最近的 2 的幂**;
|
||
2. **每个 pool 的 `pg_num` 都应是 2 的幂** —— 其它值很可能造成跨 OSD 的数据分布不均;
|
||
3. **只在可行且可取时才把 `pg_num` 提到下一个更高的 2 的幂**。
|
||
⚠️ **2 的幂规则是 per-pool 的**:**既没有必要、也不容易**让所有 pool 的 `pg_num` 之和对齐到 2 的幂。
|
||
|
||
**官方算例**:
|
||
|
||
```text
|
||
200 OSD,单 pool,3 副本:
|
||
(200 × 100) / 3 = 6667 → 向上取到最近的 2 的幂:8192
|
||
```
|
||
|
||
**多 pool 时要平衡"每 pool 的 PG 数"与"每 OSD 的 PG 数"**:
|
||
|
||
| 例子 | 结果 |
|
||
|---|---|
|
||
| 10 个 pool,每 pool 512 PG,10 个 OSD | 5120 PG / 10 OSD = 每 OSD 512 个 PG → **不会消耗过多资源** |
|
||
| 1000 个 pool,每 pool 512 PG,10 个 OSD | 每 OSD 要处理 ≈ 50,000 个 PG → **需要显著更多的资源,peering 也会显著更慢** |
|
||
|
||
### 4.6 PG Calc 工具(pgcalc)用法与公式
|
||
|
||
PG Calc 是官方提供的**交互式 PG 计算器**(`rados/operations/pgcalc`,页面上是一个 JS 小工具)。
|
||
|
||
**使用步骤(原文 Instructions)**:
|
||
|
||
1. 通过阅读下方的 Key 确认你理解各个字段;
|
||
2. 从下拉菜单选择一个 **"Ceph Use Case"**;
|
||
3. 调整下方**绿色**底纹字段的值(提示:**表头可点击,用于整列改值**);
|
||
4. 你会看到 **Suggested PG Count** 随输入更新;
|
||
5. 点 **"Add Pool"** 按钮新增一行新 pool;
|
||
6. 点垃圾桶图标删除某个特定 pool;
|
||
7. 关于所用逻辑和重要细节,见表格下方区域;
|
||
8. 所有值调整完毕后,点 **"Generate Commands"** 按钮获取 pool 创建命令。
|
||
|
||
**核心公式(原文 "Logic behind Suggested PG Count")**:
|
||
|
||
```text
|
||
( Target PGs per OSD ) × ( OSD # ) × ( %Data )
|
||
─────────────────────────────────────────────
|
||
( Size )
|
||
```
|
||
|
||
**后处理三步**:
|
||
|
||
1. **下限保护**:若上式结果 **小于 `( OSD# ) / ( Size )`**,则把结果**提升到 `( OSD# ) / ( Size )`**。
|
||
> 目的:**确保每个 OSD 对每个 pool 至少分到一个 Primary 或 Secondary PG**,从而实现均匀的负载/数据分布。
|
||
2. **取 2 的幂**:输出值**四舍五入到最近的 2 的幂**。
|
||
> Tip:最近的 2 的幂为 **CRUSH 算法的效率带来边际改善**。
|
||
3. **上调保护**:若"最近的 2 的幂"**比原值低了超过 25%**,则**改用再高一级的 2 的幂**。
|
||
|
||
**目标(Objective)**:这套计算和 Key 中的目标区间的目的是 —— **确保有足够的 PG 让数据在整个集群中均匀分布,同时又不让 PG-per-OSD 比率高到在 Recovery / Backfill 时造成问题**。
|
||
|
||
**空 pool / 非活跃 pool 的影响(原文)**:
|
||
|
||
- 空的或非活跃的 pool **不应被视为对集群均匀分布有益**;
|
||
- **但是**,这些空/非活跃 pool 关联的 PG **仍然消耗内存和 CPU 开销**。
|
||
|
||
### 4.7 PG 相关运维命令
|
||
|
||
```bash
|
||
# 查看某 pool 的 PG 数
|
||
ceph osd pool get {pool-name} pg_num
|
||
|
||
# 查看集群 PG 明细(格式:plain(默认)/ json)
|
||
ceph pg dump [--format {format}]
|
||
|
||
# 查看处于指定状态的"卡住"的 PG 的统计
|
||
ceph pg dump_stuck inactive|unclean|stale|undersized|degraded [--format <format>] [-t|--threshold <seconds>]
|
||
```
|
||
|
||
`dump_stuck` 的四类:
|
||
|
||
| 类别 | 含义 |
|
||
|---|---|
|
||
| **Inactive** | PG 无法处理读写,因为它们在等待**足够多的持有最新数据的 OSD** 变为 `up` 且 `in` |
|
||
| **Undersized** | PG 中包含的对象**尚未被复制期望的次数**。正常情况下可认为这些 PG 正在恢复 |
|
||
| **Stale** | PG 处于未知状态 —— 承载它们的 OSD **已经有一段时间没向 monitor 集群上报了**(该时长由 `mon_osd_report_timeout` 决定) |
|
||
| **unclean** | ❗原文在 `dump_stuck` 的参数列表中列出了 `unclean`,但**未给出其定义**(见「文档未明确项」第 5 条) |
|
||
|
||
- 合法格式:`plain`(默认)和 `json`;
|
||
- **threshold**:定义 PG 卡住多少秒后才被纳入返回的统计,**默认 300**。
|
||
|
||
```bash
|
||
# 取某个 PG 的 PG map
|
||
ceph pg map {pg-id}
|
||
# 例
|
||
ceph pg map 1.6c
|
||
```
|
||
|
||
输出形如:
|
||
|
||
```text
|
||
osdmap e13 pg 1.6c (1.6c) -> up [1,0] acting [1,0]
|
||
```
|
||
|
||
```bash
|
||
# 查看某个 PG 的统计
|
||
ceph pg {pg-id} query
|
||
```
|
||
|
||
#### Scrub
|
||
|
||
```bash
|
||
# 高优先级、立即执行(不受星期/时间限制,不受 osd_max_scrubs 限制,不必等待副本的 scrub 资源)
|
||
ceph tell {pg-id} scrub
|
||
ceph tell {pg-id} deep-scrub
|
||
|
||
# 按"常规 scrub"的方式调度(受调度限制)
|
||
ceph tell {pg-id} schedule-scrub
|
||
ceph tell {pg-id} schedule-deep-scrub
|
||
|
||
# scrub 某个 pool 的所有 PG
|
||
ceph osd pool scrub {pool-name}
|
||
```
|
||
|
||
**Scrub 做了什么(原文)**:Ceph 检查 primary 和 replica OSD,并生成该 PG 中所有对象的目录。对每个对象,Ceph **比较该对象在所有实例(primary 和 replica OSD)中的副本以确保一致**。
|
||
|
||
- **shallow scrub**(第一种命令格式):**只比较对象元数据**;
|
||
- **deep scrub**(第二种命令格式):**还比较对象内容**;
|
||
- 若所有副本一致,还会做一次 **final semantic sweep**,确保**所有与快照相关的对象元数据一致**;
|
||
- **错误报告在日志中**。
|
||
|
||
#### 优先化 PG 的 backfill / recovery
|
||
|
||
```bash
|
||
# 指定 PG 优先 recovery / backfill
|
||
ceph pg force-recovery {pg-id} [{pg-id #2}] [{pg-id #3} ...]
|
||
ceph pg force-backfill {pg-id} [{pg-id #2}] [{pg-id #3} ...]
|
||
|
||
# 取消
|
||
ceph pg cancel-force-recovery {pg-id} [{pg-id #2}] [{pg-id #3} ...]
|
||
ceph pg cancel-force-backfill {pg-id} [{pg-id #2}] [{pg-id #3} ...]
|
||
|
||
# 让某个 pool 的所有 PG 优先
|
||
ceph osd pool force-recovery {pool-name}
|
||
ceph osd pool force-backfill {pool-name}
|
||
|
||
# 取消
|
||
ceph osd pool cancel-force-recovery {pool-name}
|
||
ceph osd pool cancel-force-backfill {pool-name}
|
||
```
|
||
|
||
**语义要点(原文)**:
|
||
|
||
- 这些命令让 Ceph **先于其他 PG** 处理指定 PG 的 recovery / backfill;
|
||
- **Prioritization 不会打断当前正在进行的 backfill 或 recovery**,而是把指定 PG **放到队列最前面**,使它们**下一个**被处理;
|
||
- 取消操作**只影响仍在排队中的 PG**,不影响正在恢复的 PG;
|
||
- **`force` flag 在 recovery 或 backfill 完成后会自动清除**。
|
||
|
||
> ⚠️ **官方警告**:这些命令**可能破坏 Ceph 内部优先级计算的顺序**,谨慎使用!
|
||
|
||
**更推荐的替代方案**:如果多个 pool 共享同一批底层 OSD,且某些 pool 的数据更重要,官方推荐使用:
|
||
|
||
```bash
|
||
ceph osd pool set {pool-name} recovery_priority {value}
|
||
```
|
||
|
||
| 用法 | 说明 |
|
||
|---|---|
|
||
| 20 个 pool 时 | 最重要的 pool 设 `20`,次重要设 `19`,依此类推 |
|
||
| 只关心 3 个重要 pool | 三个都设 `1`,其他 pool 不设 |
|
||
| 三个重要 pool 排序 | 分别设 `3`、`2`、`1` |
|
||
|
||
> ⚠️ **重要**:用 `recovery_priority` 时,**数值越大优先级越高**。例:优先级 `30` 的 pool 高于优先级 `15` 的 pool。
|
||
|
||
> 💡 补充:注意这与 `ceph osd pool set {pool-name} recovery_priority {value}` 在 pools.rst 中描述的取值范围 **`-10` 到 `10`**(默认 `0`)存在表述差异 —— placement-groups.rst 的例子用了 `20`/`19`/`30`/`15` 这类超出该范围的值。以 pools.rst 的范围说明为准,placement-groups.rst 的示例值应理解为示意。
|
||
|
||
#### 恢复丢失的 RADOS 对象
|
||
|
||
如果集群丢失了一个或多个 RADOS 对象,且你已决定放弃寻找:
|
||
|
||
```bash
|
||
ceph pg {pg-id} mark_unfound_lost revert|delete
|
||
```
|
||
|
||
**语义(原文)**:
|
||
|
||
- 该命令**只有一个受支持的选项:`revert`**;
|
||
- `revert` 会:若该 RADOS 对象**旧到有前一个版本**,则**回滚到前一个版本**;若**太新而没有前一版本**,则**彻底遗忘它**;
|
||
- 但命令签名中同时列出了 `delete`。
|
||
|
||
> ⚠️ **官方重要提示**:Use this feature with caution. It might confuse applications that expect the object(s) to exist.
|
||
|
||
### 4.8 Pool, PG and CRUSH 配置参考(pool-pg-config-ref)
|
||
|
||
#### 概述
|
||
|
||
**决定 CRUSH 算法给每个 pool 分配多少 PG 的变量值,存放在 monitor 集群的 central configuration database 中**。**容器化部署(`cephadm` 或 Rook)和非容器化部署都依赖** monitor 集群中这个中心配置数据库的值来为 pool 分配 PG。
|
||
|
||
```bash
|
||
# 查看
|
||
ceph config get osd osd_pool_default_pg_num
|
||
|
||
# 设置
|
||
ceph config set osd osd_pool_default_pg_num
|
||
```
|
||
|
||
> ⚠️ 原文给出的 `ceph config set osd osd_pool_default_pg_num` **后面没有写值**(源文本身如此,见「文档未明确项」第 6 条)。
|
||
|
||
#### 全部配置项(逐项列出)
|
||
|
||
> ⚠️ 本节 24 个配置项在源文中均为 `.. confval::` 指令,**rst 源文不包含 type / default / description 字段**(详见「阅读前须知」)。下表"含义"列取自源文散文;"💡"开头的行为本手册补充。
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 / 上下文 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `mon_max_pool_pg_num` | 文档未明确 | 64_K(= 65536 字节 = 64 KiB)(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 与 pool 的 `pg_num` 取值上界相关(`pg_num` 的 Valid Range 为 `0` 到 `mon_max_pool_pg_num`) | 💡 新建 pool 时若报 pg_num 超限,检查此项。Placement Groups 页另提到 BlueStore 集群可配 `mon_max_pg_per_osd` 到 `500`,注意这是**另一个**参数(见 4.4 节) |
|
||
| `mon_pg_stuck_threshold` | 文档未明确 | 1_min(= 60 秒)(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | 与"卡住的 PG"(stuck PG)判定阈值相关 | 💡 `ceph pg dump_stuck` 有自己的 `--threshold`(默认 300 秒),两者不同,勿混淆 |
|
||
| `mon_pg_warn_min_per_osd` | 文档未明确 | 0(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | 每 OSD 的 PG 数低于此值时可能触发告警 | 💡 与 `mon_target_pg_per_osd`(目标值)是一对:一个管下限告警,一个管目标 |
|
||
| `mon_pg_warn_min_objects` | 文档未明确 | 10000(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | PG 中对象数相关的告警下限 | 💡 只在对象数极少的小集群调优时关注 |
|
||
| `mon_pg_warn_min_pool_objects` | 文档未明确 | 1000(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | pool 中对象数相关的告警下限 | 💡 同上 |
|
||
| `mon_pg_check_down_all_threshold` | 文档未明确 | 0.5(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | 检查"全部 OSD down"场景的阈值 | 💡 大规模集群整机断电类场景相关 |
|
||
| `mon_pg_warn_max_object_skew` | 文档未明确 | 10(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | 对象数偏斜(skew)的告警上限 | 💡 与 4.4 节"少数超大对象造成分布倾斜"的现象对应 |
|
||
| `mon_delta_reset_interval` | 文档未明确 | 10(默认值取自 src/common/options/mgr.yaml.in @ v20.2.4) | monitor 侧 delta 统计的重置间隔 | 💡 一般不动 |
|
||
| `osd_crush_chooseleaf_type` | 文档未明确 | 1(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | CRUSH `chooseleaf` 默认使用的 bucket 类型(即默认故障域) | 💡 与 `ceph osd crush rule create-replicated` 的 `failure-domain-type` 语义对应;现代集群建议显式建 rule 而不是依赖此项 |
|
||
| `osd_crush_initial_weight` | 文档未明确 | -1(默认值取自 src/common/options/osd.yaml.in @ v20.2.4) | OSD 加入 CRUSH map 时的初始权重 | 💡 若用 `ceph-volume` 部署,权重会按设备容量自动计算;自定义容量映射时才需要 |
|
||
| `osd_pool_default_crush_rule` | 文档未明确 | -1(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建 `replicated` pool 默认使用的 CRUSH rule(pools.rst 明确:该 rule **必须存在**) | 💡 若建好了 `replicated-ssd` 之类的 rule 并希望成为默认,改此项 |
|
||
| `osd_pool_erasure_code_stripe_unit` | 文档未明确 | 4_K(= 4096 字节 = 4 KiB)(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | EC profile 未显式指定 `stripe_unit` 时,创建 pool 所采用的默认 stripe unit | 💡 **Tentacle EC optimizations 强烈建议提升到至少 16K**(见 7.8 节);此项是全局默认,`stripe_unit` 是 per-profile |
|
||
| `osd_pool_default_size` | 文档未明确 | 3(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建副本 pool 的默认副本数 | 💡 pools.rst 提到 Ceph 默认维护 `3` 个副本(`ceph osd dump \ | grep 'replicated size'` 的描述),但**该值是否等于本项默认值,源文未明确断言** |
|
||
| `osd_pool_default_min_size` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建 pool 的默认 `min_size` | 💡 EC 池的 `min_size` 应 **> K**(pools.rst 明确,见 6.6 节) |
|
||
| `osd_pool_default_pg_num` | 文档未明确 | 32(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建 pool 未显式指定时使用的 PG 数。pools.rst 另给出 `ceph osd pool create` 的 `{pg-num}` 参数 **Default: 32**,并强调 "**The default value is NOT suitable for most systems**" | 💡 **强烈建议不要依赖默认值**。要么建 pool 时显式给 `pg_num`,要么启用 autoscaler |
|
||
| `osd_pool_default_pgp_num` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建 pool 未显式指定时使用的 `pgp_num`。pools.rst 给出 `{pgp-num}` 参数 **Default: 32** | 💡 Nautilus 起通常不需要关心(会自动步进匹配 `pg_num`) |
|
||
| `osd_pool_default_pg_autoscale_mode` | 文档未明确 | `on`(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **控制集群初始搭建之后新建 pool 的 autoscale 模式**(`on`/`off`/`warn`) | 💡 新集群建议设为 `on` |
|
||
| `osd_pool_default_flags` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 新建 pool 默认带上的 flag 集合 | 💡 一般不动 |
|
||
| `osd_max_pgls` | 文档未明确 | 1_K(= 1024 字节 = 1 KiB)(默认值取自 src/common/options/osd.yaml.in @ v20.2.4) | 单次 `pg ls` 类命令返回的最大 PG 条数 | 💡 PG 数很多(数万)且 `ceph pg ls` 被截断时调大 |
|
||
| `osd_min_pg_log_entries` | 文档未明确 | 250(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | PG log 保留的最小条目数 | 💡 与 `osd_max_pg_log_entries` 共同决定 PG log 长度;影响 peering 能否走 log 而非 backfill |
|
||
| `osd_max_pg_log_entries` | 文档未明确 | 10000(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | PG log 保留的最大条目数 | 💡 调大可让更多场景走 log-based recovery 而非 full backfill,代价是内存 |
|
||
| `osd_default_data_pool_replay_window` | 文档未明确 | 45(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 与 data pool(cache tier 场景下)的 replay 窗口相关 | 💡 **cache tiering 已在 Reef 废弃**,新部署无需关注 |
|
||
| `osd_max_pg_per_osd_hard_ratio` | 文档未明确 | 3(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 每 OSD 的 PG 数硬上限比率(failsafe 性质) | 💡 与 `mon_max_pg_per_osd` 同属保险丝类参数 |
|
||
| `osd_pool_default_flag_ec_optimizations` | 文档未明确 | false(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **Tentacle 新增**。让 EC optimizations 成为新建 pool 的默认行为(per-pool 开关是 `allow_ec_optimizations`) | 💡 见 7.8 节;**存量 pool 需显式 `ceph osd pool set <pool> allow_ec_optimizations true`** |
|
||
|
||
> 引自:[Placement Groups](https://docs.ceph.com/en/tentacle/rados/operations/placement-groups/)、[PG Calc](https://docs.ceph.com/en/tentacle/rados/operations/pgcalc/)、[Pool, PG and CRUSH Config Reference](https://docs.ceph.com/en/tentacle/rados/configuration/pool-pg-config-ref/)
|
||
|
||
---
|
||
|
||
## 五、PG 状态机(pg-states、pg-concepts)
|
||
|
||
### 5.1 PG 核心术语(pg-concepts)
|
||
|
||
执行 `ceph -w`、`ceph osd dump` 等命令时会看到这些术语。官方定义逐条如下:
|
||
|
||
| 术语 | 官方定义 |
|
||
|---|---|
|
||
| **Peering** | 让存储某个 PG 的所有 OSD **就该 PG 中所有对象(及其元数据)的状态达成一致**的过程。⚠️ 注意:**对状态达成一致并不意味着它们都拥有最新内容** |
|
||
| **Acting Set** | 负责某个特定 PG 的 OSD **有序列表**(是"当前"或"截至某个 epoch 曾经"负责的那些 OSD) |
|
||
| **Up Set** | 依据 CRUSH,**在某个特定 epoch** 负责某个 PG 的 OSD 有序列表。**通常与 Acting Set 相同**,除非 Acting Set 被 OSD Map 中的 `pg_temp` **显式覆盖** |
|
||
| **Current Interval** / **Past Interval** | Acting Set 和 Up Set 对某个特定 PG **都不发生变化**的一段**连续的 OSD map epoch 序列** |
|
||
| **Primary** | Acting Set 的成员(**按约定是第一个**),负责**协调 peering**,并且是**唯一会接受客户端发起的、对该 PG 中对象写操作**的 OSD |
|
||
| **Replica** | Acting Set 中的**非 primary OSD**(且已被 primary **认可并 activate**) |
|
||
| **Stray** | **不是**当前 Acting Set 成员的 OSD,但**尚未被告知可以删除**它对某个 PG 的副本 |
|
||
| **Recovery** | 确保 PG 中所有对象的副本存在于 Acting Set 的**所有** OSD 上。一旦 Peering 完成,Primary 就可以**开始接受写操作**,而 Recovery **在后台进行** |
|
||
| **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 都已确认到某一点之后,这些 log 可以被截断** |
|
||
| **Missing Set** | 每个 OSD 记录更新日志条目,若某条目**意味着对象内容发生了更新**,就把该对象加入一个"需要的更新"列表。这个列表称为该 `<OSD,PG>` 的 **Missing Set** |
|
||
| **Authoritative History** | 一个**完整且完全有序**的操作集合 —— 若执行这些操作,就能把某 OSD 的 PG 副本**更新到最新** |
|
||
| **Epoch** | (单调递增的)**OSD map 版本号** |
|
||
| **Last Epoch Start** | Acting Set 中所有节点就某个 **Authoritative History 达成一致**的**最后一个 epoch**。到这一点,**Peering 被视为成功** |
|
||
| **up_thru** | 在 Primary 能成功完成 Peering 之前,**它必须通过一个存活的 monitor 把自己在 osd map 中的 `up_thru` 设置到当前 OSD map Epoch**。这有助于 Peering **忽略那些"Peering 从未完成"的先前 Acting Set** |
|
||
| **Last Epoch Clean** | Acting Set 中所有节点**完全最新(PG log 和对象内容都最新)**的最后一个 Epoch。到这一点,**recovery 被视为完成** |
|
||
|
||
#### `up_thru` 为什么必要(原文给的失败序列)
|
||
|
||
```text
|
||
- acting set = [A,B]
|
||
- acting set = [A]
|
||
- acting set = [] 很短时间之后(例如同时故障但检测有先后)
|
||
- acting set = [B] (B 重启了,A 没有)
|
||
```
|
||
|
||
如果没有 `up_thru`,B 重启后可能会错误地认为可以和 A 完成 peering。`up_thru` 机制让 Peering 能**忽略**这类"Peering 从未真正完成"的旧 Acting Set。
|
||
|
||
### 5.2 PG 状态全表(pg-states)
|
||
|
||
> 前置知识:检查集群状态时(`ceph -w` / `ceph -s`),Ceph 会报告 PG 的状态。**一个 PG 有一个或多个状态**("A placement group has one or more states")。**PG map 中 PG 的最佳状态是 `active + clean`**。
|
||
|
||
⚠️ **阅读约定**:下表"含义"列是官方文档原文定义的中文转述(权威);"典型触发原因"和"处理方向"两列**官方文档在本页未给出**,为本手册基于本册其它章节(pg-concepts、placement-groups、crush-map、pools)的补充,**均标注 💡**。
|
||
|
||
| 状态 | 含义(官方文档) | 典型触发原因 💡 | 处理方向 💡 |
|
||
|---|---|---|---|
|
||
| `creating` | Ceph 仍在创建该 PG | 新建 pool、增大 `pg_num`、PG 分裂 | 通常瞬时,无需干预。若长期停留,检查 `ceph -s` 是否有 OSD 未 up |
|
||
| `activating` | PG 已 peered 但**尚未 active** | peering 刚完成,等待激活 | 瞬时状态,一般自动转到 `active` |
|
||
| `active` | Ceph **会处理**对该 PG 的请求 | 正常 | 无需干预 |
|
||
| `clean` | Ceph 已把该 PG 中所有对象**复制了正确的次数** | 正常 | 无需干预。`active + clean` 是最佳状态 |
|
||
| `down` | **一个持有必要数据的副本 down 了**,因此该 PG 离线 | 承载该 PG 且持有唯一/必要数据的 OSD 掉线 | 尽快恢复对应 OSD;见第十一章心跳与 `mon_osd_down_out_interval` |
|
||
| `laggy` | 某个副本**没有及时确认**来自 primary 的新 lease;**I/O 被临时暂停** | 副本 OSD 高负载 / 网络抖动 / 慢盘 | 排查该副本 OSD 的磁盘与网络;与 11.4 节的 laggy 相关参数(`mon_osd_laggy_*`)有关 |
|
||
| `wait` | 该 PG 的 OSD 集合**刚刚发生变化**,I/O 被临时暂停,**直到前一个 interval 的 lease 过期** | 刚发生 OSD 增减 / PG 重映射 | 瞬时状态,等 lease 过期即可 |
|
||
| `scrubbing` | Ceph 正在**检查该 PG 的元数据**是否存在不一致 | 周期性 shallow scrub | 无需干预(见 4.7 的 scrub 命令) |
|
||
| `deep` | Ceph 正在**对照存储的校验和检查该 PG 的数据** | 周期性 deep scrub | 无需干预。可用 `ceph tell {pg-id} deep-scrub` 手动触发 |
|
||
| `degraded` | Ceph **尚未**把该 PG 中的部分对象复制够正确次数 | OSD 掉线 / 正在恢复 | 观察是否转为 `recovering`;长期 degraded 需检查 OSD 与容量 |
|
||
| `inconsistent` | Ceph 在该 PG 中**一个或多个对象副本**中检测到不一致(例如对象大小错误、**recovery 完成后**对象从某个副本中缺失等) | scrub 发现不一致 | 查看日志定位对象;通常需要 `ceph pg repair`(对应下面的 `repair` 状态) |
|
||
| `peering` | 该 PG 正在**进行 peering 过程** | OSD 重启 / OSD map 变更 | 短时正常。长期 peering 需查 OSD 间通信与 PG log 长度(`osd_min/max_pg_log_entries`) |
|
||
| `repair` | Ceph 正在检查该 PG 并**修复**它发现的任何不一致(若可能) | 由不一致触发的修复动作 | 等待完成;配合 `scrub` / `deep-scrub` 诊断 |
|
||
| `recovering` | Ceph 正在**迁移/同步对象及其副本** | OSD 恢复、PG 重映射 | 正常。可用 `ceph pg force-recovery` 提优先级(见 4.7) |
|
||
| `forced_recovery` | 该 PG 的高 recovery 优先级**由用户强制设定** | 执行了 `ceph pg force-recovery` | 完成后 `force` flag 自动清除;也可 `cancel-force-recovery` |
|
||
| `recovery_wait` | 该 PG **排队等待**开始 recovery | 并发恢复数受限 | 正常排队。可调 recovery 并发或提高该 PG 优先级 |
|
||
| `recovery_toofull` | 一个 recovery 操作正在等待,因为**目标 OSD 超过了它的 full ratio** | 目标 OSD 已满 | **扩容或清理空间**;检查 OSD full / backfillfull ratio |
|
||
| `recovery_unfound` | Recovery 因 **unfound 对象**而停止 | 有对象在所有已知位置都找不到 | 见 4.7 的 `ceph pg {pg-id} mark_unfound_lost revert` |
|
||
| `backfilling` | Ceph 正在**扫描并同步整个 PG 的全部内容**,而不是从近期操作日志推断需要同步哪些内容。**Backfill 是 recovery 的一种特殊情况** | 新 OSD 加入、PG log 不足以做增量恢复 | 正常(但比增量恢复慢、更耗 IO)。可用 `ceph pg force-backfill` 提优先级 |
|
||
| `forced_backfill` | 该 PG 的高 backfill 优先级**由用户强制设定** | 执行了 `ceph pg force-backfill` | 同 `forced_recovery` |
|
||
| `backfill_wait` | 该 PG **排队等待**开始 backfill | 并发 backfill 数受限 | 正常排队 |
|
||
| `backfill_toofull` | 一个 backfill 操作正在等待,因为**目标 OSD 超过了 backfillfull ratio** | 目标 OSD 接近满 | **扩容或清理空间** |
|
||
| `backfill_unfound` | Backfill 因 **unfound 对象**而停止 | 同 `recovery_unfound` | 同 `recovery_unfound` |
|
||
| `incomplete` | Ceph 检测到某个 PG **缺失关于"可能已发生的写入"的信息**,或者**没有任何健康的副本** | 多个 OSD 同时故障且持有不同写入;可用副本不足 | 官方给的方向:**尝试启动任何可能包含所需信息的失败 OSD**。若是**纠删码池,临时降低 `min_size` 可能允许恢复** |
|
||
| `stale` | 该 PG 处于**未知状态** —— 自 PG mapping 发生变化以来,**monitors 没有收到关于它的更新** | 承载 OSD 长时间未向 monitor 上报 | 检查对应 OSD 与 monitor 连通性;与 `mon_osd_report_timeout` 相关(见 4.7 与第十一章) |
|
||
| `remapped` | 该 PG **被临时映射到一组与 CRUSH 指定的不同的 OSD** | OSD 故障后临时映射;`pgp_num` 步进过程中 | 若为 `pgp_num` 步进导致,属**预期行为**(见 4.2)。否则等待恢复完成 |
|
||
| `undersized` | 该 PG 的副本数**少于配置的 pool 复制级别** | 部分副本不可用 | 通常伴随 `degraded`/`recovering`,观察恢复 |
|
||
| `peered` | 该 PG 已 peered,但**因副本数不足以达到 pool 配置的 `min_size` 而无法服务客户端 I/O**。**Recovery 可能在此状态下发生**,因此 PG 最终可能恢复到 `min_size` | 存活副本 < `min_size` | 恢复 OSD;或临时调整 `min_size`(**有风险**,见 6.6 节与 7.9 节) |
|
||
| `snaptrim` | 正在**修剪快照**(Trimming snaps) | 快照删除后的异步清理 | 正常 |
|
||
| `snaptrim_wait` | **排队等待**修剪快照 | snaptrim 并发受限 | 正常排队 |
|
||
| `snaptrim_error` | 修剪快照**因错误停止** | 修剪过程出错 | 查日志定位具体错误 |
|
||
| `unknown` | **ceph-mgr 自启动以来,尚未从任何 OSD 收到关于该 PG 状态的信息** | mgr 刚重启 | 通常瞬时;若长期存在,检查 mgr 与 OSD |
|
||
|
||
#### 状态速查:几个容易混淆的对照
|
||
|
||
| 对比 | 区别 |
|
||
|---|---|
|
||
| `degraded` vs `undersized` | `degraded`:**部分对象**没复制够次数;`undersized`:**副本数少于**配置的复制级别。二者常同时出现 |
|
||
| `recovering` vs `backfilling` | `backfill` 是 `recovery` 的**特例**:backfill 是**扫描并同步整个 PG 的全部内容**;而普通 recovery 是**从近期操作日志推断**需要同步哪些内容 |
|
||
| `peered` vs `active` | `peered` = 已完成 peering 但**副本数不够 `min_size`,不能服务 I/O**;`active` = 能处理请求 |
|
||
| `stale` vs `down` | `stale` = **monitor 侧**长时间没收到该 PG 的更新(信息缺失);`down` = **持有必要数据的副本掉线**(PG 离线) |
|
||
| `recovery_toofull` vs `backfill_toofull` | 前者卡在 **full ratio**,后者卡在 **backfillfull ratio**(backfillfull 通常更严格) |
|
||
|
||
> 引自:[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/)
|
||
|
||
---
|
||
|
||
## 六、Pool 管理(pools.rst)
|
||
|
||
### 6.1 Pool 是什么
|
||
|
||
> Pools are logical partitions that are used to store RADOS objects.
|
||
|
||
Pool 提供四件事:
|
||
|
||
| 能力 | 说明 |
|
||
|---|---|
|
||
| **Resilience(韧性)** | 可以规划"多少个 OSD 并行故障而不导致数据不可用或丢失"。**副本池**:不丢数据的并行故障 OSD 数 = 副本数 - 1;不导致数据不可用的数量**通常为 2**。典型配置每对象存 3 个副本(`size = 3`),但副本数**可按 pool 配置**。**纠删码池**:韧性定义为 **coding(parity)chunk 的数量**(例如默认 EC profile 的 `m = 2`) |
|
||
| **Placement Groups** | autoscaler 为 pool 设定 PG 数。**典型配置下,每 OSD 约一百五十个 PG**("the target number of PGs is approximately one-hundred and fifty PGs per OSD"),在合理均衡与不过度消耗计算资源之间取得平衡。**设置多个 pool 时,要确保每 OSD 的 PG 副本之和落在期望的 PG-per-OSD 目标区间内**。工具见 pgcalc |
|
||
| **CRUSH Rules** | 数据存入 pool 时,PG 与对象副本(EC 池则为 chunk/shard)在集群中的放置由 CRUSH rule 决定。若默认 rule 不满足用例,可为 pool 创建自定义 CRUSH rule |
|
||
| **Snapshots** | `ceph osd pool mksnap` 创建 pool 快照 |
|
||
|
||
### 6.2 Pool 命名
|
||
|
||
> ⚠️ **以 `.` 开头的 pool 名保留给 Ceph 内部操作使用。不要创建或操作这些名字的 pool。**
|
||
|
||
(例如 `.mgr`、`.rgw.root` 等。)
|
||
|
||
### 6.3 列出 Pool
|
||
|
||
```bash
|
||
# 只列名(适合脚本)
|
||
ceph osd pool ls
|
||
```
|
||
|
||
```text
|
||
.rgw.root
|
||
default.rgw.log
|
||
default.rgw.control
|
||
default.rgw.meta
|
||
```
|
||
|
||
```bash
|
||
# 带 pool 编号
|
||
ceph osd lspools
|
||
```
|
||
|
||
```text
|
||
1 .rgw.root
|
||
2 default.rgw.log
|
||
3 default.rgw.control
|
||
4 default.rgw.meta
|
||
```
|
||
|
||
```bash
|
||
# 带附加信息
|
||
ceph osd pool ls detail
|
||
```
|
||
|
||
```text
|
||
pool 1 '.rgw.root' replicated size 3 min_size 1 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 autoscale_mode on last_change 19 flags hashpspool stripe_width 0 application rgw read_balance_score 4.00
|
||
pool 2 'default.rgw.log' replicated size 3 min_size 1 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 autoscale_mode on last_change 21 flags hashpspool stripe_width 0 application rgw read_balance_score 4.00
|
||
pool 3 'default.rgw.control' replicated size 3 min_size 1 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 autoscale_mode on last_change 23 flags hashpspool stripe_width 0 application rgw read_balance_score 4.00
|
||
pool 4 'default.rgw.meta' replicated size 3 min_size 1 crush_rule 0 object_hash rjenkins pg_num 128 pgp_num 128 autoscale_mode on last_change 25 flags hashpspool stripe_width 0 pg_autoscale_bias 4 application rgw read_balance_score 4.00
|
||
```
|
||
|
||
更多信息可用 `--format` / `-f` 加 `json`、`json-pretty`、`xml` 或 `xml-pretty`。
|
||
|
||
> 💡 补充:从上面的真实输出可以读出几个常用默认值/常见值:`object_hash rjenkins`、`pg_num 32`、`autoscale_mode on`、`flags hashpspool`、`application rgw`。
|
||
|
||
### 6.4 创建 Pool
|
||
|
||
```bash
|
||
# 副本池
|
||
ceph osd pool create {pool-name} [{pg-num} [{pgp-num}]] [replicated] \
|
||
[crush-rule-name] [expected-num-objects]
|
||
|
||
# 纠删码池
|
||
ceph osd pool create {pool-name} [{pg-num} [{pgp-num}]] erasure \
|
||
[erasure-code-profile] [crush-rule-name] [expected_num_objects] [--autoscale-mode=<on,off,warn>]
|
||
```
|
||
|
||
#### 参数详解
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| `{pool-name}` | pool 名,**必须唯一** | String | 是 | — |
|
||
| `{pg-num}` | pool 中 PG 总数。**默认值对大多数系统是不合适的**("The default value is NOT suitable for most systems") | Integer | 是 | **32** |
|
||
| `{pgp-num}` | 用于放置目的的 PG 总数。**应等于 PG 总数**,除非在 `pg_num` 增减的短暂窗口期。Nautilus 起**通常不直接改** `pgp_num`:Ceph 会在 `pg_num` 调整时自动增量扩缩它 | Integer | 是(若命令中未指定则用默认值,除非 Ceph 配置中设了不同的值) | **32** |
|
||
| `{replicated\|erasure}` | pool 的数据保护策略 | String | 否 | **`replicated`** |
|
||
| `[crush-rule-name]` | 该 pool 使用的 CRUSH rule 名。**指定的 rule 必须已存在,否则命令失败** | String | 否 | **副本池**:默认用 `osd_pool_default_crush_rule` 配置选项指定的 rule,**该 rule 必须存在**。<br>**EC 池**:若用 `default` erasure code profile 则为 `erasure-code` rule;否则为 `{pool-name}` rule。**若该 rule 不存在会被隐式创建** |
|
||
| `[erasure-code-profile=profile]` | 仅 EC pool。指定要用的 erasure code profile。**该 profile 必须已存在**(通过 dashboard 或 `osd erasure-code-profile set` 定义)。⚠️ **pool 创建之后再改 EC profile 不生效**;要给已有 pool 换 EC profile,必须**修改该 pool 去使用一条用目标 profile 定义的不同 CRUSH rule** | String | 否 | — |
|
||
| `--autoscale-mode=<on,off,warn>` | `on` = 依实际用量自动调优 PG 数;`warn` = 依实际用量**建议**调整;`off` = 关闭 | String | 否 | 由 `osd_pool_default_pg_autoscale_mode` 选项决定 |
|
||
| `[expected-num-objects]` | 该 pool 预期的 RADOS 对象数量。设置此值可让 **PG 分裂在 pool 创建时发生**,避免运行时分裂带来的延迟冲击 | Integer | 否 | **0**(创建时不分裂) |
|
||
|
||
#### `replicated` vs `erasure` 的取舍(原文表述)
|
||
|
||
| | `replicated` | `erasure` |
|
||
|---|---|---|
|
||
| 类比 | 像 RAID1 和 RAID10 | 一种**通用化奇偶校验 RAID** 策略,类似 RAID6 但更灵活 |
|
||
| 可用容量 | 给定原始存储量,可用容量**较少** | 通常**更多** |
|
||
| 适用面 | **适用于所有 Ceph 组件和用例** | **只适用于部分 Ceph 组件和用例** |
|
||
| 故障域要求 | — | **通常需要比复制更多的故障域** |
|
||
| 性能 | — | **性能下降** |
|
||
| 故障容忍 | — | **可能容忍更多的重叠盘/主机故障** |
|
||
|
||
> ⚠️ 另注(pools.rst 明确):**Luminous 及以后,每个 pool 必须关联一个 application 才能被使用**。
|
||
|
||
### 6.5 关联 Application
|
||
|
||
```bash
|
||
ceph osd pool application enable {pool-name} {application-name}
|
||
```
|
||
|
||
- CephFS 用的 pool 和 RGW 自动创建的 pool 会**自动关联**;
|
||
- 给 RBD 用的 pool 应通过 dashboard 或 `rbd` CLI 工具初始化;
|
||
- 内置 application 名:**CephFS 用 `cephfs`,RBD 用 `rbd`,RGW 用 `rgw`**。
|
||
|
||
### 6.6 设置 Pool 值
|
||
|
||
```bash
|
||
ceph osd pool set {pool-name} {key} {value}
|
||
```
|
||
|
||
#### 可设置的 key 全表
|
||
|
||
| key | 说明 | 类型 | 合法值 / 默认值 | 备注 |
|
||
|---|---|---|---|---|
|
||
| `compression_algorithm` | 设置底层 BlueStore 后端存储数据时使用的**内联压缩算法**。会覆盖全局设置 `bluestore_compression_algorithm` | String | `lz4`、`snappy`、`zlib`、`zstd` | |
|
||
| `compression_mode` | 设置底层 BlueStore 后端存储数据时的**内联压缩策略**。覆盖全局 `bluestore_compression_mode` | String | `none`、`passive`、`aggressive`、`force` | |
|
||
| `compression_min_blob_size` | 设置压缩 chunk 的**最小尺寸**:小于此值的 chunk 不压缩。覆盖全局 `bluestore_compression_min_blob_size`、`..._hdd`、`..._ssd` | Unsigned Integer | — | |
|
||
| `compression_max_blob_size` | 设置 chunk 的**最大尺寸**:大于此值的 chunk 会**被拆成不大于此值的更小 blob** 再进行压缩 | Unsigned Integer | — | |
|
||
| `size` | 设置 pool 中对象的**副本数**。**只能用于 `replicated` pool。EC pool 会*报告*一个等于 K+M 的 `size`,但该值不能被直接*设置*** | Integer | — | `{num-replicas}` **包含对象本身**。例:想在原对象之外再有两份副本(共 3 个实例)→ 设 `3` |
|
||
| `min_size` | 设置 **PG 能 active、从而能进行 I/O 所需的最小活跃副本(或分片)数** | Integer | Version `0.54` 及以上 | **对纠删码池,应设为大于 `K` 的值**。若只以 `K` 个分片可用就允许 I/O,则**没有任何冗余**,一旦再有 OSD 永久故障就会丢数据 |
|
||
| `pg_num` | 指定该 pool 的 PG 总数。⚠️ **若该 pool 启用了 PG autoscaler,它可能覆盖手工设置的值** | Integer | `0` 到 `mon_max_pool_pg_num`。**若设为 `0`,则使用 `osd_pool_default_pg_num` 的值** | |
|
||
| `pgp_num` | 设置计算数据放置时使用的**有效 PG 数**。Nautilus 起管理员通常不显式设置;Ceph 会自动增量扩缩它以匹配 `pg_num` | Integer | `1` 到当前 `pg_num` 之间 | |
|
||
| `crush_rule` | 设置 Ceph 用来把该 pool 的 RADOS 对象映射到合适 OSD 的 CRUSH rule | String | — | |
|
||
| `allow_ec_overwrites` | 决定**是否允许对纠删码池的写入只更新 RADOS 对象的一部分**。这使得 CephFS 和 RBD 能用 EC 池存用户数据(**但不能存元数据**) | Boolean | — | `.. versionadded:: 12.2.0` |
|
||
| `allow_ec_optimizations` | 为纠删码池启用**性能和容量优化**。这些优化为 CephFS 和 RBD 工作负载设计;**有大量的小对象或对象小随机读的 RGW 负载也会受益**;**大对象顺序读写的 RGW 负载几乎无收益** | Boolean | — | `.. versionadded:: 20.2.0`(Tentacle) |
|
||
| `hashpspool` | 设置或取消给定 pool 上的 `HASHPSPOOL` flag | Integer | `1` 置位,`0` 取消 | |
|
||
| `nodelete` | 设置或取消 `NODELETE` flag | Integer | `1` 置位,`0` 取消 | Version: `FIXME`(源文如此) |
|
||
| `nopgchange` | 设置或取消 `NOPGCHANGE` flag | Integer | `1` 置位,`0` 取消 | Version: `FIXME` |
|
||
| `nosizechange` | 设置或取消 `NOSIZECHANGE` flag | Integer | `1` 置位,`0` 取消 | Version: `FIXME` |
|
||
| `bulk` | 设置或取消 `BULK` flag | Boolean | `true`/`1` 置位,`false`/`0` 取消 | 见 4.3 节 |
|
||
| `write_fadvise_dontneed` | 设置或取消 `WRITE_FADVISE_DONTNEED` flag | Integer | `1` 置位,`0` 取消 | |
|
||
| `noscrub` | 设置或取消 `NOSCRUB` flag | Integer | `1` 置位,`0` 取消 | |
|
||
| `nodeep-scrub` | 设置或取消 `NODEEP_SCRUB` flag | Integer | `1` 置位,`0` 取消 | |
|
||
| `target_max_bytes` | 当 `max_bytes` 阈值被触发**且已废弃的 cache tier 功能在使用中**时,Ceph 会开始 flush 或 evict 对象 | Integer | 例:`1000000000000`(#1-TB) | ⚠️ cache tiering 已废弃,见 6.11 |
|
||
| `target_max_objects` | 当 `max_objects` 阈值被触发**且已废弃的 cache tier 功能在使用中**时,Ceph 会开始 flush 或 evict 对象 | Integer | 例:`1000000`(#1M objects) | ⚠️ 同上 |
|
||
| `fast_read` | **仅纠删码池**。若开启,读请求会向**所有 shard 发出 "sub reads"**,然后等待收到足够解码所需的 shard 后再响应客户端。**若使用 jerasure 或 isa 插件,则前 K 个响应返回后,客户端请求会立即用这些数据解码出来的内容被服务**。这种做法**牺牲资源换取更好的性能** | Boolean | 默认 `0` | **仅 EC 池支持** |
|
||
| `scrub_min_interval` | 设置该 pool 的 PG 两次**浅(light)scrub** 之间的**最小间隔(秒)**。若保持默认 `0`,则改用 central config 中的 `osd_scrub_min_interval` | Double | 默认 `0` | |
|
||
| `scrub_max_interval` | 设置该 pool 的 PG 两次浅 scrub 之间的**最大间隔(秒)**。影响 scrub scheduler dump 中出现的 'overdue' 属性。若保持默认 `0`,则改用 `osd_scrub_max_interval` | Double | 默认 `0` | |
|
||
| `deep_scrub_interval` | 设置该 pool 的 PG 两次 **deep scrub** 之间的**间隔(秒)**。若保持默认 `0`,则改用 `osd_deep_scrub_interval` | Double | 默认 `0` | |
|
||
| `recovery_priority` | 调整一个 pool 的**计算预留优先级(computed reservation priority)**。**取值范围 `-10` 到 `10`**。⚠️ **任何被赋予负值的 pool 都会比任何新建 pool 优先级更低**,因此建议把负值给低优先级 pool | Integer | 默认 `0` | ⚠️ 与 4.7 节 placement-groups.rst 的用法示例(用了 20/19/30/15)取值范围不一致,见 4.7 的 💡 补充注 |
|
||
| `recovery_op_priority` | 设置某个 pool 的 PG 的 **recovery 操作优先级**。覆盖由 `osd_recovery_op_priority` 决定的通用优先级 | Integer | 默认 `0` | |
|
||
|
||
#### 读取 Pool 值
|
||
|
||
```bash
|
||
ceph osd pool get {pool-name} {key}
|
||
```
|
||
|
||
可读取的 key:`size`、`min_size`、`pg_num`、`pgp_num`、`crush_rule`、`target_max_bytes`、`target_max_objects`、`fast_read`、`scrub_min_interval`、`scrub_max_interval`、`deep_scrub_interval`、`allow_ec_overwrites`、`recovery_priority`、`recovery_op_priority`。
|
||
|
||
(`pgp_num` 的读取 Valid Range:**等于或小于** `pg_num`。)
|
||
|
||
### 6.7 副本数
|
||
|
||
```bash
|
||
# 设置副本数(num-replicas 包含对象本身)
|
||
ceph osd pool set data size 3
|
||
|
||
# 设置 I/O 所需的最小副本数
|
||
ceph osd pool set data min_size 2
|
||
|
||
# 查看
|
||
ceph osd dump | grep 'replicated size'
|
||
```
|
||
|
||
> ⚠️ **官方重要提示**:生产环境**把 `size` 设为 `2` 或把 `min_size` 设为 `1` 有数据丢失风险**,只应在**某些紧急情况下、且只是临时地**这么做。
|
||
|
||
### 6.8 配额
|
||
|
||
```bash
|
||
ceph osd pool set-quota {pool-name} [max_objects {obj-count}] [max_bytes {bytes}]
|
||
```
|
||
|
||
例:
|
||
|
||
```bash
|
||
ceph osd pool set-quota data max_objects 10000
|
||
```
|
||
|
||
- **把值设为 `0` 即移除该配额**;
|
||
- 可以只设 bytes、只设对象数,或两者都设。
|
||
|
||
### 6.9 快照
|
||
|
||
```bash
|
||
# 创建 pool 快照
|
||
ceph osd pool mksnap {pool-name} {snap-name}
|
||
|
||
# 删除 pool 快照
|
||
ceph osd pool rmsnap {pool-name} {snap-name}
|
||
```
|
||
|
||
### 6.10 重命名与删除
|
||
|
||
```bash
|
||
# 重命名
|
||
ceph osd pool rename {current-pool-name} {new-pool-name}
|
||
```
|
||
|
||
> ⚠️ 若被重命名的 pool 上有已认证用户的 per-pool capabilities,**必须更新该用户的 caps** 以引用新的 pool 名。
|
||
|
||
```bash
|
||
# 删除
|
||
ceph osd pool delete {pool-name} [{pool-name} --yes-i-really-really-mean-it]
|
||
```
|
||
|
||
**前置条件**:必须在 central configuration 中把 **`mon_allow_pool_delete`** flag 设为 `true`,否则 Ceph monitors 会**拒绝删除 pool**。
|
||
|
||
**善后建议(原文)**:
|
||
|
||
1. **清理不再使用的自定义 CRUSH rule**:
|
||
|
||
```bash
|
||
# 查看该 pool 用的 rule
|
||
ceph osd pool get {pool-name} crush_rule
|
||
|
||
# 例:若自定义 rule 是 "123",检查所有 pool 是否还在用它
|
||
ceph osd dump | grep "^pool" | grep "crush_rule 123"
|
||
|
||
# 若没有 pool 用,可安全删除
|
||
ceph osd crush rule rm {rule-name}
|
||
```
|
||
|
||
2. **清理权限限定在已删除 pool 上的用户**:
|
||
|
||
```bash
|
||
ceph auth ls | grep -C 5 {pool-name}
|
||
ceph auth del {user}
|
||
```
|
||
|
||
### 6.11 Cache tier 相关(已废弃)
|
||
|
||
`target_max_bytes` / `target_max_objects` 两个 key 的描述里出现了 **"the deprecated cache tier functionality"**;erasure-code.rst 中更明确:
|
||
|
||
> ⚠️ **Cache tiering was deprecated in Reef. We strongly advise not deploying new cache tiers, and working to remove them from existing deployments.**
|
||
|
||
历史用法(**仅供理解存量集群,新部署不要用**):
|
||
|
||
```bash
|
||
ceph osd tier add ecpool hot-storage
|
||
ceph osd tier cache-mode hot-storage writeback
|
||
ceph osd tier set-overlay ecpool hot-storage
|
||
```
|
||
|
||
> 引自:[Pools](https://docs.ceph.com/en/tentacle/rados/operations/pools/)
|
||
|
||
### 6.12 Stretch pool(延伸集群 pool)
|
||
|
||
```bash
|
||
# 设置
|
||
ceph osd pool stretch set {pool-name} {peering_crush_bucket_count} {peering_crush_bucket_target} {peering_crush_bucket_barrier} {crush_rule} {size} {min_size} [--yes-i-really-mean-it]
|
||
|
||
# 取消(回到非 stretch)
|
||
ceph osd pool stretch unset {pool-name} {crush_rule} {size} {min_size}
|
||
|
||
# 查看
|
||
ceph osd pool stretch show {pool-name}
|
||
```
|
||
|
||
#### `stretch set` 参数
|
||
|
||
| 参数 | 说明 | 类型 | 必填 |
|
||
|---|---|---|---|
|
||
| `{pool-name}` | pool 名。**必须是已存在的 pool**(本命令不创建新 pool) | String | 是 |
|
||
| `{peering_crush_bucket_count}` | 与 `peering_crush_bucket_barrier` 一起用于判断所选 acting set 中的 OSD **能否彼此 peering**,依据是 acting set 中**不同 bucket 的数量** | Integer | 是 |
|
||
| `{peering_crush_bucket_target}` | 与 `peering_crush_bucket_barrier` 和 `size` 一起用于计算 `bucket_max`,**限制同一 bucket 中被选入某 PG acting set 的 OSD 数量** | Integer | 是 |
|
||
| `{peering_crush_bucket_barrier}` | pool 的 PG 分布在什么类型的 CRUSH bucket 之间,例如 `rack`、`row` 或 `datacenter` | String | 是 |
|
||
| `{crush_rule}` | pool 使用的 CRUSH rule。**pool 类型必须与 CRUSH rule 类型匹配**(`replicated` 或 `erasure`) | String | 是 |
|
||
| `{size}` | pool 中 RADOS 对象(以及 PG)的副本数 | Integer | 是 |
|
||
| `{min_size}` | 能服务 I/O 所必须活跃的最小副本数 | Integer | 是 |
|
||
| `--yes-i-really-mean-it` | 确认你确实想**绕过安全检查**来设置这些值。例:试图把 `peering_crush_bucket_count` 或 `peering_crush_bucket_target` 设成**大于 CRUSH map 中 bucket 数量**时 | Flag | 否 |
|
||
|
||
#### `stretch unset` / `show` 参数
|
||
|
||
- `unset`:`{pool-name}` 必须是**已 stretch 的** pool;`{crush_rule}` 为退出 stretch 后使用的 rule(**类型必须与 pool 匹配**);`{size}` / `{min_size}` 为退出后的副本数与最小副本数。
|
||
- `show`:`{pool-name}` 必须是已 stretch 的 pool。
|
||
|
||
> 💡 补充:Stretch mode 的完整说明在 `rados/operations/stretch-mode`,**不在本册 25 个源文范围内**,此处仅收录 pools.rst 中出现的命令与参数定义。
|
||
|
||
### 6.13 查看 Pool 统计
|
||
|
||
```bash
|
||
# 利用率统计
|
||
rados df
|
||
|
||
# 某 pool(或所有 pool)的 I/O 信息
|
||
ceph osd pool stats [{pool-name}]
|
||
```
|
||
|
||
> 引自:[Pools](https://docs.ceph.com/en/tentacle/rados/operations/pools/)
|
||
|
||
---
|
||
|
||
## 七、纠删码(Erasure Code)
|
||
|
||
### 7.1 基本原理
|
||
|
||
默认情况下 Ceph pool 以 **`replicated`** 类型创建 —— 每个对象被复制到多个磁盘,这就是"复制(replication)"这种数据保护方法。
|
||
|
||
**纠删码(erasure-coded)pool** 用不同的方法:
|
||
|
||
- 数据被拆成两类片段:**data blocks(数据块)** 和 **parity blocks(奇偶校验块)**;
|
||
- 若某个驱动器故障或损坏,**用 parity blocks 重建数据**;
|
||
- **在规模上,纠删码相比复制节省空间**("At scale, erasure coding saves space relative to replication")。
|
||
|
||
**术语约定(本册统一)**:
|
||
|
||
| 本册/官方术语 | 含义 |
|
||
|---|---|
|
||
| **data chunks(数据块)** | data blocks |
|
||
| **coding chunks(编码块)** | parity blocks |
|
||
|
||
纠删码也叫 **"forward error correction codes(前向纠错码)"**。第一个前向纠错码由 **Richard Hamming 于 1950 年在贝尔实验室**开发。
|
||
|
||
### 7.2 快速上手
|
||
|
||
最简单的纠删码池类似 **RAID5**,**至少需要三台 host**:
|
||
|
||
```bash
|
||
ceph osd pool create ecpool erasure
|
||
```
|
||
|
||
```text
|
||
pool 'ecpool' created
|
||
```
|
||
|
||
```bash
|
||
echo ABCDEFGHI | rados --pool ecpool put NYAN -
|
||
rados --pool ecpool get NYAN -
|
||
```
|
||
|
||
```text
|
||
ABCDEFGHI
|
||
```
|
||
|
||
### 7.3 Profile 基础:K、M 与默认 profile
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile get default
|
||
```
|
||
|
||
```text
|
||
k=2
|
||
m=2
|
||
plugin=isa
|
||
crush-failure-domain=host
|
||
technique=reed_sol_van
|
||
```
|
||
|
||
⚠️ **两个易混淆的概念(原文专门澄清)**:
|
||
|
||
| | K / M | 说明 |
|
||
|---|---|---|
|
||
| **默认的(default)纠删码池** | `k=2 m=2` | 有 **2 个 data chunk + 2 个 coding chunk** |
|
||
| **最简单的(simplest)纠删码池** | `k=2 m=1` | 有 **2 个 data chunk + 1 个 coding chunk** |
|
||
|
||
**默认 profile 的能力**:可承受**两个 OSD 的重叠丢失**而不丢数据。**等价于一个三副本的副本池,但存储需求不同**:不是用 3TB 存 1TB,而是**只需 2TB 存 1TB**。
|
||
|
||
> ⚠️ **Profile 选错无法补救(原文重点强调)**:
|
||
> Choosing the right profile is important because **the profile cannot be modified after the pool is created**. 若发现需要不同 profile 的 EC 池,**必须**用不同的 profile 创建新 pool,然后把配错的 pool 里的**所有对象迁移过去**。**没有任何办法在 pool 创建之后修改它的 profile。**
|
||
|
||
**最重要的三个参数**:***K***、***M*** 和 ***crush-failure-domain***,因为它们定义了**存储开销**和**数据持久性**。
|
||
|
||
**示例**:若目标架构必须承受**两个 rack 的丢失**、且存储开销为 67%:
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set myprofile \
|
||
k=3 \
|
||
m=2 \
|
||
crush-failure-domain=rack
|
||
ceph osd pool create ecpool erasure myprofile
|
||
echo ABCDEFGHI | rados --pool ecpool put NYAN -
|
||
rados --pool ecpool get NYAN -
|
||
```
|
||
|
||
```text
|
||
ABCDEFGHI
|
||
```
|
||
|
||
对象 *NYAN* 会被分成 **3 份(K=3)**,并额外创建 **2 个 chunk(M=2)**。
|
||
|
||
- ***M* 的值定义了可以同时丢失多少个 OSD 而不丢数据**;
|
||
- `crush-failure-domain=rack` 会创建一条 CRUSH rule,**确保没有两个 chunk 存在同一个 rack 中**。
|
||
|
||
**编码过程示意(原文 ditaa 转述)**:
|
||
|
||
```text
|
||
name | NYAN |
|
||
content | ABCDEFGHI |
|
||
|
|
||
v
|
||
+------+------+
|
||
+---------------+ encode(3,2) +-----------+
|
||
| +--+--+---+---+ |
|
||
| | | | |
|
||
| +-------+ | +-----+ |
|
||
| | | | |
|
||
+--v---+ +--v---+ +--v---+ +--v---+ +--v---+
|
||
| NYAN | | NYAN | | NYAN | | NYAN | | NYAN |
|
||
+------+ +------+ +------+ +------+ +------+
|
||
shard | 1 | | 2 | | 3 | | 4 | | 5 |
|
||
+------+ +------+ +------+ +------+ +------+
|
||
content | ABC | | DEF | | GHI | | YXY | | QGC |
|
||
+--+---+ +--+---+ +--+---+ +--+---+ +--+---+
|
||
| | | | |
|
||
v v v v v
|
||
OSD1 OSD2 OSD3 OSD4 OSD5 (各在不同 rack)
|
||
```
|
||
|
||
### 7.4 Erasure code profile 命令与通用参数
|
||
|
||
#### 命令
|
||
|
||
```bash
|
||
# 创建
|
||
ceph osd erasure-code-profile set {name} \
|
||
[{directory=directory}] \
|
||
[{plugin=plugin}] \
|
||
[{stripe_unit=stripe_unit}] \
|
||
[{key=value} ...] \
|
||
[--force]
|
||
|
||
# 删除
|
||
ceph osd erasure-code-profile rm {name}
|
||
|
||
# 查看
|
||
ceph osd erasure-code-profile get {name}
|
||
|
||
# 列出所有 profile 名
|
||
ceph osd erasure-code-profile ls
|
||
```
|
||
|
||
#### 通用(与 plugin 无关的)参数
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| `{directory=directory}` | 加载 erasure code plugin 的**目录**名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `{plugin=plugin}` | 用哪个 erasure code **plugin** 来计算 coding chunk 并恢复缺失 chunk | String | 否 | **`isa`** |
|
||
| `{stripe_unit=stripe_unit}` | **每个 stripe 中,一个 data chunk 里放多少数据**。例:一个 2 data chunk 且 `stripe_unit=4K` 的 profile 会把 0-4K 放进 chunk 0,4K-8K 放进 chunk 1,然后 8K-12K 又回到 chunk 0。**为获得最佳性能,该值应为 4K 的倍数**。默认值在创建 pool 时取自 monitor 配置选项 `osd_pool_erasure_code_stripe_unit`。**使用该 profile 的 pool 的 `stripe_width` = data chunk 数 × 本 `stripe_unit`** | String | 否 | 取自 `osd_pool_erasure_code_stripe_unit` |
|
||
| `{key=value}` | 其余 key/value 对的语义**由具体的 erasure code plugin 定义** | String | 否 | — |
|
||
| `--force` | **覆盖同名的已有 profile**,并**允许设置非 4K 对齐的 `stripe_unit`**。⚠️ 覆盖已有 profile 是危险的,**因此必须同时用 `--yes-i-really-mean-it`** | String | 否 | — |
|
||
|
||
#### ⚠️ 删除 profile 的一个坑
|
||
|
||
> **Removing an erasure code profile using `osd erasure-code-profile rm` does not automatically delete the associated CRUSH rule** associated with the erasure code profile. It is recommended to **manually remove the associated CRUSH rule** using `ceph osd crush rule remove {rule-name}` to avoid unexpected behavior.
|
||
|
||
(若 profile 被某个 pool 引用,删除会失败。)
|
||
|
||
### 7.5 各 Plugin 逐项参数
|
||
|
||
#### 7.5.1 jerasure
|
||
|
||
> **官方评价**:The *jerasure* plugin is a generic and flexible plugin. However, **the *jerasure* library is no longer maintained** and has not been updated to support modern CPU instructions that can improve performance when encoding and decoding data.
|
||
|
||
(封装 `Jerasure` 库。建议阅读 jerasure 文档以理解参数。⚠️ 原文注:`jerasure.org` 网站**截至 2023 年可能已不再与原始项目相关联,也不再是正当的**。)
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set {name} \
|
||
plugin=jerasure \
|
||
k={data-chunks} \
|
||
m={coding-chunks} \
|
||
technique={reed_sol_van|reed_sol_r6_op|cauchy_orig|cauchy_good|liberation|blaum_roth|liber8tion} \
|
||
[crush-root={root}] \
|
||
[crush-failure-domain={bucket-type}] \
|
||
[crush-device-class={device-class}] \
|
||
[directory={directory}] \
|
||
[--force]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 / 示例 |
|
||
|---|---|---|---|---|
|
||
| `k` | 每个对象被拆成 **data-chunks** 份,每份存在不同 OSD 上 | Integer | **是** | 例 `4` |
|
||
| `m` | 为每个对象计算 **coding chunks** 并存在不同 OSD 上。coding chunk 的数量 = **可以有多少个 OSD 挂掉而不丢数据** | Integer | **是** | 例 `2` |
|
||
| `technique` | 见下方详述 | String | 否 | **`reed_sol_van`** |
|
||
| `packetsize` | **编码以 *bytes* 大小的包为单位进行**。选对包大小很困难,jerasure 文档中有大量相关信息 | Integer | 否 | **2048** |
|
||
| `crush-root` | CRUSH rule 第一步使用的 crush bucket 名。例如 `step take default` | String | 否 | **`default`** |
|
||
| `crush-failure-domain` | 确保**没有两个 chunk 落在具有相同故障域的 bucket 中**。例:故障域为 `host` 时,不会有两个 chunk 存在同一 host。用于生成形如 `step chooseleaf host` 的 CRUSH rule step | String | 否 | **`host`** |
|
||
| `crush-device-class` | 把放置限制到特定 class 的设备(如 `ssd` 或 `hdd`),使用 CRUSH map 中的 crush device class 名 | String | 否 | 文档未明确(表格中未填) |
|
||
| `directory` | 加载 plugin 的目录名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `--force` | 覆盖同名的已有 profile | String | 否 | — |
|
||
|
||
**`technique` 的取值与特性**:
|
||
|
||
| technique | 特性 |
|
||
|---|---|
|
||
| `reed_sol_van` | **最灵活**:只需设置 *k* 和 *m* |
|
||
| `cauchy_good` | **可能更快**,但**需要仔细选择 *packetsize*** |
|
||
| `reed_sol_r6_op`、`liberation`、`blaum_roth`、`liber8tion` | 都是 **RAID6 等价物**,含义是**只能用 `m=2` 配置** |
|
||
|
||
> ⚠️ **blaum_roth 的 `w` 坑(官方 note,重要)**:
|
||
> 使用 `blaum_roth` 编码时,**默认字长 `w=7` 是次优的**,因为 `blaum_roth` 在 **`w+1` 为素数**时工作最好。**创建 `technique=blaum_roth` 的新 EC profile 时,应把 `w` 设为"比某个素数小 1 的数"(例如 `6`)**。
|
||
> 背景:`w` 的默认值在源码中不容易改(见 Loic Dachary 的 commit f51d21b);限制本身见 Plank and Greenan 的 *"Jerasure: A Library in C Facilitating Erasure Coding for Storage Applications"* 第 29 页第二个要点。该信息由 Benjamin Mare 于 2024 年 9 月提供给 Ceph 上游。
|
||
|
||
#### 7.5.2 isa(**默认 plugin**)
|
||
|
||
> The *isa* plugin is **the default for Ceph erasure coded pools.** 封装 ISA(Intel Storage Acceleration Library)。
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set {name} \
|
||
plugin=isa \
|
||
technique={reed_sol_van|cauchy} \
|
||
[k={data-chunks}] \
|
||
[m={coding-chunks}] \
|
||
[crush-root={root}] \
|
||
[crush-failure-domain={bucket-type}] \
|
||
[crush-device-class={device-class}] \
|
||
[directory={directory}] \
|
||
[--force]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| `k` | 每个对象被拆成 **data-chunks** 份,每份存在不同 OSD 上 | Integer | 否 | **7** |
|
||
| `m` | 计算 **coding chunks** 并存在不同 OSD 上。数量 = 可挂掉的 OSD 数而不丢数据 | Integer | 否 | **3** |
|
||
| `technique` | ISA plugin 有两种 Reed Solomon 形式:`reed_sol_van` = **Vandermonde(范德蒙德)**;`cauchy` = **Cauchy(柯西)** | String | 否 | **`reed_sol_van`** |
|
||
| `crush-root` | CRUSH rule 第一步使用的 crush bucket 名。例 `step take default` | String | 否 | **`default`** |
|
||
| `crush-failure-domain` | 确保没有两个 chunk 落在具有相同故障域的 bucket 中。用于生成形如 `step chooseleaf host` 的 CRUSH rule step | String | 否 | **`host`** |
|
||
| `crush-device-class` | 把放置限制到特定 class 的设备(如 `ssd` / `hdd`) | String | 否 | (源文 `:Default:` 后为空) |
|
||
| `directory` | 加载 plugin 的目录名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `--force` | 覆盖同名的已有 profile | String | 否 | — |
|
||
|
||
> 💡 补充:注意 isa 的默认 `k=7 m=3`(空间开销 `(7+3)/7 ≈ 1.43`),而**集群初始化时创建的 `default` profile 是 `k=2 m=2`**(见 7.3)。**"isa plugin 的默认 k/m" 与 "default profile 的 k/m" 是两个不同的东西**,不要混淆。
|
||
|
||
#### 7.5.3 lrc(Locally Repairable Code,本地可修复码)
|
||
|
||
**要解决的问题**(原文):
|
||
|
||
> With the *isa* plugin, when an erasure coded object is stored on multiple OSDs, **recovering from the loss of one OSD requires reading from *k* others**. For instance if *isa* is configured with *k=8* and *m=4*, recovering from the loss of one OSD requires reading from eight others.
|
||
|
||
**lrc 的做法**:创建**本地 parity chunk**,使得恢复只需**更少的存活 OSD**。例:`k=8, m=4, l=4` 时,会**每 4 个 OSD 额外创建一个 parity chunk**。当单个 OSD 丢失时,**只需 4 个 OSD 就能恢复**(而不是 8 个)。
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set {name} \
|
||
plugin=lrc \
|
||
k={data-chunks} \
|
||
m={coding-chunks} \
|
||
l={locality} \
|
||
[crush-root={root}] \
|
||
[crush-locality={bucket-type}] \
|
||
[crush-failure-domain={bucket-type}] \
|
||
[crush-device-class={device-class}] \
|
||
[directory={directory}] \
|
||
[--force]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 / 示例 |
|
||
|---|---|---|---|---|
|
||
| `k` | 每个对象拆成 **data-chunks** 份,各存不同 OSD | Integer | **是** | 例 `4` |
|
||
| `m` | 计算 **coding chunks**,各存不同 OSD。数量 = 可挂掉的 OSD 数而不丢数据 | Integer | **是** | 例 `2` |
|
||
| `l`(locality) | 把 coding chunk 和 data chunk **分组,每组大小为 locality**。例:**k=4, m=2, locality=3** 时会创建**两个大小为 3 的组**。**每一组都可以在不读取其他组 chunk 的情况下被恢复** | Integer | **是** | 例 `3` |
|
||
| `crush-root` | CRUSH rule 第一步使用的 crush bucket 名 | String | 否 | **`default`** |
|
||
| `crush-locality` | 由 **l** 定义的**每一组 chunk 存放在什么类型的 CRUSH bucket 中**。例:设为 `rack` 时,每组 **l** 个 chunk 会被放在**不同的 rack** 中。用于生成形如 `step choose rack` 的 CRUSH rule step。**若不设置,则不进行此类分组** | String | 否 | 文档未明确(表格中未填) |
|
||
| `crush-failure-domain` | 确保没有两个 chunk 落在具有相同故障域的 bucket 中。用于生成 `step chooseleaf host` 之类 | String | 否 | **`host`** |
|
||
| `crush-device-class` | 限制到特定 class 的设备 | String | 否 | (源文 `:Default:` 后为空) |
|
||
| `directory` | 加载 plugin 的目录名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `--force` | 覆盖同名的已有 profile | String | 否 | — |
|
||
|
||
**约束**:***k* 与 *m* 之和必须是 *l* 参数的整数倍**("The sum of **k** and **m** must be a multiple of the **l** parameter")。
|
||
|
||
##### 三个 profile 示例
|
||
|
||
**① 减少 host 间的恢复带宽**
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
k=4 m=2 l=3 \
|
||
crush-failure-domain=host
|
||
ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
> 原文评价:虽然"所有 host 都接在同一个交换机上"时这大概不是个有意思的用例,但**带宽用量的降低确实是可观测的**。
|
||
|
||
**② 减少 rack 间的恢复带宽**
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
k=4 m=2 l=3 \
|
||
crush-locality=rack \
|
||
crush-failure-domain=host
|
||
ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
> ⚠️ **In Firefly the bandwidth reduction will only be observed if the primary OSD is in the same rack as the lost chunk.**
|
||
|
||
##### 底层(low level)配置
|
||
|
||
底层配置参数**不强制** "k+m 必须是 l 的倍数" 这个限制,在特定用途下反而有优势:
|
||
|
||
- 可以定义两个组,一个 4 个 chunk、另一个 3 个 chunk;
|
||
- 也可以**递归地定义 locality 集合**,例如 datacenter、以及 datacenter 内的 rack。
|
||
|
||
**k/m/l 是通过生成一份底层配置来实现的。lrc plugin 递归地应用纠删码技术,使得大多数情况下从某些 chunk 的丢失中恢复只需要可用 chunk 的一个子集。**
|
||
|
||
**三层编码示例**:
|
||
|
||
```text
|
||
chunk nr 01234567
|
||
step 1 _cDD_cDD
|
||
step 2 cDDD____
|
||
step 3 ____cDDD
|
||
```
|
||
|
||
(*c* = 由 data chunk *D* 计算出的 coding chunk)
|
||
|
||
- 丢失 chunk **7** → 可用**最后四个** chunk 恢复;
|
||
- 丢失 chunk **2** → 可用**前四个** chunk 恢复。
|
||
|
||
**底层 profile 示例**:
|
||
|
||
```bash
|
||
# 最小测试:等价于 K=2 M=1(DD 意味着 K=2,c 意味着 M=1,默认用 isa plugin)
|
||
ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
mapping=DD_ \
|
||
layers='[ [ "DDc", "" ] ]'
|
||
ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
```bash
|
||
# 减少 host 间恢复带宽:等价于 k=4, m=2, l=3,但 chunk 布局不同
|
||
$ ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
mapping=__DD__DD \
|
||
layers='[
|
||
[ "_cDD_cDD", "" ],
|
||
[ "cDDD____", "" ],
|
||
[ "____cDDD", "" ],
|
||
]'
|
||
$ ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
```bash
|
||
# 减少 rack 间恢复带宽
|
||
$ ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
mapping=__DD__DD \
|
||
layers='[
|
||
[ "_cDD_cDD", "" ],
|
||
[ "cDDD____", "" ],
|
||
[ "____cDDD", "" ],
|
||
]' \
|
||
crush-steps='[
|
||
[ "choose", "rack", 2 ],
|
||
[ "chooseleaf", "host", 4 ],
|
||
]'
|
||
|
||
$ ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
> ⚠️ In Firefly the reduced bandwidth will only be observed if the primary OSD is in the same rack as the lost chunk.
|
||
|
||
**指定每层的 EC 后端**:`layers='[ [ "DDc", "" ] ]'` 中的**第二个参数实际上就是用于该层的 erasure code profile**。
|
||
|
||
```bash
|
||
# LRC 默认使用 ISA 作为 EC 后端;可逐层指定
|
||
ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
mapping=DD_ \
|
||
layers='[ [ "DDc", "plugin=jerasure technique=cauchy" ] ]'
|
||
ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
```bash
|
||
# 每层用不同 profile
|
||
$ ceph osd erasure-code-profile set LRCprofile \
|
||
plugin=lrc \
|
||
mapping=__DD__DD \
|
||
layers='[
|
||
[ "_cDD_cDD", "plugin=isa technique=cauchy" ],
|
||
[ "cDDD____", "plugin=isa" ],
|
||
[ "____cDDD", "plugin=jerasure" ],
|
||
]'
|
||
$ ceph osd pool create lrcpool erasure LRCprofile
|
||
```
|
||
|
||
> 原文明确:**LRC now uses ISA as the default EC backend.**
|
||
|
||
##### 编解码算法(原文逐步推演)
|
||
|
||
以 4K 对象、上述三层为例:
|
||
|
||
**编码**:
|
||
|
||
1. **step 1**:对象先被分成**四个 1K chunk**(四个大写 D),按序存在 chunk 2、3、6、7。由此计算出**两个 coding chunk**(两个小写 c),分别存在 chunk 1 和 5。
|
||
2. **step 2**:以类似方式**复用 step 1 创建的内容**,在**位置 0** 存一个 coding chunk `c`。最后四个 chunk(用 `_` 标记便于阅读)被忽略。
|
||
3. **step 3**:在**位置 4** 存一个 coding chunk `c`。这个 coding chunk 是用 **step 1 创建的三个 chunk** 算出来的 —— **即 step 1 的 coding chunk 在 step 3 中变成了 data chunk**。
|
||
|
||
**解码(以丢失 chunk 2 为例)**:
|
||
|
||
```text
|
||
chunk nr 01234567
|
||
|
||
step 1 _c D_cDD
|
||
step 2 cD D____
|
||
step 3 __ _cDDD
|
||
```
|
||
|
||
解码会**按相反顺序遍历各 step**:step 3 → step 2 → step 1。
|
||
|
||
1. **step 3** 对 chunk 2 一无所知(是下划线),**被跳过**;
|
||
2. **step 2** 的 coding chunk(存在 chunk 0)**允许恢复 chunk 2 的内容**。没有更多 chunk 需要恢复,**过程停止,不再考虑 step 1**。
|
||
3. **恢复 chunk 2 需要读取 chunk 0、1、3,并写回 chunk 2。**
|
||
|
||
**解码(丢失 chunk 2、3、6)**:
|
||
|
||
```text
|
||
chunk nr 01234567
|
||
|
||
step 1 _c _c D
|
||
step 2 cD __ _
|
||
step 3 __ cD D
|
||
```
|
||
|
||
1. **step 3** 可以恢复 chunk **6** 的内容 → 状态变为:
|
||
|
||
```text
|
||
chunk nr 01234567
|
||
|
||
step 1 _c _cDD
|
||
step 2 cD ____
|
||
step 3 __ cDDD
|
||
```
|
||
|
||
2. **step 2** 恢复失败并被跳过,因为**缺两个 chunk(2、3),而它只能从一个缺失 chunk 中恢复**;
|
||
3. **step 1** 的 coding chunk(存在 chunk 1、5)**允许恢复 chunk 2、3 的内容** → 回到完整状态。
|
||
|
||
##### 控制 CRUSH 放置
|
||
|
||
默认 CRUSH rule 提供**位于不同 host 上的 OSD**。例如上例需要**恰好 8 个 OSD**(每个 chunk 一个)。若这些 host 位于**两个相邻 rack**,可以把前四个 chunk 放在第一个 rack、后四个放在第二个 rack,这样**恢复单个 OSD 就不需要跨 rack 带宽**。
|
||
|
||
```bash
|
||
crush-steps='[ [ "choose", "rack", 2 ], [ "chooseleaf", "host", 4 ] ]'
|
||
```
|
||
|
||
→ 这条规则会选**两个 `rack` 类型的 crush bucket**,并为其中每个选**四个 OSD**,且这些 OSD 各自位于**不同的 `host` 类型 bucket** 中。
|
||
|
||
> 原文:The CRUSH rule can also be manually crafted for finer control.(也可以手工编制 CRUSH rule 以获得更精细的控制。)
|
||
|
||
#### 7.5.4 shec(Shingled Erasure Code)
|
||
|
||
> The *shec* plugin encapsulates the multiple SHEC library. **It allows ceph to recover data more efficiently than Reed Solomon codes.**
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set {name} \
|
||
plugin=shec \
|
||
[k={data-chunks}] \
|
||
[m={coding-chunks}] \
|
||
[c={durability-estimator}] \
|
||
[crush-root={root}] \
|
||
[crush-failure-domain={bucket-type}] \
|
||
[crush-device-class={device-class}] \
|
||
[directory={directory}] \
|
||
[--force]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 |
|
||
|---|---|---|---|---|
|
||
| `k` | 每个对象拆成 **data-chunks** 份,各存不同 OSD | Integer | 否 | **4** |
|
||
| `m` | 计算 **coding-chunks** 并存在不同 OSD 上。⚠️ **coding-chunk 的数量不一定等于"可以挂掉的 OSD 数而不丢数据"** | Integer | 否 | **3** |
|
||
| `c` | **每个 data chunk 被包含在多少个 parity chunk 的计算范围内**。这个数字被用作 **durability estimator(持久性估计量)**。例:**c=2 时可以有 2 个 OSD 挂掉而不丢数据** | Integer | 否 | **2** |
|
||
| `crush-root` | CRUSH rule 第一步使用的 crush bucket 名 | String | 否 | **`default`** |
|
||
| `crush-failure-domain` | 确保没有两个 chunk 落在具有相同故障域的 bucket 中。生成 `step chooseleaf host` 之类 | String | 否 | **`host`** |
|
||
| `crush-device-class` | 限制到特定 class 的设备 | String | 否 | (源文 `:Default:` 后为空) |
|
||
| `directory` | 加载 plugin 的目录名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `--force` | 覆盖同名的已有 profile | String | 否 | — |
|
||
|
||
**SHEC 的三个设计维度**:
|
||
|
||
| 维度 | 定义 | 调节方式 |
|
||
|---|---|---|
|
||
| **Space Efficiency(空间效率)** | **data chunk 与对象中所有 chunk 之比**,表示为 **`k / (k + m)`** | **提高 k 或降低 m** 可改善。例:`SHEC(4,3,2)` 的空间效率 = `4/(4+3)` = **0.57**;`SHEC(5,3,2)` 或 `SHEC(4,2,2)` 都能改善它 |
|
||
| **Durability(持久性)** | 第三个参数 `c` 是 **durability estimator**,近似等于"可挂掉的 OSD 数而不丢数据" | 例:`SHEC(4,3,2)` 的 durability estimator = **2** |
|
||
| **Recovery Efficiency(恢复效率)** | 原文明确:描述其计算方法**超出本文档范围**,但至少**"增加 m 而不增加 c"可以改善恢复效率**(代价是牺牲空间效率) | 例:`SHEC(4,2,2)` → `SHEC(4,3,2)` 即改善恢复效率 |
|
||
|
||
**示例**:
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set SHECprofile \
|
||
plugin=shec \
|
||
k=8 m=4 c=3 \
|
||
crush-failure-domain=host
|
||
ceph osd pool create shecpool erasure SHECprofile
|
||
```
|
||
|
||
#### 7.5.5 clay(Coupled-Layer,耦合层码)
|
||
|
||
**设计目标**:**在一个故障 node / OSD / rack 被修复时,显著节省网络带宽和磁盘 IO**。
|
||
|
||
定义 **`d` = 修复期间联系到的 OSD 数量**。
|
||
|
||
- 若 *jerasure* 配置为 `k=8, m=4`,**丢失一个 OSD 需要从 *d=8* 个其他 OSD 读取**来修复。恢复 1 GiB 需要下载 **8 × 1 GiB = 8 GiB**。
|
||
- 而 **clay** 的 **`d` 是可配置的**,范围是 **`k+1 <= d <= k+m-1`**。
|
||
- **默认情况下 clay 取 `d = k+m-1`**,因为它在网络带宽和磁盘 IO 上节省最多。
|
||
- 例:`k=8, m=4, d=11` 时,单个 OSD 故障会联系 **d=11** 个 OSD,**每个下载 250 MiB**,总共下载 **11 × 250 MiB = 2.75 GiB**。
|
||
|
||
**磁盘 IO 对比公式**:
|
||
|
||
| plugin | 总磁盘 IO 量 |
|
||
|---|---|
|
||
| **jerasure、isa** | `k S` |
|
||
| **clay** | `d S / (d - k + 1)` = `(k + m - 1) S / m` |
|
||
|
||
(*S* = 正在修复的单个 OSD 上存储的数据量。表中用了 **最大的 d**,因为这会带来最少的下载量。)
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set {name} \
|
||
plugin=clay \
|
||
k={data-chunks} \
|
||
m={coding-chunks} \
|
||
[d={helper-chunks}] \
|
||
[scalar_mds={plugin-name}] \
|
||
[technique={technique-name}] \
|
||
[crush-failure-domain={bucket-type}] \
|
||
[crush-device-class={device-class}] \
|
||
[directory={directory}] \
|
||
[--force]
|
||
```
|
||
|
||
| 参数 | 说明 | 类型 | 必填 | 默认值 / 示例 |
|
||
|---|---|---|---|---|
|
||
| `k` | 每个对象拆成 **data-chunks** 份,各存不同 OSD | Integer | **是** | 例 `4` |
|
||
| `m` | 计算 **coding chunks**,各存不同 OSD。数量 = 可挂掉的 OSD 数而不丢数据 | Integer | **是** | 例 `2` |
|
||
| `d` | **恢复单个 chunk 时请求发送数据的 OSD 数量**。***d* 必须满足 `k+1 <= d <= k+m-1`。*d* 越大,节省越多** | Integer | 否 | **`k+m-1`** |
|
||
| `scalar_mds` | 指定在分层构造中用作**构建块**的 plugin,可以是 `jerasure`、`isa`、`shec` | String | 否 | **`jerasure`** |
|
||
| `technique` | 指定在 `scalar_mds` plugin 内选择哪种 technique。支持的技术:**jerasure 支持** `reed_sol_van`、`reed_sol_r6_op`、`cauchy_orig`、`cauchy_good`、`liber8tion`;**isa 支持** `reed_sol_van`、`cauchy`;**shec 支持** `single`、`multiple` | String | 否 | **`reed_sol_van`**(对 jerasure、isa)/**`single`**(对 shec) |
|
||
| `crush-root` | CRUSH rule 第一步使用的 crush bucket 名 | String | 否 | **`default`** |
|
||
| `crush-failure-domain` | 确保没有两个 chunk 落在具有相同故障域的 bucket 中 | String | 否 | **`host`** |
|
||
| `crush-device-class` | 限制到特定 class 的设备 | String | 否 | (源文 `:Default:` 后为空) |
|
||
| `directory` | 加载 plugin 的目录名 | String | 否 | **`/usr/lib/ceph/erasure-code`** |
|
||
| `--force` | 覆盖同名的已有 profile | String | 否 | — |
|
||
|
||
> 💡 补充:注意 clay 的**命令模板**里没有列出 `crush-root`,但**参数表**里列了 `crush-root`(默认 `default`)。以参数表为准。
|
||
|
||
**示例**:
|
||
|
||
```bash
|
||
ceph osd erasure-code-profile set CLAYprofile \
|
||
plugin=clay \
|
||
k=4 m=2 d=5 \
|
||
crush-failure-domain=host
|
||
ceph osd pool create claypool erasure CLAYprofile
|
||
```
|
||
|
||
##### sub-chunk 概念
|
||
|
||
Clay 能节省磁盘 IO 和网络带宽,因为它是**向量码(vector code)**,能**以更细的粒度(称为 **sub-chunk**)查看和操作 chunk 内的数据**。
|
||
|
||
- 一个 chunk 内的 **sub-chunk 数量** = `q^((k+m)/q)`,其中 **`q = d - k + 1`**;
|
||
- 修复 OSD 时,**从可用 OSD 请求的 helper 信息只是 chunk 的一小部分**。确切地说,修复期间被访问的 **sub-chunk 数量** = `sub-chunk count / q`。
|
||
|
||
**例子**:
|
||
|
||
| 配置 | sub-chunk count | repair sub-chunk count | 效果 |
|
||
|---|---|---|---|
|
||
| `k=4, m=2, d=5` | 8 | 4 | **修复时只读半个 chunk** |
|
||
| `k=8, m=4, d=11` | 64 | 16 | **修复时从每个可用 OSD 读四分之一个 chunk** |
|
||
|
||
##### 如何按负载选配置
|
||
|
||
由于**只读 chunk 中的少数 sub-chunk**,且这些 sub-chunk **在 chunk 内不一定是连续存储的**,而**读连续数据对磁盘 IO 性能最有利** —— 因此**建议选择使 sub-chunk size 足够大的 stripe-size**。
|
||
|
||
对给定的 stripe-size(由负载决定),选择 `k`、`m`、`d` 使得:
|
||
|
||
```text
|
||
sub-chunk size = stripe-size / (k × sub-chunk count) = 4KB, 8KB, 12KB ...
|
||
```
|
||
|
||
| 负载 | 建议 |
|
||
|---|---|
|
||
| **大尺寸负载**(stripe size 大) | 容易选 k、m、d。例:stripe-size = 64MB,选 `k=16, m=4, d=19` → sub-chunk count = 1024,sub-chunk size = **4KB** |
|
||
| **小尺寸负载** | `k=4, m=2` 是个好配置,**同时提供网络和磁盘 IO 收益** |
|
||
|
||
##### 与 LRC 的对比
|
||
|
||
| | LRC | CLAY |
|
||
|---|---|---|
|
||
| 设计重心 | 让修复时联系到的 OSD 数(d)**最小** | 让**总下载量/磁盘 IO 最小** |
|
||
| 存储开销 | **`(m + (k+m)/d) / k`**(在 `m` 个 parity 之外还要存 `(k+m)/d` 个 parity) | **`m / k`** |
|
||
| 可恢复的故障数 | **任意 `m` 个 OSD** | **任意 `m` 个 OSD** |
|
||
|
||
**对比表(原文)**:
|
||
|
||
| 参数 | LRC:磁盘 IO, 存储开销 | CLAY:磁盘 IO, 存储开销 |
|
||
|---|---|---|
|
||
| `(k=10, m=4)` | `7 * S`, **0.6**(d=7) | `3.25 * S`, **0.4**(d=13) |
|
||
| `(k=16, m=4)` | `4 * S`, **0.5625**(d=4) | `4.75 * S`, **0.25**(d=19) |
|
||
|
||
(S = 被恢复的单个 OSD 上存储的数据量。)
|
||
|
||
> 读法提示:LRC 在 `(k=16,m=4)` 时的**磁盘 IO(4S)** 比 CLAY(4.75S)更小,但**存储开销(0.5625)** 远大于 CLAY(0.25)。**两者都能从任意 m 个 OSD 故障中恢复。** 选哪个取决于你更在意磁盘 IO 还是容量。
|
||
|
||
### 7.6 EC 与副本的对比与取舍(原文观点汇总)
|
||
|
||
| 维度 | 副本(`replicated`) | 纠删码(`erasure`) |
|
||
|---|---|---|
|
||
| 数据保护方式 | 每个对象被复制到多个磁盘 | 数据拆成 data chunk + coding chunk,用 parity 重建 |
|
||
| 原始空间开销(overhead factor) | `size=3` → **3.0** | **`(k+m)/k`**。例 `k=4,m=2` → **1.5**(1 GiB 用户数据占 1.5 GiB 底层存储) |
|
||
| 可用容量 | 给定原始存储,**可用容量较少** | 通常**更多** |
|
||
| 适用组件 | **所有 Ceph 组件与用例** | **仅部分组件与用例**(例如不支持 omap) |
|
||
| 性能 | — | **有显著的性能折衷**,**尤其在使用 HDD 以及执行集群 recovery / backfill 时** |
|
||
| 故障域要求 | — | **通常需要比复制更多的故障域** |
|
||
| 重叠故障容忍 | — | **可能容忍更多的重叠驱动器/主机故障** |
|
||
| 资源消耗 | — | **比副本池需要更多资源** |
|
||
| 部分写(partial write) | 支持 | **默认不支持**(仅支持整对象写和 append,例如 RGW);见 7.7 |
|
||
| omap | 支持 | **不支持** |
|
||
|
||
> ⚠️ 原文警句:**Do not mistake erasure coding for a free lunch**(别把纠删码当成免费的午餐)—— 有显著的性能折衷,尤其在使用 HDD 和执行 recovery / backfill 时。
|
||
|
||
### 7.7 Erasure Coding with Overwrites(部分写)
|
||
|
||
**默认行为**:纠删码池**只能配合执行"整对象写入和追加"的操作**(例如 RGW)。
|
||
|
||
**Luminous 起**,可以为纠删码池**通过 per-pool 设置启用部分写**,这让 **RBD 和 CephFS 能把数据存在纠删码池中**:
|
||
|
||
```bash
|
||
ceph osd pool set ec_pool allow_ec_overwrites true
|
||
```
|
||
|
||
**硬性前提与限制**:
|
||
|
||
| 限制 | 说明 |
|
||
|---|---|
|
||
| **必须在 BlueStore OSD 上** | 只能在位于 BlueStore OSD 的 pool 上启用,因为 **BlueStore 的 checksumming 会在 deep scrub 期间被用来检测 bitrot 或其它损坏** |
|
||
| **不要配 Filestore** | "Using Filestore with EC overwrites is **not only unsafe, but it also results in lower performance** compared to BlueStore." 且 **Filestore 已废弃**,集群中任何 Filestore OSD 都应迁移到 BlueStore |
|
||
| ** omap 限制** | **纠删码池不支持 omap**,因此 RBD 和 CephFS 必须**把数据存在 EC 池、元数据存在副本池** |
|
||
|
||
**RBD 用法**(把 EC 池作为 `--data-pool`):
|
||
|
||
```bash
|
||
rbd create --size 1G --data-pool ec_pool replicated_pool/image_name
|
||
```
|
||
|
||
**CephFS 用法**:可以在创建文件系统时把纠删码池设为**默认 data pool**,或通过 file-layouts 设置。
|
||
|
||
### 7.8 Erasure Coding Optimizations(Tentacle 新增)
|
||
|
||
> Since **Tentacle**, an erasure-coded pool may have optimizations enabled with a per-pool setting. This **improves performance for smaller I/Os** and **eliminates padding which can save capacity**.
|
||
|
||
```bash
|
||
ceph osd pool set ec_pool allow_ec_optimizations true
|
||
```
|
||
|
||
#### 收益与适用面
|
||
|
||
| 场景 | 收益 |
|
||
|---|---|
|
||
| **RBD / CephFS** | 优化会让 EC 池**更适合**与它们配合使用 |
|
||
| **RGW:大量极小对象,或对象的小随机读** | 会看到**性能和容量收益** |
|
||
| **RGW:大对象、顺序读写** | **收益甚微** |
|
||
|
||
#### 关键约束
|
||
|
||
| 约束 | 说明 |
|
||
|---|---|
|
||
| **可后加,但不可关闭** | 该 flag **可以为已有 pool 启用**;但一旦启用就**不能禁用**,因为**它改变了新数据的存储方式** |
|
||
| **全局默认值** | 可通过 central configuration 选项 **`osd_pool_default_flag_ec_optimizations`** 配置成新建 pool 的默认行为 |
|
||
| **升级要求** | **除非所有 Monitor 和 OSD 都已升级到 Tentacle 或更高版本,否则该 flag 不能被设置**。⚠️ 但**优化启用后,Gateway 和 Client 不升级也可以使用** |
|
||
| **plugin / technique 限制** | **目前仅支持 Jerasure 和 ISA-L plugin 配合 `reed_sol_van` technique**(这是旧的和当前的默认,也是最广泛使用的 plugin 和 technique)。为 plugin/technique 组合不受支持的 pool 设置该 flag **会被报错阻止** |
|
||
|
||
#### stripe unit 调优(重要)
|
||
|
||
| 项 | 说明 |
|
||
|---|---|
|
||
| 默认 stripe unit | **4K**,对标准 EC 池工作良好 |
|
||
| 启用优化后推荐 | **对大多数 I/O 负载,建议把 stripe unit 提升到至少 16K**。**性能测试表明 16K 是通用 I/O 负载的最佳选择** |
|
||
| 提升的收益/代价 | **显著提升小读性能**,但**会略微降低小顺序写的性能** |
|
||
| 读密集型负载 | 可以取**更大值,最大到 256KB**,进一步改善读性能,但**会进一步降低小顺序写性能** |
|
||
| 超过 256KB | **不太可能有任何性能收益** |
|
||
| 设置方式 | stripe unit 是 **pool create-time 选项**,可在 EC profile 中设置,或通过 central configuration 选项 **`osd_pool_erasure_code_stripe_unit`** 设置 |
|
||
| ⚠️ 不可事后更改 | **stripe unit 在 pool 创建之后不能更改** —— 因此**若对已有 pool 启用优化,你无法获得优化的全部收益** |
|
||
|
||
#### 优化对 k / m 选择的影响
|
||
|
||
| 条件 | 影响 |
|
||
|---|---|
|
||
| **未启用优化** | `k+m` 的选择影响性能。**`k` 和 `m` 越大,性能越低** |
|
||
| **启用优化后** | 随 `k` 增大**只有非常轻微的性能下降** → **使用更高的 `k` 变得更可行** |
|
||
| **`m` 的影响(两种情况)** | **增大 `m` 仍然影响写性能,尤其是小写** → **对块和文件负载,建议 `m` 不大于 3** |
|
||
|
||
### 7.9 EC 池的开销与限制
|
||
|
||
#### 开销公式与选型建议
|
||
|
||
**开销因子(空间放大)= `(k+m) / k`**。
|
||
|
||
- `k=4, m=2` → 开销 **1.5**(1 GiB 用户数据用 1.5 GiB 底层存储);
|
||
- 对比默认 `size=3` 复制 → 开销 **3.0**。
|
||
|
||
**选型建议(原文,重点)**:
|
||
|
||
1. **随 `k` 增大超过 4,容量开销的边际收益很快递减,但性能影响成比例增长**。
|
||
→ **建议:除非你充分理解其后果(包括集群拓扑呈现的故障域数量),否则不要选择 `k > 4` 或 `m > 2` 的 profile。**
|
||
2. **若选 `m=1`,预期在维护期间会出现数据不可用,在组件故障重叠时会出现数据丢失。**
|
||
→ **`m=1` 的 profile 强烈不建议用于生产数据。**
|
||
3. **必须在大量重叠组件故障中仍保持活跃且不丢数据的部署,可能倾向于 `m > 2`。**
|
||
→ 注意这类 profile 会**降低空间效率、降低性能,尤其在 backfill 和 recovery 期间**。
|
||
4. **如果确定要用 EC 但不确定用哪个 profile,选 `k=4` 和 `m=2`。**
|
||
→ **可获得相比 `size=3` 复制翻倍的可用空间,且写和恢复性能影响相对可容忍。**
|
||
|
||
#### EC 开销因子全表(原文 list-table 完整复现)
|
||
|
||
> 读法:行 = `k`,列 = `m`,单元格 = `(k+m)/k` 开销因子。
|
||
|
||
| | m=1 | m=2 | m=3 | m=4 | m=5 | m=6 | m=7 | m=8 | m=9 | m=10 | m=11 |
|
||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||
| **k=1** | 2.00 | 3.00 | 4.00 | 5.00 | 6.00 | 7.00 | 8.00 | 9.00 | 10.00 | 11.00 | 12.00 |
|
||
| **k=2** | 1.50 | 2.00 | 2.50 | 3.00 | 3.50 | 4.00 | 4.50 | 5.00 | 5.50 | 6.00 | 6.50 |
|
||
| **k=3** | 1.33 | 1.67 | 2.00 | 2.33 | 2.67 | 3.00 | 3.33 | 3.67 | 4.00 | 4.33 | 4.67 |
|
||
| **k=4** | 1.25 | **1.50** | 1.75 | 2.00 | 2.25 | 2.50 | 2.75 | 3.00 | 3.25 | 3.50 | 3.75 |
|
||
| **k=5** | 1.20 | 1.40 | 1.60 | 1.80 | 2.00 | 2.20 | 2.40 | 2.60 | 2.80 | 3.00 | 3.20 |
|
||
| **k=6** | 1.16 | 1.33 | 1.50 | 1.66 | 1.83 | 2.00 | 2.17 | 2.33 | 2.50 | 2.66 | 2.83 |
|
||
| **k=7** | 1.14 | 1.29 | 1.43 | 1.58 | 1.71 | 1.86 | 2.00 | 2.14 | 2.29 | 2.43 | 2.58 |
|
||
| **k=8** | 1.13 | 1.25 | 1.38 | 1.50 | 1.63 | 1.75 | 1.88 | 2.00 | 2.13 | 2.25 | 2.38 |
|
||
| **k=9** | 1.11 | 1.22 | 1.33 | 1.44 | 1.56 | 1.67 | 1.78 | 1.88 | 2.00 | 2.11 | 2.22 |
|
||
| **k=10** | 1.10 | 1.20 | 1.30 | 1.40 | 1.50 | 1.60 | 1.70 | 1.80 | 1.90 | 2.00 | 2.10 |
|
||
| **k=11** | 1.09 | 1.18 | 1.27 | 1.36 | 1.45 | 1.54 | 1.63 | 1.72 | 1.82 | 1.91 | 2.00 |
|
||
| **k=12** | 1.08 | 1.17 | 1.25 | 1.33 | 1.42 | 1.50 | 1.58 | 1.67 | 1.75 | 1.83 | 1.92 |
|
||
| **k=20** | 1.05 | 1.10 | 1.15 | 1.20 | 1.25 | 1.30 | 1.35 | 1.40 | 1.45 | 1.50 | 1.55 |
|
||
|
||
#### 故障域数量要求(原文 note)
|
||
|
||
> Most erasure-coded pool deployments **require at least `k+m` CRUSH failure domains**, which in most cases means `rack`s or `hosts`.
|
||
|
||
- 将 EC profile 和集群拓扑规划成**至少有 `k+m+1` 个故障域**有**运维上的好处**;
|
||
- **大多数情况下不鼓励 `k` > 8**。
|
||
|
||
#### 小文件场景的空间放大警告(原文 note)
|
||
|
||
> CephFS 和 RGW 部署中,**若有相当比例是非常小的用户文件/对象**,应仔细规划 —— **纠删码数据池会导致相当大的额外空间放大**。
|
||
|
||
**应对思路(原文给的 RGW 示例)**:CephFS 和 RGW 都支持**多个具有不同介质、性能和数据保护策略的数据池**。一个 RGW 部署可以:
|
||
|
||
- 配置**数量适中的 TLC SSD**,由**副本模式的 index 和默认 bucket 数据池**使用;
|
||
- 配置**更大规模的纠删码 QLC SSD 或 HDD**,通过 **storage class、placement target 或 Lua 脚本**把**更大、更冷的对象**导向它们。
|
||
|
||
#### 与 cache tiering 的关系(已废弃)
|
||
|
||
纠删码池**比副本池需要更多资源**,且**缺少副本池支持的部分功能(例如 omap)**。为克服这些限制,过去可以在 EC 池前设置一个 cache tier。
|
||
|
||
> ⚠️ **Cache tiering was deprecated in Reef.** 官方强烈建议**不要部署新的 cache tier**,并努力**从已有部署中移除**。
|
||
|
||
### 7.10 EC 池的恢复(Recovery)
|
||
|
||
- 若纠删码池丢失了任何 data shard,必须**从其他 shard 恢复**;
|
||
- 恢复过程包括:**从剩余 shard 读取 → 重建数据 → 写入新 shard**。
|
||
|
||
**版本行为差异**:
|
||
|
||
| 版本 | 恢复门槛 |
|
||
|---|---|
|
||
| **Octopus 及以后** | **只要有至少 *K* 个 shard 可用,纠删码池就可以恢复**(少于 *K* 个 shard 时,你实际上是丢数据了) |
|
||
| **Octopus 之前** | 即使 `min_size` 大于 `K`,也**要求至少 `min_size` 个 shard 可用**。这是设计新 pool 模式时出于充分谨慎(abundance of caution)做出的保守决定。**其后果是**:丢失了 OSD 但**数据并未完全丢失**的 pool,**无法恢复并进入 active,必须人工介入临时修改 `min_size`** |
|
||
|
||
**官方建议**:
|
||
|
||
> We recommend that **`min_size` be `K+1` or greater** to prevent loss of writes and loss of data.
|
||
|
||
### 7.11 EC 术语表(官方 Glossary)
|
||
|
||
| 术语 | 定义 |
|
||
|---|---|
|
||
| **chunk** | 调用编码函数时,它返回**大小彼此相同**的 chunk。有两类:**(1) data chunk**,拼接起来即可重建原始对象;**(2) coding chunk**,可用于重建丢失的 chunk |
|
||
| **K** | 对象被分成的 **data chunk 数量**。例:*K* = 2 时,一个 10KB 对象被分成两个 5KB 的对象 |
|
||
| **M** | 编码函数计算出的 **coding chunk 数量**。*M* **等于集群中可以有几个 OSD 缺失而不遭受数据丢失**。例:有两个 coding chunk 时,可以缺失两个 OSD 而不丢数据 |
|
||
|
||
> 引自:[Erasure code](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code/)、[Erasure code profiles](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-profile/)、[Jerasure erasure code plugin](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-jerasure/)、[ISA erasure code plugin](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-isa/)、[Locally repairable erasure code plugin](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-lrc/)、[SHEC erasure code plugin](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-shec/)、[CLAY code plugin](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code-clay/)
|
||
|
||
---
|
||
|
||
## 八、存储引擎:BlueStore、FileStore 与 Journal
|
||
|
||
### 8.1 OSD Back Ends 概览
|
||
|
||
| 后端 | 状态 |
|
||
|---|---|
|
||
| **BlueStore** | **Luminous 12.2.z 起成为默认(且推荐)后端** |
|
||
| **FileStore** | Luminous 之前是默认(且唯一)后端。⚠️ **Filestore has been deprecated in the Reef release and is no longer supported** |
|
||
|
||
### 8.2 BlueStore
|
||
|
||
#### 定位
|
||
|
||
BlueStore 是**专为 Ceph OSD 工作负载管理磁盘数据而设计的专用存储后端**,其设计基于**支持和运维 Filestore OSD 十年的经验**。
|
||
|
||
#### 六大关键特性(官方列举)
|
||
|
||
| 特性 | 说明 |
|
||
|---|---|
|
||
| **直接管理存储设备** | BlueStore 消费**裸块设备或分区**。这避免了中间抽象层(例如 XFS 这样的本地文件系统)**限制性能或增加复杂度** |
|
||
| **用 RocksDB 管理元数据** | 内嵌 RocksDB 的 key/value 数据库来管理内部元数据,**包括对象名到磁盘上块位置的映射** |
|
||
| **全量数据和元数据校验** | 默认情况下,**所有写入 BlueStore 的数据和元数据都被一个或多个校验和保护**。**任何数据或元数据从磁盘读出或返回给用户之前,都必须通过验证** |
|
||
| **内联压缩** | 数据在写入磁盘前可以**选择性地被压缩** |
|
||
| **多设备元数据分层** | BlueStore 允许把它的**内部 journal(write-ahead log)写到独立的高速设备**(如 SSD、NVMe 或 NVDIMM)以提升性能。**若有相当数量的更快存储可用,内部元数据也可以存在更快的设备上** |
|
||
| **高效的 copy-on-write** | RBD 和 CephFS 的快照依赖 copy-on-write 的 *clone* 机制,**BlueStore 中该机制被高效实现**。这让常规快照和纠删码池(依赖 clone 来实现高效的 two-phase commit)都有高效的 I/O |
|
||
|
||
### 8.3 BlueStore 的设备模型
|
||
|
||
#### 三种设备
|
||
|
||
BlueStore 管理**一个、两个、或在某些情况下三个**存储设备。这里的 "device" 是 Linux/Unix 意义上的设备 —— 列在 `/dev` 或 `/devices` 下的资产。**每个设备可以是整块存储盘、一个分区、或一个逻辑卷。**
|
||
|
||
> ⚠️ **BlueStore does not create or mount a conventional file system on devices that it uses**; BlueStore **以 "raw" 方式直接读写这些设备**。
|
||
|
||
| 设备 | 标识 | 说明 |
|
||
|---|---|---|
|
||
| **primary device(主设备)** | data 目录中的 **`block` 符号链接** | 最简单的情况下,BlueStore 占用单个存储设备的全部空间 |
|
||
| **WAL device(write-ahead log 设备)** | **`block.wal`** | 用于把 BlueStore 的**内部 journal(write-ahead log)**分离出来。**只有在 WAL 设备比主设备更快时才有优势**(例:WAL 是 SSD,主设备是 HDD) |
|
||
| **DB device** | **`block.db`** | 用于存储 BlueStore 的**内部元数据**。**BlueStore(更准确地说是内嵌的 RocksDB)会尽可能把元数据放到 DB 设备上以提升性能**。⚠️ **若 DB 设备写满,元数据会溢出(spill back)回主设备**(即没有 DB 设备时它本该在的地方)。同样,**只有在比主设备更快时配置 DB 设备才有优势** |
|
||
|
||
**data directory(数据目录)** 是一个 **`tmpfs` 挂载点**。当它被 `ceph-volume` 启动或激活时,会被填入元数据文件和链接,保存该 OSD 的信息:例如 **OSD 的标识符、所属集群名、OSD 的 private keyring**。
|
||
|
||
#### WAL vs DB 的选择策略(原文决策树)
|
||
|
||
| 可用高速存储量 | 建议 |
|
||
|---|---|
|
||
| **很少**(例如**不足 1 GB**) | **用作 WAL device** |
|
||
| **更多** | **配置 DB device 更有意义** |
|
||
|
||
**为什么更多空间时应该选 DB 而不是 WAL**:
|
||
|
||
> Because **the BlueStore journal is always placed on the fastest device available**, using a DB device provides **the same benefit that using a WAL device would**, while ***also* allowing additional metadata to be stored off the primary device** (provided that it fits).
|
||
|
||
**原理**:**当指定了 DB device 但没有显式指定 WAL device 时,WAL 会隐式地与 DB 一起共存(colocated)在更快的设备上。**
|
||
|
||
#### 部署命令
|
||
|
||
```bash
|
||
# 单设备(colocated)BlueStore OSD
|
||
ceph-volume lvm prepare --bluestore --data <device>
|
||
|
||
# 指定 WAL / DB 设备
|
||
ceph-volume lvm prepare --bluestore --data <device> --block.wal <wal-device> --block.db <db-device>
|
||
```
|
||
|
||
> ⚠️ `--data` 参数可以接受以下任一形式:**以 *vg/lv* 记法指定的逻辑卷**、**已存在的逻辑卷**、**GPT 分区**。
|
||
|
||
### 8.4 BlueStore 的两种常见部署形态
|
||
|
||
#### block (data) only(所有设备同类型)
|
||
|
||
**适用场景**:所有设备类型相同(例如都是 HDD),且**没有可用于存元数据的快速设备**。
|
||
|
||
做法:**只指定 block 设备,把 `block.db` 和 `block.wal` 保持不分离**。
|
||
|
||
```bash
|
||
# 单个 /dev/sda 设备
|
||
ceph-volume lvm create --bluestore --data /dev/sda
|
||
|
||
# 若用预创建的逻辑卷 ceph-vg/block-lv
|
||
ceph-volume lvm create --bluestore --data ceph-vg/block-lv
|
||
```
|
||
|
||
#### block 和 block.db(混合快慢设备)
|
||
|
||
**适用场景**:混合了快速与慢速设备(例如 SSD 或 HDD)。
|
||
|
||
做法:**推荐把 `block.db` 放在更快的设备上,而 `block`(即数据)放在较慢的设备(即旋转盘)上**。
|
||
|
||
> ⚠️ **必须手工创建这些 volume group 和 logical volume** —— **`ceph-volume` 工具目前无法自动创建它们**。
|
||
|
||
**完整示例(原文,4 个 HDD + 1 个 SSD)**:
|
||
|
||
```bash
|
||
# 1. 创建 volume groups(4 块旋转盘 sda/sdb/sdc/sdd)
|
||
vgcreate ceph-block-0 /dev/sda
|
||
vgcreate ceph-block-1 /dev/sdb
|
||
vgcreate ceph-block-2 /dev/sdc
|
||
vgcreate ceph-block-3 /dev/sdd
|
||
|
||
# 2. 为 block 创建 logical volumes
|
||
lvcreate -l 100%FREE -n block-0 ceph-block-0
|
||
lvcreate -l 100%FREE -n block-1 ceph-block-1
|
||
lvcreate -l 100%FREE -n block-2 ceph-block-2
|
||
lvcreate -l 100%FREE -n block-3 ceph-block-3
|
||
|
||
# 3. 假设 /dev/sdx 上有一块 200GB SSD,创建 4 个 50GB 的 LV
|
||
vgcreate ceph-db-0 /dev/sdx
|
||
lvcreate -L 50GB -n db-0 ceph-db-0
|
||
lvcreate -L 50GB -n db-1 ceph-db-0
|
||
lvcreate -L 50GB -n db-2 ceph-db-0
|
||
lvcreate -L 50GB -n db-3 ceph-db-0
|
||
|
||
# 4. 创建 4 个 OSD
|
||
ceph-volume lvm create --bluestore --data ceph-block-0/block-0 --block.db ceph-db-0/db-0
|
||
ceph-volume lvm create --bluestore --data ceph-block-1/block-1 --block.db ceph-db-0/db-1
|
||
ceph-volume lvm create --bluestore --data ceph-block-2/block-2 --block.db ceph-db-0/db-2
|
||
ceph-volume lvm create --bluestore --data ceph-block-3/block-3 --block.db ceph-db-0/db-3
|
||
```
|
||
|
||
结果:**4 个 OSD**,`block` 在 4 块 HDD 上,每块 HDD 在共享的 SSD 上有一个 50GB 的 logical volume(即 DB 设备)。
|
||
|
||
### 8.5 Sizing:block.db 该多大
|
||
|
||
**总原则**:使用混合旋转盘+固态盘配置时,为 BlueStore 创建**足够大的 `block.db` logical volume** 很重要。**`block.db` 对应的 logical volume 应尽可能大。**
|
||
|
||
| 场景 | 建议 `block.db` 大小 |
|
||
|---|---|
|
||
| **通用** | **`block` 大小的 1% ~ 4%** 之间 |
|
||
| **RGW 负载** | **至少 `block` 大小的 4%** —— 因为 **RGW 大量使用 `block.db` 存元数据(尤其是 omap keys)**。<br>例:`block` 为 1TB 时,`block.db` **至少 40GB** |
|
||
| **RBD 负载** | 通常**不需要超过 `block` 大小的 1% ~ 2%** |
|
||
|
||
#### RocksDB level size 的历史包袱与现状
|
||
|
||
**老版本**:内部 level size 使得 DB **只能充分利用**那些对应 `L0`、`L0+L1`、`L1+L2` ... 之和的特定分区/逻辑卷大小 —— 即**在默认设置下大约 3GB、30GB、300GB 等等**。**大多数部署不会因为把容量做到容纳 L3 及以上而实质受益**,不过 **把上述数字翻倍(6GB、60GB、600GB)有助于 DB compaction**。
|
||
|
||
**新版本**:**Nautilus 14.2.12、Octopus 15.2.6 及后续版本的改进,使得任意大小的 DB 设备都能被更好地利用**。此外 **Pacific 带来了实验性的 dynamic-level 支持**。
|
||
|
||
> 💡 建议(基于原文):由于这些进步,**老版本用户可能希望现在就预置更大的 DB 设备,以便未来升级时能享受到规模化的收益**。
|
||
|
||
#### 单一设备类型时
|
||
|
||
**不使用快慢混合设备时**,**没有必要** 为 `block.db` 或 `block.wal` 创建独立的 logical volume —— **BlueStore 会自动把它们与 `block` 共存(colocate)在 `block` 的空间内**。
|
||
|
||
### 8.6 Automatic Cache Sizing(自动缓存大小)
|
||
|
||
**启用条件(两者都要满足)**:
|
||
|
||
1. **TCMalloc 必须被配置为内存分配器**(memory allocator);
|
||
2. **`bluestore_cache_autotune` 配置选项必须启用**(⚠️ 原文注:**note that it is currently enabled by default** —— **当前默认是启用的**)。
|
||
|
||
**工作机制**:
|
||
|
||
- BlueStore 尝试**把 OSD 堆内存使用保持在某个目标大小之下**(该目标由 **`osd_memory_target`** 决定);
|
||
- 这是一种 **best-effort 算法**;
|
||
- **缓存不会缩到小于 `osd_memory_cache_min` 所定义的大小**;
|
||
- **缓存比率按优先级层次结构选择**;但**如果优先级信息不可用,则用 `bluestore_cache_meta_ratio` 和 `bluestore_cache_kv_ratio` 的值作为兜底(fallback)缓存比率**。
|
||
|
||
#### 相关配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_cache_autotune` | 文档未明确 | **启用(源文:"it is currently enabled by default")** | 是否启用 BlueStore 缓存自动调优 | 💡 保持默认开启;只在需要精确控制缓存布局时才关掉并改用手工 sizing |
|
||
| `osd_memory_target` | 文档未明确 | 4_G(= 4294967296 字节 = 4 GiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 自动缓存调优试图把 OSD 堆内存使用保持在其下的**目标大小** | 💡 容器化部署时尤其要注意与 cgroup memory limit 对齐,避免 OOM |
|
||
| `bluestore_cache_autotune_interval` | 文档未明确 | 5(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 自动调优的调整间隔 | 💡 一般不动;若观察到缓存频繁抖动可考虑放慢 |
|
||
| `osd_memory_base` | 文档未明确 | 768_M(= 805306368 字节 = 768 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 计算内存目标时的**基线/基础内存** | 💡 一般不动 |
|
||
| `osd_memory_expected_fragmentation` | 文档未明确 | 0.15(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 计算内存目标时**预期为内存碎片预留的比例** | 💡 TCMalloc 碎片率明显高于预期时可调 |
|
||
| `osd_memory_cache_min` | 文档未明确 | 128_M(= 134217728 字节 = 128 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **缓存收缩的下限** —— 自动调优时 caches do not shrink smaller than this | 💡 若发现缓存被压得过小导致命中率下降,可适当抬高 |
|
||
| `osd_memory_cache_resize_interval` | 文档未明确 | 1(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 缓存 resize 的间隔 | 💡 一般不动 |
|
||
|
||
### 8.7 Manual Cache Sizing(手工缓存大小)
|
||
|
||
**内存预算的决定方式**:
|
||
|
||
- 每个 OSD 用于 BlueStore cache 的内存量由 **`bluestore_cache_size`** 决定;
|
||
- **若该选项未指定(即保持为 `0`)**,Ceph 用另一个配置选项决定默认内存预算:
|
||
- 主设备是 **HDD** → **`bluestore_cache_size_hdd`**
|
||
- 主设备是 **SSD** → **`bluestore_cache_size_ssd`**
|
||
|
||
> ⚠️ 原文提醒:**除了配置的缓存大小之外,还有 OSD 自身消耗的内存**,以及**内存碎片和其它分配器开销带来的额外占用**。BlueStore 和 Ceph OSD 守护进程的其他部分**会尽一切努力在这个内存预算内工作**。
|
||
|
||
**这段缓存内存可用于存放三类东西**:
|
||
|
||
1. **Key/Value 元数据**(即 RocksDB 的内部缓存);
|
||
2. **BlueStore 元数据**;
|
||
3. **BlueStore 数据**(即最近读或最近写的对象数据)。
|
||
|
||
**分配规则**:
|
||
|
||
- 缓存内存使用由 **`bluestore_cache_meta_ratio`** 和 **`bluestore_cache_kv_ratio`** 控制;
|
||
- **为数据保留的那部分缓存** 由「有效 BlueStore 缓存大小(取决于相应的 `bluestore_cache_size[_ssd|_hdd]` 选项和主设备的 device class)」以及「meta 与 kv 比率」共同决定;
|
||
- **数据部分的计算公式(官方给出)**:
|
||
|
||
```text
|
||
<effective_cache_size> × (1 - bluestore_cache_meta_ratio - bluestore_cache_kv_ratio)
|
||
```
|
||
|
||
#### 相关配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_cache_size` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文:**若未指定即保持 `0`,则用 `_hdd` / `_ssd` 变体**) | 每个 OSD 用于 BlueStore cache 的内存量 | 💡 设为 0 让 Ceph 按设备类型自动选;需要精确控制时再显式设 |
|
||
| `bluestore_cache_size_hdd` | 文档未明确 | 1_G(= 1073741824 字节 = 1 GiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 主设备为 HDD 时的默认缓存内存预算 | 💡 HDD OSD 通常给较小值(元数据访问慢,但 RAM 也少) |
|
||
| `bluestore_cache_size_ssd` | 文档未明确 | 3_G(= 3221225472 字节 = 3 GiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 主设备为 SSD 时的默认缓存内存预算 | 💡 NVMe OSD 可适当给大以提升命中率 |
|
||
| `bluestore_cache_meta_ratio` | 文档未明确 | 0.45(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 缓存中留给 **BlueStore 元数据**的比例 | 💡 与 kv_ratio 之和应 < 1,剩下的才是 data 部分(见上方公式) |
|
||
| `bluestore_cache_kv_ratio` | 文档未明确 | 0.45(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 缓存中留给 **RocksDB KV** 的比例 | 💡 RGW 这类 omap 重负载可适当提高 |
|
||
|
||
### 8.8 Checksums(校验和)
|
||
|
||
**覆盖范围**:**BlueStore 对写入磁盘的所有元数据和所有数据都做校验和**。
|
||
|
||
| 对象 | 由谁做 | 算法 |
|
||
|---|---|---|
|
||
| **元数据** | **RocksDB** | **`crc32c`** |
|
||
| **数据** | **BlueStore** | **`crc32c`** / **`xxhash32`** / **`xxhash64`** |
|
||
|
||
> **`crc32c` 是默认校验和算法,且适用于大多数用途。**
|
||
|
||
**校验和的元数据开销**:
|
||
|
||
- 全量数据校验和**增加了 BlueStore 必须存储和管理的元数据量**;
|
||
- **只要有可能**(例如客户端提示数据是顺序写入和读取的),BlueStore 会**校验更大的块**;
|
||
- **但在很多情况下,它必须为每 4 KB 数据块存一个校验和值(通常 4 字节)**。
|
||
|
||
**缩短校验和以省元数据(有代价)**:
|
||
|
||
| 校验和长度 | 随机错误未被检出的概率 |
|
||
|---|---|
|
||
| 32-bit(4 字节) | 约 **四十亿分之一** |
|
||
| 16-bit(2 字节,`crc32c_16`) | **1 / 65,536** |
|
||
| 8-bit(1 字节,`crc32c_8`) | **1 / 256** |
|
||
|
||
> 想用更短的校验和值,就把校验和算法选成 **`crc32c_16`** 或 **`crc32c_8`**。
|
||
|
||
**设置方式**:校验和算法可以**通过 per-pool 的 `csum_type` 配置选项**指定,**也可以通过全局配置选项**指定:
|
||
|
||
```bash
|
||
ceph osd pool set <pool-name> csum_type <algorithm>
|
||
```
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_csum_type` | 文档未明确 | **`crc32c`**(源文明确:"**`crc32c` is the default checksum algorithm**") | 数据校验和算法。可选 `crc32c`、`xxhash32`、`xxhash64`、`crc32c_16`、`crc32c_8` | 💡 元数据极多、想省空间时可考虑 `crc32c_16`(代价是检错率降到 1/65536);常规保持默认 |
|
||
|
||
### 8.9 Inline Compression(内联压缩)
|
||
|
||
**支持的算法**:**`snappy`、`zlib`、`lz4`、`zstd`**。
|
||
|
||
**是否压缩由两个因素决定**:**① compression mode** 和 **② 与写操作关联的 client hints**。
|
||
|
||
#### 四种 compression mode
|
||
|
||
| mode | 行为 |
|
||
|---|---|
|
||
| **`none`** | **永不**压缩数据 |
|
||
| **`passive`** | **不压缩**,除非该写操作**设置了 *compressible* hint** |
|
||
| **`aggressive`** | **压缩**数据,除非该写操作**设置了 *incompressible* hint** |
|
||
| **`force`** | **无论如何都尝试**压缩数据 |
|
||
|
||
> 关于 *compressible* 和 *incompressible* I/O hints,官方指向 `rados_set_alloc_hint`(C API)。
|
||
|
||
#### 压缩的收益门槛
|
||
|
||
> 数据只有在 **data chunk 被充分减小**(由 **`bluestore compression required ratio`** 设置决定)时才会被压缩。**无论使用哪种压缩模式,若压缩后的 data chunk 太大,它会被丢弃,转而存储原始(未压缩)数据。**
|
||
|
||
**例子**:若 `bluestore compression required ratio` 设为 `.7`,则**只有压缩后数据大小不超过原始数据大小的 70% 时才会真正压缩**。
|
||
|
||
#### 设置方式(per-pool 或全局)
|
||
|
||
```bash
|
||
ceph osd pool set <pool-name> compression_algorithm <algorithm>
|
||
ceph osd pool set <pool-name> compression_mode <mode>
|
||
ceph osd pool set <pool-name> compression_required_ratio <ratio>
|
||
ceph osd pool set <pool-name> compression_min_blob_size <size>
|
||
ceph osd pool set <pool-name> compression_max_blob_size <size>
|
||
```
|
||
|
||
> 可设置的五项:**compression mode、compression algorithm、compression required ratio、min blob size、max blob size** —— 都可以**通过 per-pool 属性或全局 config 选项**指定。
|
||
|
||
#### 相关配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_compression_algorithm` | 文档未明确 | `snappy`(默认值取自 src/common/options/global.yaml.in @ v20.2.4;合法值:`snappy` / `zlib` / `lz4` / `zstd`) | 内联压缩算法 | 💡 追求CPU/压缩率平衡首选 `lz4`;要更高压缩率用 `zstd` |
|
||
| `bluestore_compression_mode` | 文档未明确 | `none`(默认值取自 src/common/options/global.yaml.in @ v20.2.4;合法值:`none` / `passive` / `aggressive` / `force`) | 内联压缩策略 | 💡 生产建议从 `none` 或 `passive` 起步;`force` 会尝试压缩一切,CPU 代价高 |
|
||
| `bluestore_compression_required_ratio` | 文档未明确 | 0.875(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文举例用 `.7`) | 压缩收益门槛:压缩后大小 / 原始大小 不超过此比例才真正压缩 | 💡 默认逻辑就是"压不动就不压",避免负收益 |
|
||
| `bluestore_compression_min_blob_size` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 压缩 chunk 的最小尺寸(小于此值不压缩) | 💡 有大量小对象时抬高此值可避免无效压缩 |
|
||
| `bluestore_compression_min_blob_size_hdd` | 文档未明确 | 8_K(= 8192 字节 = 8 KiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,HDD 变体 | 💡 按主设备类型自动选择 |
|
||
| `bluestore_compression_min_blob_size_ssd` | 文档未明确 | 64_K(= 65536 字节 = 64 KiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,SSD 变体 | 💡 同上 |
|
||
| `bluestore_compression_max_blob_size` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 压缩 chunk 的最大尺寸(大于此值先拆分成不大于此值的更小 blob 再压缩) | 💡 与 RBD/CephFS 的 I/O 大小对齐能提升压缩效率 |
|
||
| `bluestore_compression_max_blob_size_hdd` | 文档未明确 | 64_K(= 65536 字节 = 64 KiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,HDD 变体 | 💡 同上 |
|
||
| `bluestore_compression_max_blob_size_ssd` | 文档未明确 | 64_K(= 65536 字节 = 64 KiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,SSD 变体 | 💡 同上 |
|
||
|
||
### 8.10 RocksDB Sharding(分片)
|
||
|
||
#### 原理
|
||
|
||
BlueStore 维护**多种类型的内部 key-value 数据**,全部存在 RocksDB 中。**每种数据类型在 BlueStore 中被分配一个唯一的 prefix**。
|
||
|
||
| 版本 | 存储方式 |
|
||
|---|---|
|
||
| **Pacific 之前** | 所有 key-value 数据存在**单个 RocksDB column family**:`'default'` |
|
||
| **Pacific 及以后** | BlueStore 可以把 key-value 数据**划分到多个 RocksDB column family** |
|
||
|
||
**收益**:当 key 相似时(具体地:**相似的访问频率、相似的修改频率、相似的生命周期**),BlueStore 能获得**更好的缓存**和**更精确的 compaction**。在此条件下:
|
||
|
||
- **性能被改善**;
|
||
- **compaction 期间需要的磁盘空间更少**(因为**每个 column family 更小,且能独立于其他 column family 进行 compaction**)。
|
||
|
||
#### 启用
|
||
|
||
- **Pacific 或以后版本中部署的 OSD 默认使用 RocksDB sharding**;
|
||
- ⚠️ **但若 Ceph 是从之前版本升级到 Pacific 或更高版本的,则在 Pacific 之前创建的任何 OSD 上 sharding 是被禁用的**。
|
||
|
||
**为特定 OSD 启用 sharding 并应用 Pacific 默认值**:先停掉该 OSD,然后执行:
|
||
|
||
```bash
|
||
ceph-bluestore-tool \
|
||
--path <data path> \
|
||
--sharding="m(3) p(3,0-12) O(3,0-13)=block_cache={type=binned_lru} L P" \
|
||
reshard
|
||
```
|
||
|
||
#### 相关配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_rocksdb_cf` | 文档未明确 | true(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 是否为 BlueStore 的 RocksDB 启用 column family(sharding) | 💡 Pacific+ 新建 OSD 默认启用;升级上来的老 OSD 需用上面的 `reshard` 手工开启 |
|
||
| `bluestore_rocksdb_cfs` | 文档未明确 | `m(3) p(3,0-12) O(3,0-13)=block_cache={type=binned_lru} L=min_write_buffer_number_to_merge=32 P=min_write_buffer_number_to_merge=32`(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 定义各个 column family 的具体配置(即上例 `--sharding=` 后那串内容的配置项形式) | 💡 仅在需要精细调优 RocksDB 布局时修改 |
|
||
|
||
### 8.11 Throttling(限流)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_throttle_bytes` | 文档未明确 | 64_M(= 67108864 字节 = 64 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | BlueStore 在**开始限流(defer 或阻塞)之前允许的在途(in-flight)写入字节数** | 💡 写压力大、出现 deferred write 堆积时可配合下面几项调节 |
|
||
| `bluestore_throttle_deferred_bytes` | 文档未明确 | 128_M(= 134217728 字节 = 128 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 允许在途的 **deferred(延迟)写入**的字节数 | 💡 与 WAL/DB 设备性能强相关;快速设备上可放宽 |
|
||
| `bluestore_throttle_cost_per_io` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 每个 I/O 被计入限流的"成本" | 💡 一般不动 |
|
||
| `bluestore_throttle_cost_per_io_hdd` | 文档未明确 | 670000(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,HDD 变体 | 💡 一般不动 |
|
||
| `bluestore_throttle_cost_per_io_ssd` | 文档未明确 | 4000(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 同上,SSD 变体 | 💡 一般不动 |
|
||
|
||
### 8.12 Minimum Allocation Size(最小分配单元,重要)
|
||
|
||
#### 概念
|
||
|
||
**BlueStore 在底层存储设备上分配的最小存储量**是配置好的。**实践中,这是即使一个极小的 RADOS 对象在每个 OSD 的主设备上也会消耗的最少容量。**
|
||
|
||
**取值来源**:`bluestore_min_alloc_size` 的值**派生自** `bluestore_min_alloc_size_hdd` 或 `bluestore_min_alloc_size_ssd`,**取决于该 OSD 的 `rotational` 属性**:
|
||
|
||
- HDD 上创建的 OSD → 用 `bluestore_min_alloc_size_hdd` 的当前值初始化;
|
||
- SSD OSD(**包括 NVMe 设备**)→ 用 `bluestore_min_alloc_size_ssd` 的当前值初始化。
|
||
|
||
#### 默认值随版本演进
|
||
|
||
| 版本 | HDD(rotational) | SSD(non-rotational) |
|
||
|---|---|---|
|
||
| **Mimic 及更早** | **64KB** | **16KB** |
|
||
| **Octopus** | 64KB | **改为 4KB** |
|
||
| **Pacific** | **改为 4KB** | 4KB |
|
||
|
||
> **变更动因**:托管大量小文件(S3/Swift 对象)的 Ceph RADOS Gateway(RGW)部署所经历的**空间放大(space amplification)**。
|
||
|
||
#### 空间放大如何发生(原文算例)
|
||
|
||
| 用户对象 | 存成 | 底层分配 | 浪费 | 开销 |
|
||
|---|---|---|---|---|
|
||
| **1 KB** S3 对象 | 单个 RADOS 对象 | **4 KB** 底层空间 | 约 3 KB 被分配但从不使用 | **300% 开销 / 25% 效率** |
|
||
| **5 KB** 用户对象 | 两个 RADOS 对象(4KB + 1KB) | 4KB + 4KB | 4KB 设备容量被闲置 | 开销百分比**小得多** |
|
||
|
||
> 开销**百分比**随对象增大而**迅速下降**(原文提示:把它想成**取模运算的余数**)。
|
||
|
||
#### 一个容易忽略的放大:每个副本都会发生
|
||
|
||
> There is an additional subtlety that is easily missed: **the amplification phenomenon just described takes place for *each* replica.**
|
||
|
||
| 保护策略 | 1 KB S3 对象实际搁浅的设备容量 |
|
||
|---|---|
|
||
| 默认三副本(3R) | 约 **9 KB** |
|
||
| EC `k=4, m=2` | **24 KB**(即 4 KB × 6) |
|
||
|
||
**什么时候要重视**:当 RGW bucket pool 包含**大量相对较大的用户对象**时,这个现象的影响**通常可以忽略**。但对于**预期会有相当比例较小用户对象的部署**,**应该把这个效应纳入考虑**。
|
||
|
||
#### 新型 QLC SSD 的特殊考虑
|
||
|
||
- **4KB 默认值与传统 HDD 和 SSD 设备契合良好**;
|
||
- ⚠️ **某些新型 coarse-IU(Indirection Unit)QLC SSD**,当把 **`bluestore_min_alloc_size_ssd` 设为与该设备的 IU 匹配**时(**可能是 8KB、16KB,甚至 64KB**),**性能和磨损表现最佳**;
|
||
- 这类新型盘可以达到**与传统 TLC SSD 相当的读性能**、**比 HDD 更快的写性能**,且**比 TLC SSD 更高的密度和更低的成本**。
|
||
|
||
> ⚠️ **在这些新型设备上创建 OSD 时,必须小心:只对合适的设备应用非默认值,不要应用到传统 HDD 和 SSD 上。** 避免出错的手段:**仔细安排 OSD 创建的顺序**、**使用自定义 OSD device classes**、**尤其是使用中心配置的 *masks***。
|
||
|
||
#### 自动探测(Quincy+)
|
||
|
||
**Quincy 及以后**,可以用 **`bluestore_use_optimal_io_size_for_min_alloc_size`** 选项,让**每个 OSD 创建时自动发现正确的值**。
|
||
|
||
> ⚠️ 干扰因素:**`bcache`、`OpenCAS`、`dmcrypt`、`ATA over Ethernet`、`iSCSI` 等其它设备分层与抽象技术可能会干扰正确值的判定**。此外,**部署在 VMware 存储之上的 OSD 有时被发现会报告与底层硬件不匹配的 `rotational` 属性**。
|
||
|
||
**排查方法**:
|
||
|
||
1. **通过日志和 admin socket 检查这类 OSD 的启动行为**(⚠️ 在较老内核上这种检查可能不如预期工作);
|
||
2. **检查 `/sys/block/<drive>/queue/optimal_io_size` 的存在性和值**;
|
||
3. **Reef 及以后**,`min_alloc_size` **会由 `ceph osd metadata` 方便地报告出来**:
|
||
|
||
```bash
|
||
ceph osd metadata osd.1701 | egrep rotational\|alloc
|
||
```
|
||
|
||
#### 空间放大的外部表现(原文)
|
||
|
||
| 现象 | 排查命令 |
|
||
|---|---|
|
||
| `ceph df` 报告的 **raw 与 stored 数据之比异常高** | `ceph df` |
|
||
| `ceph osd df` 报告的 **`%USE` / `VAR`** 相比其他"表面相同"的 OSD 异常高 | `ceph osd df` |
|
||
| 使用了 `min_alloc_size` 值不匹配的 OSD 的 pool 中,出现**意料之外的 balancer 行为** | — |
|
||
|
||
#### ⚠️ 生效时机
|
||
|
||
> This BlueStore attribute **takes effect *only* at OSD creation**; if the attribute is changed later, a specific OSD's behavior will not change **unless and until the OSD is destroyed and redeployed** with the appropriate option value(s). **Upgrading to a later Ceph release will *not* change the value used by OSDs that were deployed under older releases or with other settings.**
|
||
|
||
#### 相关配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_min_alloc_size` | 文档未明确 | 由 `_hdd` 或 `_ssd` 派生(取决于 OSD 的 `rotational` 属性) | OSD 实际生效的最小分配单元 | 💡 只读性质的派生值;要改就改下面两个 |
|
||
| `bluestore_min_alloc_size_hdd` | 文档未明确 | **Pacific 起为 4KB**(Mimic 及更早为 **64KB**) | HDD OSD 的最小分配单元 | 💡 **小对象密集的 RGW 场景保持 4KB**;大对象顺序负载可考虑调大以减少元数据 |
|
||
| `bluestore_min_alloc_size_ssd` | 文档未明确 | **Octopus 起为 4KB**(Mimic 及更早为 **16KB**) | SSD(含 NVMe)OSD 的最小分配单元 | 💡 **coarse-IU QLC SSD 应设为与设备 IU 匹配(8KB/16KB/64KB)**,且只作用于这类设备 |
|
||
| `bluestore_use_optimal_io_size_for_min_alloc_size` | 文档未明确 | false(默认值取自 src/common/options/global.yaml.in @ v20.2.4;Quincy 起可用) | 允许每个 OSD **在创建时自动发现**正确的 `min_alloc_size` | 💡 混合介质集群推荐开启;但**注意 bcache/dmcrypt/iSCSI/VMware 等抽象层会干扰判定** |
|
||
|
||
### 8.13 SPDK Usage(NVMe 用户态驱动)
|
||
|
||
**前置条件**:先准备好系统,参见 [SPDK document](http://www.spdk.io/doc/getting_started.html#getting_started_examples)。SPDK 提供一个自动配置设备的脚本,需要 root 权限运行:
|
||
|
||
```bash
|
||
sudo src/spdk/scripts/setup.sh
|
||
```
|
||
|
||
**指定设备**:必须为 `bluestore_block_path` 指定该 NVMe 设备的**设备选择器(device selector)**,并加 **`spdk:` 前缀**。
|
||
|
||
```bash
|
||
# 查找 Intel NVMe SSD 的设备选择器
|
||
lspci -mm -n -D -d 8086:0953
|
||
```
|
||
|
||
设备选择器的形式是 **`DDDD:BB:DD.FF`** 或 **`DDDD.BB.DD.FF`**。
|
||
|
||
```ini
|
||
# 例:若 lspci 输出中找到的设备选择器是 0000:01:00.0
|
||
bluestore_block_path = "spdk:trtype:PCIe traddr:0000:01:00.0"
|
||
```
|
||
|
||
也可以指定**基于 TCP 传输的远程 NVMeoF target**:
|
||
|
||
```ini
|
||
bluestore_block_path = "spdk:trtype:TCP traddr:10.67.110.197 trsvcid:4420 subnqn:nqn.2019-02.io.spdk:cnode1"
|
||
```
|
||
|
||
**每节点运行多个 SPDK 实例**时,必须**为每个实例指定它将使用的 DPDK 内存量(MB)**,以确保每个实例使用自己的 DPDK 内存。
|
||
|
||
**colocate(数据、DB、WAL 共用一个设备)时的必配项** —— 若不设置这些,当前实现会用**内核文件系统符号**填充 SPDK map 文件,并**使用内核驱动来下发 DB/WAL I/O**:
|
||
|
||
```ini
|
||
bluestore_block_db_path = ""
|
||
bluestore_block_db_size = 0
|
||
bluestore_block_wal_path = ""
|
||
bluestore_block_wal_size = 0
|
||
```
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `bluestore_block_path` | String | 文档未明确 | 主设备(block)路径。使用 SPDK 时填 `spdk:...` 形式的设备选择器 | 💡 仅在使用 SPDK 用户态 NVMe 驱动时设置 |
|
||
| `bluestore_block_db_path` | String | 文档未明确 | DB 设备路径 | 💡 SPDK colocate 时必须显式置空 |
|
||
| `bluestore_block_db_size` | 文档未明确 | 0(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | DB 设备大小 | 💡 SPDK colocate 时必须显式置 `0` |
|
||
| `bluestore_block_wal_path` | String | 文档未明确 | WAL 设备路径 | 💡 SPDK colocate 时必须显式置空 |
|
||
| `bluestore_block_wal_size` | 文档未明确 | 96_M(= 100663296 字节 = 96 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | WAL 设备大小 | 💡 SPDK colocate 时必须显式置 `0` |
|
||
|
||
### 8.14 DSA(Data Streaming Accelerator)Usage
|
||
|
||
**用途**:若想用 **DML 库驱动 DSA 设备**来**卸载持久内存(PMEM)上的读写操作**。
|
||
|
||
**硬件/软件前提**:
|
||
|
||
- 安装 [DML](https://github.com/intel/DML) 和 [idxd-config](https://github.com/intel/idxd-config) 库;
|
||
- **仅在拥有 SPR(Sapphire Rapids)CPU 的机器上工作**。
|
||
|
||
**配置共享工作队列(WQ)**:
|
||
|
||
```bash
|
||
accel-config config-wq --group-id=1 --mode=shared --wq-size=16 --threshold=15 --type=user --name="MyApp1" --priority=10 --block-on-fault=1 dsa0/wq0.1
|
||
accel-config config-engine dsa0/engine0.1 --group-id=1
|
||
accel-config enable-device dsa0
|
||
accel-config enable-wq dsa0/wq0.1
|
||
```
|
||
|
||
### 8.15 BlueStore vs FileStore 对比
|
||
|
||
| 维度 | **BlueStore** | **FileStore(已废弃)** |
|
||
|---|---|---|
|
||
| **状态** | Luminous 12.2.z 起**默认且推荐** | Luminous 之前是默认(且唯一)。⚠️ **Reef 起废弃且不再被支持** |
|
||
| **数据路径** | **直接管理裸块设备或分区**,不创建也不挂载常规文件系统 | 依赖**标准文件系统(通常是 XFS)** + 一个 key/value 数据库(**传统是 LevelDB,现在是 RocksDB**)存部分元数据 |
|
||
| **抽象层** | 无中间抽象层 —— 避免了诸如 XFS 这类本地文件系统**限制性能或增加复杂度** | 依赖传统文件系统存对象数据,**整体设计导致许多性能缺陷** |
|
||
| **元数据** | 内嵌 RocksDB 管理(含对象名→磁盘块位置的映射) | 文件系统 + KV 数据库 |
|
||
| **校验和** | **全量数据和元数据校验**(元数据 RocksDB 用 crc32c;数据 BlueStore 用 crc32c/xxhash32/xxhash64) | 本册源文未描述 FileStore 的校验和能力 |
|
||
| **内联压缩** | 支持(snappy/zlib/lz4/zstd) | 本册源文未描述 |
|
||
| **多设备分层** | 支持把 WAL/DB 放到更快设备 | 见下面的 journal 机制 |
|
||
| **copy-on-write clone** | **高效实现**,让常规快照和 EC 池(依赖 clone 做高效 two-phase commit)都高效 | 本册源文未描述 |
|
||
| **部分写 / EC overwrites** | **支持**(`allow_ec_overwrites` 只能在 BlueStore 上启用) | **不支持且不安全**:"Using Filestore with EC overwrites is **not only unsafe, but it also results in lower performance** compared to BlueStore." |
|
||
| **推荐文件系统** | N/A | **只推荐 XFS**。**btrfs 和 ext4 都有已知 bug 和缺陷,使用它们可能导致数据丢失**。⚠️ **默认情况下所有 Ceph provisioning 工具都用 XFS** |
|
||
| **成熟度** | 当前标准 | "FileStore is well-tested and widely used in production." —— 但有上述性能缺陷 |
|
||
|
||
> **迁移**:官方提供 BlueStore Migration 文档(`rados/operations/bluestore-migration`),**不在本册 25 个源文范围内**。
|
||
|
||
### 8.16 FileStore 配置参考(历史引擎,全部逐项列出)
|
||
|
||
> ⚠️ 官方 note:自 Luminous 起 FileStore 不再是默认后端(BlueStore 是)。**FileStore OSD 一直支持到 Quincy;在 Reef 中不被支持。**
|
||
|
||
#### 8.16.1 调试
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_debug_omap_check` | Boolean | **`false`** | 对同步做调试检查。**开销昂贵(Expensive)**,**仅供调试** | 💡 只在排障时临时开 |
|
||
|
||
#### 8.16.2 Extended Attributes(扩展属性)
|
||
|
||
**背景**:XATTRs 对 FileStore OSD 很重要。**用底层文件系统存 XATTRs 有两类缺点**:某些文件系统**对可存储的 XATTR 字节数有限制**,且某些情况下**比替代的 XATTR 存储方式更慢**。因此**用一种外在于底层文件系统的 XATTR 存储方法可能提升性能**。
|
||
|
||
**规则**:
|
||
|
||
- 若底层文件系统**没有大小限制** → Ceph XATTRs 用文件系统提供的 XATTR 存为 **inline xattr**;
|
||
- 若**有大小限制**(例:**ext4 限制总共 4 KB**)→ **达到限制后**,部分 Ceph XATTRs 会被存进 key/value 数据库。更准确地说,**当 `filestore_max_inline_xattr_size` 或 `filestore_max_inline_xattrs` 阈值被达到时**开始发生。
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_max_inline_xattr_size` | Unsigned 32-bit Integer | **`0`** | 可在文件系统(XFS/Btrfs/ext4)中存储的**每个 XATTR 的最大大小**。指定的大小**不应超过文件系统能处理的大小**。**用默认值 `0` 表示让 Filestore 使用文件系统特定的值** | 💡 保持 0,让 Ceph 按文件系统自动选 |
|
||
| `filestore_max_inline_xattr_size_xfs` | Unsigned 32-bit Integer | **`65536`** | 可存在 **XFS** 中的 XATTR 最大大小。**仅当 `filestore_max_inline_xattr_size == 0` 时使用** | 💡 一般不动 |
|
||
| `filestore_max_inline_xattr_size_btrfs` | Unsigned 32-bit Integer | **`2048`** | 可存在 **Btrfs** 中的 XATTR 最大大小。仅当上项为 0 时使用 | 💡 一般不动 |
|
||
| `filestore_max_inline_xattr_size_other` | Unsigned 32-bit Integer | **`512`** | 可存在**其它文件系统**中的 XATTR 最大大小。仅当 `filestore_max_inline_xattr_size == 0` 时使用 | 💡 一般不动 |
|
||
| `filestore_max_inline_xattrs` | 32-bit Integer | **`0`** | 可在文件系统中存储的**每个对象的 XATTR 最大数量**。**默认值 `0` 表示让 Filestore 使用文件系统特定的值** | 💡 保持 0 |
|
||
| `filestore_max_inline_xattrs_xfs` | 32-bit Integer | **`10`** | **XFS** 中每对象可存的 XATTR 最大数量。仅当上项为 0 时使用 | 💡 一般不动 |
|
||
| `filestore_max_inline_xattrs_btrfs` | 32-bit Integer | **`10`** | **Btrfs** 中每对象可存的 XATTR 最大数量。仅当上项为 0 时使用 | 💡 一般不动 |
|
||
| `filestore_max_inline_xattrs_other` | 32-bit Integer | **`2`** | **其它文件系统**中每对象可存的 XATTR 最大数量。仅当上项为 0 时使用 | 💡 一般不动 |
|
||
|
||
#### 8.16.3 Synchronization Intervals(同步间隔)
|
||
|
||
**背景**:Filestore 必须周期性地**静默写入并同步文件系统**。**每次同步创建一个一致的提交点(consistent commit point)。** 创建提交点时,Filestore 能够**释放到该点为止的所有 journal 条目**。
|
||
|
||
| 频率 | 影响 |
|
||
|---|---|
|
||
| **更频繁的同步** | 倾向于**减少同步时间**以及**需要保留在 journal 中的数据量** |
|
||
| **更不频繁的同步** | 让后端文件系统**合并小写和元数据更新**,**可能提升同步效率**,但**也可能增加尾延迟(tail latency)** |
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_max_sync_interval` | Double | **`5`** | 同步 Filestore 的**最大间隔(秒)** | 💡 与 journal 大小联动;journal 小则调小此项 |
|
||
| `filestore_min_sync_interval` | Double | **`.01`** | 同步 Filestore 的**最小间隔(秒)** | 💡 一般不动 |
|
||
|
||
#### 8.16.4 Flusher(冲刷器)
|
||
|
||
**背景**:Filestore flusher **在同步之前用 `sync_file_range` 把大写入的数据强制写出**。理想情况下这会**降低最终同步的成本**。⚠️ **但在实践中,禁用 `filestore_flusher` 在某些情况下似乎能提升性能。**
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_flusher` | Boolean | **`false`** | 启用 Filestore flusher | ❗**自 v.65 起已废弃(deprecated)** |
|
||
| `filestore_flusher_max_fds` | Integer | **`512`** | flusher 的最大文件描述符数 | ❗**自 v.65 起已废弃** |
|
||
| `filestore_sync_flush` | Boolean | **`false`** | 启用同步 flusher | ❗**自 v.65 起已废弃** |
|
||
| `filestore_fsync_flushes_journal_data` | Boolean | **`false`** | 在文件系统同步期间冲刷 journal 数据 | 💡 一般不动 |
|
||
|
||
#### 8.16.5 Queue(队列)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_queue_max_ops` | Integer | **`50`** | Filestore 在阻塞新操作入队之前接受的**在途操作最大数量**。⚠️ **对性能影响极小(Minimal impact on performance)** | 💡 一般不动(官方明确说影响极小) |
|
||
| `filestore_queue_max_bytes` | Integer | **`100 << 20`** | **每个操作**允许的最大字节数 | 💡 一般不动 |
|
||
|
||
#### 8.16.6 Timeouts(超时)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_op_threads` | Integer | **`2`** | 并行执行的文件系统操作线程数 | 💡 一般不动 |
|
||
| `filestore_op_thread_timeout` | Integer | **`60`** | 文件系统操作线程的超时(秒) | 💡 一般不动 |
|
||
| `filestore_op_thread_suicide_timeout` | Integer | **`180`** | 提交操作在被取消前的超时(秒) | 💡 一般不动 |
|
||
|
||
#### 8.16.7 B-Tree Filesystem(btrfs)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_btrfs_snap` | Boolean | **`true`** | 为 `btrfs` Filestore 启用快照。**仅用于 `btrfs`** | ❗**不要用于生产**(官方只推荐 XFS,btrfs 有已知 bug 可能导致数据丢失) |
|
||
| `filestore_btrfs_clone_range` | Boolean | **`true`** | 为 `btrfs` Filestore 启用 range cloning。**仅用于 `btrfs`** | ❗同上 |
|
||
|
||
#### 8.16.8 Journal(FileStore 侧)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_journal_parallel` | Boolean | **`false`** | 启用并行 journaling,**`btrfs` 的默认** | 💡 一般不动 |
|
||
| `filestore_journal_writeahead` | Boolean | **`false`** | 启用 write-ahead journaling,**XFS 的默认** | 💡 一般不动 |
|
||
| `filestore_journal_trailing` | Boolean | **`false`** | ⚠️ **已废弃。永远不要使用(Never use)** | ❗**禁止启用** |
|
||
|
||
#### 8.16.9 Misc(杂项)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `filestore_merge_threshold` | Integer | **`-10`** | 子目录在被合并回父目录之前**允许包含的最少文件数**。⚠️ **负值意味着禁用子目录合并** | 💡 默认 `-10` 即**默认禁用合并** |
|
||
| `filestore_split_multiple` | Integer | **`2`** | 子目录在**被拆分成子目录之前允许包含的最大文件数**,公式为:`(filestore_split_multiple * abs(filestore_merge_threshold) + (rand() % filestore_split_rand_factor)) * 16` | 💡 目录过大影响查找性能时可下调 |
|
||
| `filestore_split_rand_factor` | Unsigned 32-bit Integer | **`20`** | 加到 split 阈值上的**随机因子**,用于**避免过多(昂贵的)Filestore split 同时发生**。⚠️ **要对已有 OSD 改这个设置,必须先把该 OSD 下线,再运行 `ceph-objectstore-tool apply-layout-settings`** | 💡 一般不动 |
|
||
| `filestore_update_to` | Integer | **`1000`** | 限制自动升级到指定版本的 Filestore。适用于想避免升级到某个特定版本的情况 | 💡 一般不动 |
|
||
| `filestore_blackhole` | Boolean | **`false`** | **丢弃任何新的事务**,类似重定向到 NULL | 💡 仅测试用 |
|
||
| `filestore_dump_file` | Boolean | **`false`** | 事务 dump 存放的文件 | 💡 仅调试用(⚠️ 源文 Type 写的是 Boolean,但描述是"文件",疑为源文笔误,见「文档未明确项」第 7 条) |
|
||
| `filestore_kill_at` | String | **`false`** | 在**第 n 次机会**注入一个失败 | 💡 仅故障注入测试用(⚠️ 源文 Type 为 String 但 Default 为 `false`,疑为源文笔误) |
|
||
| `filestore_fail_eio` | Boolean | **`true`** | 遇到 EIO 时失败/崩溃 | 💡 一般不动(默认开启,即不允许静默吞掉 EIO) |
|
||
|
||
### 8.17 Journal 配置参考(FileStore journal,全部逐项列出)
|
||
|
||
> ⚠️ **Filestore has been deprecated in the Reef release and is no longer supported.**
|
||
|
||
**FileStore OSD 使用 journal 的两个原因**:
|
||
|
||
| 目的 | 机制 |
|
||
|---|---|
|
||
| **Speed(速度)** | journal 让 Ceph OSD Daemon **能快速提交小写**。Ceph 把**小的随机 I/O 顺序地写入 journal**,这倾向于**通过给后端文件系统更多时间来合并写入**从而**加速突发性(bursty)负载**。⚠️ 但 Ceph OSD Daemon 的 journal **会导致尖刺式性能**:**短促的高速写入之后,是一段没有任何写入进展的时期**(文件系统在追赶 journal) |
|
||
| **Consistency(一致性)** | Ceph OSD Daemon 需要一个**保证原子复合操作**的文件系统接口。OSD **先把操作描述写入 journal,再把操作应用到文件系统**,这使得**对对象的原子更新成为可能**(例如对 placement group 元数据的更新)。**每隔几秒 —— 在 `filestore max sync interval` 与 `filestore min sync interval` 之间** —— OSD **停止写入并把 journal 与文件系统同步**,从而允许 OSD **裁剪 journal 中的操作并重用空间**。**发生故障时,OSD 从最后一次同步操作之后开始重放(replay)journal** |
|
||
|
||
> 注:自 Luminous 起 BlueStore 后端是首选且默认。本节信息**提供给已存在的 OSD,以及极少数新部署仍选择 Filestore 的情况**。
|
||
|
||
#### 全部配置项
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `journal_dio` | 文档未明确 | true(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 与 journal 的 **Direct I/O** 相关(源文仅以 `confval` 列出,无散文描述) | 💡 仅在调优已有 FileStore OSD 的 journal 时关注 |
|
||
| `journal_aio` | 文档未明确 | true(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 与 journal 的 **Async I/O** 相关(源文仅以 `confval` 列出) | 💡 同上 |
|
||
| `journal_block_align` | 文档未明确 | true(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | journal 是否按**块对齐**写入(源文仅以 `confval` 列出) | 💡 同上 |
|
||
| `journal_max_write_bytes` | 文档未明确 | 10_M(= 10485760 字节 = 10 MiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **单次 journal 写入的最大字节数**(源文仅以 `confval` 列出) | 💡 同上 |
|
||
| `journal_max_write_entries` | 文档未明确 | 100(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **单次 journal 写入的最大条目数**(源文仅以 `confval` 列出) | 💡 同上 |
|
||
| `journal_align_min_size` | 文档未明确 | 64_K(= 65536 字节 = 64 KiB)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 需要进行**对齐的最小 I/O 大小**(源文仅以 `confval` 列出) | 💡 同上 |
|
||
| `journal_zero_on_create` | 文档未明确 | false(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **创建 journal 时是否清零**(源文仅以 `confval` 列出) | 💡 同上 |
|
||
|
||
> 💡 补充:由于 FileStore 已在 Reef 废弃,且本页 7 个配置项在源文中**只有 `confval` 指令、没有任何散文描述**,上表"含义"列是根据**配置项名称**给出的语义推断,**这属于本手册的补充,官方文档未给出权威描述**。请勿据此做生产调参。
|
||
|
||
> 引自:[BlueStore Configuration Reference](https://docs.ceph.com/en/tentacle/rados/configuration/bluestore-config-ref/)、[Filestore Config Reference](https://docs.ceph.com/en/tentacle/rados/configuration/filestore-config-ref/)、[Journal Config Reference](https://docs.ceph.com/en/tentacle/rados/configuration/journal-ref/)、[Storage Devices](https://docs.ceph.com/en/tentacle/rados/configuration/storage-devices/)
|
||
|
||
---
|
||
|
||
## 九、存储设备建议(Storage Devices)
|
||
|
||
### 9.1 集群中需要存储的几类守护进程
|
||
|
||
| 守护进程 | 存储需求 |
|
||
|---|---|
|
||
| **Ceph OSDs** | **存储 Ceph 中的大部分数据**。通常**每个 OSD 由单个存储设备支撑** —— 可以是传统硬盘(HDD)或固态盘(SSD)。**OSD 也可以由多个设备的组合支撑**:例如 **HDD 存大部分数据 + SSD(或 SSD 的一个分区)存部分元数据**。**集群中 OSD 的数量通常是以下因素的函数**:① 要存的数据量;② 每个存储设备的大小;③ 指定的冗余级别与类型(复制或纠删码) |
|
||
| **Ceph Monitor** | 管理**关键集群状态**,包括**集群成员关系(membership)和认证信息**。**小型集群只需几 GB** 来存放 monitor database。**大型集群中,monitor database 可能达到数十 GB 到数百 GB** |
|
||
| **Ceph Manager** | 与 monitor 守护进程一起运行,提供额外的监控能力,并向外部监控与管理系统提供接口 |
|
||
|
||
### 9.2 OSD Back Ends 的选择
|
||
|
||
| 后端 | 状态 |
|
||
|---|---|
|
||
| **BlueStore** | **Luminous 12.2.z 起是默认(且推荐)后端** |
|
||
| **FileStore** | Luminous 之前是默认(且唯一)后端。⚠️ **Reef 起废弃且不再被支持** |
|
||
|
||
### 9.3 HDD / SSD / NVMe 选型要点(基于本册源文可推导的结论)
|
||
|
||
> ⚠️ 说明:`storage-devices.rst` **没有给出具体的型号、容量或 IOPS 选型建议表**。以下是从本册多个源文中**可推导的、与介质选型直接相关的结论**,凡属推导的均标 💡。
|
||
|
||
| 维度 | 结论 | 依据 |
|
||
|---|---|---|
|
||
| **OSD 与设备的对应关系** | 通常**每个 OSD 由一个存储设备支撑**(HDD 或 SSD);也可以由**设备组合**支撑(HDD 存数据 + SSD/SSD 分区存元数据) | storage-devices.rst(原文) |
|
||
| **OSD 数量的决定因素** | 数据量 × 单设备大小 × 冗余级别与类型(复制 / EC) | storage-devices.rst(原文) |
|
||
| **HDD vs SSD 的权重表达** | CRUSH 权重以 **1 TB = `1.00`** 为惯例,**权重通常以 TiB 衡量** | crush-map-edits.rst(原文) |
|
||
| **哪种设备需要 `block.db` / `block.wal`** | **只有当该设备比主设备更快时才"有优势"(advantageous)**。WAL 例:SSD 作 WAL + HDD 作主设备 | bluestore-config-ref.rst(原文) |
|
||
| **NVMe 属于 SSD class** | 默认 device class 自动识别为 `hdd` / `ssd` / `nvme`。⚠️ **`bluestore_min_alloc_size_ssd` 适用于 SSD OSD(包括 NVMe 设备)** | crush-map.rst + bluestore-config-ref.rst(原文) |
|
||
| **coarse-IU QLC SSD** | 应把 `bluestore_min_alloc_size_ssd` 设为**与设备 IU 匹配**(可能 8KB / 16KB / 64KB),可获得**与 TLC SSD 相当的读性能、快于 HDD 的写性能、比 TLC 更高的密度和更低成本** | bluestore-config-ref.rst(原文) |
|
||
| **PMEM(持久内存)** | 可用 **DSA(Data Streaming Accelerator)** 卸载 PMEM 上的读写,但**仅在有 SPR(Sapphire Rapids)CPU 的机器上工作** | bluestore-config-ref.rst(原文) |
|
||
| **用 SPDK 跑 NVMe** | 需要 `spdk:` 前缀的 device selector;也可指定 TCP 上的远程 NVMeoF target | bluestore-config-ref.rst(原文) |
|
||
| **BlueStore 高速设备的用途(SSD/NVMe/NVDIMM)** | 官方原文点名可用于**内部 journal(write-ahead log)**的高速设备:"like an SSD, NVMe, or NVDIMM" | storage-devices.rst(原文) |
|
||
|
||
> 💡 补充(跨章节推导的分区建议,官方文档未以"分区建议"形式给出):
|
||
>
|
||
> 1. **全 HDD 集群**:只指定 `block`,让 `block.db` / `block.wal` 与 `block` 共存(colocate)—— 源文明确"**没有必要**为它们创建独立 LV"。
|
||
> 2. **HDD + SSD 混布**:`block.db` 放 SSD,`block` 放 HDD。**`block.db` 大小取 `block` 的 1%~4%**;**RGW 至少 4%**(1TB block → 至少 40GB db);**RBD 1%~2% 通常够**。
|
||
> 3. **高速空间 < 1GB**:用作 **WAL**;**更多空间**则应做 **DB**(因为指定 DB 但未指定 WAL 时,WAL 会隐式与 DB 共存于更快设备,既享有 WAL 收益又能额外卸载元数据)。
|
||
> 4. **LV 大小的历史甜蜜点**:老版本约 **3GB / 30GB / 300GB**(对应 L0 / L0+L1 / L1+L2),**翻倍到 6GB / 60GB / 600GB 有助于 compaction**;Nautilus 14.2.12 / Octopus 15.2.6 之后**任意大小都能被更好利用**。
|
||
> 5. **必须手工创建 VG/LV** —— `ceph-volume` 目前无法自动创建。
|
||
|
||
### 9.4 官方推荐的文件系统(仅 FileStore 相关)
|
||
|
||
> ⚠️ 虽然 FileStore 能运行在大多数 POSIX 兼容文件系统上(包括 btrfs 和 ext4),**但我们推荐只把 XFS 文件系统用于 Ceph**。**btrfs 和 ext4 都有已知的 bug 和缺陷,使用它们可能导致数据丢失。默认情况下所有 Ceph provisioning 工具都用 XFS。**
|
||
|
||
> 引自:[Storage Devices](https://docs.ceph.com/en/tentacle/rados/configuration/storage-devices/)
|
||
|
||
---
|
||
|
||
## 十、upmap 与数据均衡(pg-upmap)
|
||
|
||
### 10.1 是什么
|
||
|
||
**Luminous v12.2.z 及以后**,OSDMap 中有一个 ***pg-upmap* exception table**,允许集群**显式地把特定 PG 映射到特定 OSD**。这让集群能**微调数据分布**,以在大多数情况下**把 PG 均匀分布在各 OSD 上**。
|
||
|
||
> ⚠️ **重要的前提条件**:该特性**要求所有客户端都能理解 OSDMap 中新的 *pg-upmap* 结构**。
|
||
|
||
### 10.2 在线优化(Online Optimization)
|
||
|
||
#### 启用
|
||
|
||
- **前提**:集群**不能有任何 pre-Luminous 客户端**;
|
||
- **默认情况**:**新集群默认启用 *balancer* module**,而它会使用 `pg-upmap`;
|
||
- 若你想**使用不同的 balancer**,或**想自己做自定义 `pg-upmap` 条目**,**可能需要关掉 balancer 以避免冲突**:
|
||
|
||
```bash
|
||
ceph balancer off
|
||
```
|
||
|
||
#### 在已有集群上启用该特性
|
||
|
||
必须**把集群限制为只支持 Luminous(及更新)客户端**:
|
||
|
||
```bash
|
||
ceph osd set-require-min-compat-client luminous
|
||
```
|
||
|
||
> ⚠️ **若有任何 pre-Luminous 的客户端或守护进程连在 monitor 上,该命令会失败。**
|
||
|
||
查看当前在用的客户端版本:
|
||
|
||
```bash
|
||
ceph features
|
||
```
|
||
|
||
#### Balancer Module
|
||
|
||
> The `balancer` module for `ceph-mgr` will **automatically balance the number of PGs per OSD**.
|
||
|
||
(详见 `rados/operations/balancer`,**不在本册 25 个源文范围内**。)
|
||
|
||
### 10.3 离线优化(Offline Optimization)
|
||
|
||
Upmap 条目由**内置于 `osdmaptool` 的离线优化器**更新。
|
||
|
||
#### 第 1 步:取最新的 osdmap
|
||
|
||
```bash
|
||
ceph osd getmap -o om
|
||
```
|
||
|
||
#### 第 2 步:跑优化器
|
||
|
||
```bash
|
||
osdmaptool om --upmap out.txt [--upmap-pool <pool>] \
|
||
[--upmap-max <max-optimizations>] \
|
||
[--upmap-deviation <max-deviation>] \
|
||
[--upmap-active]
|
||
```
|
||
|
||
| 选项 | 含义 | 默认值 |
|
||
|---|---|---|
|
||
| `--upmap-pool <pool>` | 指定要优化的 pool。**可多次指定** | — |
|
||
| `--upmap-max <max-optimizations>` | **最多找出多少个 upmap 条目** | **`10`**(与 `ceph-mgr` balancer module 相同)。⚠️ **若在做离线优化,应该用更大的数字**。若找不到更多可做的变更(即 pool 分布已完美),它会**提前停止** |
|
||
| `--upmap-deviation <max-deviation>` | 若某 OSD 的 PG 计数与计算出的目标数**相差不超过此值**,就认为它是**完美的** | **`5`** |
|
||
| `--upmap-active` | **模拟 active balancer 在 upmap 模式下的行为**。**持续循环直到 OSD 达到均衡**,并**报告发生了多少轮、每轮耗时**。⚠️ **每轮的耗时可用来指示 `ceph-mgr` 计算下一份优化计划时消耗的 CPU 负载** | — |
|
||
|
||
> ⚠️ **强烈建议(原文原话:It is highly recommended)**:**逐 pool 单独做优化**,或**对"使用方式相似的 pool 集合"做优化**。
|
||
>
|
||
> **"Similarly utilized pools" 的定义**:**映射到相同设备、且存储同类数据的 pool**。
|
||
> - ✅ **是**:多个 RBD image pool 互为 similarly utilized;
|
||
> - ❌ **不是**:**RGW index pool 与 RGW data pool 不算** similarly utilized。
|
||
|
||
#### 第 3 步:应用变更
|
||
|
||
```bash
|
||
source out.txt
|
||
```
|
||
|
||
> `out.txt` 中写入的是**建议的变更**;其中的命令是**普通的 Ceph CLI 命令**,可以按顺序执行以把变更应用到集群。
|
||
|
||
**上述步骤可以重复执行任意多次**,直到为每个 pool 集合达成完美的 PG 分布。
|
||
|
||
### 10.4 调试
|
||
|
||
| 目的 | 命令 |
|
||
|---|---|
|
||
| 看优化器在做什么(一些"血腥"细节) | `osdmaptool ... --debug-osd 10` |
|
||
| 看更多细节 | `osdmaptool ... --debug-crush 10` |
|
||
|
||
### 10.5 适用场景小结
|
||
|
||
| 场景 | 建议 |
|
||
|---|---|
|
||
| 新集群 | 默认已启用 balancer module(使用 `pg-upmap`),**无需额外操作** |
|
||
| 已有集群想启用 `pg-upmap` | 先 `ceph features` 确认无 pre-Luminous 客户端 → `ceph osd set-require-min-compat-client luminous` |
|
||
| 想自己精确控制 upmap 条目 | 先 `ceph balancer off`,再做自定义条目 |
|
||
| 大规模/复杂集群、在线 balancer 效果不佳 | 用 `osdmaptool --upmap` 做**离线优化**,并**逐 pool**(或按 similarly utilized 分组)执行 |
|
||
| 想知道 `ceph-mgr` 计算优化计划要花多少 CPU | 用 `--upmap-active` 观察每轮耗时 |
|
||
|
||
> 引自:[Using pg-upmap](https://docs.ceph.com/en/tentacle/rados/operations/upmap/)
|
||
|
||
---
|
||
|
||
## 十一、MON 与 OSD 的交互机制
|
||
|
||
### 11.1 总体机制
|
||
|
||
当你执行 `ceph health` 或 `ceph -s` 时,**Ceph Monitor 报告 Ceph Storage Cluster 的当前状态**。Monitor 通过两种方式了解集群:
|
||
|
||
1. **要求每个 Ceph OSD Daemon 上报**;
|
||
2. **接收 OSD 关于其邻居 OSD 状态的报告**。
|
||
|
||
**若 Monitor 没收到报告,或收到关于集群变更的报告,Monitor 就更新 Ceph Cluster Map 的状态。**
|
||
|
||
> Ceph 为 Monitor / OSD 交互提供了合理的默认设置,但可以覆盖默认值。
|
||
|
||
### 11.2 OSDs Check Heartbeats(心跳检测)
|
||
|
||
| 参数 | 值(源文明确) |
|
||
|---|---|
|
||
| **心跳检查间隔** | 每个 OSD **以小于每 6 秒的随机间隔**检查其他 OSD 的心跳 |
|
||
| **宽限期(grace period)** | 若邻居 OSD **在 20 秒宽限期内没有显示心跳**,该 OSD **可以**认为邻居 `down` 并**报告给 Monitor**,Monitor 随后更新 Ceph Cluster Map |
|
||
| **可配置项** | 可通过在 `ceph.conf` 的 **`[mon]`** 和 **`[osd]`** 或 **`[global]`** 段添加 **`osd heartbeat grace`** 设置,或在运行时设置该值,来修改这个宽限期 |
|
||
|
||
**心跳流程示意(原文 ditaa 转述)**:
|
||
|
||
```text
|
||
OSD 1 OSD 2
|
||
| |
|
||
|----+ Heartbeat |
|
||
| | Interval |
|
||
|<---+ Exceeded |
|
||
| |
|
||
| Check |
|
||
| Heartbeat |
|
||
|------------------------->|
|
||
| |
|
||
|<-------------------------|
|
||
| Heart Beating |
|
||
| |
|
||
|----+ Heartbeat |
|
||
| | Interval |
|
||
|<---+ Exceeded |
|
||
| |
|
||
| Check |
|
||
| Heartbeat |
|
||
|------------------------->|
|
||
| |
|
||
|----+ Grace |
|
||
| | Period |
|
||
|<---+ Exceeded |
|
||
| |
|
||
|----+ Mark |
|
||
| | OSD 2 |
|
||
|<---+ Down |
|
||
```
|
||
|
||
### 11.3 OSDs Report Down OSDs(上报 down 的 OSD)
|
||
|
||
**默认规则**:
|
||
|
||
- **默认需要来自不同 host 的两个 OSD** 向 Monitor 报告另一个 OSD `down`,Monitor 才认可它 `down`;
|
||
- **动机**:有可能所有上报故障的 OSD 都在一个**交换机有问题的 rack** 里,而该 OSD 只是连不上它们。为避免这类误报,Ceph **把上报故障的 peer 视为"整体集群中一个潜在 subcluster(同样 lag)"的代理(proxy)**。
|
||
> 原文坦诚:This is clearly not true in all cases, but will sometimes help us localize the grace correction to a subset of the system that is unhappy.
|
||
- **分组依据**:**`mon osd reporter subtree level`** 用于**按 peer 在 CRUSH map 中的共同祖先类型**把它们归入"subcluster";
|
||
- **默认只需要来自不同 subtree 的两份报告**即可上报另一个 OSD `down`。
|
||
|
||
**可配置项**(在 `ceph.conf` 的 `[mon]` 段,或运行时设置):
|
||
|
||
| 配置项 | 作用 |
|
||
|---|---|
|
||
| **`mon osd min down reporters`** | 改变**上报 OSD down 所需的、来自唯一 subtree 的报告数量** |
|
||
| **`mon osd reporter subtree level`** | 改变**用于分组的共同祖先类型** |
|
||
|
||
**上报流程示意**:
|
||
|
||
```text
|
||
OSD 1 OSD 2 Monitor
|
||
| | |
|
||
| OSD 3 Is Down | |
|
||
|------------------+----------------->|
|
||
| | |
|
||
| | OSD 3 Is Down |
|
||
| |----------------->|
|
||
| | |
|
||
| | |---------+ Mark
|
||
| | | | OSD 3
|
||
| | |<--------+ Down
|
||
```
|
||
|
||
### 11.4 OSDs Report Peering Failure(上报 peering 失败)
|
||
|
||
若某个 OSD **无法与它的 Ceph 配置文件(或 cluster map)中定义的任何 OSD 完成 peering**,它会**每 30 秒 ping 一次 Monitor 以获取最新的 cluster map 副本**。
|
||
|
||
**可配置项**:在 `ceph.conf` 的 `[osd]` 段添加 **`osd mon heartbeat interval`**,或在运行时设置。
|
||
|
||
**流程示意**:
|
||
|
||
```text
|
||
OSD 1 OSD 2 OSD 3 Monitor
|
||
| | | |
|
||
| Request To| | |
|
||
| Peer | | |
|
||
|----------->| | |
|
||
|<-----------| | |
|
||
| Peering | |
|
||
| | |
|
||
| Request To | |
|
||
| Peer | |
|
||
|------------------------>| |
|
||
| |
|
||
|----+ OSD Monitor |
|
||
| | Heartbeat |
|
||
|<---+ Interval Exceeded |
|
||
| |
|
||
| Failed to Peer with OSD 3 |
|
||
|------------------------------------->|
|
||
|<-------------------------------------|
|
||
| Receive New Cluster Map |
|
||
```
|
||
|
||
### 11.5 OSDs Report Their Status(上报自身状态)
|
||
|
||
| 行为 | 时间(源文明确) |
|
||
|---|---|
|
||
| **Monitor 判定 OSD down** | 若某 OSD 不上报,**在 `mon osd report timeout` 超时后**,Monitor 认为该 OSD `down` |
|
||
| **触发上报的事件** | 当发生**可上报事件**时上报:**故障、PG stats 变化、`up_thru` 变化**,或 **OSD 在 5 秒内启动** |
|
||
| **最小上报间隔** | 可用 **`osd mon report interval`** 修改(`[osd]` 段,或运行时) |
|
||
| **最大上报间隔** | **OSD 每 120 秒向 Monitor 上报一次,无论是否发生值得注意的变化**。可用 **`osd mon report interval max`** 修改(`[osd]` 段,或运行时) |
|
||
|
||
**上报流程示意**:
|
||
|
||
```text
|
||
OSD 1 Monitor
|
||
| |
|
||
|----+ Report Min |
|
||
| | Interval |
|
||
|<---+ Exceeded |
|
||
| |
|
||
|----+ Reportable |
|
||
| | Event |
|
||
|<---+ Occurs |
|
||
| |
|
||
| Report To |
|
||
| Monitor |
|
||
|------------------------->|
|
||
| |
|
||
|----+ Report Max |
|
||
| | Interval |
|
||
|<---+ Exceeded |
|
||
| |
|
||
| Report To |
|
||
| Monitor |
|
||
|------------------------->|
|
||
| |
|
||
|----+ Monitor |
|
||
| | Fails |
|
||
|<---+ |
|
||
+----+ Monitor OSD
|
||
| | Report Timeout
|
||
|<---+ Exceeded
|
||
|
|
||
+----+ Mark
|
||
| | OSD 1
|
||
|<---+ Down
|
||
```
|
||
|
||
### 11.6 配置设置
|
||
|
||
> ⚠️ **修改心跳设置时,应把它们放进配置文件的 `[global]` 段。**
|
||
|
||
#### Monitor Settings(15 项,逐项列出)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `mon_osd_min_up_ratio` | 文档未明确 | 0.3(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 集群允许继续服务所需的**最小 up OSD 比例** | 💡 低于此比例集群会停止 I/O 以保护数据;一般不动 |
|
||
| `mon_osd_min_in_ratio` | 文档未明确 | 0.75(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 集群允许继续服务所需的**最小 in OSD 比例** | 💡 同上 |
|
||
| `mon_osd_laggy_halflife` | 文档未明确 | 1_hr(= 3600 秒)(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 计算 OSD "laggy" 评分时的**半衰期** | 💡 与下面两项共同决定 laggy 判定;抖动频繁的环境可放慢(加大半衰期) |
|
||
| `mon_osd_laggy_weight` | 文档未明确 | 0.3(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | laggy 评分在 OSD 排名中的**权重** | 💡 一般不动 |
|
||
| `mon_osd_laggy_max_interval` | 文档未明确 | 5_min(= 300 秒)(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | laggy 评分考虑的**最大间隔** | 💡 一般不动 |
|
||
| `mon_osd_adjust_heartbeat_grace` | 文档未明确 | true(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 是否让 Monitor **根据观察到的 laggy 情况自动调整心跳宽限期** | 💡 与 11.3 节的"把上报故障的 peer 当作 subcluster 代理"机制对应 |
|
||
| `mon_osd_adjust_down_out_interval` | 文档未明确 | true(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 是否让 Monitor **自动调整 `mon_osd_down_out_interval`** | 💡 一般不动 |
|
||
| `mon_osd_auto_mark_in` | 文档未明确 | false(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 是否**自动把 OSD 标记为 `in`** | 💡 自动化运维场景可开启 |
|
||
| `mon_osd_auto_mark_auto_out_in` | 文档未明确 | true(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 是否**自动把被 auto-out 的 OSD 重新标记为 `in`** | 💡 与 `mon_osd_auto_mark_out`(未在本页列出)相关 |
|
||
| `mon_osd_auto_mark_new_in` | 文档未明确 | true(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 是否**自动把新加入的 OSD 标记为 `in`** | 💡 扩容自动化场景可开启 |
|
||
| `mon_osd_down_out_interval` | 文档未明确 | 10_min(= 600 秒)(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | **OSD 被标记 `down` 多久之后被自动标记 `out`**(从而触发数据重平衡) | 💡 **这是最关键的一个**:太短会因短暂抖动引发大量回填;太长会延长降级窗口 |
|
||
| `mon_osd_down_out_subtree_limit` | 文档未明确 | `rack`(默认值取自 src/common/options/mon.yaml.in @ v20.2.4) | 限制 **down-out 判定的作用范围**到某种 CRUSH subtree 类型 | 💡 大规模集群可防止整机/整机柜下线时触发全集群重平衡风暴 |
|
||
| `mon_osd_report_timeout` | 文档未明确 | 15_min(= 900 秒)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | **OSD 多久不上报就被 Monitor 判定为 `down`** | 💡 与 11.5 节及 `ceph pg dump_stuck stale` 的定义直接对应 |
|
||
| `mon_osd_min_down_reporters` | 文档未明确 | **默认需要"来自不同 subtree 的两份报告"**(源文明确:only two reports from different subtree are required) | **需要多少份来自唯一 subtree 的报告才判定某 OSD `down`** | 💡 与 `mon_osd_reporter_subtree_level` 配对调整;机架级故障场景可能需要提高 |
|
||
| `mon_osd_reporter_subtree_level` | 文档未明确 | `host`(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文给出配置项名 `mon osd reporter subtree level`,用于按共同祖先类型把 peer 分组进 "subcluster") | **用哪一级 CRUSH 类型作为"共同祖先"来把上报 peer 分组** | 💡 例如设成 `rack` 时,来自同一 rack 的多份报告只算一份 |
|
||
|
||
#### OSD Settings(5 项,逐项列出)
|
||
|
||
| 配置项 | 类型 | 默认值 | 含义 | 调参建议 |
|
||
|---|---|---|---|---|
|
||
| `osd_heartbeat_interval` | 文档未明确 | 6(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文散文:**每个 OSD 以"小于每 6 秒的随机间隔"检查邻居心跳**) | OSD 检查邻居心跳的间隔 | 💡 调小能更快发现故障但增加网络与 CPU 开销 |
|
||
| `osd_heartbeat_grace` | 文档未明确 | 20(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文散文:**20 秒宽限期**) | 判定邻居 `down` 前等待心跳的宽限期 | 💡 网络抖动大的环境可适当加大;与 `mon_osd_adjust_heartbeat_grace` 联动 |
|
||
| `osd_mon_heartbeat_interval` | 文档未明确 | 30(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文散文:**OSD 无法 peer 时每 30 秒 ping Monitor 一次**) | OSD 在无法 peer 时向 Monitor 请求最新 cluster map 的间隔 | 💡 一般不动 |
|
||
| `osd_mon_heartbeat_stat_stale` | 文档未明确 | 1_hr(= 3600 秒)(默认值取自 src/common/options/global.yaml.in @ v20.2.4) | 与 OSD 向 Monitor 上报的 heartbeat 统计"过期"判定相关 | 💡 一般不动 |
|
||
| `osd_mon_report_interval` | 文档未明确 | 5(默认值取自 src/common/options/global.yaml.in @ v20.2.4;源文散文:提到**最小上报间隔**;并另有 `osd mon report interval max` 对应 **120 秒**的最大上报间隔) | OSD 向 Monitor 上报的最小间隔(另:OSD 启动时**在 5 秒内**上报) | 💡 一般不动;注意 `osd_mon_report_interval_max` 未出现在本页的 confval 列表中,只在散文里提到 |
|
||
|
||
### 11.7 关键时间参数速查(全部来自源文散文,可核对)
|
||
|
||
| 场景 | 时间 | 出处 |
|
||
|---|---|---|
|
||
| OSD 检查邻居心跳的间隔 | **小于每 6 秒**(随机间隔) | OSDs Check Heartbeats |
|
||
| 判定邻居 down 的 grace period | **20 秒** | OSDs Check Heartbeats |
|
||
| OSD 无法 peer 时 ping Monitor 的间隔 | **每 30 秒** | OSDs Report Peering Failure |
|
||
| OSD 启动时上报 Monitor 的时限 | **5 秒内** | OSDs Report Their Status |
|
||
| OSD 无论是否有变化都上报的最大间隔 | **每 120 秒** | OSDs Report Their Status |
|
||
| 判定 OSD down 所需的报告数 | **来自不同 subtree 的 2 份** | OSDs Report Down OSDs |
|
||
| `ceph pg dump_stuck` 的 threshold 默认值 | **300 秒** | placement-groups(Get Statistics for Stuck PGs) |
|
||
|
||
> 引自:[Configuring Monitor/OSD Interaction](https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/)
|
||
|
||
---
|
||
|
||
## 文档未明确项
|
||
|
||
以下问题在本册覆盖的 25 个官方源文中**找不到答案**,本手册不做推测,如实列出:
|
||
|
||
1. **file / image / S3 object → RADOS object 的切分规则**:RBD 的条带化(striping)、CephFS 的文件到对象映射、RGW 的对象布局,**均由上层接口定义**,相关页面(`rbd/`、`cephfs/`、`radosgw/`)**不在本册范围内**。本册只覆盖 RADOS 层(object → pool → PG → OSD)。
|
||
|
||
2. **CRUSH location 合法 type 集合的两处表述不一致**:
|
||
- `crush-map.rst` 的 note 列出 9 个:`root`、`datacenter`、`room`、`row`、`pod`、`pdu`、`rack`、`chassis`、`host`;
|
||
- 同一文件 "Types and Buckets" 段与 `crush-map-edits.rst` 的 types 声明示例列出 12 个:`osd`、`host`、`chassis`、`rack`、`row`、`pdu`、`pod`、`room`、`datacenter`、`zone`、`region`、`root`。
|
||
**两处不完全一致,官方文档未说明差异原因。**
|
||
|
||
3. **CRUSH rule / CRUSH map 相关配置项的 type 与 default 值**:`pool-pg-config-ref.rst` 中 `osd_crush_chooseleaf_type`、`osd_crush_initial_weight`、`osd_pool_default_crush_rule` 等只以 `.. confval::` 出现,源文无散文描述其默认值。**✅ 已补全**:三项默认值已按 `src/common/options/{global,osd}.yaml.in @ v20.2.4` 填入 4.8 节表格(分别为 `1`、`-1`、`-1`)。(「类型」列仍保留「文档未明确」。)
|
||
|
||
4. **`ceph osd crush weight-set create` 的 `mode=positional` 的完整语义边界**:源文只给了定义和"replica count 为 3 则有三个权重"的示例,**未说明当 EC pool 或 `pg_num` 变化时 positional weight set 的行为**。
|
||
|
||
5. **`ceph pg dump_stuck` 的 `unclean` 类别的定义**:该参数出现在命令签名中,但**源文未给出 `unclean` 的定义**(只定义了 `Inactive`、`Undersized`、`Stale`)。
|
||
|
||
6. **`ceph config set osd osd_pool_default_pg_num` 缺少值**:`pool-pg-config-ref.rst` 的示例命令写的是 `ceph config set osd osd_pool_default_pg_num`,**后面没有值**(疑为官方文档笔误)。
|
||
|
||
7. **两个 FileStore 配置项的 Type 与 Default 不匹配**:
|
||
- `filestore_dump_file`:`:Type: Boolean`,但 `:Default: false`,而 Description 是"Defines the file that transaction dumps are stored on"(应该是个路径);
|
||
- `filestore_kill_at`:`:Type: String`,但 `:Default: false`,而 Description 是"Injects a failure at the n-th opportunity"(应该是整数)。
|
||
**疑为官方文档笔误,本手册照录原值并在此标注。**
|
||
|
||
8. **`recovery_priority` 的取值范围不一致**:`pools.rst` 明确范围为 **`-10` 到 `10`**(默认 `0`),但 `placement-groups.rst` 的用法示例使用了 `20`、`19`、`30`、`15` 等超出该范围的值。**官方文档未澄清。**
|
||
|
||
9. **`osd_pool_default_size` 是否等于 `3`**:`pools.rst` 在 "Getting the Number of Object Replicas" 段说 "By default, Ceph maintains three replicas or copies, for a size of `3`",但**未明确断言 `osd_pool_default_size` 的默认值就是 3**(该配置项在 `pool-pg-config-ref.rst` 中仅有 `confval` 指令)。**✅ 已补全**:`src/common/options/global.yaml.in @ v20.2.4` 中 `osd_pool_default_size` 的 `default: 3`,与源文散文一致。
|
||
|
||
10. **`crush-device-class` 在 jerasure / lrc 插件参数表中的默认值**:`:Default:` 后**为空**(isa / shec / clay 同样为空),文档未明确其默认行为(crush-map.rst 中 EC profile 的 `crush-device-class` 描述为 "default: none, which means that all devices are used",但这是 **profile 层面**的描述,与**插件参数表**层面是否等价,文档未明确)。
|
||
|
||
11. **clay 插件 `crush-root` 参数**:参数表中列出(默认 `default`),但**命令模板中未列**。官方文档未说明这是遗漏还是有意为之。
|
||
|
||
12. **所有通过 `.. confval::` 引用、且源文散文中无描述的配置项的 type / default / description**:包括 BlueStore 的 ~30 项(8.6 ~ 8.13 各表)、pool-pg 的 24 项(4.8)、mon-osd-interaction 的 20 项(11.6)、journal 的 7 项(8.17)。这些值在 rst 源文中不存在(在文档构建期从 Ceph 选项数据库拉取),本手册在对应表格中一律标注"文档未明确"。
|
||
|
||
**✅ 已补全(80 处)**:现已按 `src/common/options/*.yaml.in @ v20.2.4` 权威默认值库逐项回填,覆盖 4.8 节 24 项、8.6 ~ 8.13 各表、8.17 journal 7 项、11.6 节 20 项,每处均标注来源文件名。
|
||
**仍保留「文档未明确」的 6 项**:`crush-num-failure-domains`、`crush-device-class`、`crush-locality`(均为 EC / CRUSH 命令行参数,**不在 options/*.yaml.in 中**);`bluestore_block_path`、`bluestore_block_db_path`、`bluestore_block_wal_path`(**在 yaml.in 中但无 `default:` 字段**,即无默认值)。
|
||
|
||
13. **`balancer` module 详细行为**(11 章引用)、**`read-balancer`**、**`stretch-mode`** 的完整说明:这些页面**不在本册 25 个源文范围内**,本册仅在涉及处标注引用关系。
|
||
|
||
14. **`ceph osd pool create` 的 `read_balance_score` 字段**:在 `ceph osd pool ls detail` 的示例输出中出现(值 `4.00`),但**本册 25 个源文中没有任何页面解释该字段的含义**。
|
||
|
||
15. **PG → OSD 映射的详细图解**:`placement-groups.rst` 明确把 "Mapping PGs to OSDs" 链接到 `architecture` 页面,**该页面不在本册范围内**(应属于第 01 册)。本册第 1.4 节的演算示例为本手册 💡 补充,非官方文档内容。
|