LizardFS による分散ファイルシステムの構築（GA 版 3.12 yum インストール編）

## 1. 要約
この記事では、LizardFS による分散ファイルシステムの構築（GA 版 3.12 yum インストール編）の例と、keepalived や、スクラッチの Golang デーモンによる、自前のマスターフェイルオーバー機構の構築例について投稿します。

## 2. はじめに
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” では、lizardfs-uraft パッケージが入ったバージョンの構築例と、keepalived やスクラッチの Golang デーモンを使った、自前のマスターフェイルオーバー機構の構築例について投稿しました。
今回の記事では、lizardfs-uraft パッケージのない yum インストール編の構築例と、前回作った keepalived 構成やスクラッチの Golang デーモンを使った、自前の自動フェイルオーバー環境をカスタマイズした構築例を投稿します。
LizardFS の GA 版 3.12 yum インストール編と RC 版 3.13 RPM パッケージ編の違いは、“<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

## 3. LizardFS 環境
* RHEL 7 系
* LizardFS 3.12
* keepalived 1.3.5
* Go 1.9

## 4. LizardFS 設計
* LizardFS マスターサーバー x 2
  * mfs-master1
  * mfs-master2
* LizardFS チャンクサーバー x 2
  * mfs-chunk1
  * mfs-chunk2
* LizardFS クライアント x 2
  * mfs-client1
  * mfs-client2

### LizardFS パッケージ一覧
```
lizardfs-master – LizardFS master server
lizardfs-chunkserver – LizardFS data server
lizardfs-client – LizardFS client
lizardfs-adm – LizardFS administration tools (e.g, lizardfs-probe)
lizardfs-cgi – LizardFS CGI Monitor
lizardfs-cgiserv – Simple CGI-capable HTTP server to run LizardFS CGI Monitor
lizardfs-metalogger – LizardFS metalogger server
lizardfs-debuginfo – (CentOS 7 / RHEL 7 only) Debugging symbols and sources for all the LizardFS binaries
```

## 5. LizardFS マスターサーバー
### 5-1. epel リポジトリインストール
@ mfs-master1, 2
`$ sudo yum install -y epel-release`
`$ sudo vim /etc/yum.repos.d/epel.repo`

```diff:/etc/yum.repos.d/epel.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 5-2. LizardFS リポジトリインストール
@ mfs-master1, 2
`$ sudo curl http://packages.lizardfs.com/yum/el7/lizardfs.repo > /etc/yum.repos.d/lizardfs.repo`
`$ sudo vim /etc/yum.repos.d/lizardfs.repo`

```diff:/etc/yum.repos.d/lizardfs.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 5-6. LizardFS マスターインストール
@ mfs-master1, 2
`$ sudo yum install -y --enablerepo=lizardfs,epel lizardfs-master lizardfs-adm`

### 5-7. hosts ファイル設定
@ mfs-master1, 2
`$ vim /etc/hosts`

```conf:/etc/hosts
10.0.0.1	mfs-master1
10.0.0.2	mfs-master2
10.0.0.3	mfs-chunk1
10.0.0.4	mfs-chunk2
10.0.0.5	mfs-client1
10.0.0.6	mfs-client2
```

### 5-8. metadata.mfs 設定
@ mfs-master1, 2
`$ sudo cd /var/lib/mfs/`
`$ sudo cp -a metadata.mfs.empty metadata.mfs`

### 5-9. mfsmaster.cfg 設定
@ mfs-master1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfsmaster.cfg.dist mfsmaster.cfg`
`$ sudo vim mfsmaster.cfg`

@ mfs-master1

```conf:/etc/mfs/mfsmaster.cfg
PERSONALITY = master
# ホスト名または IP アドレス
MASTER_HOST = mfs-cluster
```

@ mfs-master2

```conf:/etc/mfs/mfsmaster.cfg
PERSONALITY = shadow
# ホスト名または IP アドレス
MASTER_HOST = mfs-cluster
```

### 5-10. mfsgloas.cfg 設定
@ mfs-master1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfsgoals.cfg.dist mfsgoals.cfg`
`$ sudo vim mfsgoals.cfg`

```text:/etc/mfs/mfsgoals.cfg
2 2 : _ _
```

### 5-11. その他設定ファイル
@ mfs-master1, 2
`$ sudo cp -a globaliolimits.cfg.dist globaliolimits.cfg=
`$ sudo cp -a mfsexports.cfg.dist mfsexports.cfg`
`$ sudo cp -a mfstopology.cfg.dist mfstopology.cfg`

```
globaliolimits.cfg       mfsexports.cfg       mfsgoals.cfg       mfsmaster.cfg       mfstopology.cfg
globaliolimits.cfg.dist  mfsexports.cfg.dist  mfsgoals.cfg.dist  mfsmaster.cfg.dist  mfstopology.cfg.dist
```

## 6 LizardFS チャンクサーバー
### 6-1. epel リポジトリインストール
@ mfs-chunk1, 2
`$ yum install -y epel-release`

```diff:/etc/yum.repos.d/lizardfs.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 6-2. LizardFS リポジトリインストール
@ mfs-chunk1, 2
`$ curl http://packages.lizardfs.com/yum/el7/lizardfs.repo > /etc/yum.repos.d/lizardfs.repo`
`$ vim /etc/yum.repos.d/lizardfs.repo`

```diff:/etc/yum.repos.d/lizardfs.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 6-3. LizardFS チャンクサーバーインストール
@ mfs-chunk1, 2
`$ sudo yum install -y --enablerepo=lizardfs,epel lizardfs-chunkserver lizardfs-adm`

### 6-4. hosts ファイル設定
@ mfs-chunk1, 2
`$ vim /etc/hosts`

```conf:/etc/hosts
10.0.0.1	mfs-master1
10.0.0.2	mfs-master2
10.0.0.3	mfs-chunk1
10.0.0.4	mfs-chunk2
10.0.0.5	mfs-client1
10.0.0.6	mfs-client2
```

### 6-5. mfschunkserver.cfg 設定
@ mfs-chunk1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfschunkserver.cfg.dist mfschunkserver.cfg`
`$ sudo vim mfschunkserver.cfg`

```conf:/etc/mfs/mfschunkserver.cfg
# ホスト名または IP アドレス
MASTER_HOST = mfs-cluster
```

### 6-6. mfshdd.cfg 設定
@ mfs-chunk1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfshdd.cfg.dist mfshdd.cfg`
`$ sudo vim mfshdd.cfg`

```conf:/etc/mfs/mfshdd.cfg
/mnt/mfs
```

# 7. LizardFS クライアント
### 7-1. epel リポジトリインストール
@ mfs-client1, 2
`$ sudo yum install -y epel-release`

```diff:/etc/yum.repos.d/lizardfs.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 7-2. LizardFS リポジトリインストール
@ mfs-client1, 2
`$ sudo curl http://packages.lizardfs.com/yum/el7/lizardfs.repo > /etc/yum.repos.d/lizardfs.repo`
`$ sudo vim /etc/yum.repos.d/lizardfs.repo`

```diff:/etc/yum.repos.d/lizardfs.repo
- enabled=1
+ enabled=0
```
※ diff 表示

### 7-3. LizardFS クライアントインストール
@ mfs-client1, 2
`$ sudo yum install -y --enablerepo=lizardfs,epel lizardfs-client lizardfs-adm`

### 7-4. hosts ファイル設定
@ mfs-client1, 2
`$ sudo vim /etc/hosts`

```conf:/etc/hosts
10.0.0.1	mfs-master1
10.0.0.2	mfs-master2
10.0.0.3	mfs-chunk1
10.0.0.4	mfs-chunk2
10.0.0.5	mfs-client1
10.0.0.6	mfs-client2
```

### 7-5. mfsmount.cfg 設定
@ mfs-client1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfsmount.cfg.dist mfsmount.cfg`
`$ sudo vim mfsmount.cfg`

```conf:/etc/mfs/mfsmount.cfg
mfsmaster=mfs-cluster,mfsport=9421
/mnt/mfs
```

## 8. マスターフェイルオーバー環境の準備
ここから、マスターフェイルオーバー機構を自前で用意します。
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” で、keepalived 構成と、Go 言語デーモンのスクラッチ開発の 2 通りで用意しましたので、それらを少しだけカスタマイズしていきます。

## 9. keepalived マスターフェイルオーバー機構
以降で、keepalived による、マスターフェイルオーバー機構を構築します。

### 9-1. keepalived インストール
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#13-1-keepalived-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 9-3. keepalived 設定
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#13-2-keepalived-%E8%A8%AD%E5%AE%9A'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 9-4. notify Bash スクリプト作成
@ mfs-master1, 2
`$ sudo cd /etc/keepalived`
`$ sudo touch notify.sh`
`$ sudo vim notify.sh`

```shell:/etc/keepalived/notify.sh
#!/bin/bash
# notify.sh

# Check argument counts
if [ $# -ne 3 ]; then
    echo "Three arguments must be passed in $HOSTNAME." 1>&2
    exit 1
fi

# Init variables
NOTIFY=$1
IPV4=$2
PORT=$3

# Validate 1st argument values
if [ $NOTIFY != "master" ] && [ $NOTIFY != "backup" ]; then
    echo "The passed argument is invalid." 1>&2
    exit 1
fi

# Validate 2nd argument values
#...

# Validate 3rd argument values
#...

# Take cluster personality
PERSONALITY=`lizardfs-admin metadataserver-status $IPV4 $PORT`

# Notify master
if [ $NOTIFY = "master" ]; then
    ## Promote master
    if [[ $PERSONALITY =~ "personality: shadow" ]]; then
		### Restart master
        lizardfs-master -o initial-personality=master -o auto-recovery restart
    fi
# Notify backup
elif [ $NOTIFY = "backup" ]; then
    ## Demote Master
    if [[ $PERSONALITY =~ "personality: master" ]]; then
		### Restart shadow
		lizardfs-master -o initial-personality=shadow restart
    fi
fi

# Check promotion or demotion of master
if [ $? != 0 ]; then
    echo "Could not !$ in $HOSTNAME."
    exit 1
fi

## Restart LizardFS
systemctl restart lizardfs-master 2>&1 >/dev/null
if [ $? != 0 ]; then
    echo "Could not !$ in $HOSTNAME." 1>&2
    exit 1
fi

exit 0
```

## 10. 起動
### 10-1. keepalived 起動
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#13-4-keepalived-%E8%B5%B7%E5%8B%95'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 10-2. LizardFS マスターサーバー起動
@ mfs-master1, 2
`$ sudo systemctl start lizardfs-master`
`$ sudo systemctl status lizardfs-master`

### 10-3. LizardFS チャンクサーバー起動
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#9-2-%E3%83%81%E3%83%A3%E3%83%B3%E3%82%AF%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E8%B5%B7%E5%8B%95'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 10-4. クライアントマウント
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#9-3-%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 10-5. 同期テスト
@ mfs-client1
`$ sudo touch /mnt/mfs/test`

@ mfs-client2
`$ sudo ls /mnt/mfs`
`$ sudo rm -rf /mnt/mfs/test`

@ mfs-client1
`$ sudo ls /mnt/mfs`

### 10-6. keepalived マスターフェイルオーバーテスト
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#13-5-keepalived-%E8%87%AA%E5%8B%95%E3%83%95%E3%82%A7%E3%82%A4%E3%83%AB%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%86%E3%82%B9%E3%83%88'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

### 10-7. keepalived マスターフェイルバックテスト
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#13-6-keepalived-%E8%87%AA%E5%8B%95%E3%83%95%E3%82%A7%E3%82%A4%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E3%83%86%E3%82%B9%E3%83%88'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

## 11. Golang マスターフェイルオーバーデーモン
以降で、スクラッチで Golang マスターフェイルオーバーデーモンを作成します。

### 11-1. VIP のアサイン
この構築例の VIP（フローティング IP）は、手動アサインします。

@ mfs-master1
`$ sudo ip -f inet addr add 10.0.0.9/24 dev eth0`
`$ sudo arping -q -U -c5 -w1 10.0.0.9 -I eth0`

### 11-2. Golang マスターフェイルオーバープログラム作成
@ mfs-master1, 2
`$ sudo cd ~/go/`
`$ sudo nvim main.go`

```go:~/go/main.go
package main

import (
    "fmt"
    "net"
    "os"
    "os/exec"
    "strings"
    "time"
    "unsafe"
)

func main() {

    cluster_vip := os.Args[1]
    cluster_port := os.Args[2]
    cluster_address := cluster_vip + ":" + cluster_port
    self_ip := os.Args[3]
    self_port := os.Args[4]
    greeting_interval := 1
    greeting_timeout := 5
    retry_interval := 3
    retry_count := 3
    retry_timeout := 5

    for {
        // Sleep healthcheck interval seconds
        time.Sleep(time.Duration(greeting_interval) * time.Second)

        // Greet to cluster
        conn, err := net.DialTimeout("tcp", cluster_address, time.Duration(greeting_timeout)*time.Second)

        // Succeeded greeting
        if err == nil {
            conn.Close()
            fmt.Println("Could greet to cluster.")
            continue
        }

        // Failed greeting
        // Log
        //...
        fmt.Println("Could not greet to cluster.")

        // Loop re-connection to cluster
        health_flag := false
        for j := 0; j < retry_count; j++ {

            // Sleep re-connection interval seconds
            time.Sleep(time.Duration(retry_interval) * time.Second)

            // Re-connect
            conn, err = net.DialTimeout("tcp", cluster_address, time.Duration(retry_timeout)*time.Second)

            // Failed re-connection
            if err != nil {
                // Log
                //...
                fmt.Println("Could not re-connect to cluster.")
                continue
            }

            // Succeeded re-connection
            fmt.Println("Succeeded re-connection to cluster.")
            health_flag = true
            break
        }

        // Alive
        if health_flag {
            continue
        }

        // Dead
        personality_byte, err := exec.Command("lizardfs-admin", "metadataserver-status", self_ip, self_port).Output()
        personality := *(*string)(unsafe.Pointer(&personality_byte))

        // Failed get personality
        if err != nil {
            // Log
            //...
            fmt.Println("Could not get personality.")
            os.Exit(1)
        }

        // Demote from master
        if -1 != strings.Index(personality, "personality: master") {
            // Restart shadow
            err = exec.Command("lizardfs-master", "-o initial-personality=shadow", "restart").Run()

            // Failed master demotion
            if err != nil {
                // Log
                //...
                fmt.Println("Could not demote to master.")
                os.Exit(1)
            }
            fmt.Println("Succeeded master demotion.")

            // Unasign VIP
            err = exec.Command("ip", "-f inet addr delete 10.0.0.9/24 dev eth0").Run()

            // Failed VIP Unasigning
            if err != nil {
                // Log
                //...
                fmt.Println("Could not unasigning vip.")
                os.Exit(1)
            }
            fmt.Println("Succeeded master demotion.")

        // Promote to master
        } else if -1 != strings.Index(personality, "personality: shadow") {
            // Asign VIP
            err = exec.Command("ip", "-f inet addr add 10.0.0.9/24 dev eth0").Run()
            // Failed asigning VIP
            if err != nil {
                // Log
                //...
                fmt.Println("Could not asign VIP.")
                os.Exit(1)
            }

            // Execute arping
            err = exec.Command("arping", "-q -U -c5 -w1 10.0.0.9 -I eth0").Run()
            // Failed arping
            if err != nil {
                // Log
                //...
                fmt.Println("Could not execute arping.")
                os.Exit(1)
            }

            // Restart master
            err = exec.Command("lizardfs-master", "-o initial-personality=master -o auto-recovery", "restart").Run()

            // Failed master promotion
            if err != nil {
                // Log
                //...
                fmt.Println("Could not promote to master.")
                os.Exit(1)
            }
            fmt.Println("Succeeded master promotion.")
        }

        // Restart LizardFS
        err = exec.Command("systemctl", "restart", "lizardfs-master").Run()

        // Failed restart
        if err != nil {
            // Log
            //...
            fmt.Println("Could not restart LizardFS.")
            os.Exit(1)
        }
        fmt.Println("Succeeded restart LizardFs.")
    } // End Loop
}
```

`$ sudo go build main.go`

### 11-3. Golang マスターフェイルオーバープログラムテスト
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#14-2-golang-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%82%A4%E3%83%AB%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%86%E3%82%B9%E3%83%88'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

## 12. Golang マスターフェイルオーバーデーモン化
前回の記事 “<a href='https://qiita.com/KyojiOsada/items/b0b32b2fcda4e967164b#14-3-golang-%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%82%A4%E3%83%AB%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%87%E3%83%BC%E3%83%A2%E3%83%B3%E4%BD%9C%E6%88%90'>LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）</a>” をご参照ください。

## 13. まとめ
この記事では、 LizardFS による分散ファイルシステムの構築（GA 版 3.12 yum インストール編）の例について投稿しました。
自前のマスターフェイルオーバー機構につきましては、keepalvied や、スクラッチの Golang デーモンにより実現しましたが、死活監視パターンにつきましては、実用にあわせて適宜実装してください。
他の分散ファイルシステムとのパフォーマンスのベンチマークテストにつきましては、別の機会に投稿しようと思います。