IBM Cloud VPCでGlusterFSをMZRで構成する

# 目的

IBM Cloudは多くのデータセンターでMZRに対応していますが、残念ながらゾーンまたぎで利用可能なストレージは標準ではObject Storageしかありません。Portworxのようなサードパーティの分散ストレージはありますが、非常に巨大なリソースを必要とするため、安価に使うことはできません。

そこは、今回はOSSのGlusterFSを使って、MZRに対応した分散ストレージを構成してみます。

# 環境

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241424/bb2749bb-be70-f5e6-e6c5-0589ed650d55.png)

GlusterFS用にVPCの仮想サーバをjp-tok-1,2,3それぞれに1台プロビジョンします。OSはCentOS8を選択します。

> Red Hat Linuxを使う場合は有償のRed Hat Gluster Storageを使うのが筋だと思いますので、今回はCentOSとしています。

それとは別に、ボリュームマウントとheketiによるクラスタ管理用の仮想サーバを1台使用します。

# 手順

手順は下記のサイトが非常に参考になります。

> https://computingforgeeks.com/setup-glusterfs-storage-with-heketi-on-centos-server/

## GlusterFSサーバ（CentOS）

### 仮想サーバのプロビジョン

詳細手順は省略します。Block Storageをブートボリュームとは別にデータボリュームとして追加してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241424/f827e521-33c4-6b2b-5a87-bb0ee84be061.png)

### 日本語化（オプション）

これは個人の趣味です。

```
# dnf -y update
# dnf -y install langpacks-ja
# localectl set-locale ja_JP.UTF-8
# timedatectl set-timezone Asia/Tokyo
# shutdown -r now
```

### 名前解決の設定

お互いに名前解決できるよう3台とも/etc/hostsに各ホストを追加します。

```/etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.244.3.10     vs-gluster-tok1
10.244.67.9     vs-gluster-tok2
10.244.131.10   vs-gluster-tok3
```

### firewalldの停止

個々の穴あけは大変なので、今回はfirewalldを停止します。

```
# systemctl disable firewalld --now
```

### GlusterFSパッケージのインストール

GlusterFSのパッケージをインストールし、glusterdを起動します。

```
# dnf -y install centos-release-gluster9.noarch
# dnf -y install glusterfs-server
# systemctl enable --now glusterd
```

### ボリューム用ディスクの確認

データボリュームのデバイスを確認します。この場合vdbがデータボリュームです。

```
# lsblk
NAME                MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda                 252:0    0  100G  0 disk
├─vda1              252:1    0    1G  0 part /boot
└─vda2              252:2    0   99G  0 part
  ├─vg_virt-lv_root 253:0    0   98G  0 lvm  /
  └─vg_virt-lv_swap 253:1    0    1G  0 lvm
vdb                 252:16   0  100G  0 disk
vdc                 252:32   0  366K  0 disk
vdd                 252:48   0   44K  0 disk
```

## 管理サーバ（RHEL）

### 名前解決の設定

GlusterFSサーバを名前解決できるよう/etc/hostsに各ホストを追加します。

```/etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.244.3.10     vs-gluster-tok1
10.244.67.9     vs-gluster-tok2
10.244.131.10   vs-gluster-tok3
```

### heketiのインストール

```
# curl -LO https://github.com/heketi/heketi/releases/download/v10.4.0/heketi-v10.4.0-release-10.linux.amd64.tar.gz
# tar zxvf heketi-v10.4.0-release-10.linux.amd64.tar.gz
heketi/
heketi/heketi
heketi/heketi-cli
# cp heketi/{heketi,heketi-cli} /usr/local/bin/
# groupadd --system heketi
# useradd -s /sbin/nologin --system -g heketi heketi
# mkdir -p /var/lib/heketi /etc/heketi /var/log/heketi
# cp heketi/heketi.json /etc/heketi/
```

### heketi.jsonの構成

下記のとおり修正します。

```
# cd /etc/heketi
# cp -p heketi.json heketi.json.org
# vi heketi.json

# diff /etc/heketi/heketi.json /etc/heketi/heketi.json.org
23c23
<       "key": "P@ssw0rd"
---
>       "key": ""
28c28
<       "key": "P@ssw0rd"
---
>       "key": ""
49c49
<     "executor": "ssh",
---
>     "executor": "mock",
53,62c53,62
<       "keyfile": "/etc/heketi/heketi_key",
<       "user": "root",
<       "port": "22",
<       "fstab": "/etc/fstab",
<       "mountopts": "rw,inode64,noatime,nouuid",
<       "pv_data_alignment": "256K",
<       "vg_physicalextentsize": "4MB",
<       "lv_chunksize": "256K",
<       "xfs_sw": 1,
<       "xfs_su": 4096,
---
>       "keyfile": "path/to/private_key",
>       "user": "sshuser",
>       "port": "Optional: ssh port.  Default is 22",
>       "fstab": "Optional: Specify fstab file on node.  Default is /etc/fstab",
>       "mountopts": "Optional: Specify brick mount options.  Default is rw,inode64,noatime,nouuid",
>       "pv_data_alignment": "Optional: Specify PV data alignment size. Default is 256K",
>       "vg_physicalextentsize": "Optional: Specify VG physical extent size. Default is 4MB",
>       "lv_chunksize": "Optional: Specify LV chunksize. Default is 256K",
>       "xfs_sw": "Optional: Specify number of data disks in the underlying RAID device",
>       "xfs_su": "Optional: Specifies a stripe unit or RAID chunk size.",
64c64
<       "gluster_cli_timeout": 30,
---
>       "gluster_cli_timeout": "Optional: Timeout, in seconds, passed to the gluster cli invocations",
83,84c83,84
<       "xfs_sw": 1,
<       "xfs_su": 4096,
---
>       "xfs_sw": "Optional: Specify number of data disks in the underlying RAID device.",
>       "xfs_su": "Optional: Specifies a stripe unit or RAID chunk size.",
86c86
<       "gluster_cli_timeout": 30,
---
>       "gluster_cli_timeout": "Optional: Timeout, in seconds, passed to the gluster cli invocations",
```

### SSHキーの生成と登録

管理サーバからGlusterFSサーバにsshするためのキーを生成します。

```
# ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ""
```

公開鍵の方（heketi_key.pub）をGlusterFSサーバそれぞれの/root/.ssh/authorized_keysに追加します（手順省略）。

### heketiサービスの登録

heketiをsystemdで実行できるよう下記ファイルを作成します。

```/etc/systemd/system/heketi.service
[Unit]
Description=Heketi Server

[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
User=heketi
ExecStart=/usr/local/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target
```

### 各ディレクトリのパーミッション設定

```
# chown -R heketi:heketi /var/lib/heketi /var/log/heketi /etc/heketi
```

### heketiサービスの起動

```
# systemctl daemon-reload
# systemctl enable --now heketi

# systemctl status heketi
● heketi.service - Heketi Server
   Loaded: loaded (/etc/systemd/system/heketi.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2021-09-18 09:11:23 JST; 1s ago
 Main PID: 1395810 (heketi)
    Tasks: 7 (limit: 23184)
   Memory: 12.0M
   CGroup: /system.slice/heketi.service
           └─1395810 /usr/local/bin/heketi --config=/etc/heketi/heketi.json

 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: 2021/09/18 09:11:23 no SSH_KNOWN_HOSTS specified, skipping ssh host verification
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Loaded ssh executor
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Volumes per cluster limit is set to default value of 1000
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Block: Auto Create Block Hosting Volume set to true
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Block: New Block Hosting Volume size 500 GB
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Block: New Block Hosting Volume Options: group gluster-block
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 GlusterFS Application Loaded
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Started Node Health Cache Monitor
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: [heketi] INFO 2021/09/18 09:11:23 Started background pending operations cleaner
 9月 18 09:11:23 vs-gate-tok1 heketi[1395810]: Listening on port 8080
```

### トポロジファイルの作成

下記のファイルを作成します。

```/etc/heketi/topology.json
{
  "clusters": [
    {
      "nodes": [
        {
          "node": {
            "hostnames": {
              "manage": [
                "vs-gluster-tok1"
              ],
              "storage": [
                "vs-gluster-tok1"
              ]
            },
            "zone": 1
          },
          "devices": [
            "/dev/vdb"
          ]
        },
        {
          "node": {
            "hostnames": {
              "manage": [
                "vs-gluster-tok2"
              ],
              "storage": [
                "vs-gluster-tok2"
              ]
            },
            "zone": 2
          },
          "devices": [
            "/dev/vdb"
          ]
        },
        {
          "node": {
            "hostnames": {
              "manage": [
                "vs-gluster-tok3"
              ],
              "storage": [
                "vs-gluster-tok3"
              ]
            },
            "zone": 3
          },
          "devices": [
            "/dev/vdb"
          ]
        }
      ]
    }
  ]
}
```

### トポロジーのロード

このときGlusterFSのクラスタが構成されます。

```
# heketi-cli --server http://localhost:8080 --user admin --secret P@ssw0rd topology info

# heketi-cli --server http://localhost:8080 --user admin --secret P@ssw0rd topology load --json /etc/heketi/topology.json
Creating cluster ... ID: c39872430f7216ff3cbdd32dd72be53c
        Allowing file volumes on cluster.
        Allowing block volumes on cluster.
        Creating node vs-gluster-tok1 ... ID: 670d2adcd3f454bac325506a43ce0bfd
                Adding device /dev/vdb ... OK
        Creating node vs-gluster-tok2 ... ID: 8f800deb8de779105bb2982d01853ec3
                Adding device /dev/vdb ... OK
        Creating node vs-gluster-tok3 ... ID: 5bf6fb2b4f9772bc3b5ca9c826dfaa0a
                Adding device /dev/vdb ... OK

# heketi-cli --server http://localhost:8080 --user admin --secret P@ssw0rd topology info

Cluster Id: c39872430f7216ff3cbdd32dd72be53c

    File:  true
    Block: true

    Volumes:


    Nodes:

        Node Id: 5bf6fb2b4f9772bc3b5ca9c826dfaa0a
        State: online
        Cluster Id: c39872430f7216ff3cbdd32dd72be53c
        Zone: 3
        Management Hostnames: vs-gluster-tok3
        Storage Hostnames: vs-gluster-tok3
        Devices:
                Id:a094bbe75e252d293cce8c314c624eb4   State:online    Size (GiB):99      Used (GiB):0       Free (GiB):99
                        Known Paths: /dev/disk/by-id/virtio-02g7-49d3ba7d-031d-4 /dev/disk/by-path/pci-0000:00:05.0 /dev/disk/by-path/virtio-pci-0000:00:05.0 /dev/vdb

                        Bricks:

        Node Id: 670d2adcd3f454bac325506a43ce0bfd
        State: online
        Cluster Id: c39872430f7216ff3cbdd32dd72be53c
        Zone: 1
        Management Hostnames: vs-gluster-tok1
        Storage Hostnames: vs-gluster-tok1
        Devices:
                Id:5515c316a64a20f20ff91cf49585edd0   State:online    Size (GiB):99      Used (GiB):0       Free (GiB):99
                        Known Paths: /dev/disk/by-id/virtio-02e7-fee0f877-dc3a-4 /dev/disk/by-path/pci-0000:00:05.0 /dev/disk/by-path/virtio-pci-0000:00:05.0 /dev/vdb

                        Bricks:

        Node Id: 8f800deb8de779105bb2982d01853ec3
        State: online
        Cluster Id: c39872430f7216ff3cbdd32dd72be53c
        Zone: 2
        Management Hostnames: vs-gluster-tok2
        Storage Hostnames: vs-gluster-tok2
        Devices:
                Id:de71fbdb6baf96294a5200617f0b8d4c   State:online    Size (GiB):99      Used (GiB):0       Free (GiB):99
                        Known Paths: /dev/disk/by-id/virtio-02f7-9f65e986-aa43-4 /dev/disk/by-path/pci-0000:00:05.0 /dev/disk/by-path/virtio-pci-0000:00:05.0 /dev/vdb

                        Bricks:

```

# 動作確認

## ボリュームの作成

お試しで1GBのボリュームを作成します。デフォルトで3レプリカ構成になります。

```
# heketi-cli --server http://localhost:8080 --user admin --secret P@ssw0rd volume create --size 1
Name: vol_b1c248c26d97f6f85e247cc6b37641f5
Size: 1
Volume Id: b1c248c26d97f6f85e247cc6b37641f5
Cluster Id: c39872430f7216ff3cbdd32dd72be53c
Mount: vs-gluster-tok3:vol_b1c248c26d97f6f85e247cc6b37641f5
Mount Options: backup-volfile-servers=vs-gluster-tok1,vs-gluster-tok2
Block: false
Free Size: 0
```

> サーバや認証情報は環境変数に設定しておくと都度指定しなくてよいので楽です。
$ export HEKETI_CLI_SERVER=http://localhost:8080
$ export HEKETI_CLI_USER=admin
$ export HEKETI_CLI_KEY="P@ssw0rd"

> Bash Completionを使っている方はオートコンプリートを使うことができます。
$ sudo dnf install -y bash-completion
$ heketi-cli completion bash >/etc/bash_completion.d/heketi

## ボリュームのマウント

GlusterFSのボリュームをマウントするために下記パッケージをインストールします。

```
# dnf -y install glusterfs glusterfs-fuse
```

マウントします。サーバは上記出力の``Mount``の値を、オプションは``Mount Options``の,を:に置き換えた値を指定します。

```
# mount -t glusterfs -o backup-volfile-servers=vs-gluster-tok1:vs-gluster-tok2 vs-gluster-tok3:vol_b1c248c26d97f6f85e247cc6b37641f5 /mnt
# mount | grep gluster
vs-gluster-tok3:vol_b1c248c26d97f6f85e247cc6b37641f5 on /mnt type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072)
```

## 書き込みテスト

ボリュームにデータを書いて読み出す処理をループ実行します。

```
# while :; do date >/mnt/test.dat; cat /mnt/test.dat; sleep 0.1; done
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
2021年  9月 18日 土曜日 23:00:12 JST
```

GlusterFSサーバ1号機を停止します。停止はIBM Cloud Consoleから行ってください。OSレベルでシャットダウンしても自動的に再起動されてしまうためです。1号機を停止しただけでは特にエラーは発生しないことを確認してください。

続いて2号機を停止します。下記のようにエラーになります。

```
cat: /mnt/test.dat: Transport endpoint is not connected
-bash: /mnt/test.dat: Transport endpoint is not connected
cat: /mnt/test.dat: Transport endpoint is not connected
-bash: /mnt/test.dat: Transport endpoint is not connected
```

これは、3レプリカのうち生きているものが1つになったため、3レプリカで正しいデータを多数決で判別不能になったためです（ビザンチン将軍問題）。

1号機を開始します。しばらく待つとエラーが出なくなります。1号機と3号機で正しいデータが確定するためです。

2号機を起動して終了します。