GlusterFSをDocker環境上で動かしてみる

# はじめに
三番煎じ(そんな日本語はないが)ですが、Kubenetesを使うこととなり、Kubenetesがサポートする分散ファイルシステムのGlusterFSを動かしてみます。ぐぐったら、多くの方が検証されていたので驚きました。CockroachDBと比較すると天と地の差があります... 
その他、HDFSとは違ってマスターレスな構成のため、どのノードが落ちても大丈夫なのは非常に良いと思いました。でも、デメリットは何だろう。小さい大量のファイルを扱う場合、NFSより性能が悪くなるところなのですかね。
という訳で、CentOS、Docker、GlusterFSの組み合わせを選択します。

# 環境
Docker 17.06.2-ce
CentOS 7.4
GlusterFS 3.10.5

# トポロジー
Docker環境ですので、Gluster層とStorage層に分けました。Glusterサーバーにデータを格納してしまうと、コンテナとデータが疎結合ではなくなり、Dockerのメリットであるポータビリティー性が悪くなります。

| サーバー名称      |      役割                    |    備考    |
|:-----------------|:------------------|:------------------|
| gluster-client   |クライアント|172.17.0.5|
| gluster1-server  |Glusterサーバー用|172.17.0.6|
| gluster1-volume  |データボリューム用|gluster1-serverからマウント|
| gluster2-server  |Glusterサーバー用|172.17.0.7|
| gluster2-volume  |データボリューム用|gluster2-serverからマウント|
![26.jpeg](https://qiita-image-store.s3.amazonaws.com/0/199531/4ba67d46-923b-107c-eff1-61ee5b453be1.jpeg)

# 1. 事前準備
-1.1. HostOSの作成と起動
-
boot2dockerを使います。デスクトップにあるアイコンをダブルクリックし、Docker Quick Start Terminalを起動します。いつも通りクジラで出てきます。

```
                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/

docker is configured to use the default machine with IP 192.168.99.100
For help getting started, check out the docs at https://docs.docker.com
```


-1.2. CentOS7イメージのダウンロード
-
あ、CentOS6は200MBオーバーだったのに、CentOS7は200MBアンダーになっています。CentOS7で、不要なモジュールを削除したのかな。

```
$ docker pull centos:centos7
centos7: Pulling from library/centos
(省略)
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             196e0ce0c9fb        6 days ago          196.6 MB
```

-1.3. Failed to get D-Bus connection: Operation not permittedへの対応
-
CentOS7では、docker run xxx /bin/bashでコンテナを起動すると、systemctl実行時に下記エラーが表示されます。従いまして、今回は、docker run --privileged xxx /sbin/initで起動することにします。privilegedはセキュリティーの課題があるかもしれないのですが、GlusterFSの検証が目的なので目をつぶります。
・特権モードにする(--privileged)
・コンテナで起動されるコマンドを/sbin/initにする

```
# systemctl start gluster
Failed to get D-Bus connection: Operation not permitted
```

また、/sbin/initで起動すると、docker attachコマンドが使用できなくなります。そこで、nsenterを使用します。

```
$ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
(省略)
Status: Downloaded newer image for jpetazzo/nsenter:latest
Installing nsenter to /target
Installing docker-enter to /target
Installing importenv to /target
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             196e0ce0c9fb        6 days ago          196.6 MB
jpetazzo/nsenter    latest              c16fe938c1a5        14 months ago       370.9 MB
```

# 2. GlusterFS用のマスターイメージの作成と起動
-2.1. マスターイメージの作成
-
GlusterFS用のマスターイメージを作成すべく、centos7を指定してコンテナを起動します。

```
$ docker run --privileged -itd --name GlusterMaster centos:centos7 /sbin/init
35740bdcf45f594163f5a9bd9cc0bc38151e4bf2f0c54d54cf7e7e2aa067b93d
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
b92d93620ccc        centos:centos7      "/sbin/init"        9 seconds ago       Up 43 seconds                           GlusterMaster
```

Gluster関連パッケージをインストールします。下記を実行すれば、epelを入れる必要がありませんでした。

```
$ docker-machine ssh default
# docker-enter GlusterMaster
# yum update -y
# yum -y install centos-release-gluster
# yum -y install glusterfs-server
# yum -y install glusterfs
# exit
```

-2.2. GlusterMasterイメージの作成
-

Gluster関連パッケージをインストールしたコンテナを、docker commitコマンドにてイメージ化します。

```
$ docker ps -a
$ docker commit GlusterMaster shimizu/glusterserver
$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
shimizu/glusterserver   latest              b2f06387fe86        7 seconds ago       296MB
centos                  centos7             196e0ce0c9fb        6 days ago          197MB
jpetazzo/nsenter        latest              c16fe938c1a5        14 months ago       371MB
```

-2.3. マスターイメージからコンテナの起動
- 

データボリューム用のコンテナを起動します。

```
$ docker run -itd -v /data --name gluster1-volume centos:centos7 /sbin/init
$ docker run -itd -v /data --name gluster2-volume centos:centos7 /sbin/init
```

データボリューム用のコンテナをマウントし、コンテナを起動します。実行するターミナルは、それぞれ別にします。

```
ターミナルA: gluster-client
$ docker run --privileged -itd --name gluster-client shimizu/glusterserver /sbin/init
```

```
ターミナルB: gluster1-server
$ docker run --privileged -itd --volumes-from gluster1-volume --name gluster1-server shimizu/glusterserver /sbin/init
```

```
ターミナルC: gluster2-server
$ docker run --privileged -itd --volumes-from gluster2-volume --name gluster2-server shimizu/glusterserver /sbin/init
```

```
$ docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED                  STATUS              PORTS               NAMES
3a5e2c99d4f6        shimizu/glusterserver   "/sbin/init"        Less than a second ago   Up 6 seconds                            gluster2-server
2933fd28433e        shimizu/glusterserver   "/sbin/init"        Less than a second ago   Up 14 seconds                           gluster1-server
1e23056c183c        shimizu/glusterserver   "/sbin/init"        40 seconds ago           Up About a minute                       gluster-client
92bed989c92d        centos:centos7          "/sbin/init"        About a minute ago       Up About a minute                       gluster2-volume
5c498f023402        centos:centos7          "/sbin/init"        About a minute ago       Up About a minute                       gluster1-volume
b92d93620ccc        centos:centos7          "/sbin/init"        22 minutes ago           Up 23 minutes                           GlusterMaster
```


コンテナに接続し、起動時に指定した/dataがマウントされていることを確認します。

```
ターミナルB: gluster1-server
$ docker-machine ssh default
$ sudo -s
# docker-enter gluster1-server
# df -k | grep data
/dev/sda1       18745336 461712  17292900   3% /data
```

```
ターミナルC: gluster2-server
$ docker-machine ssh default
$ sudo -s
# docker-enter gluster2-server
# df -k | grep data
/dev/sda1       18745336 461724  17292888   3% /data
```

# 3. GlusterFSの設定
-3-1. glusterdの起動
-

最初に、gluster1-serverとgluster2-serverのglusterdを起動します。

```
ターミナルB: gluster1-server
# docker-enter gluster1-server
# systemctl start glusterd
# systemctl status glusterd
● glusterd.service - GlusterFS, a clustered file-system server
   Loaded: loaded (/usr/lib/systemd/system/glusterd.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2017-09-21 11:50:35 UTC; 5s ago
  Process: 116 ExecStart=/usr/sbin/glusterd -p /var/run/glusterd.pid --log-level $LOG_LEVEL $GLUSTERD_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 117 (glusterd)
   CGroup: /system.slice/glusterd.service
           └─117 /usr/sbin/glusterd -p /var/run/glusterd.pid --log-level INFO
(省略)
```

```
ターミナルC: gluster2-server
# docker-enter gluster2-server
# systemctl start glusterd
#  systemctl status glusterd
● glusterd.service - GlusterFS, a clustered file-system server
   Loaded: loaded (/usr/lib/systemd/system/glusterd.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2017-09-21 11:52:28 UTC; 5s ago
  Process: 98 ExecStart=/usr/sbin/glusterd -p /var/run/glusterd.pid --log-level $LOG_LEVEL $GLUSTERD_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 99 (glusterd)
   CGroup: /system.slice/glusterd.service
           └─99 /usr/sbin/glusterd -p /var/run/glusterd.pid --log-level INFO
(省略)
```

-3-2. ストレージプールの作成
-
gluster1-serverから、gluster2-serverをpeerとして登録します。gluster2-serverから、gluster1-serverをpeerとして登録することも可能です。gluster peerを実行することでノード間を認識させ、ストレージプールを作成します。

```
ターミナルB: gluster1-server
# gluster peer status
Number of Peers: 0
# gluster peer probe 172.17.0.7
peer probe: success.
# gluster peer status
Number of Peers: 1

Hostname: 172.17.0.7
Uuid: 28d83406-c655-4617-955a-96150c409081
State: Peer in Cluster (Connected)
# gluster pool list
UUID                                    Hostname        State
28d83406-c655-4617-955a-96150c409081    172.17.0.7      Connected
ca7bb3f6-7d14-4738-a047-2bcbfbaa9b68    localhost       Connected
```

gluster2-serverからも状況を確認します。

```
ターミナルC: gluster2-server
#  gluster peer status
Number of Peers: 1

Hostname: 172.17.0.6
Uuid: ca7bb3f6-7d14-4738-a047-2bcbfbaa9b68
State: Peer in Cluster (Connected)
#  gluster pool list
UUID                                    Hostname        State
ca7bb3f6-7d14-4738-a047-2bcbfbaa9b68    172.17.0.6      Connected
28d83406-c655-4617-955a-96150c409081    localhost       Connected
```

-3-3. ボリュームの作成
- 
/dataをGlusterFS上のReplicated Volumeとして作成します。最初に、brick用のディレクトリを作成します。

```
ターミナルB: gluster1-server
# mkdir /data/vol1
```

```
ターミナルC: gluster2-server
# mkdir /data/vol1
```

gluster1-serverからGlusterFSのボリュームを作成します。

```
ターミナルB: gluster1-server
# gluster volume create test-vol replica 2 172.17.0.6:/data/vol1 172.17.0.7:/data/vol1
volume create: test-vol: success: please start the volume to access data
# gluster volume start test-vol
volume start: test-vol: success
# gluster volume info
Volume Name: test-vol
Type: Replicate
Volume ID: 02a988b9-f365-4028-8e12-72a34c1df544
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 2 = 2
Transport-type: tcp
Bricks:
Brick1: 172.17.0.6:/data/vol1
Brick2: 172.17.0.7:/data/vol1
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
# gluster volume list
test-vol
# gluster volume status
Status of volume: test-vol
Gluster process                             TCP Port  RDMA Port  Online  Pid
------------------------------------------------------------------------------
Brick 172.17.0.6:/data/vol1                 49152     0          Y       160
Brick 172.17.0.7:/data/vol1                 49152     0          Y       126
Self-heal Daemon on localhost               N/A       N/A        Y       146
Self-heal Daemon on 172.17.0.6              N/A       N/A        Y       180

Task Status of Volume test-vol
------------------------------------------------------------------------------
There are no active volume tasks
```

# 4. テスト
-4.1. レプリカの確認
- 
gluster-clientからVolumeをマウントします。マウントコマンド実行時に、下記のWARNINGが出力されましたが、WARNINGなので先に進めました。attr is a command used for extended attributes on XFS filesystem objects　とのことで、getfattrを導入すればよいと思ったのですが、導入済みでした。

```
ターミナルA: gluster-client
# docker-enter gluster-client
# df -k
Filesystem     1K-blocks   Used Available Use% Mounted on
none            18745336 867708  16886904   5% /
tmpfs             509824      0    509824   0% /dev
tmpfs             509824      0    509824   0% /sys/fs/cgroup
/dev/sda1       18745336 867708  16886904   5% /etc/hosts
shm                65536      0     65536   0% /dev/shm
tmpfs             509824   6652    503172   2% /run
tmpfs             101968      0    101968   0% /run/user/0
# mkdir /data
# mount -t glusterfs 172.17.0.6:/test-vol /data
WARNING: getfattr not found, certain checks will be skipped..
```

テストしてみます。すごいです。ファイルを更新しても問題ありません、レプリカされています。

```
ターミナルA: gluster-client <ファイルを作成
# pwd
/data
# touch shimizu
# ls -la | grep shimizu
-rw-r--r--  1 root root    0 Sep 21 12:36 shimizu
```

```
ターミナルB:gluster1-server </data/vol1を確認
# ls -la /data/vol1 | grep shimizu
-rw-r--r-- 2 root root    0 Sep 21 12:36 shimizu
```

```
ターミナルC:gluster2-server </data/vol1を確認
# ls -la /data/vol1 | grep shimizu
-rw-r--r-- 2 root root    0 Sep 21 12:36 shimizu
```

-4.2. 排他制御の確認
- 
gluster-clientでファイルを操作している時に、gluster2-serverで操作できるのかを確認します。

```
ターミナルA: gluster-client
# vi /data/shimizu
```
ターミナルAでviでファイル操作中に、ターミナルBでviを実行してみると、下記になる。想定通りというか、当たり前か...  どのノードからファイルを更新しても良いが、1時点では1ノードからしかアクセスできない。

```
# vi shimizu
E325: ATTENTION
Found a swap file by the name ".shimizu.swp"
          owned by: root   dated: Fri Sep 22 04:50:04 2017
         file name: /data/shimizu
          modified: no
         user name: root   host name: 71d3366cf052
        process ID: 208
While opening file "shimizu"
             dated: Fri Sep 22 04:50:00 2017

(1) Another program may be editing the same file.  If this is the case,
    be careful not to end up with two different instances of the same
    file when making changes.  Quit, or continue with caution.
(2) An edit session for this file crashed.
    If this is the case, use ":recover" or "vim -r shimizu"
    to recover the changes (see ":help recovery").
    If you did this already, delete the swap file ".shimizu.swp"
    to avoid this message.
```

# 5. 用語の整理
| 用語 | 意味 |
|:--|:--|
|Storage Pool   |ストレージクラスタに参加するサーバーの集まり。   |
|Brick   |個々のサーバー上のディレクトリになり、GlusterFSにストレージとして提供される。   |
|Volume   |Brickを論理的に集約した、GlusterFS上の仮想的なストレージ領域。ストレージを使う側は、このVolumeをマウントして利用することになる。   |

Volumeには3つのタイプ(Distributed Volume、Replicated Volume、Striped Volume)があり、Volumeを作成するとき選択できます。タイプ毎にファイルの保存方法が異なります。

# まとめ
所感は、OSSなのにすごいです。簡単に作れます。
kubenetesと連携してテストできていないのですが、これからは、GlusterFS等でストレージを仮想化、Kubenetes等でDockerコンテナを仮想化ですね。IBM社は[ubiquity](https://www.ibm.com/blogs/systems/containers-persistent-storage-ibm/)を提供していますので、何が違うのかについて今後調べたいと思います。

# 参考
[Gluster](https://www.gluster.org/)
[DockerでGlusteFS。分散ファイルシステムの実装。](https://qiita.com/iron-breaker/items/eab40505cab908da886f)
[CentOS7でGlusterFSの環境構築](https://qiita.com/Yuki-Inamoto/items/2ea1efe862aeba34019c)