LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）

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

## 2. はじめに
LizardFS は、MooseFS から 2013 年にフォークしたフォールトトレラントな分散ファイルシステムで、CloudWeavers などに採用されているようです。
<a href='https://qiita.com/KyojiOsada/items/aa38868c0bbc057ab4e4'>過去の記事で GlusterFS について</a>、いくつか投稿しましたが、LizardFS については 2018/12/8 時点で日本語の記事がほとんど無いので、LizardFS の一部の解説も含め LizardFS の構築例を投稿してみます。
LizardFS と他の分散ファイルシステムのパフォーマンスのベンチマークテストについては別の機会に投稿しようと思います。
アドバイスをくれた LizardFS Inc. の Mark Mulrainey に感謝します。

## 3. LizardFS 環境
* RHEL 7 系
* LizardFS 3.13.0-rc1
* keepalived 1.3.5
* Go 1.9

## 4. LizardFS 構築の前に
RHEL-7 系最新バージョンは、2018/12/8 時点では GA 版が 3.12 で、RC 版が 3.13.0 です。
この 2 つの大きな違いは、マスターサーバーをクラスター管理してくれる “lizardfs-uraft” パッケージが入ってるかどうかのようです。
現時点の <a href='https://docs.lizardfs.com/'>公式ドキュメント</a> では、“lizardfs-uraft” パッケージ無しの GA 版 3.12 用の yum インストールと構築例が載っています。
しかし、“lizardfs-uraft” パッケージ有りの RC 版 3.13 についてはまだ載ってませんので、この記事ではまず、公式ドキュメントに載ってない RC 版 3.13 用の RPM パッケージインストールと構築例を投稿します。
GA 版 3.12 用の yum インストールと構築例については、“<a href='https://qiita.com/KyojiOsada/items/e28f5de9e9bb2e7c9c16'>LizardFS による分散ファイルシステムの構築（GA 版 3.12 yum インストール編）</a>” に投稿しました。

~~マスターフェイルオーバー機構の提供については商用版のみのようで、もし公式のマスターフェイルオーバー機構が必要な場合には、Skytechnology 社のサポートを受けるほうがいいかもしれません。~~
&#8593;
**こちらにつきまして、GA 版 3.12 まではこの通りでライセンスが必要となりますが、RC 版 3.13 以降からライセンスなしで利用可能となりました。
“[修正版 - LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）](https://qiita.com/KyojiOsada/items/d561cba228d93830e103)” で修正記事をアップしましたので、もし、オフィシャルのマスターフェイルオーバー機構を利用する場合は [修正記事](https://qiita.com/KyojiOsada/items/d561cba228d93830e103) を、自前で構築する場合はこの記事をご参照ください。**

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

### LizardFS パッケージ一覧
```
lizardfs-3.13.0-0el7.src.rpm
lizardfs-debuginfo-3.13.0-0el7.x86_64.rpm
lizardfs-adm-3.13.0-0el7.x86_64.rpm
lizardfs-lib-client-3.13.0-0el7.x86_64.rpm
lizardfs-cgi-3.13.0-0el7.x86_64.rpm
lizardfs-master-3.13.0-0el7.x86_64.rpm
lizardfs-cgiserv-3.13.0-0el7.x86_64.rpm
lizardfs-metalogger-3.13.0-0el7.x86_64.rpm
lizardfs-chunkserver-3.13.0-0el7.x86_64.rpm
lizardfs-nfs-ganesha-3.13.0-0el7.x86_64.rpm
lizardfs-client-3.13.0-0el7.x86_64.rpm
lizardfs-uraft-3.13.0-0el7.x86_64.rpm
lizardfs-client3-3.13.0-0el7.x86_64.rpm
```

## 6. LizardFS マスターサーバー
### 6-1. LizardFS ダウンロード
<a href='http://lizardfs.com/download/'>LizardFS ダウンロードページ</a>

@ mfs-master1, 2
`$ sudo mkdir /usr/local/src/lizardfs`
`$ sudo cd /usr/local/src/lizardfs`
`$ sudo wget https://lizardfs.com/wp-content/uploads/2018/07/lizardfs-bundle-CentOS-7.5.1804.tar `

### 6-2. LizardFS インストール
@ mfs-master1, 2
`$ sudo tar -xvf lizardfs-bundle-CentOS-7.5.1804.tar`
`$ sudo cd lizardfs-bundle-CentOS-7.5.1804`
`$ sudo yum -y localinstall lizardfs-master-3.13.0-0el7.x86_64.rpm lizardfs-uraft-3.13.0-0el7.x86_64.rpm lizardfs-adm-3.13.0-0el7.x86_64.rpm`

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

```conf:/etc/hosts
10.0.0.9    mfs-cluster
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-4. metadata.mfs 設定
@ mfs-master1, 2
`$ sudo cd /var/lib/mfs`
`$ sudo cp -a metadata.mfs.empty metadata.mfs`

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

```conf:/etc/mfs/mfsmaster.cfg
PERSONALITY = ha-cluster-managed
ADMIN_PASSWORD = パスワード
#  VIP アドレスまたは VIP ホスト名
MASTER_HOST = mfs-cluster
```

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

@ mfs-master1

```conf:/etc/mfs/lizardfs-uraft.cfg
URAFT_PORT = 9427
URAFT_STATUS_PORT = 9428
URAFT_NODE_ADDRESS = 10.0.0.1
URAFT_NODE_ADDRESS = 10.0.0.2
URAFT_ID = 0
LOCAL_MASTER_ADDRESS = localhost
LOCAL_MASTER_MATOCL_PORT = 9421
ELECTION_TIMEOUT_MIN = 400
ELECTION_TIMEOUT_MAX = 600
HEARTBEAT_PERIOD = 20
LOCAL_MASTER_CHECK_PERIOD = 250
URAFT_FLOATING_IP = 10.0.0.9
URAFT_FLOATING_NETMASK = 255.255.255.0
URAFT_FLOATING_IFACE = eth0
```

@ mfs-master2

```conf:/etc/mfs/lizardfs-uraft.cfg
URAFT_PORT = 9427
URAFT_STATUS_PORT = 9428
URAFT_NODE_ADDRESS = 10.0.0.1
URAFT_NODE_ADDRESS = 10.0.0.2
URAFT_ID = 1
LOCAL_MASTER_ADDRESS = localhost
LOCAL_MASTER_MATOCL_PORT = 9421
ELECTION_TIMEOUT_MIN = 400
ELECTION_TIMEOUT_MAX = 600
HEARTBEAT_PERIOD = 20
LOCAL_MASTER_CHECK_PERIOD = 250
URAFT_FLOATING_IP = 10.0.0.9
URAFT_FLOATING_NETMASK = 255.255.255.0
URAFT_FLOATING_IFACE = eth0
```

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

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

### 6-8. 他の cfg ファイル設定
@ mfs-master1, 2
`$ sudo cd /etc/mfs`
`$ 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       lizardfs-uraft.cfg.dist  mfsgoals.cfg       mfsmaster.cfg.dist
globaliolimits.cfg.dist  mfsexports.cfg           mfsgoals.cfg.dist  mfstopology.cfg
lizardfs-uraft.cfg       mfsexports.cfg.dist      mfsmaster.cfg      mfstopology.cfg.dist
```

### 6-9. カーネル設定
@ mfs-master1, 2
`$ sudo echo 1 > /proc/sys/net/ipv4/conf/all/arp_accept`

## 7 LizardFS チャンクサーバー
### 7-1. LizardFS ダウンロード
@ mfs-chunk1, 2
<a href='http://lizardfs.com/download/'>LizardFS ダウンロードページ</a>

`$ sudo mkdir /usr/local/src/lizardfs`
`$ sudo cd /usr/local/src/lizardfs`
`$ sudo wget https://lizardfs.com/wp-content/uploads/2018/07/lizardfs-bundle-CentOS-7.5.1804.tar` 

### 7-2. LizardFS インストール
@ mfs-chunk1, 2
`$ sudo tar -xvf lizardfs-bundle-CentOS-7.5.1804.tar`
`$ sudo cd lizardfs-bundle-CentOS-7.5.1804`
`$ sudo yum -y localinstall lizardfs-master-3.13.0-0el7.x86_64.rpm lizardfs-uraft-3.13.0-0el7.x86_64.rpm lizardfs-adm-3.13.0-0el7.x86_64.rpm`

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

```conf:/etc/hosts
10.0.0.9    mfs-cluster
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-4. mfschunkserver.cfg 設定
@ mfs-chunk1, 2
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfschunkserver.cfg.dist mfschunkserver.cfg`
`$ sudo vim mfschunkserver.cfg`

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

### 7-5. mfshdd.cfg 設定
@ mfs-chunk1, 2
`$ sudo mkdir /mnt/mfs`
`$ sudo chown -R mfs:mfs /mnt/mfs`
`$ sudo cd /etc/mfs/`
`$ sudo cp -a mfshdd.cfg.dist mfshdd.cfg`
`$ sudo vim mfshdd.cfg`

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

## 8 LizardFS クライアント
### 8-1. LizardFS ダウンロード
@ mfs-client1, 2
<a href='http://lizardfs.com/download/'>LizardFS ダウンロードページ</a>

`$ sudo mkdir /usr/local/src/lizardfs`
`$ sudo cd /usr/local/src/lizardfs`
`$ sudo wget https://lizardfs.com/wp-content/uploads/2018/07/lizardfs-bundle-CentOS-7.5.1804.tar`

### 8-2. LizardFS クライアントインストール
@ mfs-client1, 2
`$ sudo tar -xvf lizardfs-bundle-CentOS-7.5.1804.tar`
`$ sudo cd lizardfs-bundle-CentOS-7.5.1804`
`$ sudo yum -y localinstall lizardfs-master-3.13.0-0el7.x86_64.rpm lizardfs-uraft-3.13.0-0el7.x86_64.rpm lizardfs-adm-3.13.0-0el7.x86_64.rpm`

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

```conf:/etc/hosts
10.0.0.9    mfs-cluster
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
```

### 8-4. 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
```

## 9  LizardFS 起動
### 9-1. クラスター起動
@ mfs-master1, 2
`$ sudo systemctl start lizardfs-uraft`
`$ sudo systemctl status lizardfs-uraft`
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`

```
Server 1:
	IP: 10.0.0.9
	Port: 9421
	Hostname: mfs-master1
	Personality: master
	Status: running
	Metadata version: 1
	Version: 3.13.0
Server 2:
	IP: 10.0.0.2
	Port: 9421
	Hostname: mfs-master2
	Personality: shadow
	Status: connected
	Metadata version: 1
	Version: 3.13.0
```

### 9-2. チャンクサーバー起動
@ mfs-chunk1, 2
`$ sudo systemctl start lizardfs-chunkserver`
`$ sudo systemctl status lizardfs-chunkserver`
`$ sudo lizardfs-admin list-chunkservers 10.0.0.9 9421`

```
Server 10.0.0.3:9422:
	version: 3.13.0
	label: _
	chunks: 0
	used space: 6.5GiB / 27GiB
	chunks marked for removal: 0
	used space marked for removal: 0B / 0B
	errors: 0
Server 10.0.0.4:9422:
	version: 3.13.0
	label: _
	chunks: 0
	used space: 6.5GiB / 27GiB
	chunks marked for removal: 0
	used space marked for removal: 0B / 0B
	errors: 0
```

### 9-3. クライアントマウント
@ mfs-client1, 2
`$ sudo mkdir /mnt/mfs`
`$ sudo mfsmount -o cacheexpirationtime=0 -o readaheadmaxwindowsize=4096 -o big_writes,nosuid,nodev,noatime /mnt/mfs`
`$ sudo lizardfs-admin list-mounts 10.0.0.9 9421`

```
session 1: 
	ip: 10.0.0.5
	mount point: /mnt/mfs
	version: 3.13.0
	root dir: /
	root uid: 0
	root gid: 0
	users uid: 999
	users gid: 999
	read only: no
	restricted ip: yes
	ignore gid: no
	all can change quota: no
	map all users: no
session 2: 
	ip: 10.0.0.6
	mount point: /mnt/mfs
	version: 3.13.0
	root dir: /
	root uid: 0
	root gid: 0
	users uid: 999
	users gid: 999
	read only: no
	restricted ip: yes
	ignore gid: no
	all can change quota: no
	map all users: no
```

## 10 同期テスト
@ 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`

## 11. 障害テスト
@ mfs-master2
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`

```
Server 1:
	IP: 10.0.0.9
	Port: 9421
	Hostname: mfs-master1
	Personality: master
	Status: running
	Metadata version: 42
	Version: 3.13.0
Server 2:
	IP: 10.0.0.2
	Port: 9421
	Hostname: mfs-master2
	Personality: shadow
	Status: connected
	Metadata version: 42
	Version: 3.13.0
```

`$ sudo lizardfs-admin lizardfs-admin stop-master-without-saving-metadata 10.0.0.9 9421`
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`

```
Server 1:
	IP: 10.0.0.9
	Port: 9421
	Hostname: mfs-master1
	Personality: master
	Status: running
	Metadata version: 42
	Version: 3.13.0
```

## 12. フェイルオーバー機構準備
<b>12/14 追記：
LizardFS 3.13 以降のバージョンを利用する限りは、マスターフェイルオーバー機構は lizardfs-uraft パッケージで実現可能です。</b>

ここから、マスターフェイルオーバー機構を自前で用意します。
候補としては、VRRPd, keepalived, スクラッチ開発ですが、VRRPd については、RHEL 7 系から動かなくなったので、keepalived 構成と、Go 言語デーモンのスクラッチ開発の 2 通りで用意しようと思います。

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

### 13-1. keepalived インストール
@ master 1, 2
`$ sudo yum -y install keepalived`
`$ sudo cd /etc/keepalived`
`$ sudo cp -a keepalived.conf keepalived.conf.org`
`$ sudo vim keepalived.conf`

### 13-2. keepalived 設定
@ master 1

```conf:/etc/keepalived/keepalived.conf
global_defs {
   notification_email {
       test@example.com
   }
   notification_email_from master1@example.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id master1.example.com
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 1
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass パスワード
    }
    virtual_ipaddress {
        10.0.0.9/24 dev eth0
    }
	notify_master "/etc/keepalived/notify.sh master 10.0.0.1 9421"
	notify_backup "/etc/keepalived/notify.sh backup 10.0.0.1 9421"
}
```

@ master 2

```conf:/etc/keepalived/keepalived.conf
global_defs {
   notification_email {
       test@example.com
   }
   notification_email_from master2@example.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id master2.example.com
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 1
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass パスワード
    }
    virtual_ipaddress {
        10.0.0.9/24 dev eth0
    }
    notify_master "/etc/keepalived/notify.sh master 10.0.0.2 9421"	 
    notify_backup "/etc/keepalived/notify.sh backup 10.0.0.2 9421"
}
```

### 13-3. notify Bash スクリプト作成
@ master 1, 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
		lizardfs-uraft-helper quick-stop
	fi
# Notify backup
elif [ $NOTIFY = "backup" ]; then
	## Demote Master
	if [[ $PERSONALITY =~ "personality: master" ]]; then
		lizardfs-uraft-helper demote
	fi
fi

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

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

exit 0
```

`$ sudo chmoe 0770 notify.sh`

### 13-4. keepalived 起動
@ master 1, 2
`$ sudo systemctl start keepalived`
`$ sudo systemctl status keepalived`
`$ sudo ip addr`

### 13-5. keepalived 自動フェイルオーバーテスト
@ master 1
`$ sudo systemctl stop keepalived`
`$ sudo systemctl status keepalived`
`$ sudo ip addr`
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`

@ master 2
`$ ip addr`
`$ lizardfs-admin list-metadataservers 10.0.0.9 9421`

### 13-6. keepalived 自動フェイルバックテスト
@ master 1
`$ sudo systemctl start keepalived`
`$ sudo systemctl status keepalived`
`$ sudo ip addr`
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`

@ master 2
`$ sudo ip addr`
`$ sudo lizardfs-admin list-metadataservers 10.0.0.9 9421`


## 14. Golang マスターフェイルオーバーデーモン
以降で、Go プログラムによる自動フェイルオーバーデーモンのサンプルをスクラッチで作成します。

### 14-1. Golang マスターフェイルオーバープログラム作成
@ master 1, 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") {
			err := exec.Command("lizardfs-uraft-helper", "demote").Run()

			// Failed master demotion
			if err != nil {
				// Log
				//...
				fmt.Println("Could not demote to master.")
				os.Exit(1)
			}
			fmt.Println("Succeeded master demotion.")
			// Promote to master
		} else if -1 != strings.Index(personality, "personality: shadow") {
			err := exec.Command("lizardfs-uraft-helper", "remote").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-uraft").Run()

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

`$ sudo go build main.go`

### 14-2. Golang プログラムマスターフェイルオーバーテスト
@ master 1
プログラム起動
`$ sudo ./main 10.0.0.9 9421 10.0.0.1 9421`

@ master 2
プログラム起動
`$ sudo ./main 10.0.0.9 9421 10.0.0.2 9421`

@ master 1
VIP（フローティング IP）の割り当て解除
`ip -f inet addr delete 10.0.0.9/24 dev eth0`

@ master 1, 2
標準出力で自動フェイルオーバーしていることを確認

### 14-3. Golang マスターフェイルオーバーデーモン作成
@ master 1, 2
適宜、デーモンプログラム最適化をしておく。
（必要な死活監視パターンの追加や、fmt.Println() を外してロガーを仕込むなど。）

`$ sudo cp -a ~/go/main /opt/lizardfs-failover`
`$ sudo cd /usr/lib/systemd/system/`
`$ sudo touch lizardfs-failover.service`
`$ sudo vim lizardfs-failover.service`

```conf:/usr/lib/systemd/system/foo.service
[Unit]
Description = LizardFS auto failover daemon

[Service]
ExecStart = /opt/lizardfs-failover
Restart = always
Type = simple

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

### 14-4. Golang マスターフェイルオーバーデーモン起動
`$ sudo systemctl start lizardfs-failover`
`$ sudo systemctl enable lizardfs-failover`

## 15. まとめ
この記事では、 LizardFS による分散ファイルシステムの構築（RC 版 3.13 RPM パッケージ編）の例について投稿しました。


自前の自動フェイルオーバー環境につきましては、keepalvied や、スクラッチの Golang デーモンにより実現しましたが、死活監視パターンにつきましては、実用にあわせて適宜実装が必要です。
他の分散ファイルシステムとのパフォーマンスのベンチマークテストは別の機会に投稿します。
GA 版 3.12（lizardfs-uraft パッケージなし）の構築例につきましては、
“<a href='https://qiita.com/KyojiOsada/items/e28f5de9e9bb2e7c9c16'>LizardFS による分散ファイルシステムの構築（GA 版 3.12 yum インストール編）</a>” に投稿しました。