飞行的蜗牛

一个正在觉醒的无名氏修行者,略懂编程,略懂音乐。

Ceph-05 Ceph 块存储之 RBD

本文讲述 Ceph 块设备存储的相关介绍以及 RBD 相关操作。

什么是块设备?

首先,块是一个字节序列(例如,一个 512 字节的数据块)。

块设备是 I/O 设备中的一类,是将信息存储在固定大小的块中,每个块都有自己的地址,
还可以在设备的任意位置读取一定长度的数据。最常见的块设备例如硬盘,U盘,SD卡等。

做一个不是很准确的但是方便你理解的概括:块设备就是以作为存储单位的设备。

顺便提一下,与块设备相对应的 I/O 设备是 字符设备,它是在 IO 传输过程中以字符为单位进行传输的设备。例如键盘,打印机等。

Linux 系统下一般块设备都在 /dev/ 目录下,通常块设备(硬盘)连接到计算机之后,系统检测到有新的块设备,
就会自动在 /dev/ 目录下创建一个块设备文件,用户就可以通过这个文件去访问块设备。

通常会有 sda, sdb, hda, loop 等这样的块设备文件,其中以 sd 开头的块设备文件对应的是SATA接口的硬盘,
而以hd开头的块设备文件对应的是IDE接口的硬盘。以 loop 开头是循环设备,一般链接到某个文件。

RBD 是什么?

接下来回到我们的正题,那 rbd 是什么呢? rbd 就是由 Ceph 集群提供出来的块设备。
可以这样理解,sda 和 hda 都是通过数据线连接到了真实的硬盘,而 rbd 是通过网络连接到了 Ceph 集群中的一块存储区域,
往 rbd 设备文件写入数据,最终会被存储到 Ceph 集群的这块区域中。

接下来我们看看官网对 RBD 的介绍:

RBD : Ceph’s RADOS Block Devices , Ceph block devices are thin-provisioned,
resizable and store data striped over multiple OSDs in a Ceph cluster.

Ceph 块设备是精简配置的、大小可调且将数据条带化存储到集群内的多个 OSD 。从这句介绍中我们可以得到 RBD 下面的特性。

  1. RBD 就是 Ceph 里的块设备,挂载以后可以当普通的硬盘使用。
  2. resizable: 这个块可大可小。
  3. data striped: 数据条带化,这个块在 Ceph 里面是被切割成若干小块来保存的。
  4. thin-provisioned: 精简配置的,这个特性有点像虚拟机里面的 VDI 磁盘,块的大小和在 ceph 中实际占用的大小是没有关系的,
    并不是你创建 1 个 1GB 的块,它就得占 1GB 的空间。刚开始创建的块设备,甚至不占空间的,今后用多大空间,才会在 Ceph 中占用多大空间。

Ceph 块设备利用 RADOS 的多种能力,如快照、复制和一致性。
Ceph 的 RADOS 块设备( RBD )使用内核模块或 librbd 库与 OSD 交互。下图是 RBD 的基本架构:

Note: 内核模块可使用 Linux 页缓存。对基于 librbd 的应用程序, Ceph 可提供 RBD 缓存。

环境说明

  • 操作系统: Ubuntu 18.04 LTS
  • Ceph 版本: 12.2.12 Luminous (stable)

如果你还没有安装好 Ceph 环境,请参考 Ceph-03 搭建 Ceph 存储集群

RBD 操作

RBD 的操作有很多,这里只列举几个常用的操作。

Note: 为了方便起见,下文我们统一用 rbd 来指代 Ceph 的块设备。

创建块设备

1
2
3
4
5
root@ceph1:~# rbd create foo --size=1024
# 查看当前集群所有 rbd
root@ceph1:~# rbd ls
foo
test

--size 是必须传入的参数,单位默认是 MB,你也可以指定成 GB,例如:

1
rbd create foo --size=100G

查看 rbd 的详细信息:

1
2
3
4
5
6
7
8
9
root@ceph1:~# rbd info foo
rbd image 'foo':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.d36b6b8b4567
format: 2
features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
flags:
create_timestamp: Sun Feb 16 01:36:36 2020

我们可以看到 Ceph 默认创建的 rbd 的默认格式是 format2 (Hammer 以及Hammer 之前默认的格式是 format1)。
我们再创建一个 format1 格式的 rbd:

1
2
3
4
5
6
root@ceph1:~# rbd create foo-1 --size=1024 --image-format=1
rbd: image format 1 is deprecated
root@ceph1:~# rbd ls
foo-1
foo
test

我们可以看到虽然创建成功了,但是 Ceph 发出一个警告:”image format 1 is deprecated”,
可见在 Luminous 版本中 format1 格式的 rbd 已经被废弃了。我们再看下 foo-1 的信息:

1
2
3
4
5
rbd image 'foo-1':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rb.0.d372.327b23c6
format: 1

我们可以看到 format1 和 format2 最明显的区别就是 block_name_prefix 的结构不一样。

接下来我们再看看文件结构:

1
2
3
root@ceph1:~# rados -p rbd ls |grep foo
foo-1.rbd
rbd_id.foo

format2 采用rbd_id.{rbd_name}的形式保存了这个块的 prefix,而 format1 采用的是 {rbd_name}.rbd 的形式。

下面对这两种格式进行简要的对比总结:

格式 rbd_diretory ID prefix data
format1 保存了所有 RBD 的名称 foo.rbd 在 ID 中 形如:rb.0.d372.327b23c6
format2 rbd_id.foo 在 ID 中并在 rbd_header.prefix 显示输出 形如 rbd_data.d36b6b8b4567

除了上面意外 format1 和 format2 还有一个很重要的功能区别,那就是只有 format2 的 rbd 的才支持克隆。
这个后面在下文讲 rbd 克隆操作的时候还会提到。

默认创建的 rbd 的 object 大小是 4MiB(2 的 order 次方),我们也可以自己指定大小,例如:

1
2
3
4
5
6
7
8
9
10
root@ceph1:~# rbd create foo-2 --size=1024 --object-size=8M
root@ceph1:~# rbd info foo-2
rbd image 'foo-2':
size 1GiB in 128 objects
order 23 (8MiB objects)
block_name_prefix: rbd_data.d37c6b8b4567
format: 2
features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
flags:
create_timestamp: Sun Feb 16 03:33:53 2020

如果没有指定,那么默认创建的 rbd 都会在 rbd 这个默认的存储池,如果你需要在其他存储池创建 rbd,可以通过下面的 2 种方式:

1
2
3
4
5
root@ceph1:~# rbd create foo-3 --size=1024 --pool rbd2
root@ceph1:~# rbd create rbd2/foo-4 --size=1024
root@ceph1:~# rbd ls rbd2
foo-3
foo-4

映射块设备

命令形式:

rbd map {rbd_name}

例如:

1
2
3
4
5
root@ceph1:~# rbd map test
/dev/rbd1
root@ceph1:~# rbd showmapped
id pool image snap device
0 rbd test - /dev/rbd0

如果你的 rbd 格式是 format2,那么映射的时候可能会遇到如下错误:

1
2
3
4
5
root@ceph1:~# rbd map foo
rbd: sysfs write failed
RBD image feature set mismatch. You can disable features unsupported by the kernel with "rbd feature disable foo object-map fast-diff deep-flatten".
In some cases useful info is found in syslog - try "dmesg | tail".
rbd: map failed: (6) No such device or address

出现这种错误的原因是 OS kernel 不支持块设备镜像的一些特性,所以映射失败。这种情况有是那种解决方案

方法一

直接diable这个rbd镜像的不支持的特性,首先查看该镜像支持了哪些特性:

1
2
3
4
5
6
7
8
rbd image 'foo':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.d36b6b8b4567
format: 2
features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
flags:
create_timestamp: Sun Feb 16 01:36:36 2020

可以看到特性 features 一栏,由于我 OS 的kernel只支持 layering,其他都不支持,所以需要把部分不支持的特性 disable 掉。

1
2
3
4
5
6
7
8
9
10
11
12
root@ceph1:~# rbd  feature disable foo  exclusive-lock object-map fast-diff deep-flatten
root@ceph1:~# rbd info foo
rbd image 'foo':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.d36b6b8b4567
format: 2
features: layering
flags:
create_timestamp: Sun Feb 16 01:36:36 2020
root@ceph1:~# rbd map foo
/dev/rbd2

方法二

创建rbd镜像时就指明需要的特性,如:

1
2
3
4
5
6
7
8
9
10
root@ceph1:~# rbd create foo2 --size 1024 --image-feature layering
root@ceph1:~# rbd info foo2
rbd image 'foo2':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.d39d6b8b4567
format: 2
features: layering
flags:
create_timestamp: Sun Feb 16 04:09:23 2020

方法三

如果还想一劳永逸,那么就在执行创建rbd镜像命令的主机中,修改Ceph配置文件/etc/ceph/ceph.conf,在global section下,增加:

1
rbd_default_features = 1

这里顺便提一下,这里 features 的配置是通过位运算开关来打开或者关闭某个 feature 的。所以如果你想开启 layering, exclusive-lock
支持,这里的 rbd_default_features 值就应该设置成 0x00011 也就是 3,依次类推,全部都打开就是 31(0x11111)。

重启 MON 服务之后生效 systemctl restart ceph-mon.target

挂载块设备

挂载之前,跟普通磁盘一样,你先得格式化:

1
2
3
4
5
6
7
8
9
10
11
12
root@ceph1:~# mkfs.ext4 /dev/rbd2
mke2fs 1.44.1 (24-Mar-2018)
Discarding device blocks: done
Creating filesystem with 262144 4k blocks and 65536 inodes
Filesystem UUID: 20f1bdbf-3ecc-4764-a4b9-f351ce168eb3
Superblock backups stored on blocks:
32768, 98304, 163840, 229376

Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

然后挂载块设备

1
2
3
4
root@ceph1:~# mkdir /mnt/rbd-foo
root@ceph1:~# mount /dev/rbd2 /mnt/rbd-foo
root@ceph1:~# df -h | grep rbd
/dev/rbd2 976M 2.6M 907M 1% /mnt/rbd-foo

Ceph RBD 支持 POSIX 标准,所以你可以像使用本地存储目录一样使用它。

开机自动挂载

有两种方法可以实现 Ceph rbd 的开机自动挂载。

方法一

我们知道 rbd 最所以不能像普通的硬盘设备一样开机自动挂载,是因为 Ceph 的 rbd 必须先经过映射(map)操作。
所以我们只需要把 rbd map foo 写入到开机启动脚本就好了。
不知道如何设置的同学请参考 ubuntu-18.04 设置开机启动脚本
这里就不做赘述了。

方法二

Ceph 本身为我们提供了一种更高级的自动映射 rbd 的方法。

首先我们需要在部署节点上把客户端验证的 key 推送到客户端。

1
2
3
ceph-deploy admin ceph-client
# 如果部署节点没有 ceph-client 的验证信息,可以直接用 scp 拷贝
scp /etc/ceph/ceph.client.admin.keyring root@{client.ip}:/etc/ceph/

然后我们编辑客户端的 rbdmap 文件 vim /etc/ceph/rbdmap,添加一行自动 map 的配置:

1
rbd/{rbd_name}      id=admin,keyring=/etc/ceph/ceph.client.admin.keyring

{rbd_name} 是你需要自动 map 的 rbd 名称,比如你改成 foo,这样 Ceph 就会在开机的时候自动映射 foo 块设备。

接下来就简单了,我们可以像普通硬盘一样开机挂载了。编辑 /etc/fstab,添加一行自动挂载的配置

1
/dev/rbd/rbd/{rbd_name} /mnt/rbd ext4 defaults,noatime,_netdev 0 2

这里的 {rbd_name} 同样需要改成对应的 rbd 实际名称,同时需要注意加上 _netdev 选项,表示是网络设备。

RBD 删除

这个非常简单,命令形式如下:

rbd rm {pool_name}/{rbd_name}

例如:

1
rbd rm rbd/foo

RBD 快照

创建快照:

rbd snap create {rbd_name}@{snap_name}

例如:

1
2
3
4
rbd snap create foo@foo-snap-01 
root@client:~# rbd snap ls foo
SNAPID NAME SIZE TIMESTAMP
10 foo-snap-01 4GiB Fri Feb 7 04:13:05 2020

删除快照:

rbd snap rm {rbd_name}@{snap_name}

例如:

1
rbd snap rm foo@foo-snap-01

快照回滚,如果不小心删除数据了,并且之前有创建快照的话,可以使用快照回滚:

rbd snap rollback {rbd_name}@{snap_name}

例如:

1
2
root@client:# rbd snap rollback foo@foo-snap-01
Rolling back to snapshot: 100% complete...done.

Note: 回滚之前要先 umout 块设备,回滚之后再重新 mount。

通过快照克隆 RBD

首选,被克隆的 rbd 格式必须是 format2

(1)克隆之前,先保护快照,以免被修改。

1
rbd snap protect foo@s_foo_1

(2)克隆

1
2
3
4
5
6
7
8
9
10
11
12
rbd clone foo@s_foo_1 clone_foo
rbd image 'clone_foo':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.148a76b8b4567
format: 2
features: layering
flags:
create_timestamp: Sun Feb 16 10:02:24 2020
parent: rbd/foo@s_foo_1
overlap: 1GiB

此时我们可以看到 clone_fooparent 属性为 rbd/foo@s_foo_1,表示引用的是 rbd/foo@s_foo_1 快照

(3)脱离

现在 clone_foo 还是跟 rbd/foo@s_foo_1 关联在一起的。还不是一个独立的 rbd,所以我们需要把他们脱离:

1
2
3
4
5
6
7
8
9
10
11
root@client:# rbd flatten clone_foo
Image flatten: 100% complete...done.
root@client:# rbd info clone_foo
rbd image 'clone_foo':
size 1GiB in 256 objects
order 22 (4MiB objects)
block_name_prefix: rbd_data.148a76b8b4567
format: 2
features: layering
flags:
create_timestamp: Sun Feb 16 10:02:24 2020

脱离之后 clone_foo 就是一个独立的 rbd 了。

RBD 扩容

RBD 的扩容分 2 步:

  1. 扩展块大小
  2. 扩展 fs(文件系统)

Ceph 块设备映像是精简配置,只有在你开始写入数据时它们才会占用物理空间。然而,它们都有最大容量,就是你设置的 –size 选项。
如果你想增加(或减小) Ceph 块设备映像的最大尺寸,执行下列命令:

1
2
rbd resize --size 2048 foo #增加块设备容量
rbd resize --size 2048 foo --allow-shrink # 减少块设备容量

如果 rbd 已经被挂载了,扩容/缩容之后还需要扩展挂载 rbd 的文件系统,否则不会生效。

ext4 文件系统扩容:

1
resize2fs /dev/rbdxxx

xfs 文件系统扩容:

1
xfs_growfs /dev/rbdxxx

RBD 缩容

Note: xfs 文件系统不支持文件系统缩小,只能增大,ext4 文件系统支持扩大和缩小

RBD 缩容比较麻烦,分为 4 步:

  1. 先卸载文件系统 umout /dev/rbd0
  2. 检测文件系统 e2fsck -f /dev/rbd0
  3. 缩小文件系统 resize2fs /devrbd0 400M
  4. 缩小块大小 rbd resize –image rbd1 –size 400 –allow-shrink

注意,上面的步骤不能乱,要严格按照顺序执行。操作完之后,要记得检查块设备里面的文件是否可以正常访问,是否有损坏。

参考文献