Ceph 提供了一些特性和接口, 可以让我们很方便地实现 集群的备份与恢复. 一种是 通过集群镜像,可以做到实时备份, 但是这个对网络要求比较高, 运行一个镜像集群资源消耗也比较大. 还有一种就是通过 RBD 的快照技术, 实现对 RBD 的数据备份与恢复.

RBD 的快照功能

对于快照, 熟悉存储系统和虚拟机的童鞋肯定会有印象. 快照一般是基于时间点或事件对系统做一个标记, 然后在需要的时候,将状态恢复到标记时的点. 快照的实现前提是底层系统没有损坏..

快照 的基本操作

对于镜像快照的操作, 一般是要指定 pool 名字和 镜像名字. Pool 名字通过 --pool 或者 -p 指定.

创建快照

要创建一个快照, 使用 rbd snap create 命令, 基本语法是

1
2
3
rbd -p {pool-name} snap create --sanp {snap-name} {image-name}
### or simply
rbd snap create {pool-name}/{image-name}@{snap-name}

例如:

1
2
3
rbd -p rbd snap create --snap foo.2018.09.12.20.30.30 foo
### or
rbd snap create rbd/foo@foo.2018.09.12.20.30.30

列出快照

列出一个镜像的快照, 要指定 Pool 名字和 image 名字.

1
2
3
rbd -p {pool-name} snap ls {image-name}
### or
rbd snap ls {pool-name}/{image-name}

例如,

1
2
3
rbd -p rbd snap ls foo
### or
rbd snap ls rbd/foo

快照回滚

使用 rbd snap rollback 对 image 进行回滚操作, 恢复数据,

1
2
3
rbd -p {poo-name} snap rollback --snap snap-name {image-name}
### or
rbd snap rollback {pool-name}/{image-name}@{snap-name}

例如,

1
2
3
rbd -p rbd snap rollback --snap snapname foo
### or
rbd snap rollback rbd/foo@snapname

Ceph 建议使用 快照克隆 功能而不是 回滚操作来还原数据, 因为 随着镜像大小增加, 回滚操作时间会越来越长. 相比之下, 快照克隆 要快很多.

删除快照

要删除一个快照, 使用 rbd snap rm 命令.

1
2
3
rbd -p {pool-name} snap rm --snap {snap-name} {image-name}
### or
rbd snap rm {pool-name}/{image-name}@{snap-name}

例如,

1
2
3
rbd -p rbd snap rm --snap snapname foo
### or
rbd snap rm rbd/foo@snapname

Ceph OSDs 删除 快照过程是异步的, 删除一个快照并不能立刻释放空间.

删除所有快照

要删除所有快照, 可以使用 rbd snap purge 命令.

1
2
3
rbd -p {pool-name} snap purge {image-name}
### or
rbd snap purge {pool-name}/{image-name}

例如,

1
2
3
rbd -p rbd snap purge foo
### or
rbd snap purge rbd/foo

快照层次化

Ceph 支持对 rbd 快照创建很多 写时复制(Copy-on-write, COW) 克隆. 快照层次化能够使 Ceph 块设备客户端非常快速地建立镜像. 比如你可以创建一个块设备, 让一个 Linux VM 对其读写; 然后对镜像建立快照, 保护这一快照, 这样你就可以创建任意多的 COW 克隆体.


图中的 parent 表示 Ceph 块设备(parent), 相应的child) 则表示克隆镜像. 这两个术语非常重要.

每一个克隆镜像(child) 存着一份对 parent 快照的引用, 这使得克隆 image 能够打开 parent 快照读取内容.一个 COW 的克隆体就像其他正常的块设备 image 一样. 你可以读取,写入数据, 或者调整大小, 没有什么特别的限制. 然而, COW 克隆 镜像指向 快照, 因此必须要在克隆快照前保护快照. 整个流程如下:

保护快照

克隆体能够访问 父母快照. 如果 parent 快照被删除了, 所有克隆体都会损坏. 因此为了防止数据丢失, 需要保护要克隆的快照.

1
2
3
rbd -p {pool-name} snap protect --image {image-name} --snap {snap-name}
### or
rbd snap protect {pool-name}/{image-name}@{snap-name}

例如,

1
2
3
rbd -p rbd snap protect --image test-image --snap test-image.snap
### or
rbd snap protect rbd/test-image@test-image.snap

克隆快照

这之后就可以对快照进行克隆了.

1
2
3
rbd clone -p {pool-name} --image {image-name} --snap {snap-name} --dest-pool {dest-pool-name} --dest {child-image}
### or
rbd clone {pool-name}/{parent-image}@{snap-name} {dest-pool-name}/{child-image-name}

例如,

1
2
3
rbd -p rbd --image test-image --snap test-image.snap --dest-pool dest-rbd --dest dest-test-image
### or
rbd clone rbd/test-image@test-image.snap dest-rbd/dest-test-image

列出快照的克隆体

1
2
3
rbd -p {pool-name} children --image {image-name} --snap {snap-name}
### or
rbd children {pool-name}/{image-name}${snapshot-name}

例如,

1
2
3
rbd -p rbd children --image  test-image --snap test-image.snap
### or
rbd children rbd/test-image@test-image.snap

平坦化克隆镜像

克隆的镜像保持着对 parent 快照的引用, 移除 child 克隆体对 parent 快照的引用实际上就是通过从快照拷贝信息到克隆体来将这个镜像 平坦化(flattening).

1
2
3
rbd -p {pool-name} flatten --image {image-name}
### or
rbd flatten {pool-name}/{image-name}

例如,

1
2
3
rbd -p dest-rbd --image dest-test-image
### or
rbd flatten dest-rbd/dest-test-image

一个 flattened 的 image 占据的存储空间要比 layered 克隆体大.

取消对快照的保护

在删除一个受保护的快照时, 需要先取消对快照的保护. 除此之外, 你还需要将与之有引用关系的克隆体平坦化(flatten), 再删除快照.

1
2
3
rbd -p {pool-name} snap unprotect --image {image-name} --snap {snapshot-name}
### or
rbd snap unprotect {pool-name}/{image-name}@{snapshot-name}

基于快照的增量备份

在了解了 Ceph RBD 的快照功能后, 我们就可以来看看如何通过快照来对 RBD image 进行备份. 过程也非常简单.

快照的创建和导出

  1. 我们先建一个测试 pool 和 image:

    1
    2
    [ceph@ceph0 ~]$ rbd mkpool test_pool
    [ceph@ceph0 ~]$ rbd create test_pool/test_image --size 200 --image-format 2 --order 24
  2. 在时间节点 t1t2 分别创建快照:

    1
    2
    3
    [ceph@ceph0 ~]$ rbd snap create test_pool/test_image@test.snap.t1
    [ceph@ceph0 ~]$ do_some_work
    [ceph@ceph0 ~]$ rbd snap create test_pool/test_iamge@test.snap.t2
  3. 导出 v1v2 的差异数据

    1
    2
    3
    4
    5
    6
    7
    ### rbd export-diff --from-snap snap1 {pool_name}/{image_name}@{snap-name} exported_file_name
    ### diff from t1 to t2
    rbd export-diff --from-snap test.snap.t1 test_pool/test_image@test.snap.t2 test_pool_test_image_snap.t1_to_snap.t2.diff
    ### diff from initial to t1
    rbd export-diff test_pool/test_image@test_snap.t1 test_snap_t1
    ### diff from initial to t2
    rbd export-diff test_pool/test_image@test_snap.t2 test_snap_t2

导出之后, 就可以将 diff 文件传送 到备份服务器上保存起来.

你可以查看有哪些内容被修改了:

1
rbd diff --from-snap test.snap.t1 pool/image@test.snap.t2 --format plain

快照的导入与数据恢复

为了简单, 我们在 test-pool 下新建一个 image, 再导入 diff 文件:

1
2
3
4
5
6
7
[ceph@ceph0 ~]$ rbd create test-pool/backup_image --size 1 --image-format 2 --order 24
### import diff from initial to t1
[ceph@ceph0 ~]$ rbd import-diff test_snap_t1 test_pool/backup_image
### import diff from t1 to t2
[ceph@ceph0 ~]$ rbd import-diff test_pool_test_image_snap.t1_to_snap.t2.diff test_pool/backup_image
### or import diff from initial to t2
[ceph@ceph0 ~]$ rbd import-diff test_snap_t2 test_pool/backup_image

这样就可以恢复数据了.

要是运行着两个 Ceph 集群来预防灾难并在灾难后做数据恢复, 可以在线的方式对其做快照备份:

1
rbd export-diff --from-snap test.snap.t1 test_pool/test_image@snap.t2 - | ssh user@second_cluster rbd import-diff - test_pool/test_image

备份整个 RBD Pool

上面是对一个 image 的备份和还原, 在实际中, 会有非常多的 image 需要备份. 有了上面的例子, 我们可以很容易地写一个脚本来定时备份 RBD pool. 下面是一个简单的 shell 脚本.

1
...