RBD Mirror を用いた Disaster Recovery の検証メモ

#はじめに
Ceph RBD では異なるサイトへのリアルタイム同期機能として RBD Mirroring が提供されており、Disaster Recovery 等の用途で使用することができます。
本ドキュメントでは、RBD Mirroring の動作・使い方について検証した結果をまとめます。

#RBD Mirroring を使うと何が出来るの?
RBD Mirroing を使うと、次のようなことが出来ます。

- One-Way Replication
  - Primary Cluster から Secondary Cluster に対して、データの同期を実施します
- Two-Way Replication
  - 複数の Cluster について、どちら側からでもデータの同期が出来るようになります
- Pool 全体、または Pool 内の任意の Image を指定して Mirroring を実施します
  - Promotion / Demotion についても、Pool 単位だけでなく Image 単位で個別に(少しずつ)実施することができます

RBD Mrroring には、主に次のような制約があります。

- Ceph Jewel (version 12.x) 以降で対応しています
- RBD Mirror を実現するためのプロセス (rbd-mirror) が稼働しているホストから、Primary / Secondary 両方のクラスタ(ceph-mon, ceph-osd)に対してIP通信が出来る必要があります
- 各クラスタ間のネットワークについて、データの同期に十分耐えられるだけのネットワーク帯域が必要です
- Failover を実施しただけだと RBD client は Failover 前の cluster を参照しているため RBD への接続が切れた状況になります。別途 client の設定変更や再起動(VMのOS再起動等)が必要になります

#検証環境の構成
今回、次のような RBD Mirroring 検証環境を構築しました。

1. Primary cluster (cluster1)
  - Ceph version 14.2.4-1 (nautilus)
  - 3台構成、それぞれのノードにて ceph-osd および ceph-mon を1つずつ稼働させます
    - ceph-test11
    - ceph-test12
    - ceph-test13
2. Secondary cluster (cluster2)
  - Ceph version 14.2.4-1 (nautilus)
  - 3台構成、それぞれのノードにて ceph-osd および ceph-mon を1つずつ稼働させます
    - ceph-test21
    - ceph-test22
    - ceph-test23

#検証手順
RBD Mirroring の検証結果を次にまとめます。
まず、あらかじめ Ceph クラスタ を2つ構築した上で、下記手順を順番に実施していきます。

##rbd-mirror パッケージのインストール
rbd-mirror パッケージをインストールします。

```bash
# 各クラスタ毎に、rbd-mirror daemon を起動させたいホストを1つ選んだ上で rbd-mirror をインストールします
# 今回は ceph-test11, ceph-test21 にパッケージをインストールします
sudo apt install rbd-mirror
```

##Ceph Credential File のコピー
それぞれの Ceph cluster について、自身の Credential ファイルを相手側の rbd-mirror ホストにコピーします。

```bash
# at ceph-test11
mkdir cluster2-conf
cd cluster2-conf
scp ceph-test21:~/ceph.conf ./
scp ceph-test21:~/ceph.client.admin.keyring ./
sudo chown ceph:ceph *
sudo cp -p ceph.conf /etc/ceph/remote.conf
sudo cp -p ceph.client.admin.keyring /etc/ceph/remote.client.admin.keyring

# at ceph-test21
mkdir cluster1-conf
cd cluster1-conf
scp ceph-test11:~/ceph.conf ./
scp ceph-test11:~/ceph.client.admin.keyring ./
sudo chown ceph:ceph *

sudo chown ceph:ceph /etc/ceph/remote.client.admin.keyring
sudo cp -p ceph.conf /etc/ceph/remote.conf
sudo cp -p ceph.client.admin.keyring /etc/ceph/remote.client.admin.keyring
```

##RBD Mirroing に使用するプールの作成
RBD Mirroring のテストに使用する pool を、予め それぞれのクラスタに作成しておきます。

```bash
# at ceph-test11
sudo ceph osd pool create rbd 64
sudo rbd pool init rbd

# at ceph-test21
sudo ceph osd pool create rbd 64
sudo rbd pool init rbd
```

##RBD Mirroring の有効化
RBD Mirroring を有効化します。
今回のテストでは、Pool 全体を Mirroring の対象とするため、rbd mirror のオプションに 'pool' を指定します。

また、今回のテストでは Two-way Replication にするため、Secondary cluster (cluster2) についても、rbd mirror コマンドを設定します。

```bash
# at ceph-test11
sudo rbd --cluster ceph mirror pool enable rbd pool
sudo rbd --cluster remote mirror pool enable rbd pool
```

次に、Mirroring 用の peer を作成します。両方の Cluster にて、それぞれ次のコマンドを実行します。

```bash
# at ceph-test11 and ceph-test21
sudo rbd --cluster ceph mirror pool peer add rbd client.admin@remote
```

テスト用の RBD Image を作成します。今回のテストでは、cluster1 側(ceph-test11) にて RBD Image (mirror_test) を作成し、feature に 'journaling' を追加します。

```bash
# at ceph-test11
sudo rbd --cluster ceph -p rbd create mirror_test --size=1G
sudo rbd --cluster ceph -p rbd feature enable mirror_test journaling
```

最後に、rbd-mirror daemon を起動します。今回は Two-way Replication を実施するので、cluster1 / cluster2 両方で daemon を起動しておきます。

```bash
# at ceph-test11
sudo systemctl enable ceph-rbd-mirror@admin
sudo chown ceph /etc/ceph/ceph.client.admin.keyring
sudo systemctl start ceph-rbd-mirror@admin

# at ceph-test21
sudo systemctl enable ceph-rbd-mirror@admin
sudo chown ceph /etc/ceph/ceph.client.admin.keyring
sudo systemctl start ceph-rbd-mirror@admin
```

rbd-mirror daemon 起動後に、mirroring の稼働状況を確認します。
mirroing が正常に実施されている場合は、state が 'up+ replaying' となるはずです。

```bash
ceph-test11:~$ sudo rbd --cluster remote mirror pool status rbd --verbose
health: OK
images: 1 total
    1 replaying

mirror_test:
  global_id:   edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
  state:       up+replaying
  description: replaying, master_position=[object_number=17, tag_tid=2, entry_tid=26589], mirror_position=[object_number=13, tag_tid=2, entry_tid=19097], entries_behind_master=7492
  service:     admin on ceph-test21
  last_update: 2019-12-01 00:01:02

# 正常に mirroring が実施されている場合は、cluster2 側の stateが 'up+replaying' となります
ceph-test21:~$ sudo rbd mirror image status rbd/mirror_test
mirror_test:
  global_id:   edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
  state:       up+replaying
  description: replaying, master_position=[object_number=3, tag_tid=1, entry_tid=3], mirror_position=[object_number=3, tag_tid=1, entry_tid=3], entries_behind_master=0
  service:     admin on tasogabe-ceph-test21
  last_update: 2019-12-09 00:00:00

# 正常に mirroring が実施されている場合は、cluster1 側の stateが 'up+stopped' となります
ceph-test11:~$ sudo rbd mirror image status rbd/mirror_test
mirror_test:
  global_id:   edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
  state:       up+stopped
  description: local image is primary
  service:     admin on tasogabe-ceph-test11
  last_update: 2019-12-09 00:00:00

```

rbd info コマンドを用いて Image の情報を出力すると、次のようになります。
mirroring 関連の情報が追加されていることが分かります。

```bash
sudo rbd info mirror_test
rbd image 'mirror_test':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: 10b58fdf16a4
        block_name_prefix: rbd_data.10b58fdf16a4
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten, journaling
        op_features:
        flags:
        create_timestamp: Mon Dec  1 01:02:04 2019
        access_timestamp: Mon Dec  1 01:02:04 2019
        modify_timestamp: Mon Dec  1 01:02:04 2019
        journal: 10b58fdf16a4
        mirroring state: enabled
        mirroring global id: edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
        mirroring primary: true
```

実際に RBD Image に対して write I/O を発生させて、各クラスタの稼働状況を確認します。

```bash
# Nautilus の librbd を用いて RBD mount をしたいので、rbd-nbd を用いて
# マウントを実施します
# (新しい kernel version を使っている場合は、rbd map を使用しても問題ありません)
sudo apt install rbd-nbd
sudo rbd-nbd map rbd/mirror_test

sudo mkfs.ext4 /dev/nbd0
sudo mkdir /mnt/rbd
sudo mount /dev/nbd0 /mnt/rbd

# 実際に write I/O の負荷を掛けてみて、各クラスタの挙動を確認します
sudo fio --filename=/mnt/rbd/test.fio --direct=1 --rw=randwrite --size=800M --randrepeat=0 --ioengine=libaio --bs=4k \
  --iodepth=8 --time_based=1 --runtime=60 --name=fio_direct_randwrite_4k
```

##Demotion / Promotion テスト
Primary Cluster の故障切り替えやメンテナンスをしたい場合は、次の手順を順番に実施することで Primary Role を Peer Cluster に引き継がせることができます。

1. Primary Cluster に接続している RBD Client からのアクセスを停止する(VMを停止する、または storage 接続を解除する等)
2. Primary Cluster の Demotion を実施する
   1. Primary Cluster を操作できない状況となった場合は、Promotion の際に '--force' オプションにて強制切り替えを実施
3. Secondary Cluster の Promotion を実施する
4. RBD Client からのアクセスを Promotion 後のクラスタにて再開する

```bash
# 1. RBD client からのアクセスを停止します
# at ceph-test11
sudo umount /mnt/rbd
sudo rbd-nbd unmap rbd/mirror_test

# 2. Demotion を実施します
# at ceph-test11
sudo rbd --cluster ceph mirror pool demote rbd

# 3. Promotion を実施します
# at ceph-test21
sudo rbd --cluster ceph mirror pool promote rbd

# Image(mirror_test) Primary Cluster が cluster2 側に移ったことが分かります
ceph-test21:~$ sudo rbd info mirror_test
rbd image 'mirror_test':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: 106d36e4aff4
        block_name_prefix: rbd_data.106d36e4aff4
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten, journaling
        op_features:
        flags:
        create_timestamp: Mon Dec  1 01:01:01 2019
        access_timestamp: Mon Dec  1 02:01:16 2019
        modify_timestamp: Mon Dec  1 03:01:43 2019
        journal: 106d36e4aff4
        mirroring state: enabled
        mirroring global id: edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
        mirroring primary: true

# cluster1 側については、mirroring primary: false となっているため、secondary であることが分かります
ceph-test21:~$ sudo rbd --cluster remote info mirror_test
rbd image 'mirror_test':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: 10b58fdf16a4
        block_name_prefix: rbd_data.10b58fdf16a4
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten, journaling
        op_features:
        flags:
        create_timestamp: Mon Dec  1 01:58:04 2019
        access_timestamp: Mon Dec  1 02:33:15 2019
        modify_timestamp: Mon Dec  1 03:16:43 2019
        journal: 10b58fdf16a4
        mirroring state: enabled
        mirroring global id: edf4d4bd-bf77-4d14-a5a3-e0cd4ee75bff
        mirroring primary: false
```

#検証結果
今回のテストでは、RBD Mirroring を用いた Secondary Cluster へのデータ同期
及び Failover について、問題なく動作することが確認できました。

注意点としては、RBD client 側については別途自分で Failover 処理をする必要があります。
また、ひとたび Failover を実施した後は、Failback をするために RBD client を一旦停止させる必要があるため、再度 Downtime が発生します。このため、プロダクション環境等では Failover 実施後に Failback をさせるのは難しいかもしれません。

###OpenStack Cinder + RBD Mirroring 組み合わせについての検討
OpenStack (Cinder) では Replication 機能が実装されており、Backend が Ceph の場合は RBD Mirroring を使用できます。

次のような条件が揃えば、Disaster Recovery 用途として RBD Mirroring が使えそうです。

- 2つのサイトに跨って OpenStack の Controller 及び nova-compute を配置して冗長化されていること
- Secondary サイトで Failover に必要な数だけ nova-compute が稼働出来ること
  - nova-compute の使用するネットワークが2つのサイトに跨って同一である必要があります
  - または、Failover実施後に、ネットワーク機器及び neutron network の設定を変更して Floating-ip の経路を Secondary サイトに変更する必要があります
    - neutron BGP Dynamic Routing 又は EVPN を使うことで設定変更を不要にすることも可能です
- サイトダウンの検知に要する時間、及び Cinder の Failover 作業と当該インスタンスの Evacuation 作業で発生するメンテナンス時間を許容出来るサービスであること

以上のように、OpenStack 全体をサイトダウンに備えて Disaster Recovery 対応にすることは技術的に可能であるものの、それなりのコストを掛けた上でワークフローの作り込みをする必要があります。

AWS の [Well-architected フレームワーク](https://d1.awsstatic.com/whitepapers/ja_JP/architecture/AWS-Reliability-Pillar.pdf?sc_icampaign=aware_well_architected_jp_reliability_pillar&sc_ichannel=ha&sc_icontent=awssm-3366&sc_iplace=content&trk=awssm-3366_aware_well_architected_jp_reliability_pillar) 等でもシステマチックに言及されておりますが、マルチリージョンでの冗長化が必要な要件がある場合は、何を守りたいのかを明確にした上で、全体のフレームワークの中で要件を実現する方が合理的です。
インフラ側だけで (無理に) Disaster Recovery を実現しようとすると、大きなコスト増となったり、肝心な時にうまく切り替わらないといった状況が発生しやすいので注意が必要です。

#参考情報
- https://docs.ceph.com/docs/nautilus/rbd/rbd-mirroring/
  - RBD MIRRORING についての、本家の解説ドキュメントがあります
-  https://docs.openstack.org/cinder/train/contributor/replication.html
  - Cinder を用いて Replication を使用する方法について、本家の解説ドキュメントがあります
- https://gorka.eguileor.com/ceph-replication/
  - Cinder Replication の解説記事があります