# 3.2.盖亚：Ceph

> 版图远奏尧天阔，万物呈祥乐圣情。——释智遇《偈颂二十四首》

    本章直奔主题，本章在服务器中搭建一个Ceph集群，然后将它和K8S集群集成，之后所有的Pod的存储都会以Ceph为主，整体环境搭建步骤如下：

* 搭建独立的`Ceph`集群。
* K8S集群和`Ceph`集群集成。

    考虑到后期整体的云原生**运维**，目前搭建的`Ceph`集群只是开发测试专用，后续生产环境直接使用云厂商提供的`Ceph`集群服务，云厂商在存储这层的经验远高于中小型企业，这部分的成本开销相对于K8S服务、TiDB服务而言，大部分企业是可接受的。关于这点我和很多人讨论过，最大的一个问题是企业发展初期的**成本开销**。

* **标准商业玩法**（财大气粗）：这种玩法属于标准玩法，直接申请云厂商（阿里、华为、腾讯）的服务资源，如`TiDB、MySQL、K8S、ES、Neo4j、Ceph`等，所有的服务直接向云厂商购买——这样做的优点是将运维全程托管给可靠的大厂处理，公司本身可以只关注业务层PaaS和上层SaaS的运维。
* **小成本玩法**：之所以会出现这种玩法，在于标准商业玩法对中小型企业而言其实并没带来很大的便利，特别对于业务尚未拓展的企业来说，购买上边提到的服务完全是一场**兵祸**，不是每个人都像**迅联科技**那样有一个土豪爸爸，开篇就是终局，直接上顶配。但是即使是小成本，对于最底层的存储，我个人的建议还是购买（该痛必须痛），除非你手中有经验丰富的**存储相关的基础设施人员**（小公司可能测试都不齐，运维岗位就更加不奢望了），否则就不要自信到您可以应对**故障、灾备、应急**各种IT复杂场景而保证数据不出问题。

    Aeon选择了`K8S`，所以在另外几种组件选型时也有所考虑：

* 底层存储：Ceph
* 数据库：TiDB
* 容器治理：K8S + CRI-O（后期再考虑要不要切换到Podman或Docker）
* 服务网格：Istio
* Web容器：Vertx（对的，整个系统是将Vertx作为Web容器在使用）
* 数字化中间件：Zero

***

> 基础资料来自《Ceph企业级分布式存储原理与工程实践》前四章节（理论部分）节选，若您想了解更多实战的相关信息，推荐购买此书自己学习。

## 1. 俯瞰：Ceph基本

### 1.1. 软件定义存储

    **软件定义存储**：软件定义存储将**存储操作**抽象，而不是抽象实际**物理存储**。

#### 特征

| 特点     | 描述                                         |
| ------ | ------------------------------------------ |
| 自动化    | 安装部署、扩容、运维等全面自动化，可降低成本。                    |
| 标准接口   | 用于管理、维护存储设备和服务的应用程序编程接口。                   |
| 写入类型多样 | 支持应用程序通过块、文件和对象接口写入数据。                     |
| 扩展性    | 不影响性能的情况下，可无限扩展存储容量。                       |
| 透明性    | 软件自身可以监控以及管理存储空间的使用，哪些资源可用、新数据的放置、完整性如何保证。 |

#### 优势

* 避免技术锁定。
* 节省成本：分布式的，可横向扩容。
* 介质多样：如SAS、SATA、SATA SSD、NVME SSD、虚拟磁盘。
* 简化运维
* 扩展性强
* 云存储

### 1.2. Ceph定义

    Ceph是一种**开源、高可扩展、部署在通用架构服务器**的软件定义存储产品，思路是将通用的服务器和硬盘设备通过网络协议进行集群组件，构建成一套存储集群并对外提供多种访问接口，实现满足使用场景的分布式存储。

    适用场景（对数据读写性能要求不苛刻，对计算水平要求高）：

| 场景                           | 需求                                                           |
| ---------------------------- | ------------------------------------------------------------ |
| 数据分析                         | <p>1. 大数据分析<br>2. 海量日志分析。</p>                                |
| <p>云计算平台<br>AI/ML/IoT/BC</p> | <p>1. OpenStack私有云存储、容器云后端存储<br>2. 为人工智能、机器学习、物联网等提供后端存储</p> |
| 富媒体和归档                       | <p>1. 非结构化数据（文件、视频、图片）<br>2. 数据归档、企业数据备份</p>                 |
| 企业文件同步和共享                    | 企业文件共享存储，如企业内部网盘                                             |
| 服务器和应用程序                     | <p>1. 物理机、虚拟机的数据存储盘<br>2. 应用程序数据存储</p>                       |

### 1.3. 基本执行结构

    Ceph在OpenStack中的**结构**：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-2bf266cfbaf5f8a095ec2566344fba5285333af1%2F20221111094629.png?alt=media)

> `OpenStack 2017`的后端存储统计显示`Ceph RBD`的排名第一。

    Ceph直接集成`Bareos/Bacula/Amanda`备份软件，可实现应用数据的备份。**备份架构如下**：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-8d55d1b5bd05cc002dab7008bbd6960cb4c824db%2F20221111100336.png?alt=media)

    服务器添加Ceph RBD的方式添加块设备：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-67c3dd9eeb505220127b380a6ea4ca7de4ee8a31%2F20221111104217.png?alt=media)

    应用程序和Ceph集成：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-3d34d23c06c7e3ad025c45691dda941b540dad9c%2F20221111104325.png?alt=media)

***

## 2. 囫囵：Ceph结构剖析

    Ceph的集群主要包含**三种守护进程**：

1. **Ceph OSD**：利用Ceph节点上的CPU、内存、网络进行数据复制、纠错、重新平衡、恢复、监控和报告等。
2. **Ceph Monitor**：维护Ceph集群的主副本映射、Ceph集群的当前状态和工种与运行控制相关的工作。
3. **Ceph Manager**：维护放置组（Placement Group）详细信息，代替`Monitor`处理元数据和主机元数据，显著提高大规模集群访问性能。

### 2.1. 客户端通信

    客户端和Ceph集群通所需数据：

* Ceph配置文件、集群名称（通常为ceph）、Monitor地址。
* 存储池名称
* 用户名和密钥路径

    客户端连接的**整体流程**如下：

1. Ceph客户端维护了存储的**对象ID**和存储对象的**存储池名称**。
2. Ceph客户端访问Ceph Monitor并检索最新的**Cluster Map副本**。
3. Ceph客户端向Librados提供对象名称和存储池名称。
4. Librados使用CRUSH算法为将要检索的数据计算对象放置**组和主OSD**。
5. 客户端连接**主OSD**，执行读写操作。

### 2.2. 集群架构

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-671e333e348bb5bcc97a58dbf106c71d62277e95%2F20221111112103.png?alt=media)

    核心关注点：

* **网络**：对外通信和对内通信合并、生产环境一定使用万兆网络（服务器绑定万兆网卡）。
* **服务器**：主要角色如上图。

### 2.3. MON节点

    每个Monitor节点运行**守护进程**（`ceph-mon`），该守护进程可维护集群映射的主副本，包括集群拓扑图。它的工作原理如下图：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-8761e1d97e66248f7104ce66e58e329bb82978a4%2F20221111142037.png?alt=media)

1. Ceph客户端
   * 只需连接一个Monitor节点，就可以知道所有的Monitor以及OSD的位置。
   * 连接Monitor之后，借助集群映射中存储的副本和CRUSH算法，可直接计算任意对象位置（**高扩展性、高性能的核心因素**）。
2. Ceph Monitor
   * 主要维护集群数据**主副本映射关系**。
   * 为每个组件维护一个信息图，所有集群节点都向Monitor报告（包括更改信息）。
     * OSD Map
     * MON Map
     * MDS Map
     * PG Map
     * CRUSH Map
   * Monitor本身不存储实际数据，数据交给OSD存储。
   * 提供身份验证和日志服务。
3. Paxos服务：**数据一致性保证**：
   1. Monitor将所有更改信息写入Paxos。
   2. Paxos服务更改写入的`K/V`存储，实现强一致性。
   3. Monitor使用`K/V`存储的快照和迭代器（LevelDB数据库）执行同步。
4. Paxos容错：

   * 运行在允许有服务器宕机的系统中。

   * 不要求可靠的消息传递。

   * 可容忍消息丢失、延迟、乱序和重复。

   > 大多数（Majority）机制保证`2N + 1`的容错能力，`2N + 1`个节点最多允许`N`个节点同时出现故障。

#### Ceph Cluster Map

    Cluster Map负责跟踪重要事件：

* Ceph集群中有哪些进程状态为In。
* Ceph集群中哪些进程已启动、正在运行、或关闭。
* 放置组是处于活动状态、非活动状态、清洁状态还是其他。
* 集群当前状态的其他详细信息，如：总存储空间、已使用存储空间。

#### Quorum机制

    单个节点的Monitor会存在单点故障风险，一般生产环境使用多节点模式（集群），最少3个Monitor确保高可用，规模扩大可增加到5个以上。

* 3个存活2个
* 5个存活3个
* 6个存活4个

> Monitor存在一个逻辑，就是配置信息全部保存在集群中，而不是配置文件，即使单个Monitor配置出错，而Ceph集群的每个MonMap依旧存在于集群中，不影响多个Monitor之间的通信。

### 2.4. OSD节点

    Ceph OSD是Ceph的对象存储守护进程，主要负责：

* 存储数据。
* 处理数据复制、恢复、重新平衡。
* 检查其他守护进程是否故障，向Ceph Monitor提供监控信息。

    一般是每个磁盘存储设备对应一个OSD守护进程，不论是设置`3副本`还是`2:1纠删码`，最少都要3个OSD才能实现冗余和高可用，磁盘类型可以是：`HDD, SSD, NVMe SSD`。

#### Scrub

    Scrub是Ceph集群对放置组执行数据清洗（扫描）的操作，主要检测副本数据间的一致性。它类似于对象存储中的`fsck`命令，包括：

* **Light-scrubing**：只对元数据进行扫描，速度快；一般每天检查对象的大小和属性。
* **Deep-scrubing**：除了元数据还扫描数据，速度慢，但更彻底；一般每周（频率可调）读取数据并使用校验确保数据完整性。

#### 回填

    CRUSH算法通过将放置组**移入或移出**OSD来重新平衡集群数据分布达到均匀分布，这个操作会降低性能，为了保证性能，Ceph采用回填（Backfill）的方式执行迁移。

    **Backfill**：配置Ceph降低回填操作优先级，使得比读取/写入的优先级更低，优先保证读写性能，读写完成后再平衡。

#### 恢复

    如果重新恢复的OSD对象版本比较旧，Ceph OSD进入恢复模式——寻求获取数据最新副本并将其映射恢复到最新状态。优于恢复过程耗时、耗资源，Ceph会限制恢复请求的数量——控制线程数和对象块大小，使得Ceph在`Degraded`状态下表现出良好性能。

### 2.5. Ceph Manager节点

    Ceph Manager节点从整个集群收集状态信息，一般**Ceph Manager守护进程**和**Monitor守护进程**一起运行，提供附加监控、外部监控、管理系统入口（Web UI），可跟踪运行指标。

> Ceph Manager和Ceph Monitor放在同一节点运行比较明智（不强制）。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-69a5c6e01dc530647952a7dc68f91ef7a2cf547d%2F20221111151702.png?alt=media)

### 2.6. 对象存储/对象网关

    Ceph对象网关提供了客户端访问接口。

#### 对象存储

    对象存储是一种接触和处理离散单元的方法，离散之后的数据称为对象，一般数据会离散出很多对象。和传统文件系统不同，对象存储不会通过**目录树或文件**组织，它是在一个平台的命名空间中使用对象的**Object ID**连检索所有数据对象。它的优点是**简单易用、易于扩展**，消除了传统文件系统的目录层次，也简化了对象之间的关系。

    一般就有两种访问对象API的方式：

* **Amazon S3**：将对象扁平命名空间称为桶（Bucket），不能嵌套。
* **OpenStack Swift**：OpenStack Swift将其称为容器（Container）。

> 一个账户可以访问多个桶，不同的桶有不同访问权限，用于不同对象存储。

#### 对象网关

    RADOS网关（也称Ceph对象网关、RADOSGW、RGW）是一项服务，它可以为客户端提供Geph集群的访问。该网关是建立在Librados之上的对象存储接口，主要为应用程序提供访问RADOS集群的RESTful API，RADOS网关支持两个接口：

* S3兼容接口：和Amazon S3 RESTful API的大部分子集接口兼容。
* Swift兼容接口：和OpenStack Swift API大部分子集接口兼容。

    RADOS网关是用于和Librados交互的**FastCGI模块**，由于它提供OpenStack和Amazon S3兼容接口，所以Gateway具有独立的用户管理功能。S3和Swift API共享一个名称空间，可使用**一个API写数据，另一个API检索数据**。核心守护进程`radosgw`提供以Librados库为基础封装的接口，通常将自己的Web服务器`Civetweb`作为前端来处理请求；应用程序或客户端用标准的API与RADOS网关通信，RADOS则通过Librados库和Ceph存储集群通信。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-875252651e0c4fe74b5efef56c5581f136897e89%2F20221112180742.png?alt=media)

    RADOS网关有自己的用户集，和Ceph集群用户不同：

* RADOS网关客户端可通过Amazon S3或OpenStack Swift API来使用自己的用户集执行身份认证。
* 使用`radosgw-admin`工具或基于`LDAP`的外部身份认证服务来配置用户。
* 大型多站点安装，可将RADOS网关部署在Zone Group和Realm的某一区域中。

### 2.7. MDS节点

    Ceph的元数据管理服务器（MDS）提供了客户端访问Ceph集群的接口。

#### Ceph文件存储

    CephFS是基于RADOS的高性能分布式文件系统，它是一个可扩展的、符合**POSIX标准**的分布式并行文件系统，该系统将数据和元数据作为对象存储在Ceph中，它依赖运行MDS来协调对RADOS集群的访问，并管理和文件相关的元数据。

    CephFS的目标是和POSIX标准文件系统一致，两个不同主机运行进程应该和同一主机运行进程相同——即不同主机对文件读写、实时同步的操作和效果与同一台主机上一样。CephFS至少需要运行一个MDS守护进程（`ceph-mds`），MDS守护进程主要管理存储在Ceph文件系统中和文件相关的元数据，协调Ceph存储集群的访问。

#### CephFS限制因素

    MDS将元数据提供给客户端，缓存热的元数据是为了减少对后端元数据池的请求，管理客户端缓存是为了保证一致性。CephFS工作流程：在活跃的MDS之间复制热的元数据，并将元数据变化信息合并到日志，定期刷新到后端数据池，使数据在集群之间实时同步。如下图：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-e7b203815d70f4fa44c236c8fd7bd9eecb63fff2%2F20221112182012.png?alt=media)

    CephFS使用过程中应注意：

1. 采用多MDS节点部署方式，给文件系统配置多个活跃的MDS守护进程。
2. 重点保护元数据池，RADOS底层需提供两个后端存储池：一个存数据、一个存元数据；首先创建两个数据池，一定要做好对数据的保护，使用更高级别副本，采用低延迟存储设备（SSD）。
3. 使用一个CephFS：禁止在一个集群中创建多个CephFS。

### 2.8. iSCSI网关

    作为管理员，您可以为Ceph集群安装配置iSCSI网关。借助它你可以有效对Ceph块存储进行功能接口适配，采用熟悉和常用的iSCSI来访问Ceph。iSCSI网关将Ceph和iSCSI标准集成，提供将RADOS块手中（RBD）映像导出为SCSI磁盘的高可用iSCSI启动器，该协议允许启动器通过TCP/IP网络将SCSI命令发送到iSCSI目标器，以此提供在异构客户端访问Ceph集群的能力。简单说：**Ceph集群实现了iSCSI启动器的多点（多机头）集群，保证了访问的高可用性，为Windows等系统提供了数据存储能力**。

***

## 3. 无垠之野：存储

    继续Ceph之前，聊聊存储设备[^1](https://github.com/silentbalanceyh/vertx-zero-example/blob/master/document/zero-cloud/\[%E5%B8%B8%E8%A7%81%E7%9A%84%E7%A1%AC%E7%9B%98%E7%B1%BB%E5%9E%8B%E4%BB%8B%E7%BB%8D]\(https:/zhuanlan.zhihu.com/p/490860172\)/README.md)，扫清您对底层设备类型的疑云，执行Ceph的配置以及您在K8S中配置PV/PVC时，一定会接触到存储类型，所以本章节开个支线让您对存储设备有一定的了解（纯科普）。

### 3.1. 硬盘分类（按材质）

* **HDD**（Hard Disk Driver）：传统硬盘、即机械硬盘：一个或多个铝制或玻璃制作的磁性碟片，磁头，转轴，控制电机，磁头控制器，数据转换器，接口和缓存等几个部分组成。 **优点**：容量大、价格实惠、寿命长。 **缺点**：读写慢、有噪音、体积大、抗震弱（怕震动）、发热量高。
* **SSD**（Solid Disk Driver）：固态硬盘：多个闪存芯片加主控以及缓存组成的阵列式存储，属于以固态电子存储芯片阵列制作的硬盘。 **优点**：读写速度快、抗震强、低功耗、无噪音，工作温度范围大，轻便。 **弱点**：容量小、寿命有限，价格高。
* **HHD**（Hybird Harddrive）：混合硬盘（又称**SSHD**）：机械硬盘和固态硬盘结合体，使用容量小的闪存颗粒来存储常用文件，磁盘才是存储介质，闪存做缓冲，将更多常用文件保存到闪存减小寻道时间提升读写效率。 **优点**：读写快、抗震强、低功耗、无噪音，工作温度范围大。 **缺点**：容量比机械盘小，寿命比机械盘短，成本高。

### 3.2. 硬盘接口协议

#### IDE

    **IDE**（Integrated Drive Electronics集群驱动器电子）：本意是指把控制器和盘体集成在一起的硬盘驱动器，是一种硬盘的传输接口。**ATA**（Advanced Technology Attachment）：IDE的别名（两个名字厂商都在用，一般指同样的东西）。**EIDE**（Enhanced IDE）规格的名称，这种规格又称为**Fast ATA**，不同点在于Fast ATA仅指代硬盘接口，而EIDE还指定了连接光盘、飞硬盘产品的标准，又称为**ATAPI**接口，之后升级的更快的接口都只剩下ATA的字样，如：**Ultra ATA, ATA/66, ATA/100**等。

    早期IDE传输模式一般两种：

* **PIO**（Programming I/O）模式
* **DMA**（Direct Memory Access）模式：占用资源少，但需要额外驱动程序和设置，接受程度低。

    之后厂商推出更快的DMA传输速度标准：

| 标准           | 速率        |
| ------------ | --------- |
| Ultra DMA 33 | 33MB/sec  |
| ATA 66       | 66MB/sec  |
| ATA 100      | 100MB/sec |
| ATA 133      | 133MB/sec |

> 系列标准都可以向下兼容，而ATA 133标准并没有获得广泛支持，所以最常用的还是ATA 100，而对ATA 66以上的标准而言，要使用80芯IDE排线，代替原始的40芯IDE排线。

* **优点**：价格低廉、兼容性强、性价比高。
* **缺点**：数据传输速度慢、线缆长度过短、连接设备少。

#### SATA

    **SATA**（Serial ATA）口的硬盘又称为串口硬盘，2001年正式确立Serial ATA 1.0规范；2002年Serial ATA委员会又确立了Serial ATA 2.0规范（STAT II）。SATA采用串行连接方式，该总线使用嵌入式始终信号，具备更强纠错能力，和以往相比其最大的区别在于能对传输指令执行检查和矫正，提高了数据传输的可靠性——结构简单、支持热插拔。整体优势如下：

* SATA使用连续方式传送数据，一次只传1位数据，可减少针脚数，电缆数目变少，效率更高。4个针脚分别用于**连接电缆、连接地线、发送数据、接受数据**（低功耗、减小复杂度）。
* SATA起点更高，SATA 1.0定义的数据传输速度为`150MB/s`（外部1.5Gbps），比ATA/133更高，而SATA 2.0的数据传输可达`300MB/s`（外部3Gbps），最终SATA可实现`600MB/s`（SATA Revision 3.0，外部6Gbps）的传输速率。

    SATA 2.0滥用，部分设备仅支持3Gbps而不支持NCQ：NCQ可以对硬盘的指令执行顺序进行优化，避免像传统硬盘那样机械地按照接收指令的先后顺序移动磁头读写硬盘的不同位置，相反它会在接收命令后对其排序，排序后的磁头将以高效率的顺序寻址，避免损耗、延长硬盘寿命。

    三个规范的对比：

| 规范                | 发布时间                             | 描述                                                                                                                                |
| ----------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| SATA 1.0          | 2001年                            | 150MB/s、外部1.5Gps                                                                                                                  |
| SATA 2.0          | 2002年                            | <p>300MB/s、外部3Gps<br>原生命令队列：NCQ（Native Command Queuing）<br>端口多路器（Port Multiplier）<br>交错启动（Staggered Spin-up）<br>热插拔（Hot Plug）</p> |
| SATA 2.5          | 开始支持eSATA（External SATA），完整2.0规范 |                                                                                                                                   |
| SATA Revision 3.0 | 2009年                            | <p>750MB/s、外部6Gps<br>新的NCQ支持音频、视频<br>NCQ管理功能<br>改进电源功能<br>紧凑型1.8存存储设备小型低插力接头（LIF）<br>轻薄笔记本7毫米光驱接头<br>符合INCITS ATA8-ACS标准</p>      |
| SATA Express      | 未发布                              | 1GB/s、外部8-16Gbps                                                                                                                  |

> SATA Revision 2.6/SATA Revision 3.0 禁止使用SATA 2.6和SATA 3.0的术语，必须使用完整命名。

    SATA硬盘一般可设置RAID模式：`RAID0, RAID1, RAID5, RAID10`，只是支持RAID（磁盘阵列）的功能时部分操作系统需要安装相关驱动。最新版本的SATA规范是**SATA Express**，可能会是SATA 3.2的一部分。

* **优点**：传输速度快、安装方便、容易散热、支持热插拔。

#### SCSI

    **SCSI**（Small Computer System Interface，小型计算机系统接口），是同IDE（ATA）完全不同的接口，IDE是普通PC标准接口，而SCSI并不是为硬盘设计的接口，而是一种广泛应用于小型机上的告诉数据传输技术。由于价格较高很难普及，一般用于中、高端服务器、高档工作站中。

* **优点**：应用范围广、多任务、带宽大、CPU占用率低、热插拔。

#### SAS

    **SAS**（Serial Attached SCSI，串行连接SCSI），属于新一代的SCSI技术，采用串行技术得到更高传输速度，通过缩短连接线改善内部空间等。它的设计目的：改善存储系统的效能、可用性、扩充性；提供和SATA硬盘的兼容性。

    和SATA兼容性：

* **物理层**：SAS和SATA完全兼容，SATA硬盘可直接在SAS环境中使用，简单说SATA是SAS的子标准，SAS控制器可直接操作SATA硬盘，反之不行。
* **协议层**：SAS包含3中类型的协议：

  * SSP（串行SCSI协议）用于传输SCSI命令。

  * SMP（SCSI管理协议）用于对连接设备的维护和管理。

  * STP（SATA通道协议）用于SAS和SATA之间的数据传输。

  > SAS可以和SATA部分的SCSI设备无缝结合。

    SAS的优缺点：

* **优点**：接口速度显著提升（主流Ultra 320 SCSI速度`320MB/s`，SAS起步`300MB/s`，未来可达到`600MB/s`）、更长的连接距离、提高抗干扰能力、散热很好。
* **缺点**：硬盘/控制芯片种类少、硬盘价格昂贵、实际传输速度变化不大、用户追求成熟稳定的产品。

#### FC

    **光纤**（Fiber Channel），和SCSI接口一样，最初不是为了硬盘设计，而是为网络系统设计。它是为了像服务器这样的多硬盘系统环境设计，满足高端工作站、服务器、海量存储子网络、外设间通过集线器、交换机和点对点连接进行双向、串行数据通讯等系统对高数据传输率的要求。

* **优点**：热插拔性、高速带宽、远程连接、连接设备数量大。

#### SSD

    **固态硬盘**（Solid State Disk/Solid State Drive），也称为电子硬盘、固态电子盘，由控制单元和固态存储单元（DRAM/FLASH芯片）组成的硬盘。固态硬盘介质一般分两种：

* **闪存**（IDE Flash DISK、Serial ATA Flash Disk），存储单元分为：
  * **单层单元**（SLC, Single Layer Cell），成本高、容量小、速度快。
  * **多层单元**（MLC, Multi-Level Cell），成本低、容量大、速度慢。
* **DRAM**，使用DRAM做存储介质，应用范围狭窄，使用工业标准PCI连接主机、FC接口连接服务器，应用方式分为SSD硬盘、SSD硬盘阵列两种（**缺点**是需要独立电源保护数据安全）。

    固态硬盘接口类型

* **SATA 6Gbps（SATA III）**：SATA Revision 3的参数标准。
* **mSATA**：称为`mini-SATA`接口控制器的产品规范（笔记本常用，小尺寸）。
* **M.2**：原名NGFF接口，为超级本（Ultrabook）量身打造的接口标准，基于原来的`mini PCIe`改良的mSATA接口。
* **PCI-E**：最初用于企业市场，直接对标SSD，不走SATA协议，高速率固态硬盘。
* **U.2**：别称SFF-8639，支持SATA-Express规范，还支持SATA、SAS，理论带宽到`32Gbps`。

### 3.3. 网络存储协议

    存储网络协议\[^2]允许APP、服务器、其他系统和网上设备进行交互。

#### iSCSI

    **iSCSI**是一种传输层协议，提供TCP/IP网络对存储设备的块级访问，该协议在TCP之上工作，通过LAN、WAN或互联网传输SCSI数据包。iSCSI利用多路径、巨型帧、数据中心桥接（DCB）等技术，可更长距离、更高速传输数据。目前基于iSCSI的SAN实施支持高达`25 Gb`以太网数据速率，之后是`50GbE`、`100GbE`。

#### FC

    光纤通道（FC）是一种高速网络技术，能有序地提供无损的原始块数据，该技术定义了用于使用光纤通道协议（FCP）和传输SCSI命令和信息单元的多个通信层。除SCS以外，光纤通道还可以和IP以及其他协议互操作，提供点对点、交换、环路接口，速率高达`128Gbps`。它可以创建支持存储区域网络（SAN）的光纤通道、解决SCSI、高性能并行接口（HIPPI）的缺点。

#### FCoE

    光纤通道以太网（FCoE）协议允许光纤通道（FC）直接在以太网上通信。该协议使用无损以太网结构以及自身帧格式，将光纤通道（FC）帧封装到以太帧中。FCoE允许局域网（LAN）和存储区域网（SAN）的通信共享同一物理网络，彼此隔离。它允许组织在整个数据中心使用单一的电缆连接方法，从而简化管理并降低成本，它还保留了常规光纤通道的延迟和流量管理又是，可使用数据中心桥接（DCB）来消除队列溢出期间的损失。

> 唯一的缺点是以太光纤通道不能跨路由网络（光纤通道）工作。

#### NFS

    既是一种分布式文件系统，也是一种网络协议，用于在同一局域网上的设备访问和共享文件，该系统和协议常用于支持网络连接存储（NAS）。它使用远程过程调用（RPC）协议在客户端和服务端之间路由请求，尽管参与的设备必须支持网络文件系统（NFS），但它们不需要了解网络详细信息，由于RPC调用的安全性问题，NFS最好是部署在防火墙后面的受信任网络上（主要在Linux中使用）。

#### SMB/CIFS

    服务器消息块（SMB）是一种客户端/服务器通信协议，使用户和应用程序可以访问服务器上的存储和其他网络资源。服务器消息块（SMB）在应用层之上运行、并且可在TCP/IP网络上运行，和网络文件协议（NFS）一样，该协议常用于网络连接存储（NAS）。

    公用互联网文件系统（CIFS）是最早的一个SMB，被称为易用型协议，该协议存在一些楼栋，容易出现延迟问题。所以SMB和CIFS术语经常互换使用。

#### HTTP

    HTTP通常不是一种存储协议，但有时候通过RESTful API和标准HTTP/HTTPS请求来支持对Amazon S3、Google Cloud Storage、Microsoft Azure等云存储服务的访问，其实Amazon S3已经成为了云对象存储理论上的标准。

#### NVMe

    又称NVMe-oF，它是建立在NVMe规范的基础之上的高速存储协议，用于跨网络结构（以太网、光纤通道、InfiniBand）访问固态存储。它定义了一种通用架构，用于使用NVMe消息的命令和存储系统接口，可支持很多NVMe设备，同时可扩展MVMe设备以及子系统之间的距离。

> 将Linux中的分区和K8S中的卷放到这里整理主要原因就是Ceph中部分配置有可能会牵涉其中，由于命名规则的不同，会导致您在配置过程中遇到很多和磁盘分区、磁盘相关联的知识点，基于这点所以补充两个小章节来讲解**Linux分区和K8S卷**。

### 3.4. Linux中的分区

    Linux中分区的模式：

* **MBR分区**：主引导记录，一般存在于驱动器最开始部分的特殊启动扇区，最大支持2.1T磁盘，最多支持4个分区（32位和64位系统都支持）。
* **GPT分区**：全局唯一标识分区，支持9.4ZB，理论上支持分区无限制（只支持64位操作系统）。

    GPT分区比较先进，解决了MBR的缺点，但目前MBR更多，它最多可以有4个分区。

* **主分区**：启动操作系统（操作系统引导程序必须在这个分区）。
* **扩展分区**
  1. 最多有一个扩展分区
  2. 扩展分区 + 主分区最多不超过4个
  3. 不能写入数据、不可格式化（只能用来包含逻辑分区）
* **逻辑分区**
  1. 包含在扩展分区内，可以多个
  2. 扩展分区至少包含一个逻辑分区

    按照概念陈述，结构如下：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-f240dc8213d69bfc7e7869f95d97157b1ed8d53d%2F20221112231931.png?alt=media)

    **分区命名规则**，Linux中所有设备存放在`/dev`目录，不同设备类型前缀不同，沿袭Unix风格，所有设备都会抽象成一个文件。基本命名规则如下：

| 设备前缀               | 硬盘                              | 后缀范围    |
| ------------------ | ------------------------------- | ------- |
| /dev/hd\[a-t]      | IDE设备                           | \[a-t]  |
| /dev/sd\[a-z]      | SCSI/SATA/USB                   | \[a-z]  |
| /dev/fd\[0-7]      | 标准软驱                            | \[0-7]  |
| /dev/md\[0-31]     | 软raid设备                         | \[0-31] |
| /dev/loop\[0-7]    | 本地回环设备                          | \[0-7]  |
| /dev/ram\[0-15]    | 内存                              | \[0-15] |
| /dev/null          | 无限数据接手设备（黑洞）                    |         |
| /dev/zero          | 无限零资源                           |         |
| /dev/tty\[0-63]    | 虚拟终端                            | \[0-63] |
| /dev/ttyS\[0-3]    | 串口                              | \[0-3]  |
| /dev/lp\[0-3]      | 并口                              | \[0-3]  |
| /dev/console       | 控制台                             |         |
| /dev/fb\[0-31]     | 帧缓冲（Framebuffer）                | \[0-31] |
| /dev/cdrom         | 光驱, /dev/hdc                    |         |
| /dev/modem         | 调制解调器, /dev/ttyS\[0-9]          |         |
| /dev/pilot         | 绘图仪, /dev/ttyS\[0-9]            |         |
| /dev/random        | 随机数设备                           |         |
| /dev/urandom       | 随机数设备                           |         |
| /dev/nvme\[X]n\[x] | **云盘**——I/O优化实例，通过NVMe协议挂载的数据盘  |         |
| /dev/vd\[b-z]      | **云盘**——I/O优化实例，通过非NVMe协议挂载的数据盘 | \[b-z]  |
| /dev/xvd\[b-z]     | **云盘**——非I/O优化实例数据盘             | \[b-z]  |

### 3.5. K8S中的卷

    最后看看K8S中卷类型清单，通过对清单的了解对存储进行最终的总结，然后我们再回到Ceph的主线，很多概念就十分明了。

| 卷值                    | 状态                                        | 描述                                  |
| --------------------- | ----------------------------------------- | ----------------------------------- |
| cephfs                | 现存的CephFS卷                                |                                     |
| configMap             | 提供注入配置数据的方法                               |                                     |
| downwardAPI           | 用于为应用提供downward API数据                     |                                     |
| emptyDir              | 某个节点上，节点运行时会一直存在                          |                                     |
| fc                    | 运行将现有光纤通道挂载，可指定单个或多个WWN（World Wide Names） |                                     |
| iscsi                 | 将iSCSI（基于IP的SCSI）卷挂载                      |                                     |
| local                 | 可挂载某个本地存储设备                               |                                     |
| nfs                   | 将NFS（网络文件系统）挂载                            |                                     |
| persistentVolumeClaim | 将持久卷（PV）挂载                                |                                     |
| hostPath              | 将主机节点文件系统上的文件目录挂载                         |                                     |
| projected             | 将若干现有卷映射到同一目录上                            |                                     |
| rbd                   | 将Rados块设备卷挂载                              |                                     |
| secret                | 存储敏感信息                                    |                                     |
| awsElasticBlockStore  | 1.17弃用                                    | Amazon Web服务（AWS）EBS卷               |
| azureDisk             | 1.19弃用                                    | Microsoft Azure数据盘                  |
| azureFile             | 1.21弃用                                    | Microsoft Azure文件卷, SMB2.1 / SMB3.0 |
| cinder                | 1.18弃用                                    | OpenStack Cinder卷                   |
| gcePersistentDisk     | 1.17弃用                                    | 将谷歌计算引擎（GCE）持久盘（PD）挂载               |
| gitRepo               | 弃用                                        | 挂载一个Git代码仓库                         |
| glusterfs             | 1.25弃用                                    | 将一个开源文件系统Glusterfs挂载                |
| portworxVolume        | 1.25弃用                                    | 可伸缩的块存储，超融合方式运行                     |
| vsphereVolume         | 弃用                                        | 将vSphere VMDK卷挂载                    |

### 3.6. 关于RAID和条带化

* 参考：<https://blog.csdn.net/weixin_51967583/article/details/121195615>，[温岚万叶](https://xiatwo.blog.csdn.net/?type=blog)
* 参考：<https://blog.csdn.net/m0_60360828/article/details/120290107>, [叁煌蛋](https://blog.csdn.net/m0_60360828)

    **条带化（Striping）技术**：

* 条带化技术就是将一块连续的数据分成很多小部分并把他们分别存储到不同的磁盘中去。
* 这就能使多个进程同时访问数据的多个不同部分而不会造成磁盘冲突。
* 在对这种数据进行顺序访问时可以获得最大程度上的I/O并行能力，从而获得更好的性能。

    **独立磁盘冗余阵列RAID（Redundant Arrays of Independent Disks）**：

* **磁盘阵列**是由很多块独立的磁盘，组合成一个容量局的磁盘组，利用个别磁盘提供数据所产生加成效果提升整个系统效能。利用该技术，将数据切割成许多区段，分别存放在各个硬盘上。
* 可利用**同位检查**（Parity Check）的观念，在数组中任意一个硬盘故障时，仍可独处数据，在数据重构时，将数据经计算后重新置入新硬盘中。

#### RAID0

* RAID0具有低成本、高度写性能、100%的高存储空间利用率的优点。
* 它不提供数据冗余保护，**一旦数据损坏，无法恢复**。
* RAID0一般适用于对性能要求严格但对数据安全性和可靠性不高的应用，如：**视频、音频、临时数据缓存空间**。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-498f77090f2a6ce22d8586dc6bd59130c4452a34%2F20221113232850.png?alt=media)

#### RAID1

* RAID1称为**镜像**，它将数据完全一致地分别写到工作磁盘和镜像磁盘，它的磁盘空间利用率为50%。
* RAID1提供数据写入时，响应时间会有影响，但读数据没有影响。
* RAID1提供了**最佳冗余保护**，一旦工作盘发生故障，系统自动从镜像磁盘读取数据，不影响用户工作。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-ff12a1c19b5c487b857e66e56688ee09d5d9c4da%2F20221113232906.png?alt=media)

#### RAID2

* RAID2称为纠错**海明码磁盘阵列**，设计思路是利用海明码实现数据校验冗余。
* 海明码是一种在原始数据中加入若干校验码进行**错误检测和纠正**的编码技术，第`2n`位（1,2,4,8,...）是校验码，其他位置是数据码。
* 海明码宽度和校验码计算
  * 4位数据需要4块数据磁盘、3块校验磁盘
  * 64位数据需要64块数据磁盘、7块校验磁盘
* **数据冗余开销巨大**，RAID2的数据输出性能受阵列中最慢磁盘驱动器限制，再者是按位运算，**RAID2数据重建非常耗时**。

#### RAID3

* RAID3是使用专用校验盘的**并行访问阵列**，采用一个专用的磁盘作校验盘（**性能瓶颈**），其余磁盘为数据盘，数据按位可字节的方式交叉存储到数据盘中。
* RAID3至少需要三块磁盘，不同磁盘上同一带区的数据做XOR校验，校验值写入校验盘中。
* RAID3完好时**读性能和RAID0完全一致**，并行从多个磁盘条带读取数据，性能非常高，同时还提供了数据容错能力。
* RAID3写入数据时，必须计算所有同条带校验值，并将新校验值写入校验盘中。
* 一次写操作包括了写数据块、读取同条带的数据块、计算校验值、写入校验值，**开销非常大，性能较低**。
* 如果RAID3中某一磁盘出故障，不会影响数据读取，可借助校验数据和其他完好数据来重建数据。

#### RAID4

* RAID4和RAID3原理大致相同，区别在于条带化方式不同。
* RAID4按照块的方式组织数据，写操作只设计当前数据盘和校验盘两个盘，多个I/O请求可以同时得到处理，提高了系统性能。
* RAID4按块存储可以保证单块的完整性，可以避免收到其他磁盘上同条带产生的不利影响。
* RAID4提供了**非常好的读性能**，但单一的校验盘往往成为系统性能的瓶颈（和RAID3一样）。

#### RAID5

* RAID5是目前最常见的RAID等级，校验数据分布在阵列中的所有磁盘上，而没有采用专门的校验盘。
* 对于数据和校验数据，它们的写操作可以同时发生在完全不同的磁盘上。
* RAID5还具备很好扩展性，当阵列磁盘数量增加时，并行操作量的能力也增加。
* RAID5当一个数据盘损坏时，可以根据同一带的其他数据块，和对应的校验数据来重建损坏数据。
* 重建数据时，RAID5的性能会受到较大影响。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-652d6ebb511ddf850b20f7ee1fb39612673312c1%2F20221113233113.png?alt=media)

#### RAID6

* RAID6引入双重校验概念，可以保护阵列中同时出现两个磁盘失效时，阵列仍然可以继续工作，不会发生数据丢失。
* RAID6不仅要支持数据的恢复，还要支持校验数据的恢复，因此实现代价很高，控制器的设计也比其他等级复杂、昂贵。
* RAID6思想最常见的视线方式是采用两个独立的校验算法，假设为P和Q，校验数据可以分别存储在两个不同的校验盘上，或者分散存储在所有成员磁盘中。
* RAID6具有**快速的读取性能、更高的容错能力**，但它成本高于RAID5，写性能也很差。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-f2aeae0a328e49eadab532d8145dbf14f8af6e15%2F20221113233125.png?alt=media)

#### RAID1+0

    RAID1 + RAID0，就是在两个RAID1外层套上一层RAID0，用RAID0将数据均分然后存储到不同的RAID1中，这样只存储了原来数据的一半的量，这样写入速度就提升了一倍，读取速度也是一样。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-2b9534cfe74f885adb33461edcff5ee915e334b5%2F20221113233319.png?alt=media)

#### RAID5+0 / RAID6+0

    原理同**RAID1+0**，此处不赘述。

***

## 4. 旷野：Ceph核心组件

    Ceph集群可容纳大量节点，实现无限扩展、高可用、性能要求，每个节点使用：**相互通信的非专用硬件/Ceph守护进程**实现如下功能：

* 读写数据。
* 压缩资料。
* 通过副本和纠删码确保数据安全。
* 监控并报告集群运行状况。
* 动态地重新分配数据。
* 确保数据完整性。
* 故障恢复。

    Ceph集群像一个存储池，用于存储数据，所有在Librados中的操作对Ceph客户端是透明的，**Ceph客户端/Ceph OSD**都使用CRUSH算法。

### 4.1. Ceph关键特性

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-9cab1e6f756db3608d10f320e4fd99cd16a6063f%2F20221113004604.png?alt=media)

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-aff07d6459267fd26217f38680466935cd95c40f%2F20221113010139.png?alt=media)

### 4.2. 存储池

    Ceph集群将数据对象存储在池的逻辑分区中，Ceph管理员可以为特定类型的数据（块设备、对象网关）创建池，或者使用池将一组用户和另一组用户分开。当Ceph客户端使用I/O上下文读写数据时，总是连接到Ceph集群的存储池。Ceph客户端指定**池名称、用户和密钥**，这个池看起来像一个逻辑分区，便于对数据对象访问控制。

#### Ceph技术组件全景

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-c93bd10e650c9626a4af7da7fd1063a525596fa7%2F20221113010916.png?alt=media)

    步骤如下：

1. Ceph客户端接口将文件、视频、图片等数据写入。
2. 将用户数据分割成对象（对象和定义的存储池关联）。
3. 将对象放入存储池生成规则集，由CRUSH算法计算出放置组。
4. 将放置组关联到具体服务器的硬盘，打通数据落盘前的各个关键路径。

#### 存储池内容

1. **池类型**：Ceph可以维护一个对象的多个副本，也可以使用纠删码来确保数据可靠，存储池类型定义了创建池的数据持久化方法（副本、纠删码），对客户端完全透明。
2. **放置组**：在EB级存储集群中，存储池可能存储了数百万数据对象。

   * 数据持久性：Ceph通过副本或纠删码实现。
   * 数据完整性：通过清洗和冗余校验。

   最终实现集群数据的**复制、重新平衡和故障恢复**，Ceph集群通过放置组来解决性能瓶颈。CRUSH算法用于在Ceph中定位存储数据位置并计算OSD目标集。
3. **CRUSH规则集**：CRUSH扮演另一个重要角色，用于检测故障域、性能域，它可以按介质类型识别OSD，使OSD能够跨故障域存储对象副本。CRUSH还可以使客户端将数据写入特定硬件（SSD）。

### 4.3. Ceph认证

    为了识别用户防止被攻击，Ceph提供了Cephx身份验证系统，改系统对用户和守护进程执行身份认证——它不解决通过网络传输或在OSD中存储的数据加密问题，解决的是系统认证问题。Cephx是一种特殊的用户类型`MON/OSD/MDS`：简单说这三种组件都需要账号和密码来登录Ceph系统。

### 4.4. Ceph放置组

    **放置组**（Placement Group, PG）是一个十分重要的概念，对其进行合理设置可以提高集群的性能。

#### PG

    放置组（PG）是存储池的子集，是对象的集合，Ceph将一个存储池划分为一系列放置组，每个客户端对象会分配一个放置组，然后将该放置组分配给一个**主OSD**，并将主OSD的放置组副本复制到**从OSD**。CRUSH算法会结合Cluster Map和集群状态将放置组均匀、伪随机地分配给集群中的OSD。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-d3dc4d7f11b6434bfdaa653311ef36019f0df181%2F20221113121757.png?alt=media)

#### 放置组的计算

    CRUSH为每个对象计算PG，实际上系统不知道该放置组所在的每个OSD存储多少数据，因此放置组数和OSD数可能影响数据分布。OSD和放置组数量的比例调整不能解决数据分布不均匀问题，因为CRUSH没有考虑对象的大小。

> 使用Librados接口存储一些容量相对较小的对象和容量非常大的对象可能会导致数据分布不均。

    Ceph使用RBD出，默认对象大小为固定值`4MB`，将一个块设备以`4MB`为单位切割，最后写入底层时都按照`4MB`的基本单位操作。**推荐一个OSD中配置的放置组数量为100 \~ 200**。Ceph集群放置组的计算公式如下：

$$
集群放置组总数 = \frac{(OSD总数量) \times (每个OSD中放置组的数量)}{副本总数或纠删码的 K + M}
$$

    Ceph中提供了参数`mon_target_pg_per_osd`对OSD上**放置组数量**进行限制，`target_size_bytes`参数对**存储池的容量**进行限制。

#### PG和PGP的区别

    创建存储池时通常会输入两个参数`pg_num`和`pgp_num`。

* `pg_num`表示存储池中存储对象的目录数（放置组）的数量，它的增加会引起放置组内的对象分裂，即分裂到相同的OSD上新生成的放置组中——**对象分裂，OSD分布不变**。
* `pgp_num`则表示存储池中放置组内OSD分布组合的个数，它的增加会引起部分放置在OSD上的分布变化，但不会引起放置组内的对象变动——**对象不分裂，OSD分布变化**。 **PGP**：Placement Group for Placement Purpose

### 4.5. CRUSH算法

    CRUSH算法是Ceph的设计精髓，通过计算来确定数据的存储位置，而不是查询元数据服务器才知道数据位置。CRUSH是受控复制的分布式Hash算法——一种伪随机算法，该算法可以避免单点故障、性能瓶颈、对Ceph可扩展性的物理限制。

    CRUSH计算依赖集群的映射图，使用CRUSH Map伪随机地存储和检索OSD中的数据，并使数据在整个集群中均匀分布。CRUSH Map包含了OSD列表，将设备聚合到物理为止的桶列表中，并告诉CRUSH如何在存储池中复制数据规则列表。**再通过映射底层物理基础架构信息，CRUSH可直接规避硬件设备故障带来的风险**。

    CRUSH Map分三个部分：

* **Device**：`ceph-osd`守护进程相对应的任何对象存储设备，您可使用多种类型的设备，包括HDD、SSD、NVMe以及混合，统一定义为`Device Class`，避免和Bucket中的type混淆。
* **Bucket**：包括存储位置的关系和权重，硬件设备从上到下牵涉某些逻辑单元：`数据中心 -> 设备所在机房 -> 机架 -> 行 -> 服务器 -> OSD盘`等，Bucket专门用于描述以上提到的这些逻辑单元属性。

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-595a502d957fdd5ea60b54377d5992d5e14650da%2F20221113200039.png?alt=media)

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-8893efdfcc93abdccc3ec3493b668180a86cc173%2F20221113200106.png?alt=media)

  下图展示了Bucket的作用：

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-5e08ce9fe6006e8c527491745597d387ce240102%2F20221113200154.png?alt=media)
* **Ruleset**：选择Bucket的规则集，CRUSH Rule是确定池中数据放置的规则，对大型集群，你可能会创建许多池，每个池都有自己的CRUSH Ruleset（规则集）和Rule（规则），这些规则集让你知道数据存放在哪里（副本方式、纠删码方式）。

### 4.6. Ceph数据副本

    和客户端一样，OSD可连接Monitor，以检索Cluster Map的最新副本，OSD也使用CRUSH算法来计算副本存储位置。副本写操作如下图（**3副本**）：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-5861a928c8e43445672ed4b56774ee4b94525cb6%2F20221113200535.png?alt=media)

    其步骤如：

1. Ceph客户端使用CRUSH算法计算对象的放置组ID和**主OSD**。
2. 客户端写入**主OSD**，**主OSD**会找到其他对应存储的副本数（`osd_pool_default_size`设置）。
3. **主OSD**获取对象ID、存储池名称、Cluster Map，并使用CRUSH算法计算**从OSD**的ID。
4. **主OSD**将对象写入**从OSD**。
5. 当**主OSD**接受到**从OSD**发出的写完确认回复，并且**主OSD**也完成操作时，发送响应告诉客户端本次写操作完成。

### 4.7. Ceph纠删码

    Ceph可以加载多种纠删码算法，最早且最常用的是`Reed-Solomon`。纠删码实际上是向前纠错（Forward Error Correction, FEC）码，FEC代码将**K**个块（Chunk）数据进行冗余校验处理，得到了**N**个块数据。这**N**个块数据既包含元数据，也包含校验数据，这样就能保证**K**个块中如果有数据丢失，可以通过**N**个块中包含的校验数据恢复。

    具体说，`N = K + M`中，变量**K**是数据块即**原始变量**；变量**M**代表放置故障的冗余块的数量；变量**N**是在纠删码之后创建的块的总数。这种方法保证了Ceph可访问所有原始数据，抵抗任意`N - K`个故障。纠删码存储池中，**主OSD**接收所有写操作，在副本存储池中，Ceph把放置组中的对象写入**从OSD**。纠删码存储池将每个对象存储为`K + M`个块（K个数据块，M个编码块），配置大小为`K + M`，以便Ceph将每个块存储到一个OSD中。Ceph将块的等级配置为对象的属性，**从OSD**负责将对象编码为`K + M`个块，并将其发送给其他OSD。

    参考下图：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-cfe0621eda50a7798afe4114256d5458d9cdc6b9%2F20221113213427.png?alt=media)

    场景分析理解纠删码：

1. NYAN对象包含了ABCEDFGHI，纠删码计算：
   * 三个数据块：ABC、DEF、GHI
   * 两个编码块：编码块4、编码块5
2. Ceph将每个块存储在一个OSD（上图中的OSD1 - OSD5），这些块具有相同的名称`NYAN`（对象名称），但底层位于不同OSD上。
3. 除了对象名称，还将创建块的顺序，对象的`shard_t`属性（记录块顺序）。

> 如图中编码块1包含ABC，存储在OSD5。编码块4包含YXY，存储在OSD3。

    假设恢复时OSD通知算法编码块2（OSD2）和编码块5（OSD4）丢失，其恢复流程如下（此处不探讨算法细节）：

1. 由于OSD4丢失（编码块5），从OSD中无法读取编码块5中的内容（QGC）。
2. 由于OSD2出问题（如负载严重），不能读取编码块2。
3. 但仍然有3个块在线，直接读取原始内容：
   1. 块1（OSD5）中的ABC。
   2. 块3（OSD1）中的GHI。
   3. 块4（OSD3）中的YXY。
4. 然后重建对象ABCDEFGHI的原始内容、以及包含GQC的块5的原始内容。

    纠删码模式推荐使用如下配比：

* k = 8, m = 3
* k = 8, m = 4
* k = 4, m = 2

### 4.8. Ceph对象存储技术

    **一个对象映射到具体的OSD后，Ceph如何将对象写入具体OSD？**

    对象存储为OSD的原子块提供了一个底层接口。当客户端读写数据时，它和对象接口交互。存储在集群中的对象拥有唯一的**标识符、对象数据、元数据**，对象存储接口通过确保Ceph对象语义正确来保证一致性。对象存储接口是存储介质的底层接口，提供了性能统计信息。存储参考下表格：

| 技术        | 开发 | 测试 | 生产 | 描述            |
| --------- | -- | -- | -- | ------------- |
| FileStore | o  | o  | o  | 文件系统存储对象数据。   |
| BlueStore | o  | o  | o  | 原子块设备存储对象数据。  |
| MemStore  | o  | x  | x  | RAM中直接测试读写数据。 |
| K/V Store | x  | o  | x  | 键/值数据库中读写数据。  |

    介绍下**生产环境**使用的两种（**日志部分就不在此处赘述**）。

#### FileStore

    FileStore是Ceph的原始存储实现，也是使用最广泛的实现方式。2004年Ceph启动时，Ceph完全依赖机械硬盘读写，当时基于PCIE的SSD、基于NVMe的SSD在技术和成本上没有可行性。FileStore不是直接和原子块设备交互，而是和文件系统（xfs）交互，当对象存储处理完对象的语义并传递给FileStore，它会将放置组视为目录、对象视为文件，将元数据视为XATTR或omap条目，然后剩下的操作交给文件系统处理，保证数据落盘。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-39aba22a240b6017a4b9b699c31440fb64747dd7%2F20221113215846.png?alt=media)

    日志是提高FileStore性能的关键因素，而且从图中可以知道，Ceph的数据被写了两次：第一次将数据写入日志系统，第二次将数据从日志系统写入文件系统落盘。——**这样写操作被放大，会影响性能，FileStore的致命伤**。

#### BlueStore

    BlueStore是Ceph的下一代存储实现，现有设备包括固态驱动器PCI Express、NVMe SSD。BlueStore消除了文件系统层，直接和底层原始块设备进行对象读写。BlueStore的出现主要解决：

* 去掉文件系统层，直接操作裸设备，优化日志系统。
* 针对SSD/NVMe进行专门的优化设计。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-7290b830400d6e26b328993c29d10ed49caf6fc1%2F20221113220534.png?alt=media)

1. **RocksDB**：存储WAL、对象元数据、OMAP数据、分配器元数据。它是一种嵌入式高性能键/值存储，在闪存存储上表现十分出色，它无法直接写入原始设备，需要底层文件系统来存储其持久化数据。
2. **BlueFS**：简化的文件系统，解决元数据、文件、磁盘的空间分配和管理问题，存储RocksDB日志和`sst`文件，主要作用是支持RocksDB。
3. **HDD/SSD**：物理块设备，实际存储数据。

    BlueStore自己管理裸盘，因此需要元数据管理对象，对应的`Onode`——常驻内存的数据结构，以键/值形式存储到RocksDB，进而通过BlueFS日志持久化数据。

### 4.9. Ceph心跳检查

    OSD加入集群向Monitor报告状态，在最低级别上：

* OSD状态为`up`或`down`，反映了它是否正在运行并能够为Ceph客户端请求提供服务。
* OSD状态为`down`和`in`，表示OSD可能发生故障。

    Monitor会定期检查Ceph OSD守护进程以确认它仍然在运行，若集群中存在很多OSD，那么定期检查会使得**Monitor很重**。Ceph心跳设计会让OSD判断相邻的OSD状态是否为`down`，进而更新Cluster Map映射信息并汇报给Monitor——如此执行Monitor就变得很**轻量了**。

### 4.10. Ceph Peering

    Ceph将放置组副本存储在多个OSD，放置组每个副本有一个状态，这些OSD相互对等、相互检查，确保放置组的每个副本状态一致。当Ceph在一个有效OSD集中存储一个放置组时，OSD按顺序命名，称为**主OSD、从OSD**等。

* **主OSD**是OSD集中第一个OSD，用于存储放置组第一个副本，协调该放置组的Peer过程。
* **从OSD**是唯一接受客户端向给定放置组发起对象写入的OSD。

    Ceph激活集中有一系列OSD，其中一部分OSD进程状态不一定是`up`，激活集中处于`up`状态的OSD会放入`Up Set`中。`Up Set`是一个重要的组，当OSD故障时，Ceph可以将放置组重新映射到`Up Set`的其他OSD中。

### 4.11. Ceph数据再平衡

    当管理员将Ceph OSD添加到Ceph存储集时，Ceph会更新Cluster Map，一旦更新它就会更改放置组的位置。CRUSH会均匀地防治数据（伪随机放置）。当管理员添加新的OSD时，会有少量数据移动，移动数据量通常是新OSD的数量除以集群中数据总量。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-be59da16b4067f925fab0b4a3b83d3123544c048%2F20221113222102.png?alt=media)

### 4.12. Ceph数据完整性

* **清查**：Ceph OSD守护进程可以清查放置组中的对象（通常按天），还可通过逐位比较对象中的数据执行深度清查（每周一次）。
* **CRC检查**：使用BlusStor集群时，Ceph可通过对写操作进行循环冗余校验来确保完整性，然后将CRC值存储在块数据库RocksDB中。

***

## 5. 掷地有声：Ceph客户端组件

    不论哪种存储客户端访问方式，它们都要有以下几个必备要素：

* Ceph配置文件和Ceph Monitor地址
* 存储池名称
* 用户名和密钥的路径

### 5.1. 支持类型

    Ceph支持当前主流的数据访问方式：**NFS、iSCSI、RBD、FileSystem、S3**、参考下图：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-a0fa0aae95772e1408f617279f82dbc95d9e6835%2F20221113222805.png?alt=media)

    一般应用程序会使用具有异步通信功能的简单对象存储接口，您可执行以下操作：

* 池操作
* 快照
* 读/写对象
* 创建/设置/获取/删除XATTR
* 创建/设置/获取/删除键/值对

### 5.2. Watch/Notify机制

    Ceph集群中，有一个重要的Watch/Notify机制，用于在不同客户端之间通信，使所有客户端保持一致，该机制需要关联到特定对象（**相互通知保持状态一致**）。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-8e91ca0a3bd3452d597926aa9a23b4464686a15a%2F20221113223042.png?alt=media)

### 5.3. 独占锁

    若一个RBD映射到多个客户端，独占锁可以保证同一个时刻RBD锁定单个客户端，解决多个客户端同时写入一个对象的冲突问题（此功能基于Watch/Notify）。创建/删除快照等操作期间更改RBD结构时，独占锁极为重要。

    只是默认情况创建RBD不启用独占锁，必须使用`--image-feature`参数显式启用此功能。

```shell
rbd create --size 102400 mypool / myimage --image-feature 5
# 逗号后边是二进制
# 5,101 = 1, 001 + 4, 100
# 1 - 启用layering
# 4 - 启用独占锁
```

### 5.4. 对象映射

    当客户端对RBD映像执行写操作时，对象映射可跟踪写入链路后端对象。发生写操作时，该写操作将转换为后端RADOS对象内偏移量。启用对象映射后，Ceph将跟踪这些RADOS对象，您可以知道对象是否实际存在。对象映射会保存在客户端中，这样避免在OSD中查询不存在的对象——**简单说对象映射是实际存在对象的索引**。

    默认情况下不启用对象映射，创建RBD时必须使用`--image-features`参数显式启用该特性。

```shell
rbd create --size 102400 mypool / myimage --image-feature 13
# 逗号后边是二进制
# 13,0001 = 4, 0100 + 8, 1000 + 1,0001
# 1 - 启用layering
# 4 - 启用独占锁
# 8 - 启用对象映射
```

### 5.5. Ceph客户端的数据条带化

    存储设备有吞吐量限制，会映像性能和可伸缩性。因此，存储系统通常使用**条带化技术**（Striping）解决该问题——将存储内容进行顺序分片，然后在多个存储设备之间分布式存储每一个分片，以提高吞吐量和性能。最常见的形式是RAID，和Ceph条带化最相似的RAID类型是RAID 0，即条带化卷。Ceph条带化提供类似RAID 0的吞吐量、n路RAID镜像的可靠性、更快的恢复速度。Ceph提供三种类型客户端：

* Ceph块设备
* Ceph文件系统
* Ceph对象存储

    条带化和对象副本无关，因为CRUSH会跨OSD实现对象的副本复制，所以条带会跟着对象复制而自动复制。

**简单的条带化格式**

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-6b97f460a9a7d9d40bc13398830fe82df65ad144%2F20221113225128.png?alt=media)

**对象集复杂条带化格式**

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-0ccfb2ba41d6f21941d7f45523bfe8858a11ba77%2F20221113225145.png?alt=media)

    决定Ceph做数据条带化的因素如下：

* 对象大小：对象大小应足够大，容纳多个条带单元，并且是条带单元的倍数。安全值：**16M**。
* 条带大小：条带单元具有可配置大小，Ceph客户端会把对象的数据划分为大小相等的条带单元（最后一个除外）。
* 条带数量：根据条带大小和对象大小判断一份数据需要多少个条带单元，然后将这些条带单元写入一组对象，这组对象称为对象集。

***

## 6. 怀述：OSD

### 6.1. LVM

    基本环境规划好了，那么本章主要聊聊LVM\[^3]，做一个章节的知识补充。LVM是Linux操作系统的逻辑卷管理器，目前主要有`LVM1`和`LVM2`两个版本：

* LVM1：已经是认定为稳定几年的成熟产品。
* LVM2：目前是最新最好的LVM版本，LVM2几乎是完全向后兼容使用LVM1创建的卷的。

    逻辑卷管理提供了比传统磁盘和分区视图更高级别的计算机系统上磁盘存储的视图，使得系统管理员可以更灵活地将存储分配给应用程序和用户。在逻辑卷管理器的控制下创建的存储卷可随意调整大小、移动（有可能要对文件系统工具进行升级），还允许管理用户定义组中的存储卷，允许系统管理员处理命名的卷组，而非物理磁盘名。

    参考下图：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-5611f9a8a3824dc09a2c79a058c4d24f8ad9a140%2F20221122143715.png?alt=media)

* 图片来源：<https://blog.csdn.net/knight_zhou/article/details/114872756>

#### 基本术语介绍

* **物理存储介质**（Physical Media）：LVM存储介质，可以是硬盘分区、整个硬盘、RAID阵列、SAN硬盘，设备必须初始化为LVM物理卷，才可以和LVM结合使用。
* **物理卷**（PV，Physical Volume）：物理卷就是LVM的基本存储逻辑块，但和基本物理存储介质比较却包含了和LVM相关的管理参数，创建物理卷可以使用硬盘分区，也可以用硬盘本身。
* **物理块**（PE，Physical Extent）：物理卷PV中可以分配的最小存储单元，PE的大小可指定，默认为**4MB**。
* **逻辑卷**（LV，Logical Volume）：类似于非LVM系统中的硬盘分区，逻辑卷LV建立在卷组VG之上，在逻辑卷LV之上建立文件系统，它可以创建跨多个硬盘空间的分区。
* **逻辑块**（LE，Logical Extent）：逻辑卷LV中可以分配的最小存储单元，在同一卷组VG中LE的大小和PE是相同的，并且一一相对。**由于内核限制，一个逻辑卷最多只能包含65536个PE，所以一个PE的大小决定了逻辑卷的最大容量，4MB的PE决定了单个逻辑卷最大容量是256GB**。
* **卷组**（VG，Volume Group）：LVM卷组类似于非LVM系统中的物理硬盘，一个卷组VG由一个或多个物理卷PV组成，可以在卷组VG上建立逻辑卷LV，卷组VG可以使用多个硬盘空间看起来像一个大硬盘。

#### LVM命令

```shell
yum install lvm2        # CentOS
apt install lvm2        # Ubuntu
```

| 命令      | 含义        | 作用范围        |
| ------- | --------- | ----------- |
| scan    | 扫描        | PV，VG，LV    |
| create  | 创建        | PV，VG，LV    |
| display | 显示详细信息    | PV，VG，LV    |
| remove  | 移除        | PV，VG，LV    |
| extend  | 扩展        | VG，LV       |
| reduce  | 减少        | VG，LV       |
| s       | 简单查看对应卷信息 | PVS，VGS，LVS |

#### PV管理命令

| 命令        | 功能         |
| --------- | ---------- |
| pvcreate  | 创建PV       |
| pvscan    | 扫描并列出所有的PV |
| pvdisplay | 列出PV属性信息   |
| pvremove  | 移除PV       |
| pvmove    | 移动PV中的数据   |
| pvs       | 详细信息       |

#### VG管理命令

| 命令        | 功能         |
| --------- | ---------- |
| vgcreate  | 创建VG       |
| vgscan    | 扫描并列出所有的VG |
| vgdisplay | 列出VG属性信息   |
| vgremove  | 移除VG、删除VG  |
| vgreduce  | 从VG中移除PV   |
| vgextend  | 将PV添加到VG中  |
| vgchange  | 修改VG属性     |
| vgrename  | 重命名        |

#### LV管理命令

| 命令                 | 功能         |
| ------------------ | ---------- |
| lvcreate           | 创建LV       |
| lvscan             | 扫描并列出所有的LV |
| lvdisplay          | 列出LV属性信息   |
| lvremove           | 移除LV，即删除LV |
| lvreduce(lvresize) | 缩小LV容量     |
| lvextend(lvresize) | 增大LV容量     |
| lvresize           | 改变LV容量     |
| lvs                | 详细信息       |

### 6.2. UDEV规则

    传统Linux中\[^5]，/dev目录下的设备节点是一系列静态存在的文件，而**UDEV**规则（旧版本使用`devfs`，此处不再详细说明）动态提供了在系统中实际存在的设备节点；UDEV规则比devfs的**优点在于**：

* UDEV支持设备的固定命名，而不依赖设备插入系统的顺序；默认的UDEV设置提供了存储设备的固定命名，可使用vid（vendor）、pid（device）、设备名称（model）等属性或父设备的对应属性确认某一设备。
* UDEV完全在用户空间执行，而不是类似`devfs`在内核空间执行，它会将命名策略从内核中移走，并可在节点创建前用任意程序在设备属性中为设备命名。

    UDEV实际是一个通用的内核设备管理器，但它以守护进程的方式运行于Linux系统，并监听新设备初始化或从系统中移除时内核（通过`netlink socket`）所发出的`uevent`。系统提供了一套规则用于匹配可发现的设备事件、属性导出值，匹配规则可能命名并创建设备节点，运行配置程序对设备进行设置。UDEV规则可以匹配像内核子系统、内核设备名称、设备的物理等属性、设备序列号的属性，规则也可以请求外部程序提供信息来命名设备，或指定一个永远一样的自定义名称来命名设备，而不管设备什么时候被系统发现。

    UDEV系统主要分三个部分：

* libudev函数库，用来获取设备信息。
* udevd守护进程，处于用户空间，用于管理虚拟`/dev`。
* 管理命令udevadm，用来诊断出错情况。

    UDEV规则文件中，除了`#`开头的行（注释），所有非空行都视为一条规则，但此规则不可以扩展到多行。规则由多个键值对（`key-value`）组成，并由逗号隔开，键值对可以分为**条件匹配键值对**（匹配键）和**赋值键值对**（赋值键），一条规则可以有多条匹配键和多条赋值键。匹配键是匹配一个设备属性的所有条件，当一个设备的属性匹配了规则中所有匹配键，就认为这条规则生效，然后按赋值键的内容执行规则中的赋值。

    UDEV规则的匹配键

| 键                | 含义                                                                                                                                                                            |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ACTION           | 事件(uevent)的行为。                                                                                                                                                                |
| KERNEL           | 在内核里看到的设备名字，如`sd*`表示SCSI磁盘设备。                                                                                                                                                 |
| DEVPATH          | 内核设备路径，如`/devices/*`。                                                                                                                                                         |
| SUBSYSTEM        | 子系统名称，如`sda`的子系统为block。                                                                                                                                                       |
| BUS              | 总线名称，IDE、USB。                                                                                                                                                                 |
| DRIVER           | 设备驱动的名称，如`ide-column`。                                                                                                                                                        |
| ID               | 独立于内核名称的设备名称。                                                                                                                                                                 |
| SYSFS{filename}  | 设备devpath路径下，设备属性文件`filename`里的内容。                                                                                                                                            |
| ENV{key}         | 环境变量，可表示任意。                                                                                                                                                                   |
| PROGRAM          | 可执行的外部程序，若范围0，该键则为真`true`。                                                                                                                                                    |
| RESULT           | 上一个PROGRAM调用返回的标准输出。                                                                                                                                                          |
| NAME             | 根据规则创建设备文件名称。                                                                                                                                                                 |
| SYMLINK          | 为/dev/下的设备文件产生符号链接。由于UDEV只能为某个设备产生一个设备文件，为了不覆盖系统默认的UDEV规则产生的文件，推荐使用符号链接。                                                                                                      |
| OWNER            | 设备文件的属组。                                                                                                                                                                      |
| GROUP            | 设备文件所在的组。                                                                                                                                                                     |
| MODE             | 设备文件的权限，采用8进制。                                                                                                                                                                |
| RUN              | 为设备而执行的程序列表。                                                                                                                                                                  |
| LABEL            | 在配置文件里为内部控制而采用的名字标签（下边的GOTO服务）                                                                                                                                                |
| GOTO             | 跳到匹配的规则（LABEL来标识），类似程序中的GOTO。                                                                                                                                                 |
| IMPORT{type}     | 导入一个文件或者一个程序执行后而生成的规则集到当前文件。                                                                                                                                                  |
| WAIT\_FOR\_SYSFS | 等待一个特定的设备文件的创建，主要用作时序和依赖问题。                                                                                                                                                   |
| OPTIONS          | <p><code>last\_rule</code>：对这类设备终端规则执行。<br><code>ignore\_device</code>：忽略当前规则。<br><code>ignore\_remove</code>：忽略接下来的并移走请求。<br><code>all\_partitions</code>：为所有磁盘分区创建设备文件。</p> |

    UDEV主要作用

* 重命名设备节点的缺省名字为其他名字。
* 通过创建符号链接到缺省设备节点来提供一个可选的固定的设备节点名字。
* 基于程序的输出命名设备节点。
* 改变设备节点的权限和所有权。
* 在设备节点被创建或删除时（通常是添加设备或拔出设备时）执行一个脚本。
* 重命名网络接口。

### 6.3. ceph-volume

* 参考链接：<https://docs.ceph.com/en/quincy/ceph-volume/lvm/#ceph-volume-lvm>

    使用不同设备（**lvm或物理磁盘**）发布OSD都是基于插件工具（lvm本身就是插件式的），参考下边方式：**准备、激活、启动**OSD。

>     早期的Ceph使用的是`ceph-disk`工具，主要用于部署OSD数据、数据分区、journal分区、目录挂载，使用该工具可以提高Ceph部署运维的自动化程度，降低操作出错的几率。`ceph-disk`工具需要支持不同类型的**init**系统，如**upstart**或**sysvinit**，同时会发现设备。所以，`ceph-disk`只会专注于GUID分区表（GPT）分区，具体在GPT GUID中，以独特方式标记设备。如此解答了下边问题：
>
> * 此设备是否为`journal`？
> * 此设备是否加密的数据分区？
> * 设备是否准备好了？
>
>     为了解决这些问题，`ceph-disk`使用UDEV规则和GUID匹配。     **ceph-disk的缺点**：
>
> * **UDEV规则**调用`ceph-disk`可能会导致在`ceph-disk`的`systemd`单元和`ceph-disk`之间的相互往来，整个过程非常不可靠且很消耗时间，导致OSD在节点启动过程中不会出现；另外，由于UDEV的异步行为，很难调试甚至复制这些问题。
> * `ceph-disk`只能用于GPT分区，所以它不支持其他技术，如逻辑卷管理器（LVM）卷或类似的设备映射器。
> * 为了确保GPT分区能与设备发现工作流正常工作，`ceph-disk`需要大量特殊标志，此外，这些分区需要设备完全归Ceph所有。

    `ceph-volume`子命令主要包含如下三个：

* lvm：该命令主要用于部署OSD。
* simple：该命令等价于原始的`ceph-disk`，但做了部分改动。
* zfs：该命令主要运行于FreeBSD集群。

### 6.4. lvm prepare

    该命令主要用于准备LV逻辑卷，它会添加元数据到逻辑卷上，但不会修改元数据信息，该步骤是**部署OSD两步**中的第一步；该命令会将元数据中的部分LVM标签写入LV逻辑卷，该卷一旦有了标签之后更容易被Ceph识别。BlueStore是默认的后端存储，您可以使用不同的参数标签修改：

* \--filestore
* \--bluestore

#### BlueStore

    该选项是新建OSD的默认选项，它为底层存储设备提供了弹性块模式，支持下边几种搭配：

| block | block.wal | block.db |
| ----- | --------- | -------- |
| 1     | 1         | 1        |
| 1     | 1         | 0        |
| 1     | 0         | 1        |
| 1     | 0         | 0        |

    `bluestore`子命令可直接操作**块设备、块设备上的分区、逻辑卷LV**。若您提供了物理块设备，则它会自动创建逻辑卷，创建逻辑卷时命令检查**卷组**是否以`ceph`作前缀，若有则直接使用。参考下边命令组：

```shell
# --data标记可直接指定LV逻辑卷、VG卷组
ceph-volume lvm prepare --bluestore --data vg/lv
# 您可以直接指定原始磁盘
ceph-volume lvm prepare --bluestore --data /path/to/device
# 若要开启盘符加密，则可直接使用--dmcrypt标记
ceph-volume lvm prepare --bluestore --dmcrypt --data /vg/lv
```

    若您的集群中需要使用`block.db`或`block.wal`设备，您可以指定`--block.db`或`--block.wal`选项，它们可作用于**物理设备、分区、或逻辑卷**。ceph-volume创建OSD默认命名规则如下：

* 卷组：`ceph-{cluster fsid}`（若卷组存在则直接使用`ceph-{random uuid}`。
* 逻辑卷：`osd-block-{osd_fsid}`

#### FileStore

    该选项可让您在**逻辑卷**上准备基于文件存储的**对象存储OSD**，基础内容和BlueStore一样，此处不多讲，参考下边命令：

```shell
# 创建基本文件存储OSD：
ceph-volume lvm prepare --filestore --data <data block device>
# 创建带有外部日志external journal的设备
ceph-volume lvm prepare --filestore --data <data block device> \
--journal <journal block device>
# 若开启加密，则考虑使用--dmcrypt选项
ceph-volume lvm prepare --filestore --dmcrypt --data <data block device> \
--journal <journal block device>
```

    上述命令中`<data block device>`和`journal`可以使用下边三种设备

* 物理块设备
* 物理块设备上的分区：如果使用分区则分区必须包含`PARTUUID`属性，且可以被`blkid`发现。
* 逻辑卷：使用逻辑卷时您提供的参数格式必须是`volume_group/logical_volume_name`。

```shell
# 使用逻辑卷创建OSD，并使用`/dev/sdc1`存储扩展日志
ceph-volume lvm prepare --filestore \
--data volume_group/logical_volume_name --journal /dev/sdc1
# 使用`/dev/sdc`创建OSD，并使用逻辑卷存储扩展日志
ceph-volume lvm prepare --filestore \
--data /dev/sdc --journal volume_group/journal_lv
```

    该命令的其他操作可参考：<https://docs.ceph.com/en/quincy/ceph-volume/lvm/prepare/#ceph-volume-lvm-prepare>。当创建一个新的OSD时，该命令会向集群申请一个UUID，后续命令操作OSD时使用`OSD ID, OSD UUID`来标识创建的OSD。

    一旦创建好OSD之后，下边标签会被添加到对应设备中：

| filestore            | bluestore            |
| -------------------- | -------------------- |
| `cluster_fsid`       | `cluster_fsid`       |
| `encrypted`          | `encrypted`          |
| `osd_fsid`           | `osd_fsid`           |
| `osd_id`             | `osd_id`             |
| `crush_device_class` | `crush_device_class` |
| `journal_device`     | `block_device`       |
| `journal_uuid`       | `block_uuid`         |
|                      | `db_device`          |
|                      | `db_uuid`            |
|                      | `wal_device`         |
|                      | `wal_uuid`           |

#### prepare执行步骤

*BlueStore*

1. 选择一个物理设备（Raw Physical Devices）、物理设备上的分区或逻辑卷做参数。
2. 创建任意物理设备上的逻辑卷。
3. 为新的OSD生成UUID。
4. 发送请求给Monitor得到OSD\_ID并把UUID赋值给它。
5. 使用`tmpfs`为OSD数据创建目录。
6. 为`block, block.wal, block.db`创建符号链接。
7. 查找`monmap`用于激活。
8. 准备`ceph-osd`的数据目录。
9. 使用LVM标签为逻辑卷赋予所有的Ceph元数据标签。

*FileStore*

1. 选择一个物理设备（Raw Physical Devices）、物理设备上的分区或逻辑卷做参数。
2. 为新的OSD生成UUID。
3. 发送请求给Monitor得到OSD\_ID并把UUID赋值给它。
4. 创建OSD数据目录并挂载数据卷。
5. 若设置了Journal则从数据卷中指定Journal位置。
6. 查找`monmap`用于激活。
7. 准备`ceph-osd`的数据目录。
8. 使用LVM标签为逻辑卷赋予所有的Ceph元数据标签。

### 6.5. lvm activate

    `prepare`命令执行完成后，该卷就处于可激活`activated`状态；当前命令会对卷执行激活——它启用一个`systemd`单元将OSD ID和UUID持久化（所以称为Ceph CLI的`fsid`），则启动时Ceph可以感知什么OSD是启用的且可以被挂载。

    此命令就是**部署OSD两步**的第二步，`activate`是一个无副作用的命令（可反复执行）。参考如下命令：

```shell
# 若激活一个准备好的OSD，您需要提供OSD ID和OSD UUID
ceph-volume lvm activate --bluestore 0 0263644D-0BF1-4D6D-BC34-28BD98AE3BC8
# UUID通常会存储在`fsid`文件中
# 激活所有的OSD
ceph-volume lvm activate --all
```

* 使用该命令时`OSD uuid`是必须的。
* 如果之前`prepare`命令中使用了`--dmcrypt`，当前步骤则无需再使用（`--dmcrypt`在activate命令中是不可用的）。

    后台服务操作：

```shell
# systemctl enable ceph-volume@lvm-$id-$uuid
systemctl enable ceph-volume@lvm-0-8715BEB4-15C5-49DE-BA6F-401086EC7B41
```

    systemd单元会查找匹配的OSD设备，且查找LVM标签：

```shell
/var/lib/ceph/osd/<cluster name>-<osd id>/
```

#### activate执行步骤

*BlueStore*

1. 同时读取`OSD id`和`OSD uuid`。
2. 根据匹配的`id`和`uuid`启用systemd单元：`systemctl enable`。
3. 使用`tmpfs`挂载OSD目录`/var/lib/ceph/osd/$cluster-$id/`。
4. 创建`ceph-bluestore-tool prime-osd-dir`需要的所有文件，并将文件引用指向块设备。
5. systemd单元会确认所有设备都就绪和链接。
6. 匹配的`ceph-osd`被直接启动。

*FileStore*

1. 同时读取`OSD id`和`OSD uuid`。
2. 根据匹配的`id`和`uuid`启用systemd单元：`systemctl enable`。
3. systemd单元会确认所有设备都就绪和链接。
4. 匹配的`ceph-osd`被直接启动。

### 6.6. lvm create

    这个命令会将`prepare`和`activate`两个命令组合成一个命令使用。它同样支持如下参数（其他参数参考`prepare`）：

* \--filestore
* \--bluestore

### 6.7. lvm list

    该命令用于查询所有和Ceph集群相关的设备（逻辑的或物理的），可选参数如：

* `--format`：支持`json`或`pretty`两个值，`pretty`值更有助于人工阅读。

```shell
# ceph-volume lvm list

====== osd.1 =======

  [journal]    /dev/journals/journal1

      journal uuid              C65n7d-B1gy-cqX3-vZKY-ZoE0-IEYM-HnIJzs
      osd id                    1
      cluster fsid              ce454d91-d748-4751-a318-ff7f7aa18ffd
      type                      journal
      osd fsid                  661b24f8-e062-482b-8110-826ffe7f13fa
      data uuid                 SlEgHe-jX1H-QBQk-Sce0-RUls-8KlY-g8HgcZ
      journal device            /dev/journals/journal1
      data device               /dev/test_group/data-lv2
      devices                   /dev/sda

  [data]    /dev/test_group/data-lv2

      journal uuid              C65n7d-B1gy-cqX3-vZKY-ZoE0-IEYM-HnIJzs
      osd id                    1
      cluster fsid              ce454d91-d748-4751-a318-ff7f7aa18ffd
      type                      data
      osd fsid                  661b24f8-e062-482b-8110-826ffe7f13fa
      data uuid                 SlEgHe-jX1H-QBQk-Sce0-RUls-8KlY-g8HgcZ
      journal device            /dev/journals/journal1
      data device               /dev/test_group/data-lv2
      devices                   /dev/sdb
```

***

## 7. 沉思者：Ceph集群部署

### 7.1. Ceph集群规划

    本文的目的不是搭建生产环境的集群，而是开发测试环境，所以规划细节不用多做赘述，以实验室可观测环境为主。集群规划的基本选型：

* Linux操作系统：`RHEL/CentOS > 8.1`。
* Ceph各种组件尽量**避免复用**。

#### 限制条件

    正确选择服务器或虚拟机（避免不稳定因素），下表是部分限制条件：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-91af2861d697e6a2ba0a49ea0f8b380866459993%2F20221114182435.png?alt=media)

#### 支持特性

    下表提到集中指标和推荐配置是经过验证实践检验的：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-4a43a1d5002cc32c14e969d1649cdbbf3845843b%2F20221114182618.png?alt=media)

#### 服务器规划

* **追求良好的IOPS的场景**：很多企业将IOPS密集型工作负载托管在Ceph集群（结构化数据）。

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-7a228cc790dd8a6910d4dcb6d965815b5b0eff3c%2F20221114182840.png?alt=media)
* **追加良好的吞吐量的场景**：存储半结构化数据或非结构化数据时，一般是顺序读写较大文件，需要更大的带宽。

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-c5b5228b0a1bfcae829fc9b75b6c9398494e0bdb%2F20221114182954.png?alt=media)
* **追求低成本、高容量的场景**：处理容量比较大，存储时间较长的数据，且按顺序读写：

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-ec5018076376cdaa935144d78936cb1ea1a0e054%2F20221114183155.png?alt=media)
* **实验环境下服务器的最小配置**：非生产环境下Ceph集群最小化部署，推荐最少配置3个节点（节点角色可复用）：

  ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-e8e5613d688e24781b585845173072ddc95d740a%2F20221114183318.png?alt=media)

#### 组网规划

    Ceph集群上有两个网络：**公网和私网**——公网承载Ceph的对外通信和数据交互，私网仅承载和Ceph集群相关的流量（不配置两个网络性能会相对很低）。

* **公网**：客户端访问，客户端向Ceph发送读写请求，Monitor监管集群，各种网关发起的访问请求通过公网处理。
* **私网**：内部数据同步，OSD之间副本数据同步、数据自我恢复均需要通过私网处理，带宽比公网要求还要高很多。

    网络平面示意图如：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-83027f77c341be70766b39ae7e2c65d783c9fc6c%2F20221114183820.png?alt=media)

#### 消息通信框架

    Ceph客户端和服务端消息通信框架有三种：**SimpleMessenger、AsyncMessenger、XIOMessenger**。

* **SimpleMessenger**：使用TCP Socket实现，每个Socket有两个线程：一个用于接收，另一个用于发送。这种通信机制在集群规模较大时会大量创建大量连接，对集群性能有一定挑战。
* **AsyncMessenger**：使用带有固定大小线程池的TCP Socket进行连接，该Socket数应等于最大副本数或者纠删码块数，这种框架兼容不同网络传输类型：`posix, rdma, dpdk`。Ceph默认的消息框架和传输类型`async + posix`，对于`rdma`和`dpdk`其在很多时候技术还不够成熟。
* **XIOMessenger**：基于开源网络通信库Accelio实现，Accelio xio是和传输相对独立的消息对象，当前在RDMA上运行，处于实验阶段。

#### 防火墙规则

    默认情况，Monitor监听端口为`6789`，此外它始终在公网上运行。OSD绑定到某节点从端口6800开始的第一个可用端口上，对于在节点上运行的每个OSD进程，确保至少打开4个从端口6800开始的端口。

* 一个端口用于与公网上的客户端和Monitor节点对话。
* 一个端口用于将数据发送到私网的其他OSD节点上。
* 两个端口用于在私网上发送心跳包。

### 7.2. 环境准备

#### 虚拟机参考环境

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-03dcbd4cfa15da31a405c9a605cca05571ae60a6%2F20221114193109.png?alt=media)

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-ae79ac4e8765cb5ea33e25ece1e949db63d96232%2F20221114193151.png?alt=media)

    由于开发测试环境只有三台机器，所以最终规划会根据三台节点的方式来部署，具体环境在部署中再做相关说明，由于Ceph集群的部署方式很多，本文只选择一种可以达到目的的方式来安装配置。

#### Linux配置常规套餐

> 大部分组件配置时都需要设置的**套餐**。

1. **修改Host映射**：安装K8S时已经处理过`hosts`地址了，此处就不赘述。
2. 关闭`firewalld`防火墙，并禁止开机启动。
3. 禁用`SELinux`选项。

#### 时间同步服务器

1. 「**可选**」在主节点`k8s-master`中安装时钟服务，将其作为时钟同步服务器：

   ```shell
   vim /etc/chrony.conf
   # 追加下边内容
   local stratum 10
   manual
   allow 192.168.0.0/24
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-1f3dfe9daad552e31234c8c153b212eeae3bcd1f%2F20221114194353.png?alt=media)
2. 「**可选**」重启主节点的时钟服务：

   ```shell
   # 重启服务器主节点
   systemctl restart chronyd
   ```
3. 「**可选**」在其他节点上配置时钟服务连接到该主节点：

   ```shell
   vim /etc/chrony.conf
   # 追加下边内容
   server k8s-master minpoll 4 maxpoll 10 iburst
   # 追加好过后重启
   systemctl restart chronyd
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-1759692a9d46946cdeaf3691ce8c2204b5da5fa8%2F20221114195251.png?alt=media)

#### 创建账户

1. 在每个节点中创建用户`ceph`：

   ```shell
   adduser -d /home/ceph -m ceph
   ```
2. 设置账号密码（同用户名）

   ```shell
   passwd ceph
   ```
3. 设置账户权限

   ```shell
   echo "ceph ALL = (root) NOPASSWD:AL" | sudo tee /etc/sudoers.d/ceph
   chmod 0440 /etc/sudoers.d/ceph
   ```

#### requiretty准备

1. 编辑`/etc/sudoers`文件

   ```shell
   chmod u+w /etc/sudoers
   vim /etc/sudoers
   ```
2. 查找下边这一行，如果没有可不用管，找到后注释掉

   ```shell
   # Defaults requiretty
   # 不配置的错误：
   # sudo: sorry, you must have a tty to run sudo
   ```
3. 同时新增下边这行：

   ```shell
   Defaults visiblepw
   # CentOS 8.x 中可能是下边格式，直接去掉感叹号即可
   # Defaults !visiblepw
   # 不配置的错误：
   # sudo: no tty present and no askpass program specified
   # 
   ```
4. 修改完成后还原权限：

   ```shell
   chmod u-w /etc/sudoers
   ```

#### 免密码登录

1. 使用终端登录（`ceph/ceph`）到主节点。
2. 使用命令生成免密登录令牌：

   ```shell
   ssh-keygen
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-19bd4881604ade06c2a5cbe1e5bf233f41efbd70%2F20221114205512.png?alt=media)
3. 系统已经生成了`id_rsa.pub`文件，使用命令将文件信息设置到SSH的Key文件中：

   ```shell
   cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-65d839c8f8646e3cfd6c5e7bf795df4c95577a2e%2F20221114210205.png?alt=media)
4. 修改公钥文件权限：

   ```shell
   chmod 700 ~/.ssh
   chmod 700 ~
   chmod 600 ~/.ssh/authorized_keys
   ```
5. 先登录当前机器（免密码模式）：

   ```shell
   ssh k8s-master      # 免密码登录本机（不用输密码就直接登录进去了）
   ```
6. 有了上边步骤后，本机的ssh无密码就配置完了，接下来需要拷贝到其他两个节点：

   ```shell
   ssh-copy-id -i ceph@k8s-node-01     # node 01
   ssh-copy-id -i ceph@k8s-node-02     # node 02
   ```
7. 配置完成后，使用`ceph`账号可以免密码登录所有节点：

   ```shell
   ssh k8s-master          # 免密码登录
   ssh k8s-node-01         # 免密码登录
   ssh k8s-node-02         # 免密码登录
   ```

### 7.3. cephadm 集群部署

* 参考文档：<https://docs.ceph.com/en/latest/cephadm/>

    本章节使用`cephadm`的方式部署Ceph集群\[^4]，您可以访问官方文档：<https://docs.ceph.com/en/pacific/install/>，官方推荐的安装方式如下：

| 部署方式    | 描述                                                                                                                                   |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Cephadm | 使用容器和`systemd`安装和管理`ceph`集群，并与`CLI`和仪表板`GUI`紧密集成，支持`Octopus`以后的版本，当前官方推荐部署方式，cephadm需要容器支持（podman或docker）和`python3`。                 |
| Rook    | 部署和管理运行在Kubernetes中的Ceph集群，同时也支持通过Kubernetes API管理存储资源和供应，推荐使用Rook在Kubernetes中运行Ceph，或者将现有的Ceph存储集群连接到Kubernetes，只支持Nautilus和以后的新版本。 |

    CentOS 8不在使用NTP服务，而且`pacific/el8`不再支持`ceph-deploy`，所以在CentOS 8下搭建pacific和之前的搭建方式有些区别，接下来开始搭建相关环境，核心文档部分参考详细的官方教程。

#### 版本和需求

    目前ceph主要有三个主要的Release版本、本文主要以`Quincy`版本为基础搭建：

| 版本代号    | 版本      | 初始化时间      | 最终时间       |
| ------- | ------- | ---------- | ---------- |
| Quincy  | 17.2.5  | 2022-04-19 | 2024-06-01 |
| Pacific | 16.2.10 | 2021-03-31 | 2023-06-01 |
| Octopus | 15.2.17 | 2020-03-23 | 2022-06-01 |

    安装**基础需求**：

* Python 3
* Systemd：资源管理专用。
* Podman / Docker：容器运行时，本文使用Podman。
* Chrony / NTP：时间同步服务器。
* LVM2：逻辑卷基础，存储设备供应。

    Podman和Ceph的兼容性对比：

| Ceph      | 1.9 | 2.0 | 2.1 | 2.2 | 3.0 |
| --------- | --- | --- | --- | --- | --- |
| <= 15.2.5 | o   | x   | x   | x   | x   |
| >= 15.2.6 | o   | o   | o   | x   | x   |
| >= 16.2.1 | x   | o   | o   | x   | o   |

> Podman 2.2.1 和 Ceph Pacific 无法协同工作！

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-986b9c5af55b392c49847d882349159f91a38dae%2F20221117102304.png?alt=media)

#### 步骤一：cephadm安装

1. 进入目录`cloud`，直接下载执行脚本：

   ```shell
   cd ~/cloud
   wget https://github.com/ceph/ceph/raw/quincy/src/cephadm/cephadm
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-62b3ec2081efeb899d83cd698e75a06d704bc543%2F20221117134033.png?alt=media)
2. 为`cephadm`追加可执行权限：

   ```shell
   chmod +x cephadm
   ```
3. 安装`cephadm`（直接运行如下命令）：

   ```shell
   ./cephadm add-repo --release quincy
   ./cephadm install
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-92e112efe076e7281ce7c467b498bb080d787349%2F20221117134414.png?alt=media)
4. 运行命令检查cephadm是否已安装：

   ```shell
   which cephadm
   # 检查内容查看ceph版本，最新版应该是v17
   # 记得检查：/usr/sbin/cephadm 文件内的版本
   DEFAULT_IMAGE = 'quay.io/ceph/ceph:v17'
   ```

#### cephadm镜像拉取（可选）

    该版本中定义的所需镜像如下：

```python
DEFAULT_IMAGE = 'quay.io/ceph/ceph:v17'
DEFAULT_PROMETHEUS_IMAGE = 'quay.io/prometheus/prometheus:v2.33.4'
DEFAULT_LOKI_IMAGE = 'docker.io/grafana/loki:2.4.0'
DEFAULT_PROMTAIL_IMAGE = 'docker.io/grafana/promtail:2.4.0'
DEFAULT_NODE_EXPORTER_IMAGE = 'quay.io/prometheus/node-exporter:v1.3.1'
DEFAULT_ALERT_MANAGER_IMAGE = 'quay.io/prometheus/alertmanager:v0.23.0'
DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:8.3.5'
DEFAULT_HAPROXY_IMAGE = 'quay.io/ceph/haproxy:2.3'
DEFAULT_KEEPALIVED_IMAGE = 'quay.io/ceph/keepalived:2.1.5'
DEFAULT_SNMP_GATEWAY_IMAGE = 'docker.io/maxwo/snmp-notifier:v1.2.1'
```

    您可以使用`Podman`将这些镜像一一拉取（记得带上版本号），这样就可以防止网络引起的镜像无法拉取的问题，从cephadm工具的执行流程看来，它会先检查你的本地镜像库中是否包含了该镜像，如果不包含才会去远程拉取相关信息。

    **注意**：使用`Podman`时（**Docker没有这个问题**），如果你用的是阿里云的镜像，建议优先考虑自己把这些内容全部拉去下来，但是最后一个镜像只能从`docker.io`去拉，这个镜像在仓库里是存在的，但是**阿里云无法拉取该镜像**，这个过程有可能会导致你的容器在执行部分初始化等相关操作时失效，如果从**阿里云**拉取，您可能遇到下边的错误信息（最少我**个人的阿里云镜像是拉不到这个镜像的**）：

```shell
# 拉取的异常信息
podman pull docker.io/maxwo/snmp-notifier:v1.2.1
```

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-c3ff803eb8ec6060b4595b589b0f0efdccd7c4d3%2F20221117163249.png?alt=media)

#### 步骤二：集群发布

    这个步骤遇到了很多意想不到的坑，也有相关收获，Ceph在使用`cri-o`的CRI容器运行时搭建时会卡在`Restarting the monitor...`这一步，总是不成功，但切换到`cri-dockerd`之后，这个过程就直接Pass了，所以Ceph本身和CRI的运行还是有一定的关系，此处我没有去深究问题的来源，可作为一个问题保留下来。

1. 运行下边命令发布一个集群：

   ```shell
   sudo cephadm bootstrap --mon-ip 192.168.0.154
   ```

   运行完成后，您将会看到如下界面：

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-da1009ac598e8a0386d5b10a5340c36c0f36339b%2F20221118165545.png?alt=media)
2. 从浏览器中打开，然后登录进去修改密码，就可以登录到主界面了：

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-e6bb9e7d886648e5858f950b487d9df8b2d0b8c8%2F20221118221735.png?alt=media)
3. 从规划层面考虑，安装完成后需要先更改`dashboard`和`prometheus`的端口号统一管理，由于新加的ceph的服务已经存在于`systemctl`中，所以此处不需要设置服务自启动，重启操作系统时`ceph`会自动重启。重新限定端口信息如下：

   | 端口    | 模块        | 开启SSL |
   | ----- | --------- | ----- |
   | 30100 | dashboard | o     |
   | 30101 | dashboard | x     |
4. 使用下边命令将`dashboard`改成30100：

   ```shell
   # 更改端口号：更改过后需要重启服务（或者重启操作系统）
   # dashboard -- 30101 / 30100
   # ceph config-key put mgr/dashboard/server_port 30101
   # 30100 端口会开放出来外网访问
   ceph config-key put mgr/dashboard/ssl_server_port 30100
   # 上述命令如果不生效，则直接执行
   ceph config rm mgr mgr/dashboard/ssl_server_port
   ceph config set mgr mgr/dashboard/ssl_server_port 30100
   # 注：打开telemetry时需执行：
   # ceph telemetry on --license sharing-1-0
   ```
5. 重启服务或直接重启服务器，检查端口是否更改成功：

   ```shell
   # 导出配置
   ceph config dump
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-adbc474c76fed589f936dae476bf2414e8543f02%2F20221119114431.png?alt=media)

   ```shell
   # 重启后检查
   ceph mgr services
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-4cc057171f7a131d75db34fcc55959db66452ff6%2F20221119114524.png?alt=media)

    cephadm究竟做了什么事？可能这是您目前很困惑的，此处补充一个小章节讲解`cephadm bootstrap --mon-ip *<mon-ip>*`背后的逻辑（参考：<https://docs.ceph.com/en/quincy/cephadm/install/>）：

1. 在本宿主机创建一个Monitor（MON节点，`ceph-mon`）和Manager（`ceph-mgr`）的守护进程。
2. 为Ceph集群生成一个新的**SSH Key**，并追加到`root`账号中。 **文件**：`/root/.ssh/authorized_keys`
3. 拷贝SSH Key**公钥副本**，写入文件中。 **文件**：`/etc/ceph/ceph.pub`
4. 创建集群使用的最小配置文件，该文件会和Ceph集群通信。 **文件**: `/etc/ceph/ceph.conf`
5. 拷贝`client.admin`的管理权限密钥（**私钥副本**）到对应文件。 **文件**：`/etc/ceph/ceph.client.admin.keyring`
6. 给执行bootstrap宿主机追加`_admin`标签，默认情况下，所有该标签的宿主机都会创建`/etc/ceph/ceph.conf`和`/etc/ceph/ceph.client.admin.keyring`的副本。

    上述步骤是部署过程中命令做的事，在您的机器上执行如下命令可查看可用参数选项表：

```shell
cephadm bootstrap -h
```

* **`--log-to-file`**：默认情况下，Ceph守护进程会将日志输出到`stdout/stderr`，它会抓取所有日志（包括容器docker/podman）并发送到`journald`中（`journalctl -xe`命令可查看），如果您想要开启传统日志文件，可直接使用`--log-to-file`选项，将日志写入`/var/log/ceph/$fsid`中。
* **`--cluster-network`**:大型Ceph集群中，把公共网络（public network）和集群网络（cluster network）分开是最佳实践，您可以在命令`bootstrap`中的启动参数中加上该参数来定义集群的公共网络，它的格式如：`10.90.90.0/24`。
* **`--output-dir *<directory>*`**：`cephadm boostrap`命令会将配置文件写入到`/etc/ceph`目录中，新集群会直接访问该目录读取配置。守护进程容器使用`cephadm`发布集群时不需要`/etc/ceph`目录的配置信息，但您可以设置该参数`--output-dir *<directory>*`将配置文件输出到不同的目录中——**这个动作必须防止和存在的Ceph配置冲突**。
* **`--config *<config-file>*`**：您可以使用下边这种格式（Toml）构造一个初始化配置文件，然后在执行命令中追加`--config *<config-file>*`选项，如：

  ```toml
  [global]
  osd crush chooseleaf type = 0
  ```
* **`--ssh-user *<user>*`**：该选项可以让您选择cephadm使用的SSH账号连接到宿主机，相关的SSH Key会直接添加到`/home/*<user>*/.ssh/authorized_keys`，这个账号会代理`sudo`权限。
* **`--registry-json <path to json file>`**：如果您使用一个授权注册的容器登录，您可能需要使用该参数，此处的Json内容如：

  ```json
  {
      "url": "REGISTRY_URL",
      "username": "REGISTRY_USERNAME",
      "password": "REGISTRY_PASSWORD"
  }
  ```
* 更多选项参考：[发布选项](https://docs.ceph.com/en/quincy/cephadm/install/#cephadm-deployment-scenarios)。

#### 步骤三：Ceph CLI

    Cephadm安时其实不需要任何Ceph单独的安装包，若您想要使用`ceph`命令，您可以直接使用如下方式：

```shell
# 直接打开
cephadm shell
# ERROR: Cannot infer an fsid, one must be specified (using --fsid): \
#    ['06332a00-6683-11ed-9e26-fa163ec820ba', '96c139ba-671c-11ed-b403-fa163ec820ba']
# 执行
cephadm shell -- ceph -s
# 安装`ceph-common`
cephadm add-repo --release quincy
cephadm install ceph-common
```

    如果您得到上边注释中的错误信息，则需要删除原始的fsid部分，您可以用上边的步骤解决：

```shell
cd /var/lib/ceph
ls      # 将旧的06332a00-6683-11ed-9e26-fa163ec820ba删除
```

    关于`ceph`此处需单独说明，如果使用`yum install ceph`的方式安装可能会遇到`ceph`命令直接在操作系统级别执行时和`cephadm shell`（容器内）执行时版本不一致的问题，所以推荐是直接从`cephadm shell`进入到ceph界面去执行所有ceph命令，不过您可以选择直接从`https://cbs.centos.org/koji/buildinfo?buildID=41606`下载最新版本单独安装（**版本一致**），确认`yum search release-ceph`可以查找到`quincy`的内容。最终确认**版本一致**：

| 命令              | 版本     |
| --------------- | ------ |
| cephadm version | 17.2.5 |
| ceph -v         | 17.2.5 |

> 版本不一致可能会引起不必要的麻烦，所以推荐此处维持版本一致性，版本达到一致性之后，`cephadm shell`和`ceph -v`中的版本输出应该相同。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-cc5c25e6ee9689d8422d2167af546727e5dd5a49%2F20221120122659.png?alt=media)

    Ceph命令最好在每个节点都安装一次（**注意版本一致性，最少大版本要求一致！！**）。

#### 清除步骤

    为什么先写清除步骤，就是为了防止发布失败，我写文档时ceph还没发布成功，所以枚举相关检查项来针对问题，相当于文中部分内容是检查日志，我这边遇到的问题就是停止在`mon`的启动过程，`systemctl status`查看如下：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-b9ef90bf7791d7b019a4df24a15511f48e6e3461%2F20221117175545.png?alt=media)

* 此处参考：<https://www.flamingbytes.com/posts/uninstall-ceph/>

1. 先删除集群

   ```shell
   cephadm rm-cluster --fsid <fsid> --force
   ```
2. 再删除相关配置

   ```shell
   rm -rf /run/cephadm/*.lock
   rm -rf /etc/ceph
   rm -rf /var/lib/ceph*
   ```
3. 最后查看目前环境中是否残留信息

   ```shell
   find / -name "*ceph-*"          # Ceph所有配置
   find / -name "*<fsid>*"         # 集群配置
   rpm -qa|grep ceph               # RPM包
   ```

### 7.4. 部署OSD

    前边章节中我们已经部署好了一个Ceph的基本集群：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-407d66d1c56bd036f129e70b2bef0af826b6212a%2F20221127010353.png?alt=media)

    接下来的步骤相对棘手，因为从这个步骤开始我们要追加三个核心组件到环境中：

* 添加主机（`k8s-node-01`，`k8s-node-02`）。
* 添加存储。

    基于本文之前的知识点，每个章节之前会先做一个对应的规划，若您只是想要做简单的练习而不上生产环境，那么规划部分可跳过，否则可借鉴相关思路来组织整体规划。

#### 追加主机

    本章节中我们搭建了三个节点，所以针对三个节点的不同属性此处重新罗列：

| 属性项     | k8s-master | k8s-node-01     | k8s-node-02  |
| ------- | ---------- | --------------- | ------------ |
| MON     | o          | o               | x            |
| OSD     | o          | o               | o            |
| MDS     | x          | x               | o            |
| 对象网关    | x          | x               | o            |
| iSCSI网关 | x          | o               | x            |
| 标签      | `_admin`   | `_admin`,`gw_i` | `mds`,`gw_o` |

    各标签含义

* `_admin`：管理节点。
* `gw_o`：对象网关。
* `gw_i`：iSCSI网关。
* `mds`：Metadata Server专用节点（CephFS）。

    按下边步骤执行：

1. 在新主机中安装集群的SSH公钥（root账号）。

   ```shell
   ssh-copy-id -f -i /etc/ceph/ceph.pub root@k8s-master
   ssh-copy-id -f -i /etc/ceph/ceph.pub root@k8s-node-01
   ssh-copy-id -f -i /etc/ceph/ceph.pub root@k8s-node-02
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-03df62f3fa133670ee0d94f84bea116b4a12578e%2F20221127015823.png?alt=media)
2. 根据规划为当前集群追加标签：

   ```shell
   ceph orch host add k8s-node-01 192.168.0.123 --labels=_admin,gw_i
   ceph orch host add k8s-node-02 192.168.0.208 --labels=mds,gw_o
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-f99a3adf3c4d2f34996cb22bff1e8b9714ec4548%2F20221127122958.png?alt=media)
3. 查看看板确认主机已经添加了：

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-26e0ef11b1218d78404256cbab0baee210aaf16b%2F20221127123129.png?alt=media)

#### 存储规划

    根据目前手中的资源，整体存储规划如下：

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-de6f9984ffb79ebcfab8f4615afbe92ff2b1aa84%2F20221127211932.png?alt=media)

> 我在每台机器中准备了20G的数据盘，下边的步骤所有机器上都需要执行一次。

| 存储项 | 存储项基本信息                  |
| --- | ------------------------ |
| 物理盘 | /dev/sda                 |
| 尺寸  | 20GB                     |
| PV  | /dev/sda                 |
| VG  | vg\_store                |
| LV  | lv\_hbd, 10G, 用于Block存储。 |
| LV  | lv\_hfs, 10G, 用于File存储。  |

1. 使用`pvcreate`重建物理卷：

   ```shell
   pvcreate /dev/sda
   pvdisplay
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-c708d830bf8992f71f17262aa5910de4f2e48761%2F20221127214127.png?alt=media)
2. 根据上述物理卷创建卷组：

   ```shell
   vgcreate vg_store /dev/sda
   vgdisplay
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-dff2e792b578c98ef8021ced22e783bceeb904cb%2F20221127214312.png?alt=media)
3. 在上述卷组中创建两个逻辑卷：

   ```shell
   lvcreate -l 50%VG -n lv_hbd vg_store   # Block
   lvcreate -l 50%VG -n lv_hfs vg_store   # File
   lvdisplay
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-7c949f0702ef7c460cc331626280dbc824777899%2F20221127215008.png?alt=media)
4. 在`lv_hbd`中部署OSD：

   ```shell
   ceph orch daemon add osd k8s-master:/dev/vg_store/lv_hbd
   ceph orch daemon add osd k8s-node-01:/dev/vg_store/lv_hbd
   ceph orch daemon add osd k8s-node-02:/dev/vg_store/lv_hbd
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-5b1c1107d1e5cfb3709973833ccc9d5fadcc7f43%2F20221127221341.png?alt=media)

### 7.5. CephFS和Gateway

1. 直接执行命令创建文件系统：

   ```shell
   ceph fs volume create h_store
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-b121f4dfcb351e18300ee79ccd86d6018a3b9cf7%2F20221127222610.png?alt=media)
2. 执行下边命令部署`RGW Service`：

   | 属性 | 值                |
   | -- | ---------------- |
   | 名称 | gw\_o            |
   | 位置 | label:gw\_o      |
   | 数量 | count-per-host:2 |
   | 端口 | 30200            |

   ```shell
   ceph orch apply rgw gw_o \
       '--placement=label:gw_o count-per-host:2' --port=30200
   ```

   ![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-348ce84a4d9e8e0446d77d4b14aa7397bdf99661%2F20221127223155.png?alt=media)

## 8. 小结

    本来准备搭建iSCSI部分的，由于目前项目原因，就暂时以上述搭建的内容为主，继续往下处理，接下来的章节就要把Ceph使用起来了。

![](https://22907629-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FFnX8FxOr13MrNxviJuye%2Fuploads%2Fgit-blob-d0301f4b3b309d1ff1387c2e929c9829041db014%2F20221127223454.png?alt=media)

    搭建完成后您的集群就和截图中的样子一样，这样你就可以直接使用了，至于如何用我们将在后续章节中持续讲解，本章就彻底告一段落。

\[^2]: [几种常见的网络协议](https://www.zhangshilong.cn/work/36357.html), 作者：[天道酬勤](https://www.zhangshilong.cn/work/) \[^3]: [LVM介绍和安装使用](https://www.cnblogs.com/zzxZx/p/15433909.html) \[^4]: [CentOS 8下使用cephadm搭建Ceph集群](https://blog.csdn.net/Little_cst/article/details/119839818), 作者：[Little\_cst](https://blog.csdn.net/Little_cst) \[^5]: [Linux udeev规则总结](https://blog.csdn.net/qq_36709685/article/details/118361356)，作者：[汪老师的许先生1](https://blog.csdn.net/qq_36709685?type=blog)
