简介:面向云服务运维人员及分布式存储技术爱好者,这份PDF文档系统讲解Ceph块存储(RBD)的部署与实战应用。内容基于三节点实验集群,在Ubuntu 18.04环境下完整演示了存储池创建、块设备镜像管理、映射至Linux系统块设备、格式化与挂载以及集群健康状态检查等关键操作。通过具体命令与输出样例,帮助读者快速掌握搭建稳定Ceph集群的方法,在虚拟化环境中落地高效块存储方案。
资源为单个PDF文件,压缩后仅286KB,轻量易读,适合按文档随学随练。目前已有70人学习,内容聚焦实际运维场景,尤其适合有一定存储架构基础、希望短期上手RBD的读者。
整体来看,其价值在于将部署与使用串联成完整链路,既呈现了存储池、镜像等对象级操作,也覆盖了映射、格式化、挂载后的读写验证和状态监控方法。文档在基础演示之上提示了面向数据中心规模扩展参数或研究高级特性的方向,为进阶调优留有余地。
1. CEPH块存储系统:部署门槛不在“能装”,而在“装完能不能用”
CEPH 块存储系统把多台普通服务器的磁盘聚合成一个共享存储池,客户端在网络上拿到一块可以格式化、可以挂载的“裸盘”,而数据实际分散在集群中多个节点。很多人以为难点在安装,实际上 cephadm 已经把安装压缩到一条 bootstrap 命令;真正让人翻车的是装完之后的参数、挂载方式和故障排查。这篇文章写给虚拟化、OpenStack、Kubernetes RBD 以及想自建存储的运维,按“选型 → 部署 → RBD 接入 → 避坑排查 → 压测验证”的顺序,把 3 节点集群从零到压测跑通,顺带标出几个我在生产环境里踩过的坑。
2. 为什么块存储选 CEPH:组件拆解与硬件规划
CEPH 常说“一套软件三种接口”,但实际部署时最怕的就是三种接口全开。先把选型讲清楚,后面的部署命令才有意义。
2.1 块存储、文件存储与对象存储的选型差别
CEPH 对外同时提供 RBD 块存储、CephFS 文件存储和 RGW 对象存储。三者的适用场景差别很大:
| 接口 | 典型场景 | 客户端形态 | 容易忽略的问题 |
|---|---|---|---|
| RBD 块存储 | 虚拟机磁盘、数据库数据文件 | 内核 rbd / librbd | 单盘延迟受网络和副本数影响 |
| CephFS 文件存储 | 共享目录、大数据作业 | POSIX 挂载 | 元数据频繁时压力集中到 MDS |
| RGW 对象存储 | S3 兼容、备份归档 | REST API | 不适合低延迟小文件访问 |
部署前先问自己两个问题。第一个问题:客户端到底要通过哪种方式访问?如果用 OpenStack Cinder 或 Kubernetes CSI,实际走的是 librbd,块设备的 feature 兼容性要求不同;如果直接在 Linux 机器上用内核 rbd 模块,就要避开内核不支持的 feature。第二个问题:写负载是顺序为主还是随机为主。顺序写为主可以上 HDD 加大块,甚至考虑纠删码;随机写到数据库这个级别,就要老老实实准备 SSD/NVMe,否则后面调参很难救回来。
常见误区是一个集群把三种接口全开。我见过不少团队为了让“Ceph 很全能”把 CephFS 也挂给数据库,结果元数据操作一上来,整个集群的慢查询就开始放大。实际落地时我通常先只开需要的接口,比如虚拟化就只开 RBD,RGW 那些等业务验证过再加。
2.2 会用到的核心组件:MON、OSD、MGR、RBD
部署完成后,ceph -s 看到的 service 基本就是这几个角色:
- MON 是哨兵,维护集群状态的 Map,用 Paxos 保证一致性。生产至少 3 个,超过一半存活才能选出新主;如果只有 2 个 MON,坏一个就进入只读保护。
- OSD 是真正落盘和参与数据复制的进程,一个 OSD 通常对应一块数据盘。OSD 状态 down 会直接影响写入,也是部署后最先要盯的对象。
- MGR 负责 Dashboard、Prometheus 指标和内部均衡,生产中至少要两个,否则坏了只影响观测,不影响数据读写。
- RBD 是 pool 之上的 image,也就是给客户端用的块设备。创建 image 时指定池、大小和 feature。
再往下一层是 PG(Placement Group)。所有数据按哈希分散到 PG,PG 再映射到 OSD。pg_num 决定数据分布粒度和故障迁移速度:太小会让单个 PG 数据量膨胀、恢复慢;太大则元数据开销和恢复压力上升。我的经验值:对 RBD 池,在 9 个 OSD 的 3 节点集群上设 128 起步,再开 pg_autoscale_mode on 让集群自动调整。手动估算的话,可以按每 OSD 50~100 个 PG 的范围来,但这不是绝对公式,池越多、容量越大,结论差得很远。
最后说故障域。默认 CRUSH rule 是 host 级故障域,三个副本分布在三个不同节点时,容忍单节点故障;如果只有两台机器却把副本数强行设成 3,数据根本放不下,集群会一直报 PG 异常。生产环境至少 3 节点起步,这是硬约束,不只是经验。
2.3 一套够用的生产硬件规划
常见做法是 3 节点起步,每节点部署 MON + MGR + OSD。为什么一定是 3 台?因为 RBD 默认 3 副本,只有副本分布在 3 个独立节点上,单节点故障时集群才仍然能写入。副本数降到 2,故障时数据完整性和恢复速度都没保障。
磁盘层面,系统盘与数据盘分离。系统盘用双 SSD RAID1,数据盘每 OSD 一块独立盘。一个 OSD 池内尽量不要混用 HDD 和 SSD,否则整个池的延迟都会被慢盘拖住。预算够的话,给每个 OSD 配一块独立 NVMe 或 SSD 放 WAL/DB,HDD 只放数据块,这是缓解随机写延迟最有效的硬件手段。部署时用 --block-db 参数指定高速盘,但注意每个 OSD 的 block.db 必须是独立分区,多个 OSD 共享同一个高速盘会变成新的竞争点。
网络层面,公开网络和数据网络分离。10GbE 起步,两个网络独立时在集群配置里指定 cluster-network。存储网络一旦出现丢包,OSD 心跳超时,最直接的表现就是 osd down。
内存规划,OSD 进程至少 4-8GB,MON 节点 8GB 以上。我见过把内存全留给计算、OSD 只有 2GB 的压测环境,cephadm 能装起来,但一跑 fio OOM 就开始杀进程。内存规划不是越大越好,而是别让监控组件和业务抢资源。如果手头只有两台物理机做验证,可以搭两节点加额外 monitor 的实验环境,但别拿这个拓扑当生产结论。
3. cephadm 部署集群:最小命令与每个参数的含义
选择 cephadm 的原因很直接:它以容器方式拉起所有 daemon,卸载干净,对宿主机污染小,后续升级也走统一入口。下面从环境准备开始。
3.1 环境准备:内核、时间、SSH 与防火墙
部署前先做三件事:主机名、时间同步、SSH 免密。时间不同步对 MON 共识的影响最隐蔽,表现是偶发“mon is down”但网络一切正常,排查时容易绕远路。
# 三个节点都要执行:改主机名,开启时间同步并放行 ceph 相关端口 hostnamectl set-hostname ceph-node1 timedatectl set-ntp true # 生产环境建议只放行指定端口,测试机直接停 firewalld 也行 firewall-cmd --permanent --add-port={6789,3300,8443,9283}/tcp firewall-cmd --reload # 在管理机上生成密钥,并分发到三个节点 ssh-keygen -t ed25519 -N "" -f /root/.ssh/id_ed25519 for h in ceph-node1 ceph-node2 ceph-node3; do ssh-copy-id root@$h; done端口说明:6789 是老 msgr1 端口,3300 是 msgr2 默认端口,8443 是 dashboard,9283 给 Prometheus 模块用。cephadm 通过 SSH 作为管理通道,所以从管理机到所有节点必须免密。内核版本尽量使用当前发行版的主流 LTS,太老的内核跑 bluestore 时在 discard、AIO 上容易出怪问题,这类问题往往表现为数据面正常但性能上不去。
3.2 cephadm bootstrap:一个参数一个坑
bootstrap 是整套部署的起搏器,它会在当前节点拉起第一个 MON 和 MGR,然后输出 dashboard 地址和初始密码。先准备一个 cluster.yaml,声明 3 个 MON 和 2 个 MGR:
service_type: mon placement: hosts: - ceph-node1 - ceph-node2 - ceph-node3 --- service_type: mgr placement: hosts: - ceph-node1 - ceph-node2然后拉取 cephadm 脚本并执行 bootstrap。URL 中的 要换成你选定的版本号,我一般会锁定一个 z-stream,不要追最新。
curl -fsSL https://download.ceph.com/rpm-<version>/el9/RPMS/cephadm -o /usr/local/bin/cephadm chmod +x /usr/local/bin/cephadm cephadm bootstrap \ --mon-ip 192.168.1.10 \ --cluster-network 192.168.2.0/24 \ --apply-spec /root/cluster.yaml \ --skip-monitoring-stack参数说明:--mon-ip 必须写成当前节点真实地址,写错集群直接选不出主;--cluster-network 是数据网络,如果不做双网分离可以省略;--apply-spec 会在 bootstrap 后自动创建 SPEC 中声明的 MON/MGR;--skip-monitoring-stack 用于最小化部署,跳过 Prometheus、AlertManager 和 Grafana,对只有几个节点的实验环境能省不少内存,代价是 dashboard 的性能图表会变空。生产保留监控栈没问题,但要提前规划内存。
bootstrap 结束后会在当前节点生成 /etc/ceph,需要把这个目录同步到其他节点,否则 cephadm 无法从那些节点上管理 daemon:
for h in ceph-node2 ceph-node3; do scp -r /etc/ceph root@$h:/etc/ done然后执行 ceph -s 看集群是否 HEALTH_OK。如果没到 OK,直接用 ceph health detail 看原因,多数情况是 MON 数量不足或首次 OSD 没加。
3.3 添加 OSD、创建池:从空盘到可用的 pool
cephadm 通过 orch 管理 OSD。先看设备,再统一添加:
ceph orch device ls ceph orch apply osd --all-available-devices ceph -s--all-available-devices 会把每个节点上所有未分区、无文件系统的空盘都拿来做 OSD。如果不想让某块盘被占用,在 apply 前给设备打标签,或者写一份 device spec 只匹配 /dev/sdb 和 /dev/sdc。等 OSD 起来后,确认每个 OSD 的 in 和 up 状态都正常。此时集群健康,但还没有给 RBD 用的池。
创建池的这一步要注意:
ceph osd pool create rbd-pool 128 replicated ceph osd pool application enable rbd-pool rbd ceph osd pool set rbd-pool pg_autoscale_mode onreplicated 是副本模式,默认 size=3。pg_num 设 128 适合 9 个 OSD 左右的小集群;随后开 autoscale,让集群按实际使用量自动扩缩 PG,避免你一次性拍一个过大的值。application enable 不能省:RBD 客户端发现池没有绑定 rbd 应用会直接拒绝访问。如果 OSD 数量不对,常见原因是磁盘被分区或被旧文件系统占用,用 lsblk -f 看盘上是否还有 PV/VG 遗留。
4. RBD 块设备创建与客户端接入:映射、格式化、自动挂载
池建好后,开始给业务分配块设备。这一章涉及命令最密集,也是后面踩坑最多的地方。
4.1 创建 image:size、features 怎么选
先确认池存在,然后创建 image:
rbd create rbd-pool/vm-disk-01 --size 100G --image-feature layering rbd ls -p rbd-pool rbd info rbd-pool/vm-disk-01--size 100G 直接带单位。--image-feature layering 是内核 RBD 能稳用的特性集合;object-map、fast-diff、deep-flatten 这类特性在内核 mapping 时容易出兼容性问题,如果客户端只是普通挂载,建议创建时不带,或者用 rbd feature disable 关掉。exclusive-lock 用于多客户端锁,OpenStack、Kubernetes 这类集群场景需要它,单机挂载可以关。创建完成后 rbd info 会输出 block_name_prefix、order 等参数。order 默认 22,对应对象大小 4MB,想优化随机读写可以调低到 21 或 20,但对象越多元数据开销越大,生产里我一般不默认动它。
4.2 客户端侧:map、mkfs 与挂载
客户端不需要部署整套 Ceph,装 ceph-common 并把管理机上的 keyring 拷过去即可。
apt install -y ceph-common scp root@ceph-node1:/etc/ceph/ceph.conf /etc/ceph/ scp root@ceph-node1:/etc/ceph/ceph.client.admin.keyring /etc/ceph/ rbd map rbd-pool/vm-disk-01 lsblk | grep rbd mkfs.xfs -f /dev/rbd0 mount /dev/rbd0 /srv/datamap 前先确认内核 rbd 模块已加载:lsmod | grep rbd,没有就 modprobe rbd。map 成功后 /dev/rbd0 出现,但设备号由内核分配,不保证每次都是 rbd0。格式化之前务必确认磁盘是你要的镜像,lsblk -f 看文件系统类型,空盘显示为空,已有数据会显示分区和文件系统,避免把别的盘冲掉。这里有个最常见的生产事故:有人把 fstab 直接写死 /dev/rbd0,重启后设备变成 rbd1,挂载直接失败。
4.3 自动挂载:rbdmap 与 systemd 依赖
常见做法是用 rbdmap 服务在开机时做 map,挂载交给 fstab 或 systemd。先在客户端改 /etc/ceph/rbdmap:
# /etc/ceph/rbdmap rbd-pool/vm-disk-01 id=admin,keyring=/etc/ceph/ceph.client.admin.keyring systemctl enable --now rbdmaprbdmap 会把镜像映射成 /dev/rbdN,N 由已有设备编号决定。接下来不要用设备名写 fstab,用 blkid 拿 UUID:
blkid /dev/rbd0 # /etc/fstab 追加一行,注意 nofail 和 x-systemd.device-timeout UUID=xxxx-xxxx /srv/data xfs defaults,_netdev,nofail,x-systemd.device-timeout=30 0 0_netdev 告诉 systemd 这个设备依赖网络;nofail 保证网络存储不可用时系统照样能启动,不会卡在 mount 阶段。x-systemd.device-timeout=30 用于等待 rbdmap 完成映射。如果你在数据库容器里用块设备,更推荐写一个独立的 systemd service,在 Before= 声明启动顺序,先 map 再 mount 再启动数据库,避免依赖 fstab 的启动时序。步骤多一些,但重启后不需要人工干预。
5. 避坑排查:CEPH 部署中最常见的五个翻车点
这一章是血泪经验,每一条按“现象 → 原因 → 解决”写,都是我在不同环境里实际遇到过的。
5.1 OSD 反复 down:先看网络,再看内核
现象:部署完第二天开始,ceph -s 里 OSD 状态在 up/down 之间跳,客户端写入变慢,ceph health detail 报 osd.X is down。
原因:三个方向排查优先级最高——网络丢包、内核模块版本、磁盘 SMART。很多“玄学”抖动最后都定位到交换机端口协商失败或管理网络广播风暴;其次是内核太老,bluestore 的 AIO 调用异常。
解决:先 ping 对端 IP 看丢包率,再 ethtool 看协商速率;用 dmesg 找 OSD 进程 abort 的堆栈;lsblk 确认磁盘无 SMART 错误。网络和磁盘都没问题,就把 OSD 重启观察恢复时间;如果反复出现,要检查管理网和存储网是否混用,以及 cephadm 是否因为 SSH 超时误判 OSD 状态。
5.2 写放大吃掉 SSD:WAL/DB 分离是底线
现象:全闪集群跑数据库,几个月后 SSD 寿命报告降到 60%,读多写少但盘一直在写入。
原因:Ceph 默认 3 副本,写一份数据至少放大 3 倍,再加上 bluestore 自身的 WAL 和元数据更新,写放大可能到 5 到 7 倍。如果 WAL/DB 和数据块共用同一块 SSD,磨损速度会非常快。
解决:部署 OSD 时把 block.db 和 block.wal 指定到独立 NVMe 分区,例如 ceph-volume lvm create --block-db /dev/nvme0n1p1 --data /dev/sdb。已有集群只能通过 bluestore 工具迁移,过程比新装麻烦得多,所以新集群一开始就要规划好。数据盘 HDD、缓存盘 SSD 的混构是性价比最高的方案。
5.3 重启后 /dev/rbd0 消失:别把设备名写进 fstab
现象:客户端服务器一重启,数据库服务起不来,发现 /dev/rbd0 不存在,fstab 报 failed。
原因:内核 rbd 模块没有在启动阶段加载,或者 rbdmap 服务在挂载前没有完成 map;直接写死 /dev/rbd0 也不可靠,设备号由内核动态分配。
解决:把 modprobe rbd 写入 /etc/modules-load.d/rbd.conf;挂载引用改为 blkid 拿到的 UUID,并在 fstab 加 _netdev,nofail;或者干脆用 systemd unit 管理 map 顺序。我自己的习惯是数据库节点上绝不用裸设备名,全部走 systemd 单元,启动顺序写清楚,避免开机竞态。
5.4 OOM 把 OSD 杀了:内存目标要手动锁
现象:跑压测时 ceph-osd 进程被 OOM killer 杀掉,mon 也跟着起不来,机器负载不高但内存耗尽。
原因:cephadm 默认开启 osd_memory_target_autotune,会根据机器内存自动给每个 OSD 分配上限,但 autotune 在混部场景下会跟监控组件抢内存;再加上 MGR、Prometheus、Grafana 都在同一台节点,内存就爆了。
解决:先关掉 autotune:ceph config set osd osd_memory_target_autotune false,再设具体目标:ceph config set osd osd_memory_target 8GiB。同时确认监控栈是否真的需要,实验环境 bootstrap 时用 --skip-monitoring-stack 最省心。内存问题在小集群上比性能问题更早出现,所以我会在部署阶段就规划节点内存,而不是等 OOM 再救。
5.5 版本混跑:升级前先 check,再 start
现象:某次 yum update 之后,ceph -s 显示不同节点运行不同版本,甚至 MON 之间无法选举。
原因:cephadm 管理的是容器镜像版本,宿主机包管理器自动更新不会让 Ceph 容器合理切换;如果手动在多个节点重启容器,很容易造成版本不一致、quorum 丢失。
解决:所有节点禁用 ceph 相关仓库的自动更新。升级走统一入口:ceph orch upgrade check 确认目标版本可用,再 ceph orch upgrade start --image 目标镜像。升级中途不要执行其它 daemon 重启;发现版本不一致时先回滚到最近一次全量一致的镜像,而不是继续往上升。生产集群只升不降,降级基本靠备份,所以升级前先备份 MON 的 db。
6. 生产验证与调参:fio 压测、内存参数与容量规划
6.1 用 fio 验证 RBD 性能:先随机后顺序
上线前我会用 fio 打一遍,先随机写,再顺序写:
fio --name=randwrite \ --filename=/dev/rbd0 \ --rw=randwrite \ --bs=4k --iodepth=16 --numjobs=4 \ --runtime=60 --time_based --group_reporting \ --direct=1direct=1 绕过客户端页缓存,看到的是真实的块设备能力。4K 随机写是数据库场景最残酷的指标,结果里重点看 iops 和 p99 延迟。9 个 OSD 的全闪小集群,4K 随机写跑到几千 IOPS 算正常;如果只有几百,先怀疑副本数和网络,再用 fio 分别打单 OSD 定位。
fio --name=seqwrite --filename=/dev/rbd0 \ --rw=write --bs=1M --iodepth=32 --numjobs=2 \ --runtime=60 --time_based --group_reporting --direct=1顺序 1MB 更考验网络和 OSD 聚合带宽。跑完后记录带宽和吞吐,后续调整 pool 的 size 或压缩配置时,拿这套数据当基线。
6.2 上线前调好这三个参数
| 参数 | 默认 | 建议 | 说明 |
|---|---|---|---|
| osd_memory_target | 4GiB | 按节点内存 1/2 以内 | 关掉 autotune 后手动设,避免 OOM |
| osd_max_backfills | 1-2 | 1 | 控制数据回填占用的磁盘 IO |
| osd_recovery_max_active | 3 | 2 | 恢复期间降低对业务的影响 |
设置方式统一走 ceph config set。这三个参数直接影响稳定性和故障恢复时间,比纠结单个磁盘的 IOPS 更值得先调。PG 数量交给 autoscale 管理,不要手动来回改。
6.3 容量规划与上线前最后检查
提示:可用容量 ≈ 裸容量 ÷ 副本数 × 0.85
0.85 是为数据均衡、PG 迁移和未来扩容留的安全余量。20TB 裸容量、3 副本,实际可分配约 5.6TB。对延迟不敏感的场景可以换纠删码提升可用容量,但会引入计算开销,块存储场景慎用。
上线前最后检查四件事:ceph -s 为 HEALTH_OK;ceph osd tree 确认 OSD 分布到不同主机;fio 随机写跑 30 分钟以上观察内存曲线;手动拔掉一块数据盘,观察恢复时间和业务影响。我自己踩过最深的坑是第一次交付时没做故障演练,结果一台机器下线时副本恰好落在同一机架,整个虚机存储直接卡死。所以现在每次新集群交付前,不管多忙我都会做一次拔盘演练,把恢复时间记录到运维手册里。希望帮到你。
本文还有配套的精品资源,点击获取