Ceph 分布式存储实战:三台机器搭出对象/块/文件统一存储池,RBD 挂载与 RGW 配置全流程

为什么个人站长会需要 Ceph

大多数人一听到 Ceph 就觉得那是公有云厂商才用的东西,一个人维护几台 VPS 怎么会碰得上分布式存储。这个想法在只有一台机器的时候是对的,但当你的站做到一定程度,情况就变了:图片和附件越来越多,单机磁盘快满了;数据库、应用、静态文件全挤在一台机器上,一个盘坏掉整站就停摆;想加机器却发现数据没法自动在几台机器之间均衡。这时候你会开始寻找一个能把多台服务器的硬盘"拼"成一个可以整体使用、还带副本冗余的存储池的方案,Ceph 就是这类方案里最主流的一个。

和 GlusterFS 那种"把多个目录聚合起来"的思路不同,Ceph 从底层就是一个统一的分布式存储系统,向上同时提供三种接口:对象存储(RADOS Gateway 提供 S3 兼容)、块存储(RBD,可以当远程磁盘用)、文件存储(CephFS)。也就是说,你搭一套 Ceph,三种需求都覆盖了。这篇就以一个个人站长的视角,讲清楚 Ceph 的核心概念、怎样用三台机器搭出一个最小可用的集群、以及真实运维中那些会让你半夜爬起来处理的坑。

先搞懂四个角色:MON、OSD、MGR、CRUSH

Ceph 集群里最核心的三个守护进程是 MON、OSD、MGR,加上一个算法 CRUSH。理解了这几个词,后面的操作就不会觉得是在念咒语。

MON(Monitor)是集群的大脑和账本。它维护着整个集群的状态地图(Cluster Map),包括有哪些 OSD、哪些 Pool、PG 分布在哪里。MON 之间用 Paxos 协议保持强一致,所以 MON 的个数必须是奇数,最少三个,这样才能容忍其中一台挂掉而集群状态依然可写。MON 只存元数据,不存实际数据,所以对磁盘要求不高。

OSD(Object Storage Daemon)是真正干活的,每一块参与存储的硬盘对应一个 OSD 进程。数据被切成对象存进 OSD,副本也由 OSD 之间互相复制。你的集群能装多少数据,取决于所有 OSD 的总容量;能扛几块盘坏,取决于副本策略。一般一块 SSD 或 HDD 对应一个 OSD,不要在一块盘上放多个 OSD。

MGR(Manager)是后来加入的角色,负责收集监控指标、提供 Dashboard 和管理 API。它不参与数据路径,挂了也不影响读写,但为了看图表和用 dashboard,至少要起一个(最好两个做备份)。

CRUSH 是一个算法而不是进程。它决定了"某个数据对象应该放在哪些 OSD 上"。传统哈希在节点增减时会大面积重分布数据,而 CRUSH 通过层级化的规则(比如按机架、按主机分桶),保证在扩容或故障时只移动必要的那部分数据。你要操心的不是 CRUSH 的数学,而是它依赖的故障域(failure domain)配置——这一点后面讲坑的时候会重点说。

Pool、PG 与副本数:数据到底存了几份

你把数据写进 Ceph,实际上是写进一个 Pool。Pool 是逻辑上的数据容器,你在创建它的时候要指定两件事:PG 数量和副本数(size)。

PG(Placement Group)是对象到 OSD 之间的中间层:对象先通过哈希映射到某个 PG,PG 再通过 CRUSH 映射到一组 OSD。PG 存在的意义是让映射表变小、再平衡更细粒度。PG 数量太少会导致数据分布不均,太多则增加 MON 的内存和 CPU 负担。有个经验公式:PG 总数 ≈(OSD 总数 × 100)÷ 副本数,再向上取到最接近的 2 的幂。对于一个只有 3 到 6 个 OSD 的个人小集群,每个 Pool 设置 32 或 64 个 PG 已经足够,千万别照搬网上动辄几百的配置。

副本数 size=3 表示每个对象存三份,分别落在三个不同的故障域。同时还有 min_size,表示至少要有几个副本可用集群才允许写。size=3、min_size=2 是最常见的组合:允许坏一个副本,还能继续读写;坏两个就只能读不能写了。需要注意的是,size 不能超过你实际的故障域数量——如果你只有两台机器却设 size=3,集群会一直处于降级状态,永远凑不齐三副本。

用三台机器搭一个最小集群

下面用三台 Debian/Ubuntu 服务器搭建一个能跑起来的最小 Ceph 集群。每台机器装系统盘加一块数据盘,数据盘用 /dev/sdb(生产环境请务必用 /dev/disk/by-id/ 下的稳定路径,理由后面说)。

第一步,三台机器都改好主机名和 hosts,让彼此能用名字解析——Ceph 对主机名非常敏感,后期改主机名是灾难:

# 三台分别执行,主机名依次设为 ceph1 / ceph2 / ceph3
hostnamectl set-hostname ceph1

# 三台都写入 /etc/hosts(内网 IP)
cat >> /etc/hosts <<'EOF'
10.0.0.11 ceph1
10.0.0.12 ceph2
10.0.0.13 ceph3
EOF

第二步,安装 cephadm。现在官方主推的方案是 cephadm,它用容器化的方式部署所有组件,省去了手动配置的麻烦,也不会污染系统 Python 环境:

apt update
apt install -y cephadm

# 在 ceph1 上引导集群,指定本机 IP
cephadm bootstrap --mon-ip 10.0.0.11

bootstrap 结束后会输出 Dashboard 地址和管理员密码,务必记下来。默认开启的 dashboard 端口是 8443,只监听本机,要用 SSH 隧道才能访问,别急着把它暴露到公网。接着把另外两台机器加进集群:

# 把公钥推给另外两台
ssh-copy-id root@ceph2
ssh-copy-id root@ceph3

# 加入集群
ceph orch host add ceph2 10.0.0.12
ceph orch host add ceph3 10.0.0.13

# 查看主机是否到位
ceph orch host ls

第三步,把每台机器的数据盘纳入 OSD。cephadm 可以自动发现可用磁盘并自动部署:

# 查看每台机器上可用的裸盘
ceph orch device ls

# 让 cephadm 自动接管所有可用磁盘(会自动创建 OSD)
ceph orch apply osd --all-available-devices

# 观察 OSD 上线
ceph osd tree

等一会儿,ceph -s 应该会看到 HEALTH_OK。如果显示 HEALTH_WARN 附带 OSD count below min_size,说明 OSD 还没全部起来,稍等即可。到这里,一个三节点的最小 Ceph 集群就搭好了。

从 Pool 到实际使用:RBD 与 S3 两条路

集群起来之后,你要根据用途创建 Pool。做块存储就建 RBD Pool,做对象存储就建 RGW 的 Pool。创建 RBD Pool 的完整过程如下:

# 创建 pool,指定 PG 数
ceph osd pool create rbdpool 32 32

# 设置副本数(默认就是 3,双机测试可设 2)
ceph osd pool set rbdpool size 3
ceph osd pool set rbdpool min_size 2

# 启用为 RBD pool(新版 Ceph 必须手动开启)
ceph osd pool application enable rbdpool rbd

# 创建一块 20G 的块设备
rbd create --size 20480 rbdpool/disk01

# 映射到本地,会生成 /dev/rbd0
rbd map rbdpool/disk01

# 格式化并挂载
mkfs.ext4 /dev/rbd0
mkdir -p /mnt/disk01
mount /dev/rbd0 /mnt/disk01

如果你想要 S3 兼容的对象存储(比如给网站做图片存储、配 rclone 备份),则需要部署 RGW:

# 部署一个 rgw 实例
ceph orch apply rgw myrgw --placement="1 ceph1"

# 创建 S3 用户,会返回 access_key 与 secret_key
radosgw-admin user create --uid=s3user --display-name="s3user"

# 查看生成的密钥
radosgw-admin user info --uid=s3user

拿到密钥后,用 aws s3 --endpoint http://ceph1:7480 或 rclone 的 S3 后端就能直接读写。对个人站来说,把附件和备份丢进 RGW 再通过 Nginx 反代出来,是一条很实用的路。

五个真会让你半夜爬起来的坑

前面都是理想状态,下面这些是实际运维里最容易出事的地方。

坑一:故障域全是 host 时,副本可能落在同一台机器。默认的 CRUSH rule 用 host 作为故障域,一个对象的三副本会分配到三台不同的主机上。但如果你后来在单机上加了多块盘、又把故障域改成 osd,就可能出现"两个副本在同一台机器上的两块盘"的情况,机器一挂就丢两份。个人站的正确做法是保持 host 故障域,且主机数不少于副本数。对于只有两块盘的机器,别在一个 host 里塞太多 OSD 又指望它是高可靠的。

坑二:用 /dev/sdX 建 OSD,重启后盘符漂移导致 OSD 全部起不来。这是最常见的翻车方式。Linux 的 /dev/sda 是启动顺序决定的,插拔一块盘或者换 BIOS 设置,名字就可能变。正确做法是始终使用 /dev/disk/by-id/ 下的设备名建 OSD。cephadm 的 --all-available-devices 内部用的是 by-id,所以用它自动部署比较安全;如果你是手动 ceph-volume lvm create --data /dev/sdb,就一定要换成 by-id 路径。

坑三:集群接近写满时不处理,导致全面不可写。Ceph 有个 nearfull 和 full 阈值,默认达到容量的 85% 就会告警,95% 就拒绝一切写入。分布式存储不是普通磁盘,写满的后果比单盘严重:因为数据要均衡分布,一个 OSD 满了整池都可能写不进去。个人站要养成看 ceph df 的习惯,保持在 70% 以下,并且提前规划扩容。

坑四:只有两个 MON 或者 MON 和数据混部署。MON 必须是奇数,两个 MON 反而比一个更危险,因为一旦网络分区,两个 MON 都认为自己是多数派,集群状态就分裂了。三个 MON 是底线。另外 MON 对延迟敏感,最好放在独立的机器或者至少不和数据盘抢 I/O,个人站如果只有两台机器,宁可跑一个 MON 也不要强行凑两个。

坑五:把 Ceph 当成本地磁盘用,忽略网络延迟。无论是 RBD 还是 CephFS,每次写都要经过网络往返。很多人把 MySQL 的 data 目录直接放在 CephFS 上,结果性能惨不忍睹,甚至因为网络抖动导致数据库损坏。正确的定位是:Ceph 适合放大文件、附件、备份、日志这类顺序写、并发读的场景;数据库的 data 目录请放本地盘,只把备份往 Ceph 放。内网带宽最好 10G 起步,千兆网在副本同步时会成为明显瓶颈。

日常巡检该看哪几个命令

集群搭好只是开始,日常维护要盯住几个关键指标。以下命令建议做成一个巡检脚本每天跑一遍:

# 集群整体健康状态(最重要的一条)
ceph -s

# 容量与使用率,看 %RAW USED 和每个 pool 的 %USED
ceph df

# OSD 树状分布,确认每个 host 下的 OSD 都在线
ceph osd tree

# 查看集群告警详情,HEALTH_WARN 时一定看
ceph health detail

# 慢请求排查(哪块盘在拖后腿)
ceph osd perf

# 查看 PG 状态,active+clean 才是健康
ceph pg stat

其中 ceph -s 里的 HEALTH_WARN 一定要点进去用 ceph health detail 看具体原因。常见的有:某些 PG 处于 degraded(副本不足,通常在自愈中)、undersized(副本数达不到 min_size)、stale(某个 OSD 太久没心跳)。只要不是大面积的 inactive 或 incomplete,一般等待自愈即可,不必惊慌。

什么时候不该上 Ceph

最后说句实在话:Ceph 强大,但它的复杂度远高于单机存储。如果你的站只有一台服务器,或者数据量还在几十 GB、一台机器完全扛得住,那么上 Ceph 是自找麻烦——维护成本、网络开销、CPU 占用都不划算,这时候 RAID + 定时备份就够了。真正适合上 Ceph 的门槛大概是:你有三台以上机器、数据量到了 TB 级别、并且明确需要"一块盘坏了网站不停"的可用性。对个人站长而言,用三台便宜 VPS 搭一个最小 Ceph 集群练手,理解分布式存储的思维方式,本身就是很值的一次学习,但把它直接当生产环境跑关键业务之前,请务必先把上面那五个坑踩明白。

Last modification:October 6th, 2026 at 08:25 pm

Leave a Comment