Ceph MultiSiteをやってみる

f:id:ututaq:20200630133258p:plain

この記事はRookと仲間たち、クラウドネイティブなストレージの Advent Calendar 2020 4日目の記事です。

お久しぶりです。レッドハットでストレージを中心にクラウドインフラを生業にしている宇都宮です。
去年のアドベントカレンダーぶりかもしれませんね、今年もがんばってやってますので、ストレージ愛が五臓六腑に沁み渡るみなさんのブログを楽しんで下さいね!今日はRookのブログから閑話休題で、CephのMultiSiteについて書いていきます。

qiita.com

あとRookのTwitterアカウント、@japan_rookのフォローお願いしまーす!

サイト障害に耐えるためのサイト間レプリケーション

一般的にオブジェクトストレージサービスはサイト障害に耐えられるよう、バックグラウンドでサイト間レプリケーション等が行われていることが多いと思います。
正直オブジェクトストレージはブロックやファイルと違って、高いパフォーマンスや強い整合性が期待されない (誤解を恐れず言うと、期待してはいけない) ストレージサービスなので、同期のオーバーヘッドを気にすることなくレプリケーションできます。

KubernetesやOpenStack、Proxmox等のプラットフォームのバックエンドストレージとして使われることの多いCephですが、単体ではオブジェクトストレージとして使われているケースが多いです。
Cephでサイト障害に耐えるためのパターンは2つに大別されます。

  • Stretched Cluster
    複数サイトで動くMON/OSD/RGW等のコンポーネントで、1つのCephクラスターを構成する

  • クラスター間レプリケーション/ミラー
    各サイトで動く複数のCephクラスター間で、データのレプリケーション/ミラーを構成する

一つ目のStretched Clusterは、運用対象が1つのクラスターなのでシンプルです。
writeの整合性はRADOSが担保するので各サイトActiveで使えるし、RPOも障害直前、RTOも限りなく0でしょう。
ただし、サイト間ネットワークの安定性が命になります。例えばサイト間ネットワークが途切れると、サイトを跨いでreplicated poolを組んでいると全サイトでOSDへの同期が完了できないためwriteが遅くなったり、MON同士の通信ができなくなってクラスター全体の構成が不安定になったりします。
どのサイトも普通に生きているのに、サービスに悪影響を及ぼすリスクがあるというわけです。
うまくCRUSH treeの設計を行うと回避できるかもしれませんが、そうすると構成は複雑になるので運用側が大変になります。
そういった観点からStretched Clusterは実践的には難しく、比較的近距離のサイト間でなら成立する可能性はある、というレベルと考えます。

HW/SDSを問わず、ストレージでサイト障害に対応する方法の多くは、二点目のようなクラスター間でのレプリケーションでしょう。
もちろんこれはこれで考慮点はあります。RPO、RTOはStretched Clusterより長くなるし、Active-ActiveとするかActive-Passiveとするかで切り替え/切り戻しの際にデータを整合性をどう担保するか、などでしょうか。
とは言え、サイト間ネットワークが途切れても、各サイトのクラスターは普通に動くのでサービスへの影響がStretched Clusterよりは俄然低いのは大きなメリットです。

もちろんCephでもクラスター間レプリケーションは可能です。ブロックストレージの場合は "RBD Mirror" と呼ばれる機能で、オブジェクトストレージの場合は、今回のトピックの "MultiSite" と呼ばれる機能です。

Ceph NanoでMultiSiteをやってみよう

Ceph MultiSiteはRGW(Rados Gateway)の機能です。複数のクラスターにあるRGW間でBucketなどを同期することができます。
どうなるものかお見せしたいのですが、Cephのクラスターを立てるのは割と大変です。ですので、今回はCeph Nanoを使ってお見せします。
Ceph Nanoについては去年書いたブログを見て下さい。

Ceph Nanoで2つのCephクラスターを立てて、その間でレプリケーションをするようにしてみます。
同じマシンの上にクラスターを作ってもよいのですが、下図のようなイメージで2つの違うマシンの上でそれぞれ作ってみます。

f:id:ututaq:20201203170003p:plain
[root@master ~]$ ./cn cluster start cn1 -f huge
2020/12/03 12:41:16 Running cluster cn1 | image ceph/daemon | flavor huge {4GB Memory, 2 CPU} ...

Endpoint: http://10.208.81.35:8000
Dashboard: http://10.208.81.35:5000
Access key: DFE06L7ZSW0CBRHYTIMJ
Secret key: BNAi3vAhzyGb0rmnNrJkItQWhqADuydoUELLp7jt
Working directory: /usr/share/ceph-nano
[root@secondary ~]$ ./cn cluster start cn2 -f huge
2020/12/03 12:41:36 Running cluster cn2 | image ceph/daemon | flavor huge {4GB Memory, 2 CPU} ...

Endpoint: http://10.208.81.18:8000
Dashboard: http://10.208.81.18:5000
Access key: JRLYVCV1XMS0YDXK7AXH
Secret key: ta0ikPv6CKj5qOfAyw23MH1aFpsn8AVZRGMQkjNQ
Working directory: /usr/share/ceph-nano

Endpointとして8000番ポートが指定されています。
またAccess keyとSecret keyが表示されています。Ceph Nanoは立ち上げると自動的に"nano"というRGWユーザーを作りますが、表示されているのはこのユーザーのものです。 今回は"nano"ユーザーは使わないので、このkeyは無視してもらって大丈夫です。

とりあえずクラスターが2つできました。

Masterでzone, zonegroup, realmを設定

MultiSiteをする際にはzone, zonegroup, realmを設定する必要があります。
zoneはレプリケーションをする最小の単位で、クラスターに存在するRGWはどれか1つのzoneに所属します。zoneは1つのクラスターで作成され、zone内のRGWが管理するbucketはレプリケーションをする対象となります。
zonegroupは名前の通り複数のzoneを束ねたもので、zoneは1つのzonegroupに所属します。zonegroupはクラスターをまたいで組むことができ、クラスター間でzoneの情報を共有します。zonegroup内に存在するzoneの間で、bucketはレプリケーションされます。
realmはzonegroupを束ねるユニークなnamespaceです。

クラスターが複数ありそれぞれレプリケーションする先が異なったり、bucketごとにActive-ActiveとActive-Passiveを使い分けたり、比較的複雑なレプリケーション構成をとりたい場合は、zonegroupやrealmを複数作って制御することができます。 今回は下図のように最もシンプルなパターンで、2 zones -> 1 zonegroup -> 1 realm の関係でいきます。

f:id:ututaq:20201203170031p:plain

それではMaster側のクラスターでzone, zonegroup, realmを設定します。包含関係から作成するのはrealmからです。 Ceph Nanoクラスターに入るには、cn cluster enterで入ることができます。

[root@master cephnano]# ./cn cluster enter cn1
[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin realm create --rgw-realm=rlm --default
{
    "id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "name": "rlm",
    "current_period": "332fc423-02fe-427e-85b9-97ffe24f9197",
    "epoch": 1
}
[root@ceph-nano-cn1-faa32aebf00b /]# 
[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin zonegroup create --rgw-zonegroup=zg --endpoints=http://10.208.81.35:8000 --rgw-realm=rlm --master --default
{
    "id": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
    "name": "zg",
    "api_name": "zg",
    "is_master": "true",
    "endpoints": [
        "http://10.208.81.35:8000"
    ],
    "hostnames": [],
    "hostnames_s3website": [],
    "master_zone": "",
    "zones": [],
    "placement_targets": [],
    "default_placement": "",
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "sync_policy": {
        "groups": []
    }
}
[root@ceph-nano-cn1-faa32aebf00b /]# 
[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin zone create --rgw-zonegroup=zg --rgw-zone=z1 --endpoints=http://10.208.81.35:8000 --default
{
    "id": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
    "name": "z1",
    "domain_root": "z1.rgw.meta:root",
    "control_pool": "z1.rgw.control",
    "gc_pool": "z1.rgw.log:gc",
    "lc_pool": "z1.rgw.log:lc",
    "log_pool": "z1.rgw.log",
    "intent_log_pool": "z1.rgw.log:intent",
    "usage_log_pool": "z1.rgw.log:usage",
    "roles_pool": "z1.rgw.meta:roles",
    "reshard_pool": "z1.rgw.log:reshard",
    "user_keys_pool": "z1.rgw.meta:users.keys",
    "user_email_pool": "z1.rgw.meta:users.email",
    "user_swift_pool": "z1.rgw.meta:users.swift",
    "user_uid_pool": "z1.rgw.meta:users.uid",
    "otp_pool": "z1.rgw.otp",
    "system_key": {
        "access_key": "",
        "secret_key": ""
    },
    "placement_pools": [
        {
            "key": "default-placement",
            "val": {
                "index_pool": "z1.rgw.buckets.index",
                "storage_classes": {
                    "STANDARD": {
                        "data_pool": "z1.rgw.buckets.data"
                    }
                },
                "data_extra_pool": "z1.rgw.buckets.non-ec",
                "index_type": 0
            }
        }
    ],
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3"
}

はい、できました。zonegroup createzone create--endpointのオプションがありますが、ここにRGWのエンドポイントを指定します。
また、Poolについては特段何も指定していませんが、RGWデフォルトのpoolを使っています。実際に使う場合は新規にpoolを切ると思うので、その場合はradosgw-admin zone placementを使って指定します。

レプリケーション用のsystem userを作成

クラスターをまたいでRGW間でデータやrealm等の構成を共有するために、両者で共通のシステムユーザーが必要となります。
これはMaster側で作って、後でSecondaryからpullすることで共有されます。

このシステムユーザーの名前は何でもよいのですが、作られるAccess keyとSecret keyは重要なのでメモしておきましょう。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin user create --uid="syncuser" --display-name="sync user" --system
{
    "user_id": "syncuser",
    "display_name": "sync user",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "subusers": [],
    "keys": [
        {
            "user": "syncuser",
            "access_key": "57ZYN8XHW9V03VYGEG9P",
            "secret_key": "BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "system": "true",
    "default_placement": "",
    "default_storage_class": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}

このsyncuserがMaster側のzoneを操作できるよう、先程作成したzoneにAccess keyとSecret keyの情報を追加します。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin zone modify --rgw-zone=z1 --access-key=57ZYN8XHW9V03VYGEG9P --secret=BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS
{
    "id": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
    "name": "z1",
    "domain_root": "z1.rgw.meta:root",
    "control_pool": "z1.rgw.control",
    "gc_pool": "z1.rgw.log:gc",
    "lc_pool": "z1.rgw.log:lc",
    "log_pool": "z1.rgw.log",
    "intent_log_pool": "z1.rgw.log:intent",
    "usage_log_pool": "z1.rgw.log:usage",
    "roles_pool": "z1.rgw.meta:roles",
    "reshard_pool": "z1.rgw.log:reshard",
    "user_keys_pool": "z1.rgw.meta:users.keys",
    "user_email_pool": "z1.rgw.meta:users.email",
    "user_swift_pool": "z1.rgw.meta:users.swift",
    "user_uid_pool": "z1.rgw.meta:users.uid",
    "otp_pool": "z1.rgw.otp",
    "system_key": {
        "access_key": "57ZYN8XHW9V03VYGEG9P",
        "secret_key": "BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS"
    },
    "placement_pools": [
        {
            "key": "default-placement",
            "val": {
                "index_pool": "z1.rgw.buckets.index",
                "storage_classes": {
                    "STANDARD": {
                        "data_pool": "z1.rgw.buckets.data"
                    }
                },
                "data_extra_pool": "z1.rgw.buckets.non-ec",
                "index_type": 0
            }
        }
    ],
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3"
}

ここまでできたら概ねMaster側の作業は終わりです。最後にzoneやzonegroup等の構成変更をcommitするべくradosgw-admin period update --commitをします。periodはrealmが持つ構成変更ログのようなものです。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin period update --commit
{
    "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
    "epoch": 1,
    "predecessor_uuid": "332fc423-02fe-427e-85b9-97ffe24f9197",
    "sync_status": [],
    "period_map": {
        "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
        "zonegroups": [
            {
                "id": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
                "name": "zg",
                "api_name": "zg",
                "is_master": "true",
                "endpoints": [
                    "http://10.208.81.35:8000"
                ],
                "hostnames": [],
                "hostnames_s3website": [],
                "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "zones": [
                    {
                        "id": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                        "name": "z1",
                        "endpoints": [
                            "http://10.208.81.35:8000"
                        ],
                        "log_meta": "false",
                        "log_data": "false",
                        "bucket_index_max_shards": 11,
                        "read_only": "false",
                        "tier_type": "",
                        "sync_from_all": "true",
                        "sync_from": [],
                        "redirect_zone": ""
                    }
                ],
                "placement_targets": [
                    {
                        "name": "default-placement",
                        "tags": [],
                        "storage_classes": [
                            "STANDARD"
                        ]
                    }
                ],
                "default_placement": "default-placement",
                "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
                "sync_policy": {
                    "groups": []
                }
            }
        ],
        "short_zone_ids": [
            {
                "key": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "val": 2054423084
            }
        ]
    },
    "master_zonegroup": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
    "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
    "period_config": {
        "bucket_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        },
        "user_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        }
    },
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "realm_name": "rlm",
    "realm_epoch": 2
}

RGWをzone:z1に所属させることをceph.confの[client.rgw]セクションに追記して、RGWを再起動します。Ceph Nanoの場合はcn cluster restartでクラスター再起動をします。

[root@ceph-nano-cn1-faa32aebf00b /]# vi /etc/ceph/ceph.conf 
[root@ceph-nano-cn1-faa32aebf00b /]# grep -A10 "client.rgw.ceph-nano-cn1" /etc/ceph/ceph.conf
[client.rgw.ceph-nano-cn1-faa32aebf00b]
rgw dns name = ceph-nano-cn1-faa32aebf00b
rgw enable usage log = true
rgw usage log tick interval = 1
rgw usage log flush threshold = 1
rgw usage max shards = 32
rgw usage max user shards = 1
log file = /var/log/ceph/client.rgw.ceph-nano-cn1-faa32aebf00b.log
rgw frontends = beast  endpoint=0.0.0.0:8000
rgw zone = z1   ←追記
[root@ceph-nano-cn1-faa32aebf00b /]# exit
exit
[root@master cephnano]# ./cn cluster restart cn1
2020/12/03 14:40:37 Restarting cluster cn1...

Secondaryでzone, zonegroup, realmの設定

次にSecondary側でzone, zonegroup, realmを設定します。realmとzonegroupはMasterからpullするだけなので簡単です。pullする時には、Master RGWのendpointとsyncuserのAccess keyとSecret keyを使います。

[root@secondary cephnano]# ./cn cluster enter cn2 
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin realm pull --url=http://10.208.81.35:8000 --access-key=57ZYN8XHW9V03VYGEG9P --secret=BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS
{
    "id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "name": "rlm",
    "current_period": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
    "epoch": 2
}
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin period pull --url=http://10.208.81.35:8000 --access-key=57ZYN8XHW9V03VYGEG9P --secret=BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS
{
    "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
    "epoch": 2,
    "predecessor_uuid": "332fc423-02fe-427e-85b9-97ffe24f9197",
    "sync_status": [],
    "period_map": {
        "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
        "zonegroups": [
            {
                "id": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
                "name": "zg",
                "api_name": "zg",
                "is_master": "true",
                "endpoints": [
                    "http://10.208.81.35:8000"
                ],
                "hostnames": [],
                "hostnames_s3website": [],
                "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "zones": [
                    {
                        "id": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                        "name": "z1",
                        "endpoints": [
                            "http://10.208.81.35:8000"
                        ],
                        "log_meta": "false",
                        "log_data": "false",
                        "bucket_index_max_shards": 11,
                        "read_only": "false",
                        "tier_type": "",
                        "sync_from_all": "true",
                        "sync_from": [],
                        "redirect_zone": ""
                    }
                ],
                "placement_targets": [
                    {
                        "name": "default-placement",
                        "tags": [],
                        "storage_classes": [
                            "STANDARD"
                        ]
                    }
                ],
                "default_placement": "default-placement",
                "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
                "sync_policy": {
                    "groups": []
                }
            }
        ],
        "short_zone_ids": [
            {
                "key": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "val": 2054423084
            }
        ]
    },
    "master_zonegroup": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
    "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
    "period_config": {
        "bucket_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        },
        "user_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        }
    },
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "realm_name": "rlm",
    "realm_epoch": 2
}
[root@ceph-nano-cn2-faa32aebf00b /]#
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin realm list
{
    "default_info": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "realms": [
        "rlm"
    ]
}
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin zonegroup list
{
    "default_info": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
    "zonegroups": [
        "zg",
        "default"
    ]
}

radosgw-admin realm listradosgw-admin zonegroup listの結果から、Masterから正しくrealmとzonegroupの情報をpullできていることが分かるはずです。

それではzoneを作成します。Masterと同じように作成しますが、Secondary RGWのエンドポイントを指定し、Access keyとSecret keyはsyncuserのものを指定します。

[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin zone create --rgw-zonegroup=zg --rgw-zone=z2 --endpoints=http://10.208.81.18:8000 --access-key=57ZYN8XHW9V03VYGEG9P --secret=BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS
2020-12-03T05:50:37.846+0000 7fb33d5c81c0  0 failed reading obj info from .rgw.root:zone_info.9b505afe-3229-4b80-8a69-ffbaea14ec25: (2) No such file or directory
2020-12-03T05:50:37.846+0000 7fb33d5c81c0  0 WARNING: could not read zone params for zone id=9b505afe-3229-4b80-8a69-ffbaea14ec25 name=z1
{
    "id": "ac746b19-c165-453e-abed-3e528c13377a",
    "name": "z2",
    "domain_root": "z2.rgw.meta:root",
    "control_pool": "z2.rgw.control",
    "gc_pool": "z2.rgw.log:gc",
    "lc_pool": "z2.rgw.log:lc",
    "log_pool": "z2.rgw.log",
    "intent_log_pool": "z2.rgw.log:intent",
    "usage_log_pool": "z2.rgw.log:usage",
    "roles_pool": "z2.rgw.meta:roles",
    "reshard_pool": "z2.rgw.log:reshard",
    "user_keys_pool": "z2.rgw.meta:users.keys",
    "user_email_pool": "z2.rgw.meta:users.email",
    "user_swift_pool": "z2.rgw.meta:users.swift",
    "user_uid_pool": "z2.rgw.meta:users.uid",
    "otp_pool": "z2.rgw.otp",
    "system_key": {
        "access_key": "57ZYN8XHW9V03VYGEG9P",
        "secret_key": "BhLuCDHkFRtdbJTvNJCVP7Powzu7aThdm7KDX9vS"
    },
    "placement_pools": [
        {
            "key": "default-placement",
            "val": {
                "index_pool": "z2.rgw.buckets.index",
                "storage_classes": {
                    "STANDARD": {
                        "data_pool": "z2.rgw.buckets.data"
                    }
                },
                "data_extra_pool": "z2.rgw.buckets.non-ec",
                "index_type": 0
            }
        }
    ],
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3"
}

zone構成が変わったのでperiodのupdateを行い、最後にMasterと同じように、zone:z2の情報をceph.confに追記して、restartします。

[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin period update --commit
2020-12-03T05:53:16.914+0000 7f961cc901c0  1 Cannot find zone id=ac746b19-c165-453e-abed-3e528c13377a (name=z2), switching to local zonegroup configuration
Sending period to new master zone 9b505afe-3229-4b80-8a69-ffbaea14ec25
{
    "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
    "epoch": 3,
    "predecessor_uuid": "332fc423-02fe-427e-85b9-97ffe24f9197",
    "sync_status": [],
    "period_map": {
        "id": "6f70ae90-de8a-47c3-8e77-2b58532728d5",
        "zonegroups": [
            {
                "id": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
                "name": "zg",
                "api_name": "zg",
                "is_master": "true",
                "endpoints": [
                    "http://10.208.81.35:8000"
                ],
                "hostnames": [],
                "hostnames_s3website": [],
                "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "zones": [
                    {
                        "id": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                        "name": "z1",
                        "endpoints": [
                            "http://10.208.81.35:8000"
                        ],
                        "log_meta": "false",
                        "log_data": "true",
                        "bucket_index_max_shards": 11,
                        "read_only": "false",
                        "tier_type": "",
                        "sync_from_all": "true",
                        "sync_from": [],
                        "redirect_zone": ""
                    },
                    {
                        "id": "ac746b19-c165-453e-abed-3e528c13377a",
                        "name": "z2",
                        "endpoints": [
                            "http://10.208.81.18:8000"
                        ],
                        "log_meta": "false",
                        "log_data": "true",
                        "bucket_index_max_shards": 11,
                        "read_only": "false",
                        "tier_type": "",
                        "sync_from_all": "true",
                        "sync_from": [],
                        "redirect_zone": ""
                    }
                ],
                "placement_targets": [
                    {
                        "name": "default-placement",
                        "tags": [],
                        "storage_classes": [
                            "STANDARD"
                        ]
                    }
                ],
                "default_placement": "default-placement",
                "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
                "sync_policy": {
                    "groups": []
                }
            }
        ],
        "short_zone_ids": [
            {
                "key": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
                "val": 2054423084
            },
            {
                "key": "ac746b19-c165-453e-abed-3e528c13377a",
                "val": 1074715361
            }
        ]
    },
    "master_zonegroup": "e2a4ab0d-a871-4aaa-af1e-7be3cd16615e",
    "master_zone": "9b505afe-3229-4b80-8a69-ffbaea14ec25",
    "period_config": {
        "bucket_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        },
        "user_quota": {
            "enabled": false,
            "check_on_raw": false,
            "max_size": -1,
            "max_size_kb": 0,
            "max_objects": -1
        }
    },
    "realm_id": "e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3",
    "realm_name": "rlm",
    "realm_epoch": 2
}
[root@ceph-nano-cn2-faa32aebf00b /]# 
[root@ceph-nano-cn2-faa32aebf00b /]# vi /etc/ceph/ceph.conf 
[root@ceph-nano-cn2-faa32aebf00b /]# grep -A10 "client.rgw.ceph-nano-cn2" /etc/ceph/ceph.conf
[client.rgw.ceph-nano-cn2-faa32aebf00b]
rgw dns name = ceph-nano-cn2-faa32aebf00b
rgw enable usage log = true
rgw usage log tick interval = 1
rgw usage log flush threshold = 1
rgw usage max shards = 32
rgw usage max user shards = 1
log file = /var/log/ceph/client.rgw.ceph-nano-cn2-faa32aebf00b.log
rgw frontends = beast  endpoint=0.0.0.0:8000
rgw zone = z2
[root@ceph-nano-cn2-faa32aebf00b /]# exit
exit
[root@secondary cephnano]# ./cn cluster restart cn2 
2020/12/03 14:58:44 Restarting cluster cn2...

はい、お疲れさまでした。これでzone間のレプリケーションの設定ができました。MasterとSecondaryの両方に入って、radosgw-admin sync statusを実行してみると同期状況が確認できます。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin sync status
          realm e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3 (rlm)
      zonegroup e2a4ab0d-a871-4aaa-af1e-7be3cd16615e (zg)
           zone 9b505afe-3229-4b80-8a69-ffbaea14ec25 (z1)
  metadata sync no sync (zone is master)
      data sync source: ac746b19-c165-453e-abed-3e528c13377a (z2)
                        syncing
                        full sync: 0/128 shards
                        incremental sync: 128/128 shards
                        data is caught up with source
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin sync status
          realm e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3 (rlm)
      zonegroup e2a4ab0d-a871-4aaa-af1e-7be3cd16615e (zg)
           zone ac746b19-c165-453e-abed-3e528c13377a (z2)
  metadata sync syncing
                full sync: 0/64 shards
                incremental sync: 64/64 shards
                metadata is caught up with master
      data sync source: 9b505afe-3229-4b80-8a69-ffbaea14ec25 (z1)
                        syncing
                        full sync: 0/128 shards
                        incremental sync: 128/128 shards
                        data is caught up with source

この出力を見ると、それぞれ、data sync sourceがお互いのzoneを指定しています。
つまりこれは、z1側で保存されたオブジェクトはz2へレプリケーションされ、z2側で保存されたオブジェクトはz1へレプリケーションされる、Active-Activeなレプリケーションを意味します。

S3 clientからオブジェクトを置いてみよう

それでは本当にレプリケーションが稼働するのか、クライアントからオブジェクトを保存してみます。

S3 client用のユーザーを作成する

Master側に、先程syncuserを作ったのと同じように、testuserというユーザーを作成します。今度はsystem userではないので、--systemオプションはつけません。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin user create --uid="testuser" --display-name="test user" 
{
    "user_id": "testuser",
    "display_name": "test user",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "subusers": [],
    "keys": [
        {
            "user": "testuser",
            "access_key": "WMJLX79PAZ3M24ZYACX1",
            "secret_key": "u4ePP8FxhmnHfs83TDQFSEWbC5VoflrnMe3JqExS"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "default_placement": "",
    "default_storage_class": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}
[root@ceph-nano-cn1-faa32aebf00b /]# 
[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin user list
[
    "syncuser",
    "testuser"
]

さて、testuserが作られましたが、Secondaryの方でもradosgw-admin user listを実行してみましょう。こちらにもtestuserが同期されているはずです。

[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin user list
[
    "syncuser",
    "testuser"
]

testuserのkeyを使ってS3 clientでオブジェクトを置く

おまたせしました、それではtestuserのkeyを使ってbucketを作り、オブジェクトを置いてみます。
S3 clientはなんでもいいのですが、awscliを使ってローカルのファイルをs3 cpします。

まずはMaster側のRGW(z1)のendpointを指定してbucketを作ります。

[root@s3c ~]# aws configure
AWS Access Key ID []: WMJLX79PAZ3M24ZYACX1
AWS Secret Access Key []: u4ePP8FxhmnHfs83TDQFSEWbC5VoflrnMe3JqExS
Default region name [None]: 
Default output format [None]: 
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 mb s3://z1-bucket
make_bucket: z1-bucket
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 ls
2020-12-03 15:45:23 z1-bucket

MasterとSecondaryの両方でbucketが確認できると思います。これはMaster側で作られたbucketがすぐにSecondaryに同期されているためです。

[root@ceph-nano-cn1-faa32aebf00b /]# radosgw-admin bucket list
[
    "z1-bucket"
]
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin bucket list
[
    "z1-bucket"
]

それでは何かしらMaster側にファイルを置いてみましょう。丁度いい具合にRHEL8のISOイメージがあるのでこれを置いてみます。

[root@s3c ~]# ll  /mnt/share/rhel-8.2-x86_64-dvd.iso
-rw-r--r--. 1 root root 8436842496  8月 12 12:36 /mnt/share/rhel-8.2-x86_64-dvd.iso
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 cp /mnt/share/rhel-8.2-x86_64-dvd.iso s3://z1-bucket/rhel8img
upload: ../mnt/share/rhel-8.2-x86_64-dvd.iso to s3://z1-bucket/rhel8img
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 ls s3://z1-bucket
2020-12-03 15:56:44 8436842496 rhel8img
[root@s3c ~]# 
[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 ls s3://z1-bucket
[root@s3c ~]# 
...(転送終了後)
[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 ls s3://z1-bucket
2020-12-03 15:56:44 8436842496 rhel8img

s3 cpが完了すると、すぐにMasterからSecondaryへの転送が始まります。

転送中はSecondaryに対してs3 lsを実行してもオブジェクトは出てきません。
ブロックストレージのレプリケーションやファイルストレージのコピーとは違い、転送元のストレージに書かれたデータをブロック単位で次々に転送することはありません。転送元にオブジェクトを書き切ってから、転送先にレプリケーションを行います。

転送中のSecondaryマシンでiftopの出力が下の図です。1Gbpsで繋がっているのでMaster(.35)からSecondary(.18)へワイヤースピードに近い速度で転送しています。

f:id:ututaq:20210126111300p:plain

転送中は、radosgw-admin sync statusの出力で、xx shards are recovering ; recovering shareds: [XX]と表示されます。完全に同期したらdata is caught up with sourceと表示されます。

[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin sync status
          realm e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3 (rlm)
      zonegroup e2a4ab0d-a871-4aaa-af1e-7be3cd16615e (zg)
           zone ac746b19-c165-453e-abed-3e528c13377a (z2)
  metadata sync syncing
                full sync: 0/64 shards
                incremental sync: 64/64 shards
                metadata is caught up with master
      data sync source: 9b505afe-3229-4b80-8a69-ffbaea14ec25 (z1)
                        syncing
                        full sync: 0/128 shards
                        incremental sync: 128/128 shards
                        1 shards are recovering
                        recovering shards: [76]
...
(同期完了後)
...
[root@ceph-nano-cn2-faa32aebf00b /]# radosgw-admin sync status
          realm e1bd3bc0-9ef8-460a-8d24-cd69ba7e62e3 (rlm)
      zonegroup e2a4ab0d-a871-4aaa-af1e-7be3cd16615e (zg)
           zone ac746b19-c165-453e-abed-3e528c13377a (z2)
  metadata sync syncing
                full sync: 0/64 shards
                incremental sync: 64/64 shards
                metadata is caught up with master
      data sync source: 9b505afe-3229-4b80-8a69-ffbaea14ec25 (z1)
                        syncing
                        full sync: 0/128 shards
                        incremental sync: 128/128 shards
                        data is caught up with source

これでMaster->Secondaryのレプリケーションはうまくいっていることが分かりました。

それでは反対にSecondary->Masterのレプリケーションを試してみましょう。先程と同様にS3 clientからSeconaryにbucketを作成し、ファイルを置いてみます。

[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 mb s3://z2-bucket
make_bucket: z2-bucket
[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 ls 
2020-12-03 15:45:23 z1-bucket
2020-12-03 16:07:00 z2-bucket
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 ls 
2020-12-03 15:45:23 z1-bucket
2020-12-03 16:07:00 z2-bucket
[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 cp /mnt/share/rhel-8.2-x86_64-dvd.iso s3://z2-bucket/rhel8img_second
upload: ../mnt/share/rhel-8.2-x86_64-dvd.iso to s3://z2-bucket/rhel8img_second
[root@s3c ~]# aws --endpoint-url=http://10.208.81.18:8000 s3 ls s3://z2-bucket
2020-12-03 16:21:37 8436842496 rhel8img_second
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 ls s3://z2-bucket
...(転送終了後)
[root@s3c ~]# aws --endpoint-url=http://10.208.81.35:8000 s3 ls s3://z2-bucket
2020-12-03 16:21:37 8436842496 rhel8img_second

Master->Secondaryと同じような感じですね。iftopの出力は先程とは反対に、Secondary(.18)からMaster(.35)に転送されている様子が見えます。

f:id:ututaq:20210126111750p:plain

基本的にはMultiSiteはこんな感じです

今回はCeph Nanoを使って一番シンプルな構成でCeph MultiSiteを構成してみました。
通常のCephクラスターでも今回の作業と本質的には同じです。RGWの数が増えたり、poolのplacementの設定が変わるだけですので、構成すること自体はそんなに難しいことではありません。

一般的にサイト間レプリケーションでよくある問題は、転送スピードが出ない問題です。転送スピードはサイト間のネットワークに依存するところが大きいのですが、スペック上の帯域幅は分かっていても実際にどれくらいの速度が出るかは、やってみないと正直誰も分かりません。私はおろかネットワーク事業者さんも断言はできないでしょう。

ですので、CephでMultiSiteをやってみようと思っている方は、一旦Ceph Nanoを使ってどれくらいの転送ができるのかを試してみることをお勧めします。

というわけで、今回はここまで。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。