1. 要約
この記事では、LizardFS による分散ファイルシステムの構築(RC 版 3.13 RPM パッケージ編)の例と、keepalived や、スクラッチの Golang デーモンによる、自前のマスターフェイルオーバー機構の構築例について投稿します。
2. はじめに
LizardFS は、MooseFS から 2013 年にフォークしたフォールトトレラントな分散ファイルシステムで、CloudWeavers などに採用されているようです。
過去の記事で GlusterFS について、いくつか投稿しましたが、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” パッケージが入ってるかどうかのようです。
現時点の 公式ドキュメント では、“lizardfs-uraft” パッケージ無しの GA 版 3.12 用の yum インストールと構築例が載っています。
しかし、“lizardfs-uraft” パッケージ有りの RC 版 3.13 についてはまだ載ってませんので、この記事ではまず、公式ドキュメントに載ってない RC 版 3.13 用の RPM パッケージインストールと構築例を投稿します。
GA 版 3.12 用の yum インストールと構築例については、“LizardFS による分散ファイルシステムの構築(GA 版 3.12 yum インストール編)” に投稿しました。
マスターフェイルオーバー機構の提供については商用版のみのようで、もし公式のマスターフェイルオーバー機構が必要な場合には、Skytechnology 社のサポートを受けるほうがいいかもしれません。
↑
こちらにつきまして、GA 版 3.12 まではこの通りでライセンスが必要となりますが、RC 版 3.13 以降からライセンスなしで利用可能となりました。
“修正版 - LizardFS による分散ファイルシステムの構築(RC 版 3.13 RPM パッケージ編)” で修正記事をアップしましたので、もし、オフィシャルのマスターフェイルオーバー機構を利用する場合は 修正記事 を、自前で構築する場合はこの記事をご参照ください。
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 ダウンロード
@ 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
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
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
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
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
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
LizardFS ダウンロードページ
$ 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
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
# ホスト名または 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
/mnt/mfs
8 LizardFS クライアント
8-1. LizardFS ダウンロード
@ mfs-client1, 2
LizardFS ダウンロードページ
$ 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
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
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. フェイルオーバー機構準備
12/14 追記:
LizardFS 3.13 以降のバージョンを利用する限りは、マスターフェイルオーバー機構は lizardfs-uraft パッケージで実現可能です。
ここから、マスターフェイルオーバー機構を自前で用意します。
候補としては、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
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
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
#!/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
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
[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 パッケージなし)の構築例につきましては、
“LizardFS による分散ファイルシステムの構築(GA 版 3.12 yum インストール編)” に投稿しました。