概述Ceph分布式存储集群入门,详解RADOS架构、MON/MGR/OSD核心组件及RBD/CephFS/RadosGW三种存储接口的部署与管理
时间2026-05-11
分类Ceph集群
标签Ceph RADOS 分布式存储 RBD CephFS RadosGW cephadm 集群部署

Ceph 集群开篇


image-20260520181221114

一、Ceph 概述

Ceph 是什么

Ceph 是一个开源的分布式存储系统,它把每一块待管理的数据切分为固定大小的对象(object),并以对象为原子单元完成数据存取

底层存储服务由多个主机(host)组成的存储集群提供,该集群被称为 RADOS(Reliable Automatic Distributed Object Store)——可靠自动化分布式对象存储系统


RADOS 通俗理解:把四个字母拆开就是它的全部秘密:

字母 含义 通俗解释
Reliable 可靠 每份数据自动存 3 份,坏一台机器不丢数据
Automatic 自动化 你不用管数据放哪块盘,系统自己决定
Distributed 分布式 多台机器绑在一起干活,不是单机
Object Store 对象存储 数据被切成固定大小的"对象"来存取,像一个个标准快递箱
生产环境(≥3 台主机):                 单节点实验(--single-host-defaults):

    Ceph-201    Ceph-202    Ceph-203         Ceph-201(只有一台)
   ┌────────┐ ┌────────┐ ┌────────┐      ┌───────────────────┐
    MON ✅️   MON ✅️   MON ✅️       MON ✅️                MGR ✅️   MGR      MGR           MGR ✅️ × 2           OSD.0    OSD.3    OSD.6         OSD.0 OSD.1 OSD.2     OSD.1    OSD.4    OSD.7        └───────────────────┘
    OSD.2    OSD.5    OSD.8        ⚠️ 一台挂 = 全集群挂 ❌️
   └────────┘ └────────┘ └────────┘         # 不适合生产环境
   一台挂  另两台顶上 ✅️

📌 一句话:RADOS 是 Ceph 的唯一地基——无论上层是 RBD、CephFS 还是 RadosGW

数据最终都变成"对象"沉到 RADOS 里,再由它分发到各台机器的磁盘上


Ceph 架构全景:工具 → 接口 → 集群

Ceph 整个体系就三层——从你敲的命令,到程序调用的接口,到底层真正存数据的集群:

                    ┌── 工具层:你在终端敲的命令 ──┐
                                                             rbd           ceph fs        radosgw-admin       ceph
           (块设备命令)    (文件系统命令)       (对象网关命令)    (集群管理命令)
                                                
                                                
                    ┌── 接口层:三种存储入口 ──┐ 
                                                         RBD           CephFS        RadosGW
           (块设备接口)   (文件系统接口)  (对象网关接口) 
                                                              └───────────────┴───────────────┘
                                               
            ════════════════════╪════════════════
                                                        RADOS Cluster = 分布式集群 = 服务端

                ┌───────┐  ┌───────┐  ┌───────┐
                  MON      MON      MON     大脑:管地图
                └───────┘  └───────┘  └───────┘
                ┌───────┐
                  MGR                            副手:管监控
                └───────┘
                ┌───────┐  ┌───────┐  ┌───────┐
                 OSD.0    OSD.1    OSD.2    仓库:真存数据
                 /sdb     /sdc     /sdd                  └───────┘  └───────┘  └───────┘

📌 三层角色

是什么 谁在用 举例
工具层 命令行可执行文件 运维/开发人员手工敲 rbd createceph fs lsradosgw-admin user create
接口层 三种存储入口 应用程序通过代码调用 RBD(块)、CephFS(文件)、RadosGW(对象)
集群层 服务端进程的集合 接口层把数据交给它 MON + MGR + OSD + MDS(可选)

💡 类比: - 工具层 = ATM 机的触摸屏——你用手指点它 - 接口层 = ATM 机内部的银行系统——屏上的操作转成后台调用 - 集群层 = 银行金库——钱真正存放的地方

📌 librados 为什么不在接口层:它不是独立的存储类型(你不能说"我用 librados 存储")


三种存储类型

存储类型 接口(接口层) 工具(工具层) 依赖关系 通俗理解 典型用途
块存储 RBD rbd 依赖 librados 虚拟硬盘,能格式化、分区、挂载 虚拟机磁盘、数据库
文件存储 CephFS ceph fs 不依赖 librados 共享文件夹,多台机器同时挂载 文件共享、日志存储
对象存储 RadosGW radosgw-admin 依赖 librados HTTP/HTTPS API,适合非结构化数据 图片/视频云存储

💡 关键区别

📌 librados 的特殊地位

RBD 和 RadosGW 底层都调用 librados,但 CephFS 不走这条路:

RBD ──────→ librados ──→ RADOS Cluster
RadosGW ──→ librados ──→ RADOS Cluster
CephFS ─────────────────→ RADOS Cluster (直接和 MDS / OSD 通信)

Ceph-架构

📌 核心要点

"类比理解"
  一个 RADOS Cluster 就像一栋商场大楼:
  RADOS Cluster(商场)
  ├── Pool A(RBD 专区)    只放虚拟硬盘的"扇区数据"
  ├── Pool B(CephFS 专区)  只放共享文件夹的"文件+目录信息"
  ├── Pool C(RadosGW 专区)→ 只放云存储的"图片/视频对象"
  └── Pool D(.mgr 系统池)  只放集群自己的"内部账本"
        # 初始化集群后只有一个 .mgr 系统池
✅️ 每个 Pool 有自己独立的配置——副本数、PG 数量、CRUSH 规则——互不干扰
===========================
为什么不能混用❓️
    1.RBD 存的是扇区块(像硬盘的 0/1 二进制块)
    2.CephFS 存的是文件 + 目录树(像文件夹结构)
    3.RadosGW 存的是S3 对象 + 元数据(像云存储的 Key-Value)
✅️ 数据格式完全不同,所以每种类型必须有自己的 Pool
💡 就像商场里服装店和餐厅不能共用同一个铺位

📌 一句话总结


CRUSH 算法详解

CRUSH = Controlled Replication Under Scalable Hashing

🧱 通俗理解

传统存储(比如 HDFS)的做法:

Ceph 的做法:不搞中心索引 --> 客户端自己算位置(去中心化)

传统 HDFS:                          Ceph CRUSH:
  客户端                               客户端
      "文件A存哪?"                        "我自己算!"
                                        NameNode ──→ "机器3和机器5"                对象名 + CRUSH Map
                                                哈希计算
                                         OSD.3 + OSD.7 + OSD.12
  机器3、机器5                                                                 直接找 OSD.3 写数据

💡 核心思想:CRUSH 就是一个伪随机哈希函数——给定"对象名字"和"集群地图",算出"应该存到哪几块盘"

对比维度 传统元数据服务器(HDFS NameNode) Ceph CRUSH
数据寻址 查表——问中心节点 计算——客户端自己算
中心节点 有(单点瓶颈、单点故障) (去中心化)
扩展性 受限于 NameNode 内存 理论上无限
节点增减 需要大规模重新平衡 只迁移受影响的 PG,其余不动
故障影响 NameNode 一挂 → 所有 I/O 立刻停 Monitor 不在数据路径上(门口保安)
短期:已连接客户端仍可读写
长期:无法认证新连接,集群最终不可用

🧱 为什么 Monitor 挂了"短期能读、长期不行"

HDFS NameNode "必经之路"          Ceph Monitor "门口保安"  客户端                                  客户端(缓存了地图)
     每次读写都得问                           "我知道 OSD.3 在哪"
                                            NameNode ──→ 一挂全停 ❌️              OSD.3 ←── 直接通信,不经过 Monitor
                                         ✅️ Monitor 不在数据路径上
场景 还能不能? 原因
部分 Monitor 挂(还有 quorum) ✅ 完全正常 活着的 Monitor 继续服务,3个挂1个 --> 无所谓
全部 Monitor 挂 → 已连接客户端读写 ✅ 短期能 客户端缓存了 OSD Map 和 CRUSH Map,知道数据在哪个 OSD,直接和 OSD 通信
全部 Monitor 挂 → 新建连接/打开新文件 ❌ 不能 新客户端需要向 Monitor 认证身份、获取集群地图
全部 Monitor 挂 → 长时间运行 ❌ 不能 OSD Map 有过期时间,OSD 之间心跳也会断,集群最终停摆

📌 CRUSH 三个关键优势

📌 CRUSH Map 是什么❓️

CRUSH Map 就是"集群的地图"——它描述了: - 有多少台主机 - 每台主机上有多少块 OSD - OSD 的权重(容量越大的盘权重越高) - 故障域层级(root → host → osd)

客户端下载了这份地图,就不需要问别人了——所有位置计算都在本地完成

核心组件

🚢 ① OSD(Object Storage Device,对象存储守护进程,ceph-osd

通俗解释:真正负责存数据的"仓库管理员",一块磁盘对应一个 OSD 守护进程,类似于 HDFS 的 DataNode

🧱 OSD 心跳互检——"不是 Monitor 挨个 ping,而是 OSD 互相盯着"

        Monitor(大脑)
                   "OSD.3 挂了!"
               ┌──────────┼──────────┐
                      │
OSD.0 ←──→ OSD.1 ←──→ OSD.2
    心跳        心跳      └───────────┴──────────┘

类比:老师(Monitor)不需要挨个点名——同学们(OSD)互相看着,谁没来立刻有人举手报告


🧱 OSD 负责 Cephx 身份验证——"Monitor 发证,OSD 验票":

               先去 Monitor "通行证"
客户端 ──────────────────────────> Monitor
      "我是 client.admin,这是我的 key"
                                          <────── 通行证(ticket)────────┘
             Monitor 用密钥签名
         拿着通行证直接找 OSD
  └────────────────────────────────> OSD.3
       "我要写数据,这是我的通行证"
                                                                                  OSD 自己验票:
                                         "签名是 Monitor 的❓️"
                                         '没过期❓️权限够❓️'
                                                                                                                         放行 /  拒绝

关键:Monitor 只负责签发通行证,不参与每一次数据读写——否则 Monitor 又变成瓶颈了


📦 ② Monitor(监视器,ceph-mon

通俗解释:集群的"大脑"和"元数据中心",维护整个集群的运行状态地图


⚙️ ③ Manager(管理器,ceph-mgr

通俗解释:Monitor 的"数据缓存副手"——它把查询类操作缓存下来,监控请求时能及时响应

🧱 Manager 缓存了什么——和 Dashboard 的关系

浏览器打开 Dashboard(https://Ceph-201:8443)
                     Manager ←── 提前从集群各处收集并缓存:
                ├── 集群健康状态(HEALTH_OK / WARN / ERR)
        ├── OSD 列表、状态、容量、延迟
        ├── MON 列表、quorum 情况
        ├── PG 状态(active+clean / degraded / ...)
        ├── 性能图表数据(IOPS、吞吐量、延迟曲线)
        ├── Dashboard 配置(你改过的密码、告警阈值...)
        └── 设备健康信息(SMART 数据、磁盘温度...)
简单概括: '存设备健康+数据、性能指标、Dashboard 配置等"内部账本"'

这些数据存在哪❓️
   Manager 把上述数据持久化到 .mgr 存储池(系统池,bootstrap 时自动创建)
   这样 Manager 重启后配置不丢,Dashboard 刷新时秒出数据

❌️ Manager 不存用户数据(你的 RBD 块、CephFS 文件、RadosGW 对象)
✅️ 它只管"集群自己的内部账本和监控数据"

📌 通俗类比


🗂️ ④ MDS(MetaData Server,元数据服务器,ceph-mds可选组件

通俗解释:对应 CephFS 文件系统的"目录索引",类似于 HDFS 的 NameNode——找文件时不用扫描所有 OSD


🔹 ⑤ RGW(对象存储网关,可选组件

用于对象存储系统,需要启用相关组件模块


📋 ⑥ Pool & PG(存储池 & 安置组,抽象概念

通俗解释

[!TIP]

Pool 就是 PG 的容器,PG 是数据分布的"最小单位"

数据存储流程

用一个具体例子走一遍:把一张 100MB 的照片存进 Ceph

┌───────────────────────────────────────────────┐
│  Step 1:切块                                  │
│                                               │
│  100MB 照片                                    │
│                                              │
│                                              │
│  ┌──────┐┌──────┐┌──────┐       ┌──────┐      │
│   4MB  ││ 4MB  ││ 4MB   ...    4MB        │
│   obj1 ││ obj2 ││ obj3   ×25   obj25│      │
│  └──────┘└──────┘└──────┘       └──────┘      │
│                                               │
│  Ceph 的对象固定 4MB,100MB  切成 25 个对象     │
│                                               │
│  如果只有 10MB:                                │
│  ┌──────┐ ┌──────┐ ┌──────┐                   │
│   4MB    4MB    4MB                     │
│   obj1   obj2   obj3                    │
│  └──────┘ └──────┘ └──────┘                   │
│    不足 4MB 也按 4MB 分配(2MB 数据 + 2MB 空白)  │
│  '对象大小固定 4MB,不够就补齐'                   │
│                                               │
└───────────────────────────────────────────────┘
                                                  ▼
┌──────────────────────────────────────────────┐
│  Step 2:对象→Pool→PG(CRUSH 计算)            │
│                                              │
│   obj1 为例 --> 每个对象在 RADOS 中有唯一的名字 │
│                                              │
│    "对象名"(如 rbd_data.1032.0001)           │
│         +                                    │
│    CRUSH Map                                 │
│          (集群地图:几台主机、几块盘、权重多少) │
│            'CRUSH 伪随机哈希'                │
│    命中 Pool 下的某个 PG  例如 PG.7           │
│                                              │
│  25 个对象被均匀哈希到 Pool 下的各 PG          │
│      '一个 PG 里可以存放多个对象'                │
│  Pool  PG 的容器,PG 是数据分布的"最小单位"     │
└──────────────────────────────────────────────┘
                                                  ▼
┌─────────────────────────────────────────────┐
│  Step 3:PG→OSD(副本落地)                   │
│                                             │
│  PG.7 要做 3 个副本(Pool  size=3):        │
│                                             │
│     PG.7                                    │
│                                            │
│         'CRUSH 再次计算'(故障域 = host)     │
│                                            │
│       ├─→ 副本①  Ceph-201  osd.1          │
│       ├─→ 副本②  Ceph-202  osd.4          │
│       └─→ 副本③  Ceph-203  osd.7          │
│                  '三台不同主机'               │
│  obj1 最终变成 3 份,落在 3 台不同机器的磁盘上   │
└─────────────────────────────────────────────┘

📌 三个关键角色

角色 是什么 为什么需要它
Object 4MB 固定大小的数据块 统一大小,方便管理和分布
PG Pool 内的逻辑分组 海量对象 → 少量 PG → 几块 OSD
PG 做聚合收敛:百万对象只需几百个 PG
加减 OSD 时只搬 PG,不搬海量对象
CRUSH 决定"去哪"的算法 在数据落地过程中用了两次:① 对象名→PG;② PG→OSD

🧱 CRUSH 发挥了两次作用

  对象名 ──CRUSH①──→ PG.7 ──CRUSH②──→ osd.1 / osd.4 / osd.7
          "去哪个 PG?"         "这个 PG 的 3 个副本放哪 3 块盘?"

两段分工不同,但用的都是同一套 CRUSH 算法

🧱 PG 的数量远小于对象的数量——这才是 PG 存在的意义

一个 Pool 里的数量对比:

  对象:几百万个(甚至上亿)          海量
            'CRUSH 哈希收敛'
             PG:几百个(通常 32 ~ 1024       少量
            'CRUSH 再计算'
             OSD:几十块盘                      更少

✅️ 一个 PG 里可以存放多个对象

PG.7(一个 PG)
  ├── obj1(4MB)
  ├── obj8(4MB)
  ├── obj15(4MB)
  ├── obj22(4MB)
  ├── ...(成千上万个对象)
  └── objN(4MB)

CRUSH 把对象均匀哈希到各 PG  每个 PG 里装了大量对象
迁移时以 PG 为单位搬  一次搬"一筐对象",而不是一个一个搬

📌 一句话总结:文件 → 切成对象 → CRUSH 算 PG → PG 分发到 OSD → 存 3 份到不同主机

BlueStore 存储引擎

BlueStore 是 OSD 内部的存储引擎——决定数据最终以什么方式写到磁盘上

🧱 通俗理解

OSD 收到一个对象后,怎么把它存到磁盘❓️ --> 这就靠存储引擎

Ceph 经历过一次大换血:

旧方案:FileStore                    新方案:BlueStore(v12.2.z 起默认)

  对象                                 对象
                                                                              ├──> 数据 ──> 直接写裸块设备(/dev/sdb)
  Journal(日志盘)                             先写一份到这里(第 1 次写)                                                ├──> 元数据 ──> Key-Value 数据库
  XFS 文件系统                               "对象名叫啥、存在磁盘哪个位置..."
      再从 Journal 搬到这(第 2 次写)                                            裸块设备                               └──> 都在同一块盘上,没有中间商

问题:
    1.每笔数据写两遍  慢!
    2.XFS 这层本身也有开销  又慢一点!
    # 依赖 XFS 文件系统
对比 FileStore(旧) BlueStore(新)
写数据 先写 Journal → 再搬 XFS → 再落盘(写两次 直接写裸块设备(写一次
元数据 靠 XFS 文件系统管理 内建 RocksDB(Key-Value 数据库),自己管
依赖 依赖 XFS 文件系统层 不依赖任何文件系统,直接在裸盘上存储
快满时 性能陡降 几乎不降
小随机写 快(RocksDB 优化)
额外机制 支持 COW(写时复制),RBD 快照/克隆更高效

📌 RocksDB 是什么:一个轻量级的嵌入式键值(Key-Value)数据库(Facebook 开源)


二、Ceph 高可用集群部署

部署工具概览

部署工具 特点 推荐度
cephadm 类似 kubeadm,部署服务非常方便,容器化部署 ⭐⭐⭐⭐⭐
ceph-deploy 原生工具,仅依赖 SSH + sudo + Python ⭐⭐(已丢弃)
ceph-ansible 基于 Ansible playbook ⭐⭐⭐⭐

💡 推荐使用 cephadm:与 k8s 的 kubeadm 一样好用,基于容器化部署,官方主推

Ceph 版本说明

x.0.z  0 --> 开发版本(内部测试)
x.1.z  1 --> 测试版本(开发人员测试)
x.2.z  2 --> 稳定版本(✅️ 生产环境推荐)
'x --> 大版本'
'z --> 小版本'

示例: 20.2.1 (Tentacle) 为稳定版本

image-20260520181823824

三、cephadm 快速部署实战

环境准备

官方前提条件

- Python 3
- Systemd
- Podman or Docker for running containers
- Time synchronization (such as Chrony or NTP)
- LVM2 for provisioning storage devices

集群节点规划(3 节点)

主机 IP CPU 内存 磁盘设备
Ceph-201 10.0.0.201 2C 4G /dev/sdb (300G), /dev/sdc (500G), /dev/sdd (1024G==1T)
Ceph-202 10.0.0.202 2C 4G /dev/sdb (300G), /dev/sdc (500G), /dev/sdd (1024G==1T)
Ceph-203 10.0.0.203 2C 4G /dev/sdb (300G), /dev/sdc (500G), /dev/sdd (1024G==1T)

image-20260519084030991

✅️ 我是开机添加的, "重启生效"
'查看磁盘设备是否就绪'
root@Ceph-201 ~# lsblk
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda                         8:0    0   40G  0 disk 
|-sda1                      8:1    0    1M  0 part 
|-sda2                      8:2    0    2G  0 part /boot
|-sda3                      8:3    0   38G  0 part
  └─-ubuntu--vg-ubuntu--lv 252:0    0   30G  0 lvm  /
sdb                         8:16   0  300G  0 disk     # ← 待加入 Ceph 的数据盘
sdc                         8:32   0  500G  0 disk     # ← 待加入 Ceph 的数据盘
sdd                         8:48   0    1T  0 disk     # ← 待加入 Ceph 的数据盘
sr0                        11:0    1  2.6G  0 rom  
"sdb/sdc/sdd 三块裸盘无分区无挂载,满足加入 Ceph 的条件"

基础环境配置

1)设置时区(所有节点)
root@Ceph-201 ~# timedatectl status | grep zone
                Time zone: Asia/Shanghai (CST, +0800)
root@Ceph-201 ~# timedatectl set-timezone Asia/Shanghai
root@Ceph-201 ~# ll /etc/localtime
lrwxrwxrwx 1 root root 33 May 16 09:38 /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai

2)安装 Docker 环境(所有节点)
root@Ceph-201 ~# ls
autoinstall-docker.zip
root@Ceph-201 ~# unzip autoinstall-docker.zip
root@Ceph-201 ~# chmod +x install-docker.sh 
root@Ceph-201 ~# ./install-docker.sh i
root@Ceph-201 ~# scp -O autoinstall-docker.zip root@10.0.0.202:/root/
autoinstall-docker.zip         100%   96MB 108.2MB/s
# -O 参数会告诉 scp 使用旧版的 SCP 协议进行文件传输,而不是默认的 SFTP 协议
root@Ceph-201 ~# scp -O autoinstall-docker.zip root@10.0.0.203:/root/
'把tar包拷贝过去'
# 另外两台也要进行安装

3)添加 hosts 解析(所有节点)
root@Ceph-201 ~# cat >> /etc/hosts <<EOF
10.0.0.201 Ceph-201
10.0.0.202 Ceph-202
10.0.0.203 Ceph-203
EOF

4)集群时间同步(Ubuntu 系统可跳过)

5)关机拍快照【强烈推荐】
root@Ceph-201 ~# init 0
# 快照可以让你在部署失败时快速回滚

初始化 Ceph 集群

1)下载 cephadm(选择与目标版本一致)
root@Ceph-201 ~# CEPH_RELEASE=20.2.1
root@Ceph-201 ~# curl --silent --remote-name --location https://download.ceph.com/rpm-${CEPH_RELEASE}/el9/noarch/cephadm
'建议用浏览器去提前下载安装'
root@Ceph-201 ~# ls
cephadm

2)添加到 PATH
root@Ceph-201 ~# chmod +x cephadm
'下载下来的是二进制执行文件644'(不保留x权限)
root@Ceph-201 ~# mv cephadm /usr/local/bin/
root@Ceph-201 ~# ls /usr/local/bin/cephadm 
/usr/local/bin/cephadm
root@Ceph-201 ~# cephadm version
# 验证版本
cephadm version 20.2.1 (6a49aff47758778a5f5951e731d437c317f72fb2) tentacle (stable)
'版本号确认无误 ✅️'
'提前拉取 Ceph 所需镜像(避免初始化时从外网拉取失败)'
root@Ceph-201 ~# docker pull quay.io/ceph/ceph:v20
root@Ceph-201 ~# docker pull quay.io/ceph/grafana:12.2.0
root@Ceph-201 ~# docker pull quay.io/prometheus/prometheus:v3.6.0
root@Ceph-201 ~# docker pull quay.io/prometheus/node-exporter:v1.9.1
root@Ceph-201 ~# docker pull quay.io/prometheus/alertmanager:v0.28.1
'因为这些镜像没在Docker Hub上, 所以拉取镜像还是很轻松的'

root@Ceph-201 ~# docker images
quay.io/ceph/ceph:v20                     0bae386bc859    2.15GB
quay.io/ceph/grafana:12.2.0               74144189b384     974MB
quay.io/prometheus/alertmanager:v0.28.1   27c475db5fb1     110MB
quay.io/prometheus/node-exporter:v1.9.1   d00a542e409e    40.8MB
quay.io/prometheus/prometheus:v3.6.0      76947e7ef22f     440MB
# 5 个核心镜像:ceph + grafana + prometheus + node-exporter + alertmanager

'save导出 & load导入别的节点'
root@Ceph-201 ~# docker save quay.io/ceph/ceph:v20 > ceph-v20.tar.gz
root@Ceph-201 ~# docker save quay.io/ceph/grafana:12.2.0 > grafana-12.2.0.tar.gz
root@Ceph-201 ~# docker save quay.io/prometheus/alertmanager:v0.28.1 > alertmanager-v0.28.1.tar.gz
root@Ceph-201 ~# docker save quay.io/prometheus/node-exporter:v1.9.1 > node-exporter-v1.9.1.tar.gz
root@Ceph-201 ~# docker save quay.io/prometheus/prometheus:v3.6.0 > prometheus-v3.6.0.tar.gz
root@Ceph-201 ~# ls *.tar.gz | xargs -n 1
alertmanager-v0.28.1.tar.gz
ceph-v20.tar.gz
grafana-12.2.0.tar.gz
node-exporter-v1.9.1.tar.gz
prometheus-v3.6.0.tar.gz
root@Ceph-201 ~# ls *.tar.gz | xargs -i scp -O {} root@10.0.0.202:/home/test
root@Ceph-201 ~# ls *.tar.gz | xargs -i scp -O {} root@10.0.0.203:/home/test

# 别的节点
root@Ceph-202 ~# ls /home/test/
alertmanager-v0.28.1.tar.gz
root@Ceph-202 ~# cd /home/test/
root@Ceph-202 test# for i in `ls`; do docker load -i $i;done
'另一台也一样'
root@Ceph-203 test# docker images
quay.io/ceph/ceph:v20                  
quay.io/ceph/grafana:12.2.0            
...........xxxxxxxx

初始化集群

root@Ceph-201 ~# cephadm bootstrap \
  --mon-ip 10.0.0.201 \
  --cluster-network 10.0.0.0/24 \
  --allow-fqdn-hostname \
  --skip-pull
'cephadm bootstrap 是 Ceph 集群初始化的核心命令'

📌 参数说明

参数 作用
--mon-ip 10.0.0.201 指定 Monitor 监听 IP
--cluster-network 10.0.0.0/24 集群内部通信网络(OSD 间数据复制走此网)
--allow-fqdn-hostname 允许使用主机名而非仅 IP
--skip-pull 跳过镜像拉取(已提前导入)

🔹 bootstrap 常用可选参数补充

参数 作用
--registry-url=xxxx 指定私有镜像仓库地址
--registry-username=xxxx 私有仓库用户名
--registry-password=xxx 私有仓库密码
--initial-dashboard-password=xxx ✅️ 预设 Dashboard 密码
--dashboard-password-noupdate ✅️ 禁止首次登录强制修改密码
--single-host-defaults 单节点部署优化(见下方实验)
Verifying podman|docker is present...
...........
Waiting for mgr to start...
Waiting for mgr...
mgr not available, waiting (1/15)...
mgr not available, waiting (2/15)...
mgr not available, waiting (3/15)...
mgr is available
Ceph Dashboard is now available at:

         URL: https://Ceph-201:8443/    <--'域名访问'(导入Windows中hosts表)
        User: admin                     <--"用户名"
    Password: twt8hvj9t0                <--"密码"(随机生成)

💡 Bootstrap 做了什么?

① 生成了集群 FSID(唯一标识)

② 在本地主机上为新集群创建一个 Monitor(监听 10.0.0.201:6789) 和一个 Manager 守护程序

③ 为 Ceph 集群生成一个新的 SSH 密钥,私钥自己拿着(存在 Ceph 内部)

④ 将公钥副本写入 /etc/ceph/ceph.pub(用于分发)

⑤ 将最小配置文件写入 /etc/ceph/ceph.conf(与 Ceph 守护程序通信所必需的)

⑥ 将 client.admin 管理(特权)密钥副本写入 /etc/ceph/ceph.client.admin.keyring

⑦ 将 _admin 标签添加到引导主机

1)SSH 密钥与 authorized_keys(为什么内容一模一样?)
root@Ceph-201 ~# cat /root/.ssh/authorized_keys 
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMK+kon/b+d4oV1PQNY4AKhq5R9M17zm18uLv//0/4N8 ceph-fdc2219c-5344-11f1-8042-000c2990a57e
=======================不同的文件
root@Ceph-201 ~# cat /etc/ceph/ceph.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMK+kon/b+d4oV1PQNY4AKhq5R9M17zm18uLv//0/4N8 ceph-fdc2219c-5344-11f1-8042-000c2990a57e
=======================
 ─── 通俗解释 ───
SSH 密钥对 = 私钥 + 公钥
    - 私钥(自己留着,不公开)  Bootstrap 生成了,存在 Ceph 内部
    - 公钥(可以到处分发)      写入 /etc/ceph/ceph.pub
/root/.ssh/authorized_keys 是什么❓️
     这是 SSH "访客白名单":谁的公钥在里面,谁就能"免密登录"这台机器
=======================
为什么两个文件内容一样?
    Bootstrap 拿同一把公钥做了两件事:
      把公钥"贴到"自己的 authorized_keys 文件里
          Ceph-201 自己能 SSH 免密连自己(后续编排时 master 需要连自身)
      把公钥保存到 /etc/ceph/ceph.pub
         以后用 ssh-copy-id -i /etc/ceph/ceph.pub 分发给其他节点
         其他节点收到这把公钥,也会把它写入自己的 authorized_keys
         这样 Ceph-201 就能免密 SSH 到所有节点,完成集群的编排调度
📌 一句话:ceph.pub 是钥匙的"公钥副本",authorized_keys "允许用这把钥匙的人进来"
    ✅️ 两者内容相同,因为本质上是同一把公钥


2)最小配置文件
root@Ceph-201 ~# cat /etc/ceph/ceph.conf
# minimal ceph.conf for fdc2219c-5344-11f1-8042-000c2990a57e
[global]
    fsid = fdc2219c-5344-11f1-8042-000c2990a57e    # ← 集群唯一身份证号(UUID)
    mon_host = [v2:10.0.0.201:3300/0,v1:10.0.0.201:6789/0]
=======================
 ─── 通俗解释 ───
fsid(集群 ID):
    1.就像每个人的身份证号,全球唯一,用来区分不同的 Ceph 集群
    2.当你管理多个集群时,就是靠 fsid 来区分"你在操作哪一个集群"
=======================
mon_host(Monitor 地址列表):
    告诉客户端:集群的大脑(Monitor)在哪里?
    格式:[v2:IP:3300/0, v1:IP:6789/0]
      - v2(msgr2 协议):端口 3300,新版高性能通信协议,支持加密
      - v1(msgr1 协议):端口 6789,老版协议,向后兼容
    "两个协议地址都写上,新旧客户端都能连"
=======================
为什么叫"最小配置"❓️
       fsid     知道你是谁(不能连错集群)
        就像你去公司报到,只需要知道"公司名称""公司地址"
       mon_host  知道大脑在哪里(去哪报到)
        其他所有信息(OSD 在哪、数据怎么分布…)都由 Monitor 动态告诉你   


3) 管理员密钥环(keyring)
root@Ceph-201 ~# cat /etc/ceph/ceph.client.admin.keyring
[client.admin]                                # ← 用户名:client.admin(管理员账号)
    key = AQBh9wtq2sSYJRAAweHsVl4OjRia0jObKVHapA==    # ← 管理员的"密码"
    caps mds = "allow *"       # 对 MDS 的权限: * = 所有操作
    caps mgr = "allow *"       # 对 Manager 的权限: * = 所有操作
    caps mon = "allow *"       # 对 Monitor 的权限: * = 所有操作
    caps osd = "allow *"       # 对 OSD 的权限: * = 所有操作
=======================
 ─── 通俗解释 ───
keyring(密钥环)是什么❓️
    就是 Ceph "用户名+密码+权限"文件
    类似 MySQL  user@host 认证,或 Linux  /etc/shadow
MySQL 客户端连接 ` mysql -u root -p ` 
# 你向 MySQL 服务端证明"我是 root"
Ceph 客户端连接 `  ceph -s ` ---> 背后读取 client.admin keyring
# ceph 命令行工具向 Monitor(mon_host 指定的地址)证明"我是 admin"
=======================
[client.admin]  用户名
    这是 Ceph "root 账号",拥有集群的绝对控制权
    类比:Linux  root、MySQL  root@localhost
key = AQBh...  密码(经过 base64 编码的密钥)
    这是 client.admin 的认证密钥,不是人类可读的密码,而是加密密钥
    客户端出示 key  Monitor 验证  通过后发放 session ticket
"普通客户端(只想挂个 RBD 硬盘) --> 创建一个权限受限的普通账号"
=======================
caps(capabilities,权限)→ 能做什么
    每个组件都有独立的权限控制:
      mds  = CephFS 元数据服务器  allow * = 可以读写所有文件系统操作
      mgr  = Manager            allow * = 可以访问监控和编排功能
      mon  = Monitor            allow * = 可以查看和修改集群状态
      osd  = OSD 数据守护进程   allow * = 可以直接读写所有磁盘数据


4) 浏览器访问 Dashboard
URL: https://Ceph-201:8443/
# 用上面输出的 admin 用户名和初始密码登录即可

如果用主机名访问 Dashboard,需要修改 Windows 的hosts 解析记录

⚠️ 注意:首次登录 Dashboard 需要修改密码

image-20260519171553551

配置 Ceph 集群管理命令

方式一:宿主机直接安装(推荐)

1)安装 ceph 通用工具包
root@Ceph-201 ~# apt update
root@Ceph-201 ~# apt -y install ceph-common
# 所有主机节点都装一下

2)测试使用
root@Ceph-201 ~# ceph version
ceph version 20.2.1 (6a49aff47758778a5f5951e731d437c317f72fb2) tentacle (stable)

root@Ceph-201 ~# ceph -s
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_WARN
    # 还没加 OSD,所以健康状态为 WARN
            OSD count 0 < osd_pool_default_size 3

  services:
    mon: 1 daemons, quorum Ceph-201 (age 7m) [leader: Ceph-201]
    mgr: Ceph-201.cxdvrq(active, since 7m)
    osd: 0 osds: 0 up, 0 in
    # 还没有 OSD

  data:
    pools:   0 pools, 0 pgs
    objects: 0 objects, 0 B
    usage:   0 B used, 0 B / 0 B avail
    pgs:

方式二:基于容器管理

1)启动 cephadm shell(本质是启动了一个临时容器)
root@Ceph-201 ~# cephadm shell
Inferring fsid fdc...xxx
root@Ceph-201:/# ceph -s
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_WARN
            OSD count 0 < osd_pool_default_size 3
  ...
============================
# 新开一个终端
root@Ceph-201 ~# docker ps -l
IMAGE          COMMAND   CREATED              STATUS           
0bae386bc859   "bash"    About a minute ago   Up About a minute
============================
root@Ceph-201:/# exit
exit
'exit退出shell后,容器随即自动删除'


2)非交互式执行命令(执行完自动退出容器)
'用 -- 分隔,后面跟要执行的命令,适合脚本和快速查看'
root@Ceph-201 ~# cephadm shell -- ceph -s
Inferring fsid ...
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_WARN
            OSD count 0 < osd_pool_default_size 3
  ...

3)多集群环境指定参数
'单集群环境 --> 参数可自动添加,多集群需显式指定'
root@Ceph-201 ~# cephadm shell --fsid <fsid> -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring
✅️ 明确指定集群 fsid、最小配置文件、keyring(管理员密钥环)路径

📌 关键点cephadm shell 本质是启动一个临时容器,退出即删除

添加主机到集群

'在 Ceph-201 节点上操作'

1)查看现有集群主机
root@Ceph-201 ~# ceph orch host ls
HOST      ADDR        LABELS  STATUS  
Ceph-201  10.0.0.201  _admin
1 hosts in cluster

2) SSH 公钥分发到其他节点
root@Ceph-201 ~# ssh-copy-id -f -i /etc/ceph/ceph.pub Ceph-202
-f  # 参数用于强制复制
-i  # 指定密钥
Number of key(s) added: 1
root@Ceph-201 ~# ssh-copy-id -f -i /etc/ceph/ceph.pub Ceph-203

3)添加主机到集群
root@Ceph-201 ~# ceph orch host add Ceph-202 10.0.0.202
Added host 'Ceph-202' with addr '10.0.0.202'
root@Ceph-201 ~# ceph orch host add Ceph-203 10.0.0.203
Added host 'Ceph-203' with addr '10.0.0.203'

4)验证集群节点列表
root@Ceph-201 ~# ceph orch host ls
HOST      ADDR        LABELS  STATUS  
Ceph-201  10.0.0.201  _admin          
Ceph-202  10.0.0.202                  
Ceph-203  10.0.0.203                  
3 hosts in cluster
✅️ 3 个节点全部加入集群

image-20260523161350093

四、添加 OSD 设备

OSD 加入条件

⚠️ 一个设备想要加入 Ceph 集群,必须满足 3 个条件:
  (1)设备未被使用(无分区、无文件系统、没有被 LVM 占用、当前未挂载)
  (2)设备的存储大小必须大于 5GB
  (3)需要等待 30s~3min 才能在 ceph orch device ls 中看到设备
'ceph orch device ls --refresh'
✅️ --refresh 强扫,比干等 agent 周期扫描快
'绕开缓存直接触发一次新扫描'

查看可用设备

1)查看集群可用的设备
root@Ceph-201 ~# ceph orch device ls
✅️ 优先加 --refresh 强扫选项
HOST      PATH      TYPE SIZE  AVAILABLE
Ceph-201  /dev/sdb  hdd   300G  Yes        15m ago               
Ceph-201  /dev/sdc  hdd   500G  Yes        15m ago
Ceph-201  /dev/sdd  hdd  1024G  Yes        15m ago
Ceph-201  /dev/sr0  hdd   VMware..xxx  space (<5GB)  # ← 光驱,<5GB,不可用
Ceph-202  /dev/sdb  hdd   300G  Yes        13m ago
Ceph-202  /dev/sdc  hdd   500G  Yes        13m ago
Ceph-202  /dev/sdd  hdd  1024G  Yes        13m ago
Ceph-202  /dev/sr0  hdd   VMware..xxx  space (<5GB)
Ceph-203  /dev/sdb  hdd   300G  Yes        10s ago
Ceph-203  /dev/sdc  hdd   500G  Yes        10s ago
Ceph-203  /dev/sdd  hdd  1024G  Yes        10s ago
Ceph-203  /dev/sr0  hdd   VMware..xxx  space (<5GB)
# 每个节点 3 块数据盘 × 3 个节点 = 9 个候选 OSD
============================
AVAILABLE 列的评判标准:就是"这块盘还能不能拿来当 OSD"
一旦加入OSD成功,Ceph 在上面建了 LVM、写了文件系统
再次查看时,AVAILABLE  Yes 变No --> ✅️ 不是盘坏了,是盘已经有主了


2)查看当前 OSD 列表(加盘前)
root@Ceph-201 ~# ceph osd tree
ID  CLASS  WEIGHT  TYPE NAME     STATUS  REWEIGHT  PRI-AFF
-1              0  root default
# 空 ——> 还没加 OSD
字段 说明
ID OSD 或节点的唯一标识符,负数通常表示分组节点
-1:默认根节点
CLASS 存储设备的类型分类
如 hdd(机械硬盘)、ssd(固态硬盘)、nvme(NVMe SSD)
WEIGHT CRUSH 算法中使用的权重值,决定数据分布比例
容量越大权重通常越大
TYPE 节点类型
如 root(根节点)、host(主机)、osd(存储守护进程)
NAME 节点名称
可能是主机名或 OSD 编号
STATUS 状态
up 表示在线可用,down 表示离线
REWEIGHT 临时权重调整值,用于控制数据迁移
1.0 为正常,0.0 为禁止数据
PRI-AFF Placement Group 亲和性设置

📌 CLASS 类型速查

CLASS 接口/协议 判断依据 性能定位 VMware 模拟
hdd(机械硬盘) SATA/SCSI rotational=1/dev/sd* 慢,便宜,容量大 SCSI/SATA 虚拟盘(默认)
ssd(固态硬盘) SATA/SCSI rotational=0/dev/sd* 快,适合热数据 SCSI/NVMe 虚拟盘
nvme(NVMe SSD) PCIe 直连 /dev/nvme*(自动识别) 最快,延迟最低 NVMe 虚拟盘

hdd < ssd < nvme — CRUSH 用 CLASS 区分快慢,实现数据分层

元数据放 nvme、热数据放 ssd、冷数据放 hdd

⭐批量添加 OSD

'一次一条命令,将 9 块盘全部添加为 OSD'

# --- Ceph-201 节点 ---
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdb
✅️ 太慢了,等等吧...
Created osd(s) 0 on host 'Ceph-201'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdc
Created osd(s) 1 on host 'Ceph-201'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdd
Created osd(s) 2 on host 'Ceph-201'

root@Ceph-202 ~# ceph orch daemon add osd Ceph-202:/dev/sdb
Error initializing cluster client: ObjectNotFound('RADOS object not found (error calling conf_read_file)')
# ↑ Ceph-202 本地找不到 /etc/ceph/ceph.conf,无法知道 Monitor 在哪,认证凭据也没有

🧱 通俗解释

ceph orch host add(添加主机到集群) 只是把主机名和 IP 登记到集群的"花名册"上(Monitor 知道有这台机器了),但并没有把"身份证"发给这台机器

执行 ceph orch daemon add osd 时,命令要和 Monitor 通信——需要两样东西: - ceph.conf → "公司在哪"(Monitor 地址) - ceph.client.admin.keyring → "你是谁"(管理员身份证明)

Ceph-202 本地没有这两个文件 → 报 ObjectNotFound

解决:从 Ceph-201(引导节点)拷贝过去就行

📌 延伸 — 多集群管理:既然 ceph.conf + keyring 决定了"连哪个集群、以什么身份连"

那么一个客户端拿着多套配置文件,就能同时管理多个 Ceph 集群:

# 切换到集群 A
ceph --fsid <fsid-A> -c /etc/ceph/ceph-A.conf -k /etc/ceph/ceph-A.client.admin.keyring -s

# 切换到集群 B
ceph --fsid <fsid-B> -c /etc/ceph/ceph-B.conf -k /etc/ceph/ceph-B.client.admin.keyring -s

[!note]

无需登录到不同节点,在本地切换配置文件即可——类似于 kubectl 的 context 切换

'👇在 Ceph-201 上执行,把"身份证"分发到其他节点'
root@Ceph-201 ~# scp /etc/ceph/ceph.conf root@Ceph-202:/etc/ceph/
root@Ceph-201 ~# scp /etc/ceph/ceph.client.admin.keyring root@Ceph-202:/etc/ceph/
'👇设置正确权限'
root@Ceph-201 ~# ssh root@Ceph-202 "chmod 644 /etc/ceph/ceph.conf && chmod 600 /etc/ceph/ceph.client.admin.keyring"

# --- Ceph-202 节点 ---
root@Ceph-201 ~# ceph orch daemon add osd Ceph-202:/dev/sdb
Created osd(s) 3 on host 'Ceph-202'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-202:/dev/sdc
Created osd(s) 4 on host 'Ceph-202'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-202:/dev/sdd
Created osd(s) 5 on host 'Ceph-202'

# --- Ceph-203 节点 ---
'同上'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-203:/dev/sdb
Created osd(s) 6 on host 'Ceph-203'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-203:/dev/sdc
Created osd(s) 7 on host 'Ceph-203'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-203:/dev/sdd
Created osd(s) 8 on host 'Ceph-203'

❤️LVM 技术栈

💡 底层原理:ceph 底层使用 LVM 技术栈 管理磁盘,每块盘会被初始化为 LVM PV → VG → LV

795254-20241209160139243-1571372412

🧱 LVM 技术栈详解:PV → VG → LV

LVM(逻辑卷管理器)是 Linux 下对磁盘进行灵活分区管理的技术

它的核心思路是:在物理磁盘和文件系统之间加一层抽象

让你可以随时调整"分区"大小,而不需要像传统分区那样推倒重来

① 物理卷 — PV(Physical Volume)

PV 是最底层的存储单元

📌 类比:PV 就像一块空地——不管原来是整座山(整块盘)还是山的一角(分区),圈进来之后就都是可用的地皮

② 卷组 — VG(Volume Group)

VG 是 LVM 的"资源池"——把多块 PV 整合成一个统一的大池子

📌 类比:VG 就像把多块空地合并成一个大工地

③ 逻辑卷 — LV(Logical Volume)

LV 是从 VG 中切分出来的、最终可用的存储空间

📌 类比:LV 就像从大工地里隔出来的房间——想要多大隔多大,房间不够用了还能在线扩容

📋 三层结构速查表

层次 英文 是什么 关键操作
物理卷 PV 底层存储"砖块" pvcreate /dev/sdb
卷组 VG 多块 PV 的"资源池" vgcreate vg_name /dev/sdb /dev/sdc
逻辑卷 LV 最终使用的"存储空间" lvcreate -L 100G -n lv_name vg_name

🔁 数据流向物理磁盘 → PV → VG → LV → 格式化文件系统 → 挂载使用


⚖️ LVM 的优缺点

优点 缺点
1 在线扩容:LV 不够用了,在线扩,不影响正在跑的业务 多一层开销:比直接操作裸盘多一层抽象,有微弱性能损耗
2 灵活聚合:多块小盘合成一个大 VG,打破单盘容量限制
加新盘 → 扩 VG → 扩 LV → 文件系统变大
故障面扩大:VG 中某块 PV 坏了,用到它的 LV 会受影响
3 快照功能:给 LV 拍快照,备份/回滚/测试都很方便 学习成本:多了一套概念(PV→VG→LV),排错链路变长
4 动态伸缩:不光能扩容,还能缩容(ext4 支持,XFS 不支持缩容) 固件限制:固件不认识 LVM,只认普通分区
所以 /boot 通常要单独分出来放在 LVM 外面

💻 Ubuntu 与 LVM

Ubuntu 系统安装时如果勾选"使用 LVM",底层就是这样运作的——安装程序自动创建:

# Ubuntu 默认 LVM 结构(以 40G 系统盘为例)
/dev/sda3  PV ──→ ubuntu-vg(VG)──→ ubuntu-lv(LV,挂载到 /)
                                                                              └── LV 不会占满整个 VG,留一部分空闲
                                           '不是空间浪费,是刻意的'
                                           '留着以后给 / 根分区在线扩容用'
# 验证一下(Ubuntu环境里就有)
root@Ceph-201 ~# lsblk
sda     8:0    0   40G  0 disk 
├─sda1  8:1    0    1M  0 part 
├─sda2  8:2    0    2G  0 part /boot ✅️'/boot只认普通分区'
└─sda3  8:3    0  '38G' 0 part
  └─ubuntu--vg-ubuntu--lv   252:0    0  '30G'  0 'lvm'  /
     👆 '这一整行是逻辑卷(LV)'             'LVM技术栈'👆
📌 细节 --> 从sda3开始分
✅️ 38G  PV,LV 只用了 30G  8G 空闲留在 VG 里

root@Ceph-201 ~# vgs
  VG        #PV #LV #SN Attr   VSize  VFree
  ubuntu-vg   1   1   0 wz--n- 38.00g 8.00g
    👆 '这才是卷组'
✅️ VFree = 8G  空闲空间,随时可以扩给 /

📌 Ubuntu 不会一开始就把所有空间分给根分区只会分一半左右——剩下的留在 VG 里


🔗 回到 Ceph

Ceph 添加 OSD 时,ceph orch daemon add osd 在背后做的事本质上就是这套 LVM 流程:

# 当你执行 ceph orch daemon add osd Ceph-201:/dev/sdb 时,Ceph 自动:
/dev/sdb  pvcreate  创建 PV
          vgcreate  创建 VG(名:ceph-<VG_UUID>)
          lvcreate  创建 LV(不进行文件系统格式化, BlueStore 直接写裸 LV)
                ✅️ BlueStore:   '不依赖任何文件系统,直接在裸盘上存储'
root@Ceph-201 ~# vgs
  VG                 #PV #LV #SN Attr   VSize     VFree 
  ceph-023d...f772   1   1   0 wz--n-  <500.00g     0 
  ceph-6f9f...7035   1   1   0 wz--n- <1024.00g     0 
  ceph-fadf...ac73   1   1   0 wz--n-  <300.00g     0 
  ubuntu-vg          1   1   0 wz--n-   <38.00g <8.00g
# 👆 卷组名 = ceph-<VG_UUID>(VG_UUID 是 LVM 随机生成的)
'在 Ceph-202 上验证 LVM 结构'
root@Ceph-202 ~# lsblk
sda                         8:0    0   40G  0 disk 
├─sda1                      8:1    0    1M  0 part 
├─sda2                      8:2    0    2G  0 part /boot
└─sda3                      8:3    0   38G  0 part 
  └─ubuntu--vg-ubuntu--lv   252:0    0   30G  0 lvm  /
'上面是系统盘'
sdb ✅️物理磁盘 'PV'   8:16   0  300G  0 disk 
└─ceph--fadf...ac73-osd--block--2078...bd8bd   252:1    0  300G  0 lvm  
sdc ✅️物理磁盘 'PV'   8:32   0  500G  0 disk 
└─ceph--023d...f772-osd--block--8bb5...f359   252:2    0  500G  0 lvm  
sdd ✅️物理磁盘 'PV'   8:48   0    1T  0 disk 
└─ceph--6f9f...7035-osd--block--da9a...7195   252:0    0 1024G  0 lvm
# 👆 lsblk 看到的是基于物理磁盘创建的 LV(逻辑卷)
'这些逻辑卷被用作 Ceph OSD 的数据存储'

📋 命名规律速查

层级 命名格式 示例(以 sdb 300G 为例)
VG ceph-<VG_UUID>
随机生成的
ceph-fadf...ac73
LV 短名 osd-block-<OSD_FSID> osd-block-2078...bd8bd
LV 完整名(lsblk) ceph--<VG_UUID>-osd--block--<OSD_FSID> ceph--fadf...ac73-osd--block--2078...bd8bd

📌 规律ceph- 开头 → VG(卷组)osd-block- 开头 → LV 短名

/var/lib/ceph/
└── fdc2219c-5344-11f1-8042-000c2990a57e/  <-- '这是集群的 FSID'
    ├── cephadm.adad9831..xxx
    ├── osd.3 ✅️ OSD 目录
    ├── osd.4 ✅️ OSD 目录
    ├── osd.5 ✅️ OSD 目录
    ├── ...xxx
    └── crash
root@Ceph-201 osd.0# pwd
/var/lib/ceph/<集群>/osd.0
'每个 OSD 有一个唯一的 fsid'
root@Ceph-201 osd.0# cat fsid 
2078...bd8bd
对比 Ubuntu 系统盘 LVM Ceph OSD 数据盘 LVM
PV /dev/sda3(分区) /dev/sdb(整块裸盘)
VG ubuntu-vg ceph-<VG_UUID>
LV 短名 ubuntu-lv osd-block-<OSD_FSID>
LV 完整名(lsblk) ubuntu--vg-ubuntu--lv ceph--<VG_UUID>-osd--block--<OSD_FSID>
文件系统 ext4(建在 LV 上) BlueStore 绕过文件系统直接操作裸 LV
扩容方式 lvextend + resize2fs 不需要——Ceph 已占满整块盘

📌 一句话:Ceph 底层使用 LVM 技术栈管理每块 OSD 数据盘

验证 OSD 状态

image-20260523181615360

1)查看 OSD 树
root@Ceph-201 ~# ceph osd tree
'每个字段的含义,参考上面的表格'
ID  CLASS  WEIGHT   TYPE NAME       STATUS  REWEIGHT  PRI-AFF
-1         5.34389  root default                                
-3         1.78130      host Ceph-201                           
 0    hdd  0.29300          osd.0       up   1.00000  1.00000
 1    hdd  0.48830          osd.1       up   1.00000  1.00000
 2    hdd  1.00000          osd.2       up   1.00000  1.00000
-5         1.78130      host Ceph-202                           
 3    hdd  0.29300          osd.3       up   1.00000  1.00000
 4    hdd  0.48830          osd.4       up   1.00000  1.00000
 5    hdd  1.00000          osd.5       up   1.00000  1.00000
-7         1.78130      host Ceph-203                           
 6    hdd  0.29300          osd.6       up   1.00000  1.00000
 7    hdd  0.48830          osd.7       up   1.00000  1.00000
 8    hdd  1.00000          osd.8       up   1.00000  1.00000
'hdd -->机械硬盘'
# 9 个 OSD 全部 up ✅️

2)查看集群健康状态
root@Ceph-201 ~# ceph -s
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_OK   ✅️ 健康!

  services:
    mon: 3 daemons, quorum Ceph-201,Ceph-202,Ceph-203 (age 113m) [leader: Ceph-201]
    mgr: Ceph-201.cxdvrq(active, since 5h), standbys: Ceph-202.xdeumk
    osd: 9 osds: 9 up (since 37m), 9 in (since 39m)     ✅️ 9 个全 up

  data:
    pools:   1 pools, 1 pgs
    objects: 2 objects, 449 KiB
    usage:   249 MiB used, 5.3 TiB / 5.3 TiB avail      ✅️ 总空间 5.3T
    pgs:     1 active+clean

📌 集群关键数据:3 节点 × 3 盘 = 9 OSD,总容量 ~5.3 TiB,9/9 在线,状态 HEALTH_OK

OSD 的移除与磁盘清理

加盘的反向操作——当你需要撤掉某个 OSD 或重新部署时:

1)从集群中删除 OSD 守护进程
root@Ceph-201 ~# ceph orch daemon rm osd.0 --force
Removed osd.0 from host 'Ceph-201'
rm          # 移除 OSD 守护进程(容器停止并被删除)
osd.0       # 表示OSD的ID编号
--force     # 跳过确认提示,直接执行

2)擦除磁盘上的 Ceph 数据(恢复为空盘)
ceph orch device zap <主机名> <路径> [--force] 
root@Ceph-201 ~# ceph orch device zap Ceph-201 /dev/sdb --force
zap successful for /dev/sdb on Ceph-201
zap         # "清零"——销毁 Ceph 建的 LVM 卷、分区、文件系统
--force     # 跳过确认提示
root@Ceph-201 ~# ceph osd tree
ID  CLASS  WEIGHT   TYPE NAME          STATUS  REWEIGHT  PRI-AFF
-1         1.78130  root default                                
-3         1.78130      host Ceph-201                           
 0    hdd  0.29300          osd.0      'down'   1.00000  1.00000
'已经down掉了'
root@Ceph-201 ~# ceph orch device ls
Ceph-201  /dev/sdb  hdd  300G  'Yes' ✅️ AVAILABLE为Yes --> 可以重新添加为OSD
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdb
Created osd(s) 3 on host 'Ceph-201'
root@Ceph-201 ~# ceph osd tree
ID  CLASS  WEIGHT   TYPE NAME          STATUS  REWEIGHT  PRI-AFF
-1         2.07430  root default                                
-3         2.07430      host Ceph-201                           
 0    hdd  0.29300          'osd.0'        down        0  1.00000
 1    hdd  0.48830          'osd.1'         up   1.00000  1.00000
 2    hdd  1.00000          'osd.2'         up   1.00000  1.00000
 3    hdd  0.29300          'osd.3'         up   1.00000  1.00000
root@Ceph-201 ~# ceph -s
  cluster:
    id:     3ca027a7-5400-11f1-a4e3-000c2990a57e
    health: HEALTH_WARN ❌️
    ⚠️ 此时 `ceph -s` 出现 `1 stray daemon(s)` 告警
            1 stray daemon(s) not managed by cephadm
            # 一个流浪进程,不被cephadm管理了
   services: xxx
    osd: 4 osds: 3 up (since 61s), 3 in (since 42s)
    '4 个 OSD 记录,但只有 3 个在线'
    osd.0  down(旧号,没人管了)
    osd.3  up  (新盘,新 ID)
🧱 为什么会有 stray daemon❓️

ceph orch daemon rm osd.0          只删了编排器层(cephadm 视角)的容器进程
ceph orch device zap Ceph-201 /dev/sdb   磁盘清空

但是!CRUSH 拓扑层(CRUSH 全局视角)还记着 osd.0:
   CRUSH 说:"osd.0 应该在线啊,去哪了?"
   cephadm 说:"osd.0 不归我管了"
   这个没人认领的 OSD 就是 stray daemon(流浪进程)

层面 命令入口 管的 视角
CRUSH 拓扑层 ceph osd ... CRUSH 地图、OSD 状态、数据放置规则 CRUSH 全局视角——"数据该往哪放"
编排器层 ceph orch ... 容器/进程、磁盘设备、主机节点 cephadm 视角——"服务跑在哪"

📌 stray daemon 的根因:ceph orch daemon rm 只清了编排器层,CRUSH 拓扑层还记着这个 OSD → 两层不一致 → 集群告警

✅ 正确做法:用 ceph orch osd rm 一条命令同时清两层(停进程 + 清 CRUSH + 可选 zap),详见 day03 笔记

🔧 如果已经出现 stray daemon,此时 OSD 已经是 down 状态,直接 purge 就行:

'从 CRUSH 表彻底删除 → 集群名单踢出'
ceph osd purge osd.<旧ID> --yes-i-really-mean-it
# stray daemon 的 OSD 已经 down 了(容器早就被 rm 了),所以直接 purge 不会报错
root@Ceph-201 ~# ceph -s
  cluster:
    id:     3ca027a7-5400-11f1-a4e3-000c2990a57e
    health: HEALTH_OK
  services:...xxx
    osd: 3 osds: 3 up (since 3m), 3 in (since 6m)
    '三个osd,三个都在运行'

🔹 osd out 是什么?

`osd out` 的作用是 标记 OSD "即将退役"
触发 PG 恢复——集群自动把该 OSD 上的数据副本搬到其他 OSD,等数据全部搬完后再删盘就不会丢数据
============================
osd out 之后发生的事:
osd.3 上的副本  搬到 osd.1 / osd.2 / osd.4 ...
PG 状态:recovering  active+clean
数据搬完了   osd.3 就安全了

⚠️ 但实验环境几乎没数据,osd out 反而触发无意义的恢复

删除 OSD — 生产环境 vs 实验环境

'============================='
'  生产环境(有真实数据)— 四步  '
'============================='
1)标记退役  触发 PG 恢复,数据安全搬到其他 OSD
root@Ceph-201 ~# ceph osd out osd.3
'等待 recovery 完成'
# ceph -s 确认 PG 全部 active+clean

'============================='
'  实验环境(没数据)— 三步即可  '
'============================='
📌 下篇笔记📚有更简单的方法
`ceph orch osd rm` ——> 一条命令完成停进程 +  CRUSH + 清磁盘
2)停进程
root@Ceph-201 ~# ceph orch daemon rm osd.3 --force

3)销户口
root@Ceph-201 ~# ceph osd purge osd.3 --yes-i-really-mean-it

4)清磁盘
root@Ceph-201 ~# ceph orch device zap Ceph-201 /dev/sdb --force
对比:
  生产环境:out  daemon rm  purge  zap   (多一步 out,保证数据安全)
  实验环境:       daemon rm  purge  zap   (跳过 out,避免无意义恢复)
==============================
  关键依赖:无论哪种,purge 都必须在 daemon rm 之后(要求 down 状态)

五、CRUSH 规则与故障域

🧱 故障域概念:Ceph 的 CRUSH 算法决定了数据副本如何分布在不同的 OSD 上,而"故障域"定义了故障的隔离边界

默认情况下,Ceph 按主机(host)划分故障域

默认规则长什么样❓️

root@Ceph-201 ~# ceph osd crush rule dump
'查看Ceph集群中的所有CRUSH规则'
[
    {
        "rule_id": 0, <-- 默认规则 ✅️
        "rule_name": "replicated_rule",
        "type": 1,
        "steps": [
            { "op": "take", "item": -1, "item_name": "default" },
            { "op": "chooseleaf_firstn", "num": 0, "type": "host" },
            { "op": "emit" }
        ]
    }
]

📌 chooseleaf_firstn + type: "host" 的执行步骤(假设副本数 = 3):

   take default        CRUSH 树的根节点开始
   chooseleaf_firstn    N 台不同的 host,每台 host 下再挑 1 块最佳 OSD
     num = 0            这里的 0 不是"选 0 个",是"不硬编码"的占位符
                        实际 N 从存储池的 size 参数动态读取
                         size=3  0 等价于 3(如果池 size=2,0 就等价于 2  Step 1: 选第 1  host  挑该 host 下的一块 OSD  放副本①
  Step 2: 选第 2  host(必须和 Step1 不同)→  OSD  放副本②
  Step 3: 选第 3  host(必须和 Step1/2 不同)→  OSD  放副本③

  核心约束:同一台 host 只能被选中一次!

💡 num = 0 的设计意图:让一条 CRUSH 规则能被多个不同副本数的池复用——规则本身不写死数量,而是从池的 size 参数动态读取

num 如果写死成 3,那这条规则就只能给 size=3 的池用,其他池得另建规则


单节点 + 默认规则 = 矛盾

你的单节点 CRUSH 树:

  root default
   └─ host Ceph-201         只有 1 台主机!
       ├─ osd.0 (sdb)
       ├─ osd.1 (sdc)
       └─ osd.2 (sdd)

CRUSH 尝试放置 3 个副本的过程

  选第 1  host  Ceph-201   osd.0  放副本①   选第 2  host  树里只有 Ceph-201,但它已被选过  不能再选  失败   选第 3  host  同上  失败   最终结果:3 个副本只放下了 1 

"只能选 1 块盘"这句话的真正含义

你有 3 块盘(sdb、sdc、sdd),盘是够的

但 CRUSH 规则的第一关不是"找盘",而是"找不同的主机"

每台主机只能被选一次,1 台主机 → 只能过第一关一次 → 只能选到 1 块盘


单节点 HEALTH_WARN 复现

'主要是在单节点下进行添加OSD设备'
# 我这里是进行了恢复快照

root@Ceph-201 ~# ceph orch host ls
HOST      ADDR        LABELS  STATUS  
Ceph-201  10.0.0.201  _admin          
1 hosts in cluster
'不把其他两台主机添加到集群' <-- 它俩关机状态
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdb
Created osd(s) 0 on host 'Ceph-201'
# 然后直接添加OSD设备
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdc
Created osd(s) 1 on host 'Ceph-201'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdd
Created osd(s) 2 on host 'Ceph-201'
root@Ceph-201 ~# ceph -s
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_OK
'在我添加完, 立马查看状态 ✅️ OK'
可是并没有看到 HEALTH_WARN ❌️

⚠️ 加完 3 块 OSD 立刻 ceph -s 看到 HEALTH_OK,是因为 PG 还没完成创建/peering

实测等一会儿再查,状态就变换了❌️不是版本差异,纯粹是时序问题

root@Ceph-201 ~# ceph -s
  cluster:
    id:     fdc2219c-5344-11f1-8042-000c2990a57e
    health: HEALTH_WARN ❌️ "告警暴露出来了"
            Reduced data availability: 1 pg inactive   --> 'PG 不活跃'
            Degraded data redundancy: 1 pg undersized  --> '副本数不足'
                                                    # 要 3 个只放了 1 个
root@Ceph-201 ~# ceph osd pool ls detail
'显示集群中所有存储池的详细配置信息'
pool 1 '.mgr' replicated size 3 min_size 2 crush_rule 0...
replicated size 3   # 副本数=3 → 要放 3 份
min_size 2          # 最少 2 个副本才允许读写I/O
crush_rule 0        # crush_rule 0 = 默认规则 (type = host)
📌 只有 1 台主机  CRUSH 只能选出 1 块盘  PG undersized '副本数不足'

创建以 OSD 为故障域的规则

命令 作用 一句话
ceph osd crush rule ls 列出所有规则
只列名字
"有哪些规则"
ceph osd crush rule dump <规则名> 查看规则内部细节
完整 JSON
"这条规则内部长什么样"
ceph osd crush rule create-replicated <规则名> <根节点> <故障域> 创建副本规则 "起名 + 定根 + 定故障域,三步搞定"
ceph osd crush rule create-erasure <规则名> <profile名> 创建纠删码规则 "引用 profile,让规则知道 k/m 和编码算法"
对比维度 副本规则 create-replicated 纠删码规则 create-erasure
参数 规则名 + 根节点 + 故障域 规则名 + 纠删码 profile
故障域在哪指定 命令里直接写(host / osd / rack) 不在命令里——profile 已声明 k+m,CRUSH 据此选盘
为什么不同 副本 = 简单 copy,不需要额外配置 纠删码要提前定义 k+m 比例、编码算法(jerasure / lrc 等),这些全在 profile 里
root@Ceph-201 ~# ceph osd crush rule ls
replicated_rule      默认,type = host

1)创建新规则(故障域从 host 降为 osd)
'语法:ceph osd crush rule create-replicated <规则名> <根节点> <故障域级别>'
root@ceph:~# ceph osd crush rule create-replicated rule_osd default osd
root@ceph:~# ceph osd crush rule ls
replicated_rule        默认,type = host
rule_osd               新建,type = osd

2)对比两条规则
root@ceph:~# ceph osd crush rule dump rule_osd
'指定查看我们刚创建的规则 rule_osd'
# dump后面紧跟规则名
{
    "rule_id": 1, <-- 新建规则 ✅️
    "rule_name": "rule_osd",
    "type": 1,
    "steps": [
        { "op": "take", "item": -1, "item_name": "default" },
        { "op": "choose_firstn", "num": 0, "type": "osd" },
        { "op": "emit" }
    ]
}

📌 chooseleaf_firstn + host vs choose_firstn + osd

  type = host (chooseleaf_firstn)        type = osd (choose_firstn)

   先选 host,再在 host 下选 OSD         直接在 OSD 层选,不管 OSD 属于谁
   同一 host 只能被选一次                任何 OSD 都可以被独立选中

  ┌─────────────────────┐                ┌────────────────────┐
   root                                 root                   └─ host Ceph-201                     ├─ osd.0  副本①           ├─ osd.0  副本①|                  ├─ osd.1  副本②           ├─ osd.1      |                  └─ osd.2  副本③           └─ osd.2      |                                                                             3 块盘都在同一台        只能放 1 个副本                         主机上也没关系         └─────────────────────┘                 └───────────────────┘

关联存储池到新规则

ceph osd pool set <存储池名称> <参数名> <参数值>
# 将存储池关联到新规则
root@Ceph-201 ~# ceph osd pool set .mgr crush_rule rule_osd
# rule_osd 我们新建的规则
set pool 1 crush_rule to rule_osd
===================================
`.mgr` 是什么❓️
    1.Manager 守护进程的内部存储池,bootstrap(初始化集群) 时自动创建
    2.Manager 用它来存设备健康+数据、性能指标、Dashboard 配置等"内部账本",不存用户数据
    3.点号开头是命名惯例,表示"系统池"
    4.集群启动后第一个池就是它——所以 单节点 HEALTH_WARN 也是它先暴露出来的
`Dashboard` 是什么❓️
    1.就是 Ceph 自带的 Web 管理界面(端口 8443    2.你浏览器打开后能看集群健康状态、OSD列表、性能图表、存储池信息等等
✅️ Dashboard 配置存在 .mgr 池里指的是
    1.你通过 Web 界面做的设置(比如改了登录密码、调了监控告警阈值)
    2.Manager进程把这些配置数据写到 .mgr 池里持久化保存
    3.这样即使 Manager 重启,配置也不会丢


root@Ceph-201 ~# ceph osd pool ls detail
'显示集群中所有存储池的详细配置信息'
pool 1 '.mgr' ... crush_rule 1 ...
✅️ rule_id  0 变成 1
# 由默认规则 --> 到新建规则
root@Ceph-201 ~# ceph -s
  cluster:
    health: HEALTH_OK ✅️
  ...
    pgs:     1 active+clean 🏷️

场景速查表

场景 故障域 副本数 CRUSH 规则 结果
多节点(≥3 台) host 3 chooseleaf_firstn + host ✅ HEALTH_OK
单节点 + 默认规则 host 3 chooseleaf_firstn + host ❌ PG undersized
副本数不足
单节点 + 手动改规则 osd 3 choose_firstn + osd ✅ HEALTH_OK
单节点 + --single-host-defaults osd 2 choose_firstn + osd ✅ HEALTH_OK

🧱 一句话总结:默认 CRUSH 规则 type=host 的本质是"同一个数据的多个副本必须放在不同主机上"

要在单个主机上部署 Ceph 集群,请在引导时使用 --single-host-defaults 标志

此类集群通常不适合生产环境**

该标志会自动设置以下配置选项:

💡 参考文档:不同部署场景的推荐配置


集群删除(完整流程)

root@Ceph-201 ~# ceph orch device ls
'查看集群可用设备' --> 它们已经添加为OSD(已经被 Ceph 占用,不能再加第二次)
    --> NO(不可用,不能再次添加了) Has a FileSystem(有了文件系统)
Ceph-201  /dev/sdb  hdd  300G  No❌️
Ceph-201  /dev/sdc  hdd  500G  No❌️
Ceph-201  /dev/sdd  hdd 1024G  No❌️
====================================
AVAILABLE = No 不是"坏了",是"已上岗" --> 表示磁盘上有 Ceph 数据

1)禁用 cephadm 模块(停止所有编排操作)
root@Ceph-201 ~# ceph mgr module disable cephadm

2)获取集群 FSID
root@Ceph-201 ~# ceph fsid
fdc2219c-5344-11f1-8042-000c2990a57e

3)在所有主机上清除 Ceph 守护程序和数据
'⚠️ 所有节点都要执行!'
root@Ceph-201 ~# cephadm rm-cluster --force --zap-osds --fsid fdc2219c-5344-11f1-8042-000c2990a57e
--force             # 强制删除 --> "别问我确认,直接干"
--zap-osds          # 清除磁盘上的 OSD 数据
--fsid fdcxxx       # 指定FSID
Deleting cluster with fsid: fdc2219c-...xxx
Zapping /dev/sdb...
Zapping /dev/sdc...
Zapping /dev/sdd...
'初始化集群自动创建的文件也都删除了'
# No such file or directory
root@Ceph-201 ~# ls /etc/ceph/ceph.pub
# 到处分发的公钥
'都删除了'
root@Ceph-201 ~# ls /etc/ceph/ceph.conf
# 最小配置文件
root@Ceph-201 ~# ls /etc/ceph/ceph.client.admin.keyring
# 管理员密钥环

4)清理残留
root@Ceph-201 ~# ceph -s
Error initializing cluster client: ObjectNotFound(...) --> ✅️ 集群已不存在
root@Ceph-201 ~# rm -rf /etc/ceph/rbdmap
root@Ceph-201 ~# > /root/.ssh/authorized_keys
'清空 authorized_keys 文件,移除 cephadm bootstrap 写入的 SSH 公钥'

⚠️ 注意

手动指定集群 FSID

正常装集群:cephadm bootstrap
   └── 自动生成一个随机 FSID,OSD 盘上标记的就是这个 ID
'FSID 就是集群的"身份证号"'

root@Ceph-201 ~# ceph fsid
fdc2219c-5344-11f1-8042-000c2990a57e
# 获取当前FSID号
root@Ceph-201 ~# cephadm bootstrap --fsid fdc221...xxx
--fsid 用旧 ID    磁盘上的 OSD 还认得这个 FSID,数据能恢复

单节点部署

'单节点 + --single-host-defaults'

1)设置主机名
root@Ceph-201 ~# tail -3 /etc/hosts
10.0.0.201 Ceph-201
10.0.0.202 Ceph-202
10.0.0.203 Ceph-203

2)使用 --single-host-defaults 重新引导
root@Ceph-201 ~# cephadm bootstrap \
  --mon-ip 10.0.0.201 \
  --single-host-defaults \
  --initial-dashboard-password=Oldboy123.com \
  --dashboard-password-noupdate \
  --allow-fqdn-hostname \
  --skip-pull

Ceph Dashboard is now available at:
         URL: https://Ceph-201:8443/
        User: admin
    Password: Oldboy123.com
'单节点优化模式已生效'

3)查看默认 CRUSH 规则(故障域已是 osd 级别)
root@Ceph-201 ~# ceph osd crush rule dump replicated_rule
# dump后紧跟的是默认的规则名
{
    "rule_id": 0, ✅️ 这个0也能说明它是默认的规则
    "rule_name": "replicated_rule",
    "type": 1,
    "steps": [
        { "op": "take", "item": -1, "item_name": "default" },
        { "op": "choose_firstn", "num": 0, "type": "osd" }, <-- ✅️已经是 osd 级别
        { "op": "emit" }
    ]
}

4)逐个添加 OSD(也可逐个指定磁盘)
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdb
Created osd(s) 0 on host 'Ceph-201'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdc
Created osd(s) 1 on host 'Ceph-201'
root@Ceph-201 ~# ceph orch daemon add osd Ceph-201:/dev/sdd
Created osd(s) 2 on host 'Ceph-201'
root@Ceph-201 ~# ceph -s
  cluster:
    id:     3ca027a7-5400-11f1-a4e3-000c2990a57e
    health: HEALTH_OK ✅️

  services:
    mon: 1 daemons, quorum Ceph-201 (age 49m) [leader: Ceph-201]
    mgr: Ceph-201.ylifrh(active, since 47m), standbys: Ceph-201.eukksl
    # 部署了两个 mgr
    '一台机器上跑了两个 MGR 守护进程 ——> 一个 active,一个 standby'
    osd: 3 osds: 3 up (since 39m), 3 in (since 39m)

5)验证最终状态
root@Ceph-201 ~# ceph osd tree
ID  CLASS  WEIGHT   TYPE NAME          STATUS  REWEIGHT  PRI-AFF
-1         1.78130  root default                                
-3         1.78130      host Ceph-201                           
 0    hdd  0.29300          osd.0          up   1.00000  1.00000
 1    hdd  0.48830          osd.1          up   1.00000  1.00000
 2    hdd  1.00000          osd.2          up   1.00000  1.00000
root@Ceph-201 ~# ceph osd pool ls detail
pool 1 '.mgr' replicated size 2 min_size 1 crush_rule 0 ...
'副本数size 2, min_size 1'     # --single-host-defaults 的效果

💡 推荐:实验/学习环境用 --single-host-defaults,生产环境严格多节点部署