手把手教你搭建 Ceph+JuiceFS

Ceph 提供了对象存储,可作为存储引擎在 JuiceFS 中使用。这一组合非常适合云计算、大数据分析和机器学习等数据密集型应用场景。

在日常部署中可直接通过 Ceph RADOS 配合 JuiceFS 使用,无需部署 RGW。基于此方案实践以及运维中的常见问题,结合Ceph 官方文档整理了这篇博客,旨在帮助那些不太熟悉 Ceph 的工程师顺利部署和运维 Ceph,并与 JuiceFS 进行对接。

阅读之前,简单接下来操作中涉及到的 Ceph 基本概念:

  • cephadm 是 Ceph 集群专门的运维管理工具,我们会用他初始化 Ceph 集群

  • OSD(Object Storage Daemon)是 Ceph 集群的存储服务实例,分配给 Ceph 使用的每一块盘,都由一个 OSD 来负责管理

  • 放置组(Placement Group,PG)可以简单想象成是 Ceph 中的「虚拟目录」,为 Ceph 中存储的大量对象进行分组。同一目录下的变更操作会加锁执行,而不同的目录之间可以进行并发操作。因此 PG 的数目如果太小,会由于冲突导致性能不佳,PG 数目过大,消耗的资源也会上升。在下方的教程里会遵循官方推荐的经验值计算方法进行设置

环境准备

文档中以下方环境作为示范:

  • 操作系统:Ubuntu 22.04 LTS
  • 内核版本:5.15.0-72-generic
  • 数据盘:每台节点 8 块本地 NVME SSD
  • 内存:256G
  • Ceph 版本:quincy v17.2.6

共计 16 节点,每个节点 8 块 SSD 盘(未格式化),在开始操作前,首先在 1 号节点配置好所有节点的免密登录,并且将节点信息记录在 /etc/hosts,类似:

shell 复制代码
10.38.0.1 ceph-node01
10.38.0.2 ceph-node02
10.32.0.3 ceph-node03
...
10.32.0.16 ceph-node16

准备完毕以后,接下来会把 ceph-node01 做为操作(兼 Ceph 主控)节点,接下来将会在这台机上进行所有 Ceph 相关的安装、运维操作,因此提前安装 pssh,方便后续批量操作:

apt install pssh

pssh 也就是 Parallel SSH,可以方便地并发 SSH 到多个节点、批量执行命令,这个工具需要读取主机列表,因此将所有节点(主控除外)的主机名记录到文本文件里:

ceph-node02
ceph-node03
...

节点基础设施

如果尚未搭建时钟同步,需要手动安装:

apt install chrony

# 查看能连接到的 servers
# 如外网不通则需要改配置连内部服务器,如果不清楚如何配置,联系云服务商技术支持
chronyc sources

# 时钟同步需要在所有节点设置,此处仅示范批量安装 chrony 的命令
# 如果需要更多配置,需要自行操作
parallel-ssh -h hosts.txt -i apt install chrony

如果节点开启了 swap(检查 /etc/fstab 是否存在 swap 字样),那么首先需要对所有节点禁用 swap:

swapoff -a
# 将 fstab 中的项目进行注释禁用
sed -i.bak "s/\/swap.img/# \/swap.img/" /etc/fstab

# 在主控节点上执行上方命令以后,还需要批量在所有其他节点执行
parallel-ssh -h hosts.txt -i swapoff -a
parallel-ssh -h hosts.txt -i 'sed -i.bak "s/\/swap.img/# \/swap.img/" /etc/fstab'

安装 Ceph

Docker 和镜像搬运

在所有节点上安装 Docker:

apt install docker.io
parallel-ssh -h hosts.txt -i apt install docker.io

在后续的安装过程中,ceph 和 node-exporter 这两个镜像是所有节点都需要的,因此推荐提前拉取下来、手动分发到所有节点。如果不做这一步,后续 cephadm 会从官方源拉,取决于当地网络环境,可能会非常慢。

docker image save quay.io/ceph/ceph quay.io/prometheus/node-exporter | gzip > images.tar.gz
parallel-scp -h hosts.txt ./images.tar.gz /tmp/
parallel-ssh -h hosts.txt -i 'docker load -i /tmp/images.tar.gz'

# 确认所有节点都加载成功后,清理镜像
parallel-ssh -h hosts.txt -i rm /tmp/images.tar.gz

部署 Ceph 主控

Ubuntu 22 可以方便地从系统源安装 cephadm,但对于其他 Ubuntu 版本或者其他系统,则可能需要手动安装,详见 Ceph 下载安装

# 在主控节点安装部署工具,需确认其版本为 quincy(17.2.6),这在 Ubuntu 22 已经是默认提供的版本
apt install cephadm

# 如果你的系统 apt 源提供的版本不满足要求,则需额外手动从 Ceph 官方网站下载
# 比方说 Ubuntu 20(focal),就需要用下方命令手动安装
# wget https://download.ceph.com/debian-17.2.6/pool/main/c/ceph/cephadm_17.2.6-1focal_amd64.deb
# dpkg -i ./cephadm_17.2.6-1focal_amd64.deb

# 手动安装完 cephadm 后,还需要额外将 Ceph 相关的软件源纳入到包管理器的 repo 列表
# 该命令是为了下一步能够安装版本符合要求的 ceph-common
# cephadm add-repo --release quincy

# 安装 Ceph 相关的各种工具
apt install ceph-common

初始化集群,其中 mon-ip 为主控机的内网地址,多网卡集群可以额外指定内部网络,比如 --cluster-network 10.90.90.0/24

# 命令运行完毕以后,会打印 Ceph Dashboard 的账号密码,注意保存
cephadm --image quay.io/ceph/ceph:v17.2.6-20230920 bootstrap --mon-ip 10.38.0.1 --log-to-file

Ceph 主控进程上线后,会生成自己的密钥,需要将其安装到所有节点:

# 打印公钥,复制内容
cat /etc/ceph/ceph.pub

# 撰写命令,批量执行
parallel-ssh -h hosts.txt -i 'echo ssh-rsa xxx ceph-xxx >> /root/.ssh/authorized_keys'

公钥安装完毕以后,主控节点就能登入所有 worker 节点进行运维操作了。进入 Ceph 管理容器,后续命令都在这个管理容器内执行。但考虑到主控节点已经在宿主机层面安装了所有需要的工具,事实上不进入 cephadm shell 也是完全可以的。

# 运行该命令以后,会进入容器,但是 prompt 不会有任何变化
cephadm shell

# 可以打印进程,识别当前是在宿主机,还是容器内
ps -ef

确认集群初始状态,并进行一系列预调优:

# 查看集群状态
ceph -s

# 禁用掉无用的 nfs 模块
ceph mgr module disable nfs

# 默认每个 OSD 内存上限 4G
# 在 Ceph 集群中,一块盘(块设备)会部署为一个 OSD
# 每节点 8 块盘,内存 256G,因此给与 16G 更大的内存上限
# 调整过后,OSD 占用的内存总上限为 128G,还有充足冗余
ceph config set osd osd_memory_target 17179869184

组建 Ceph 集群

将所有节点添加进 Ceph 集群,前 5 台节点(含主控)打上 _admin 标签,后续的节点则不需要。考虑到节点众多,将其撰写为脚本执行:

# 主控节点早已是集群成员,不需要 add
# 因此为 ceph-node[02:04] 添加 _admin 标签,其他节点则不需要
ceph orch host add ceph-node02 10.32.0.2 --labels _admin
...
ceph orch host add ceph-node16 10.32.0.16

至此,所有节点都已经加入 Ceph 集群,会自动探测节点下的可用块设备,具体而言,未经格式化、无人使用的盘,均会探测到。不必担心,在运行后续 add osd 前,这个过程不会对盘做任何修改。

# 查看所有节点可用的盘,确认与现场实际情况匹配,也就是所有的空盘都探测到了、没有坏盘
ceph orch device ls

# 非新盘需要先格式化
# ceph orch device zap host0 /dev/nvme0n1 --force

# 把所有节点的所有空闲 SSD 都添加为 Ceph OSD
# 运行完这个命令后,盘就归 Ceph 管理了
# 注意按顺序逐个添加以保证 ID 有序,这个过程耗时很长,建议做成脚本运行
ceph orch daemon add osd ceph-node01:/dev/nvme0n1
ceph orch daemon add osd ceph-node01:/dev/nvme1n1
...
ceph orch daemon add osd ceph-node16:/dev/nvme7n1

# 部署 OSD 过程中,用这个命令查看进度、状态
ceph orch ps --daemon_type osd

创建存储池

所有盘都顺利部署为 OSD 后,就可以创建存储池(pool),然后对接 JuiceFS 文件系统开始使用了。创建存储池之前,务必了解 Ceph 中支持的两种存储模式:

  • 副本模式(replication):默认每个对象存储 3 份(size=3)。与之伴随的另一个概念叫「最小可用副本数」,默认为 2(min_size=2),意为最少需要 2 个正常运行的副本,才能进行 I/O 操作。可想而知,在默认的 size=3,min_size=2 的配置下,最多允许 1 个副本损坏,这也是最适宜生产环境的设置。如果改为 size=2,min_size=1,那么所有对象就只有 2 副本,如果任一出现故障,数据将只存一份,丢失的风险增加。副本模式下的故障域也和集群的配置相关,在节点数 ≧ 3 的情况下,Ceph 会自动将副本分散到不同的节点上,因此对于默认的副本参数,允许任意一台节点异常而不影响服务。

  • 纠删码模式(erasure code, EC):相比副本模式提供更好的存储效率,但纠删码本身也带来一定的计算资源损耗。如果希望优先节约磁盘空间,可以选取该模式;

    两个数字是放置组(placement group)个数,需要相同

    取值公式:num_osd * 100 / 3 并向上取 2 的幂,num_osd 是 128,因此此处取 4096

    ceph osd pool create jfsvol 4096 4096 replicated
    ceph osd pool application enable jfsvol juicefs

纠删码模式的创建命令示范:

# EC 4+2 池(stripe unit 默认是 4K)
# ceph osd erasure-code-profile set jfs-ec k=4 m=2 stripe_unit=8192
# ceph osd pool create ec-pool 32 32 erasure jfs-ec

至此,Ceph 部署完可以投入使用,推荐先跳过下方的监控告警小节,直接对接 JuiceFS 文件系统。对接完成后,再搭建监控也不迟。

对接 JuiceFS 文件系统

创建 JuiceFS 文件系统,Bucket 名称对齐存储池名称,也就是上方创建好的 jfsvol。然后按照文档操作挂载。

在 CSI 驱动中挂载对接了 Ceph 的 JuiceFS 文件系统,为了让 Ceph 配置文件对 JuiceFS 客户端可见,需要把他们上传到 Kubernetes Secret:

# 上传之前,确保配置文件末尾有换行符,否则后续挂载可能引发异常
echo >> ceph.conf
echo >> ceph.client.admin.keyring

kubectl create secret generic ceph-secret --from-file=ceph.conf=ceph.conf --from-file=ceph.client.admin.keyring=ceph.client.admin.keyring

创建好 Secret 以后,还需要在文件系统认证信息里添加 configs 字段:

apiVersion: v1
kind: Secret
metadata:
  name: jfsvol-secret
type: Opaque
stringData:
  name: jfsvol
  token: xxx
  access-key: ceph
  secret-key: client.admin
  # 将上方创建的 ceph-secret 挂载到 /etc/ceph
  configs: '{"ceph-secret": "/etc/ceph/"}'

运维

Ceph 是很成熟的存储方案,如果规划和运维正确,能够很稳定地运行。阅读下方小节了解常见的运维要点。

部署监控

Cephadm 部署时,会默认安装自己的 Grafana,并且默认与其 Dashboard 进行了集成。你可以直接使用这个 Dashboard 进行监控告警,也可以选择对接已有的外部 Grafana。

如果希望复用环境中已有的外部 Grafana,那么首先浏览器打开这个 Grafana 的地址,点击左侧边栏 Administration,添加数据源,把 Ceph 集群的 Prometheus 地址填进去,默认是 http://<ceph-master-ip>:9095,如果你不确定具体端口号,也可以去主控节点 ss -lntp | grep prometheus 确认现场情况。测试并保存数据源。

Prometheus 添加成功后,导入社区的 Ceph Grafana Dashboard,监控就搭建完毕了,这个 Dashboard 里已经内置了告警规则,后续配置好告警发送通道以后,就能直接开始运行。

容量监控和故障恢复

可以方便地用 ceph -s 查看当前用量,在上一小节中导入的面板里,也已经包含了容量监控规则,对 Ceph 各个层面的容量占用进行监控:

  • CephNodeRootFilesystemFull:节点根分区用量监控

  • CephOSDNearFull|CephOSDFull|CephOSDBackfillFull:OSD 相关容量监控

  • CephPoolNearFull|CephPoolFull|CephPoolBackfillFull:存储池相关容量监控

上方报警项中,如果 Ceph 系统写满,那么会触发 CephOSDFull|CephPoolFull,同时 Ceph 不再允许写入------这是很不妙的情况,因为对于 Ceph 而言,「删」事实上也属于写请求,在 CephPoolFull 的情况下,即便是客户端发起删请求,Ceph 也会进行拦截,让请求无法完成、直至超时。出现这种故障时,如果无法立刻加盘扩容,那么处置步骤如下:

  1. 定位到问题文件系统,接下来将会清理该文件系统、释放容量,因此需要:
  • 提前将回收站设置为 0,否则即便删除了文件,也会移入回收站进行保存,无法释放对象存储容量

  • 推荐在业务侧操作下线停写(考虑到 Ceph 此时已经容量超限,所有的写请求都会卡死),这并不是必要的,只是为了增加后续运维动作的容错率

  1. 选取该文件系统的一个可用客户端,进入挂载点,清理文件、释放容量。

  2. 在 JuiceFS 挂载点删除足量的文件,如果 Ceph 一侧没有立即释放容量,则说明遇到了上方提到的「容量超限时,无法执行删除」的死锁问题,这时需要登录 Ceph 主控节点,将阈值临时提高,来允许删除请求。操作前必须确保业务已经停写,否则一旦放开阈值,就会迅速写满,再次引发故障。

    mon_osd_full_ratio 默认为 0.95,临时调高以允许写入

    ceph osd set-full-ratio 0.96

    放开阈值后,紧密观察容量是否释放

ceph -s

  1. 确认容量释放,准备回滚 full ratio 设置。这一步也需要谨慎操作:将 full ratio 回滚,必须保证操作不会再次让集群处于容量超限的状态、中断业务写入。因此先运行 ceph df 核实已用空间占比(%USED)。

如果 %USED 低于 90%,那么可以运行下方命令回滚配置:

ceph osd set-full-ratio 0.95

为了改善容量超限的处置流程,JuiceFS 客户端支持在 Ceph 集群写满的情况下进行删除操作(详见社区版相关代码变更),因此对于新版客户端,不再需要用 set-full-ratio 进行临调。

扩容

由于是最为常见的扩容方法,本小节只介绍用增添新盘来扩大集群容量的操作。

插盘已经在所有节点完成后,运行命令确认新盘:

# 输出表格里,AVAILABLE=Yes 的即为探测到的新盘
# 确认新盘参数符合预期
ceph orch device ls

在创建新的 OSD 之前,推荐先手动禁用数据均衡。这是由于盘数量可能比较多,OSD 创建需要格式化盘,整个操作耗时会比较长。如果在默认开启数据迁移的状态下加盘,每一个新盘都会伴随着数据迁移。为了避免低效的数据反复迁移,建议在所有新盘纳入 Ceph 后,再统一开启。

# 禁用数据均衡
ceph osd set nobackfill

# 确认生效
ceph -s | grep backfill

接下来运行命令添加新盘,操作和创建 Ceph 集群中的相关步骤是完全一样的,在对应的小节里搜索 ceph orch daemon add osd 命令,将所有新盘按顺序添加,就可以了。如果待添加的盘数量众多,建议提前撰写脚本运行。

所有盘添加完成后,运行下方命令进行收尾:

# 确认所有新盘均已加入集群(AVAILABLE=No)
ceph orch device ls

# 重新开启数据均衡
ceph osd unset nobackfill