Ceph RBD Volume の Backup や Disaster Recovery に最適な、RBD Incremental Snapshot を検証しました

#はじめに
Ceph は consistency を重視した設計なっていることが大きな特徴となっております。このため、サーバやネットワークが少々故障したとしてもデータの欠損が発生することはほとんどありません。
しかしながら、ひとたびオペレータの操作ミスやデータセンタ全体に及ぶ障害等が発生すると、稼働中のクラスタからデータを回収することが困難になる可能性があります。

Ceph では RadosGW Multi-Site や RBD mirror 等、複数のロケーションを跨いでデータを同期する方法も提供されておりますが、それなりのコストも必要になるため要件に合わないこともあるかと思います。
もっとお手軽に RBD Volume を Backup する方法としては、 RBD Incremental Snapshot を使う方法があります。本ドキュメントでは、RBD Incremental Snapshot を用いた Volume の Backup / Restore について検証した結果をまとめさせていただきます。

#まとめ
- rbd の snapshot に対して rbd export を実施することで、Volume の生データ(Sparse File) を出力することができます
- rbd export-diff コマンドを用いることで、snapshot 間の差分を取得できます
  - Backup サイトに対して rbd import-diff コマンドを実施することで、差分を Backup サイト側 の Ceph cluster に同期させることができます
  - rbd image feature に fast-diff が設定されていないと、diff 計算コストが大きくなるので注意が必要です(最近の OpenStack cinder/ceph であれば、default で有効化されているはずです)
- Backup サイトについては、Ceph cluster だけではなく NFS server や local disk を使用することもできます
  - ただし、restore をする際には snapshot の数だけ import-diff を実施することになるため、snapshot 数が多いと restore に時間を要する可能性があります
  - local disk 等にデータを保存する際に、snapshot ファイルの世代が多くなるのを避けたい場合は、rbd merge-diff コマンドを用いることで snapshot ファイルを併合することができます
    - merge-diff コマンドを使用する場合は、Backup サイト側でも ceph-mon を稼働させる必要があります(stand-alone 設定で大丈夫です)

#検証結果
検証結果を、次にまとめます。

##Snapshot の取得
Volume に対して、初期状態の snapshot を取得します。

```bash
sudo rbd snap create cinder-ceph/volume-a@init

node1:~$ sudo rbd snap ls cinder-ceph/volume-a
SNAPID NAME     SIZE
    12 init 10240 MB
```

次に、volume に何かを書き込んだ後に、snapshot 'snap01' を取得します。

```bash
sudo rbd snap create cinder-ceph/volume-a@snap01

node1:~$ sudo rbd snap ls cinder-ceph/volume-a
SNAPID NAME       SIZE
    12 init   10240 MB
    13 snap01 10240 MB
```

##Snapshot の export
取得した Snapshot について、export (file への書き出し) を実施します。

```bash
# 初期状態の snapshot を export します
sudo rbd export cinder-ceph/volume-a@init /tmp/volume-a_init.img

# snapshot 'snap01' を export-diff します
sudo rbd export-diff --from-snap init cinder-ceph/volume-a@snap01 /tmp/volume-a_snap01.img

# それぞれの exported file は、次のサイズになります。
node1:~$ ls -lhs /tmp/volume-a_init.img
1.1G -rw-r--r-- 1 root root 10G Dec  3 00:00 /tmp/volume-a_init.img
node1:~$ ls -lhs /tmp/volume-a_snap01.img
4.0K -rw-r--r-- 1 root root 2.1K Dec  3 00:01 /tmp/volume-a_snap01.img
```

##Snapshot の Backup サイトへの import
作成した exported file について、Backup サイトに import を実施します。

```bash
# 初回に export を実施した volume (full backup) を import します
sudo rbd import /tmp/volume-a_init.img cinder-ceph/import-test01

# 現在の volume 情報を snapshot として取得します
# export-diff ファイルに記載されている snapshot history 情報と
# 辻褄を合わせるために必要となります
sudo rbd snap create cinder-ceph/import-test01@init

# incremental snapshot を destination に適用します
sudo rbd import-diff /tmp/volume-a_snap01.img cinder-ceph/import-test01

# destination の snapshot 内容は、次のようになります
node1:~$ sudo rbd snap ls cinder-ceph/import-test01
SNAPID NAME       SIZE
    14 init   10240 MB
    15 snap01 10240 MB
```
#Backup Volumeのマウントテスト
Backup を実施した volume を用いて実際にマウントできることを確認します。

```bash
# マウントができることを確認します
sudo rbd export cinder-ceph/import-test02@snap01 /tmp/check.img
fdisk -l /tmp/check.img
# Units: sectors of 1 * 512 = 512 bytes
# no partitions
sudo losetup /dev/loop0 /tmp/check.img
sudo mount /dev/loop0 /mnt/check

# snap01 の volume として保存されていたデータが存在していることを確認します
node1:~$ cat /mnt/check/hello.txt
Hello World!

# テストに使用した volume のアンマウント及び loopback device の登録解除を実施します
sudo umount /mnt/check
sudo losetup --detach /dev/loop0
```
#Snapshot の併合テスト
最後に、export-diff にて生成された incremental backup ファイルを merge-diff コマンドにて併合できることを確認します。

```bash
# snap01 の volume に対してデータの変更を加えた上で、snapshot (snap02) を取得します
sudo rbd snap create cinder-ceph/volume-a@snap02

# snap02 の差分ファイルを作成します
sudo rbd export-diff --from-snap snap01 cinder-ceph/volume-a@snap02 /tmp/volume-a_snap02.img

# snap01 の後続に snap02 を併合し、volume-a_snap01_snap02_merged.img を作成します
sudo rbd merge-diff /tmp/volume-a_snap01.img /tmp/volume-a_snap02.img /tmp/volume-a_snap01_snap02_merged.img

# snap01 までの差分を適用した volume (cinder-ceph/import-test03) を作成します
sudo rbd import /tmp/volume-a_init.img cinder-ceph/import-test03
sudo rbd snap create cinder-ceph/import-test03@init

# 先ほどの併合済差分ファイルについて、import-diff コマンドを用いて import-test03 へ差分を適用します
sudo rbd import-diff /tmp/volume-a_snap01_snap02_merged.img cinder-ceph/import-test03
```

先ほどの Backup Volume マウントテストと同様の作業を実施すると、問題なく snap01 + snap02 の差分が適用されていることが確認できるはずです。

#参考情報
- [Incremental Snapshots with RBD - THE CEPH BLOG](https://ceph.io/geen-categorie/incremental-snapshots-with-rbd/)
- [Recovering data from a RBD image - PLANET CEPH](https://ceph.io/planet/recovering-data-from-a-rbd-image/)
- [RBD – MANAGE RADOS BLOCK DEVICE (RBD) IMAGES](https://docs.ceph.com/docs/nautilus/man/8/rbd/)