1. 为什么我需要一套分布式存储
最开始看到"分布式存储"这几个字的时候,我以为就是几台服务器挂一堆硬盘,组个大容量空间用。后来真正把自己的业务放上去才发现,这事远没那么简单。当你手里有几十TB甚至上百TB的数据,而且这些数据还不能丢、不能停,单台服务器就算插满硬盘也扛不住容量和吞吐的瓶颈,更别说一块盘坏了要怎么办。分布式存储解决的根本问题就是三件事:容量不够时的横向扩展、单点故障时的数据安全、以及并发读写时的性能分摊。我最后选了Ceph,不是因为它最炫,而是因为它在开源方案里生态最成熟,既能当块设备用,也能当对象存储用,还能当文件系统用,一套集群三种入口,对于内部基础设施来说很划算。
先说个最容易误导人的地方。很多人以为Ceph是某个公司做的商业软件,其实它是开源项目。社区版本和商业发行版都有,底层内核是一样的。我当前部署的这套环境是自建的社区版本,协议层走得是标准的Ceph后端,没有厂商绑定,换硬件、扩容节点都很自由。这个自由度在生产系统里非常重要,因为你不知道两年以后你的存储需求会变成什么样,早期绑定一个封闭方案,后面想改架构会痛不欲生。
Ceph这套系统最迷人的一点在于它把"存储逻辑"和"物理硬件"彻底解耦了。你可以理解成:它先把你扔进来的所有数据切成很多小片,然后用一套算法决定每个小片放在集群里的哪三块盘上。这个算法不是查表,而是纯数学计算,所以不需要一个中心节点来记录"数据在哪",任何客户端只要知道集群的拓扑和规则,就能算出自己要找的数据在哪个位置。这个设计是Ceph的灵魂,理解了这个,后面看什么CRUSH、PG、OSD都不会晕。
这篇文章是系列的第一篇,我按从零开始的思路来写——先把核心概念和价值讲清楚,然后把单节点怎么搭起来、怎么把数据真的写进去讲明白。适合两类人:一类是刚接触分布式存储、想在公司内部搭一套测试环境的开发者,另一类是已经用过其他存储方案、想横向对比Ceph架构的人。文章里的所有操作我都在干净环境里跑过一遍,照着做基本不会翻车。
2. 先搞懂Ceph的四大核心组件
2.1 Monitor:集群的"方向盘"
Monitor是Ceph的大脑,它不存业务数据,但它掌握着整个集群的状态地图。每个Monitor都保存着Cluster Map,包括有哪些OSD在线、每个OSD承载了多少PG、网络拓扑长什么样、当前有没有异常节点。客户端要和Ceph交互,第一步必须先问Monitor拿最新的Cluster Map,之后才知道该去哪台机器上读写数据。
有个非常重要的细节是,Monitor本身是一个"强一致"的系统。它内部跑着Paxos协议,多个Monitor节点之间通过投票来保证状态完全一致。这就是为什么Ceph要求生产环境至少部署3个Monitor——当一个Monitor挂了,其他节点还能继续对外提供集群状态。而1个Monitor虽然能跑,但它就是单点,挂掉整个集群就失去"方向盘"了。我们在测试环境里可以只起1个Monitor,但在后续规划生产时,Monitor节点必须独立出来并且做奇数部署。
我还想提醒一个新手很容易忽略的问题:Monitor对时间同步非常敏感。Ceph的健康检查里有个MON_CLOCK_SKEW告警,就是Monitor之间时钟偏差太大时会出来报警。时钟不同步会导致Monitor之间无法正确判断对方的心跳,甚至可能触发错误的故障切换。所以在装系统的时候我把NTP/Chrony服务就配好了,这个事千万别等到集群报警了再处理,后面排查起来非常痛苦。
2.2 OSD:数据真正住的地方
OSD全称是Object Storage Daemon,可以把它理解成"每块硬盘前面的一个小管家"。通常一块物理盘对应一个OSD进程,Ceph的所有数据最终都是以对象的形式落在这些OSD上。Ceph做的各种数据冗余、故障域隔离、数据回填,都是围绕OSD来操作的。
OSD不仅管读写,还定期向Monitor汇报自己的状态,包括容量、负载、读写延迟等。Monitor根据这些信息来判断OSD是活着还是挂了,是up还是down。在Ceph的世界里,"OSD down"意味着对应硬盘上的数据可能需要被重建到其他节点上,这时候集群会进入recovery状态,数据副本重新补全。这个过程如果没控制好参数,可能会把网络带宽打满,影响正常的业务IO。我后面在调优章节会详细说怎么限速。
每个OSD启动时,系统会在它所在的机器上维护一个日志盘或者日志分区。早期版本默认用与数据盘同盘存储journal,后来版本可以独立配置db和wal。如果条件允许,我强烈建议把WAL/DB放到SSD上,机械盘只做数据存储,这个组合在读写性能上的提升非常明显,尤其是随机写场景。
2.3 PG:Ceph的"中间管理层"
PG全称Placement Group。如果说OSD是物理世界的硬盘,PG就是逻辑世界的容器。客户端的数据进来后,先被切分成一个个对象,然后每个对象根据哈希计算映射到某个PG,这个PG再通过CRUSH算法被放置到一组具体的OSD上。
为什么要多加PG这一层?如果对象直接映射到OSD,那么集群里的对象数量可能上亿,管理这些细粒度映射关系会是天文数字。有了PG这一层,集群只要管理几百几千个PG就够了,每个PG内部再管理大量的对象。你可以把PG理解成快递分拣站——每个快递(对象)先按区域分到某个分拣站(PG),然后分拣站再统一配送到对应的片区(OSD)。这样管理效率高得多。
PG还有个非常关键的属性叫PG数。这个值决定了一个池子里数据分散的粒度。PG数设置太小,单个OSD上承担的数据量和IO压力就不均匀;设置太大,PG元数据又占资源,重建和迁移的粒度也变碎。Ceph社区有个经典建议公式,总PG数约等于"(OSD总数 × 100)/ 副本数",然后取靠近的2的幂,我按这个思路在这套环境里算出的结果会在实操部分展示。
2.4 CRUSH:不用查表也能找到数据
CRUSH是Ceph里最天才的算法。它做的事可以简单描述为:给定一个数据对象,通过一种确定性哈希计算,直接算出一组OSD的编号。客户端拿到这个结果后,直接去对应的OSD上读写,整个过程中不需要向任何中心节点询问"数据在哪"。
这个设计和传统存储相比优势很明显。传统的集中式元数据方案比如早期的一些分布式文件系统,所有客户端都要先访问元数据服务器,拿不到授权就没办法操作数据。元数据服务器一旦成为瓶颈,整个集群的吞吐量就上不去了。而CRUSH相当于把元数据计算分摊到了每个客户端本地,所有客户端只要持有一份Cluster Map,就能各自独立算出数据位置。
当然,CRUSH不是没代价。它要求集群的拓扑信息(比如每台机器的weight、bucket类型、故障域层级)必须准确,一旦拓扑信息变了,CRUSH计算结果的分布就可能变化,可能引发数据迁移。所以生产上调整OSD weight或者增加新节点时,Ceph会触发数据rebalance,这个过程要在业务低峰期做,并且要先把recovery的限速参数配好。
3. 部署前必须想明白的几件事
3.1 集群规模规划到底怎么定
我先给出一套适合入门测试的小集群配置,本篇文章的实操部分也是基于这个配置来的。三台物理或虚拟机都可以,每台机器上放两个OSD,另外单独用一台或者复用其中一台机器跑Monitor。具体规格如下:
| 角色 | 数量 | 配置 |
|---|---|---|
| Monitor | 1~3 | 2核CPU、4GB内存、系统盘50GB |
| OSD节点 | 3 | 4核CPU、8GB内存、每节点2块1TB数据盘 |
| 网络 | - | 千兆以上,OSD节点之间尽量走独立网段 |
这套配置整体约6个OSD、容量约5.4TB可用(三副本下打出67%的利用率算出的可用空间)。如果只是为了跑通功能、看看Ceph怎么工作,这套够了。但如果想模拟故障切换、节点宕机,我建议至少用3个Monitor且每台节点只跑一个角色的方式,避免测试故障时互相干扰。
生产环境规划的逻辑不太一样。你需要先明确自己要的是性能优先还是容量优先,然后才能定OSD承载的PG数量、选择副本还是纠删码策略。比如日志型业务,单盘容量不用太大,但IO延迟要低,所以尽量每个OSD都用机械盘之外再配一块SATASSD做WAL;而归档型业务,重点是单位成本能装多少数据,这时候大容量机械盘配合纠删码策略优势就出来了。
3.2 操作系统与存储介质的选择
Ceph对Linux的兼容性最好,我这边用的是Ubuntu Server 20.04 LTS,内核版本5.4以上,能很好支持Ceph最新的特性。CentOS/RHEL也可以,不过现在社区重心渐渐往Debian系倾斜,文档和踩坑案例都更丰富。如果你是第一次接触,我更推荐Ubuntu。
文件系统方面,Ceph OSD的底层数据盘通常有两种选择:XFS和ext4。社区官方推荐XFS,原因是它在处理大文件和高并发写放大时有更稳定的表现,并且支持扩展属性这些Ceph依赖的功能。我试过一次在ext4上部署,后来检查到部分Ceph功能受限,虽然不影响基本使用,但既然有官方推荐,没必要在这个地方给自己埋坑。
数据盘先不需要手动分区格式化,这看起来有点反直觉——正常装系统不是要先把盘格式化吗?Ceph的部署工具ceph-volume会在初始化OSD的时候自动完成分区、格式化、挂载和systemd管理,你只需要确保那块盘是空的,系统能识别到设备名就行。
3.3 网络设计的底层逻辑
Ceph集群最容易被忽视的就是网络规划。一台OSD节点通常至少需要两个网口:一个用于公共网络,承担客户端对Ceph的访问流量;另一个用于集群网络,承担OSD之间的数据复制、心跳、rebalance流量。这不只是理论建议,实测中如果不分开,业务高峰期的大流量备份任务会把内部数据同步也拖慢,客户端读写延迟直接往上飙。
比如我这套环境里,公共网络用192.168.10.0/24,集群内部网络用192.168.20.0/24。在Ceph配置里用cluster_network和public_network两个参数显式指定。如果没有独立网段,退而求其次也要用VLAN隔离,保证二层广播域内OSD之间是打通的。
网络带宽决定了一个很硬性的上限:数据恢复速度永远受限于最小链路带宽。三副本下如果一块1TB的盘坏了,理论上至少要往其他节点传1TB的数据。拿千兆网来说,理论极限是约110MB/s,实际跑满8成已经算不错,意味着坏一块盘至少需要好几个小时来恢复。这期间如果业务还在持续写入,恢复时间会更长。所以规划网络时,我建议至少预留20%的带宽余量给恢复场景,别让恢复和业务互相抢带宽。
4. 从裸机到集群:手把手部署实录
4.1 配置软件源与基础依赖
所有节点都需要先更新系统并安装必要的包。我这里直接贴出在Ubuntu上执行的命令,包括设置hostname、配置hosts、添加Ceph源和安装cephadm(Ceph的新一代部署管理工具)。
# 更新系统基础软件 sudo apt update && sudo apt upgrade -y # 设置主机名(每台机器分别执行) sudo hostnamectl set-hostname ceph-mon01 # 编辑/etc/hosts,把三台机器都加进去 192.168.10.11 ceph-mon01 192.168.10.12 ceph-osd01 192.168.10.13 ceph-osd02 # 安装docker与cephadm sudo apt install -y docker.io sudo systemctl enable --now docker # 下载cephadm最新版本 curl --silent --remote-name --location https://download.ceph.com/rpm-17.2.6/el9/mnoarch/cephadm chmod +x cephadm sudo mv cephadm /usr/local/bin/这里多解释一句cephadm的定位。早期的Ceph部署工具叫Ceph-Ansible,后来官方推出了cephadm,它直接用容器化的方式来管理Ceph组件。每个Ceph服务(mon、mgr、osd)都运行在容器里,系统层面只需要Docker环境。这个变化让升级和回滚变得非常干净,要回退版本只需要把镜像指到旧版本重新部署即可。
4.2 部署第一个Monitor节点
下面这段命令会在当前节点上引导出一个全新的Ceph集群。目前我是先用单Monitor把集群拉起来,后续如果需要加Monitor节点,用ceph mon add命令继续加。
# 使用cephadm引导集群,指定monitor节点IP sudo cephadm bootstrap --mon-ip 192.168.10.11 # 验证集群状态 sudo ceph -sbootstrap过程会自动创建集群的初始配置、生成client.admin密钥、启动一个monitor和manager,还会把/etc/ceph/ceph.conf和密钥拷贝到当前节点。一旦看到"Bootstrap complete",说明Monitor已经跑起来了。这时ceph -s会输出一个比较空的列表,没有OSD没有池,这正常。
有件事必须注意:bootstrap之后,cephadm生成的SSH密钥默认只在这个节点上有效。你要把公有密钥分发到其他OSD节点上,让cpehadm可以通过SSH远程管理它们。简单方式是:
sudo cephadm shell -- ceph orch host add ceph-osd01这个命令会自动把当前用户(通常是root)的SSH公钥同步到目标节点。也可以手动把/etc/ceph/ceph.pub内容追加到目标机器的authorized_keys文件里,效果一样。
4.3 添加OSD节点和磁盘
假设现在三台机器加进来了,我用以下命令把主机添加进集群,然后列出所有可用的磁盘。
# 添加主机 sudo ceph orch host add ceph-osd01 192.168.10.12 sudo ceph orch host add ceph-osd02 192.168.10.13 # 查看所有节点上发现的磁盘 sudo ceph orch device lsdevice ls会列出每台机器上的所有未被占用的磁盘。如果一切正常,你应该能看到例如/dev/sdb、/dev/sdc这样的设备,它们的状态标记为"available"。
你可以手动指定哪些盘变成OSD,也可以让Ceph自动接管所有可用盘。对于入门测试,手动指定更稳妥,避免误把系统盘也给接管了:
# 逐个添加OSD sudo ceph orch daemon add osd ceph-osd01:/dev/sdb sudo ceph orch daemon add osd ceph-osd01:/dev/sdc sudo ceph orch daemon add osd ceph-osd02:/dev/sdb sudo ceph orch daemon add osd ceph-osd02:/dev/sdc每次添加会返回一个osd编号,例如osd.0。这个编号是永久的,即使OSD以后故障重建,编号也不会复用,这在日志分析和数据追踪时非常有用。全部添加完后再看ceph -s,会多出osd: 6 osds: up这一行。
4.4 设置副本数与创建第一个存储池
现在集群空转着,还没有池。创建池之前需要先决定副本数。默认是3副本,这个值在测试环境里有点奢侈,1副本完全够用,但生产上我还是强烈不建议低于2,最好3。创建池的命令如下:
# 创建名为testpool的池,设置PG数为128,副本数为3 sudo ceph osd pool create testpool 128 3 # 如果测试环境想省空间,也可以改成2副本 # sudo ceph osd pool create testpool 128 2这里有个点一定要理解:pg_num是池的逻辑分片数,不是实际副本数。128的意思是这个池会被分成128个PG,每个PG承载部分对象。PG数不是越大越好,很多新人一上来就设置4096,结果每个PG里就几个对象,元数据开销倒是异常庞大。在没有明确的容量估算之前,先用上面的公式算一个基准值。
验证池是否创建成功并查看使用状态:
# 查看池列表 sudo ceph osd lspools # 查看每个PG的状态分布 sudo ceph pg stat # 查看集群整体容量 sudo ceph df如果ceph df里显示SIZE是5.4T,AVAIL可能是3.6T左右,这是因为三副本带来空间加倍消耗。你还会看到一个USED值,刚创建完池还没写数据的时候它显示0,一切正常。
4.5 启用RBD块设备并挂载到客户端
池建好之后,接下来就是真正往里面写数据。Ceph最常用的方式是RBD块设备,它可以把池导出成一个块设备,客户端像使用普通硬盘一样完成格式化然后挂载使用。
# 在管理节点上创建一个1GB的块设备 sudo rbd create testpool/image01 --size 1024 # 将块设备映射到本地内核 sudo rbd map testpool/image01 # 格式化并挂载 sudo mkfs.ext4 /dev/rbd0 sudo mkdir -p /mnt/ceph-test sudo mount /dev/rbd0 /mnt/ceph-test到这一步,你已经可以往/mnt/ceph-test里写文件了。看起来像用了一块本地硬盘,但数据实际已经打散并复制到了多台OSD上。
需要注意一点:RBD默认精简置备,也就是说创建1GB的块设备,没有写数据的时候并不会实际占用存储空间,等你真正写入数据才会占用。这个机制对节约空间很友好,但如果你需要预留空间或者对延迟敏感,可以考虑预置全量。
4.6 用rados bench快速验证读写性能
写完数据后,最好做一次性能验证,确认这套部署是正常的。rados bench是Ceph自带的基准测试工具:
# 写入1024个4MB对象,测试4MB顺序写 sudo rados -p testpool bench 120 write --max-objects 1024 -t 16 # 测试4MB顺序读 sudo rados -p testpool bench 120 seq -t 16 # 测试随机读 sudo rados -p testpool bench 120 rand -t 16输出结果里要重点看Bandwidth (MB/sec)和Average Latency(s)。在千兆网络、6个机械盘OSD的测试环境里,顺序写带宽一般能做到400~600MB/s,顺序读能上800MB/s以上。如果带宽只有几十MB/s,我建议先检查网络是否在走集群网络,以及OSD的日志是否在频繁刷写。
5. 故障模拟与自我修复机制验证
5.1 手动将OSD标记为down
部署完集群,你还应该亲手做一次故障演练,不然真出了事才发现监控没配好、恢复参数没调,那就晚了。以下操作要在确认数据已经落到存储池后再执行。
# 将某个OSD标记为down sudo ceph osd down 0 # 观察集群状态变化 sudo ceph -s过几秒再看ceph -s,会看到类似osd.0 is down的信息,同时pg_num里会出现active+degraded状态。degraded表示有PG的副本数不达预期,Ceph正在尝试从剩余OSD上把缺失的数据重新复制出来。
这里有个容易引起恐慌的点:down一个OSD不代表它对应的数据已经丢失。Ceph只要还有一个副本在另一个OSD上,它就能通过那个副本把数据恢复到其他OSD。只有当某个PG的所有副本对应的OSD都同时down了,数据才真正不可恢复。
5.2 观察数据恢复过程
数据恢复不是一个瞬时的操作。为了防止恢复过程影响正常业务,Ceph默认会把恢复速率调得比较保守。观察恢复进度可以用:
# 查看正在恢复的PG sudo ceph pg ls | grep recovering # 查看恢复相关参数 sudo ceph config get osd osd_max_backfills sudo ceph config get osd osd_recovery_max_active如果恢复速度太慢,或者你是在测试环境希望快速看到效果,可以临时把恢复限速调高:
sudo ceph config set osd osd_max_backfills 4 sudo ceph config set osd osd_recovery_max_active 8但注意,这些都是测试期的临时手段。在生产环境里,恢复限速应该根据业务特征谨慎调整,毕竟恢复流量和业务流量是抢同一份带宽的。
5.3 恢复完成后验证数据完整性
数据恢复完成后,ceph -s会回到HEALTH_OK状态,所有PG变成active+clean。这时可以去客户端挂载目录里验证之前写入的文件是否完整。
# 在挂载目录里生成几个测试文件 dd if=/dev/urandom of=/mnt/ceph-test/testfile bs=1M count=100 # 掉线模拟前先记录MD5 md5sum /mnt/ceph-test/testfile # 恢复完成后再次校验 md5sum /mnt/ceph-test/testfile如果前后两次MD5一致,说明Ceph在你完全没干预的情况下,自动把丢失的副本从其他节点补了回来。这就是三副本机制的核心价值——用户无感知的数据自愈。做一次这个实验,你对Ceph的信心就不是从文档里看来的,而是实际验证过的。
6. 常见问题与排查技巧实录
6.1 第一次集群部署,ceph -s卡住无输出
很多第一次部署的人会碰到这个情况。命令敲下去,光标在闪烁,什么都不显示。先别急,首先要确认的是Monitor进程是否在运行:
sudo cephadm shell -- ceph -s如果cephadm shell能正常进入,说明集群上层没问题,可能是/etc/ceph/ceph.conf里mon_host配置指向了错误的IP。这个问题常见于多网卡机器上,bootstrap的时候没显式指定mon-ip,系统自己挑了一个不对的IP,导致后续客户端连不上Monitor。解决方式是检查并修正ceph.conf里的mon_host和mon_addr。
6.2 新增OSD成功,但集群不健康
ceph -s里的状态如果出现HEALTH_WARN,常见原因有很多,比如PG数量分布不均、时钟偏差、OSD版本不一致等。先用ceph health detail看具体告警项,不要看到HEALTH_WARN就慌了。
我在这套环境里遇到过一次PG_AVAILABILITY告警,原因是新增了一批OSD后没有创建新池,而旧池的PG全部落在老OSD上,新OSD没承担数据。表面上看集群是扩容了,实际数据还是挤在老地方。解决办法是给相关池调整pg_num,或者干脆手动把一部分PG迁移到新OSD上。
6.3 为什么要保持Ceph各节点版本一致
Ceph对版本混跑有严格的兼容限制。不同的minor版本之间可以共存,但major版本最好不要混用。如果你在一部分节点上升级到了新版,另一部分还是旧版,Monitor之间和OSD之间的通信协议可能不兼容,轻则告警,重则IO卡死。
我在测试中养成一个习惯:每次升级前先看ceph versions确认所有组件版本一致,升级时先升级Monitor再升级OSD,并且一次只升级一个节点,观察ceph -s从HEALTH_WARN回到HEALTH_OK后再继续下一个。这套流程虽然保守,但能最大化保证升级不翻车。
6.4 性能到底卡在哪个环节
如果发现读写性能比预期低很多,我通常会按照下面的顺序排查:
- 网络带宽是否跑满,查看port counters有没有dropped。
- OSD所在机器的
iostat是否显示磁盘util接近100%。 - PG在OSD上的分布是否均匀,
ceph pg dump里看有没有热点PG。 - Monitor是否CPU占用过高,Monitor在处理大量心跳上报时也可能成为瓶颈。
- 客户端到集群的网络延迟,
ping测试是否能保持在1ms以内。
其中第二点最容易被忽略。机械盘本身随机写性能就弱,如果WAL和DB没有放到SSD上,随机写场景下OSD会成为第一个瓶颈。所以安装OSD时,有条件就一定要分配高速存储给WAL/DB,这是最立竿见影的优化手段。
7. 初版使用心得与下一步规划
这套三节点集群我已经跑了大约两周,稳定性没问题,测试数据读写都能正常工作。对我来说最有价值的不是部署本身,而是通过故障演练真正理解了Ceph的副本机制是怎么运作的。看到自己手动down掉的OSD在几分钟内被集群自动补齐副本,数据端到端校验一致,那种感觉比读十篇文档都有用。
有几个实际踩坑经验分享出来。第一,系统时间同步必须在部署之前做好,不然后期出现的各种诡异告警会浪费你大量排查时间。第二,Ceph的容器化部署让组件管理方便了很多,但也意味着主机上的Docker不能随便停,否则所有daemon都会掉线。第三,如果你打算在生产环境使用,务必在部署前就想清楚网络分隔方案和扩容策略,这些架构层面的决定在后期很难低成本推翻。
下一篇文章我会围绕Ceph的RGW对象存储网关和CephFS文件系统来展开,把同一套集群怎么同时提供三种存储入口讲透。此外,关于CRUSH Map的自定义故障域配置,比如让副本分布在不同的物理机架上,这部分内容也需要单独用一篇来详细拆解。到时候我们继续聊。