やってみました。
Vagrant
sv01/sv02 が NFS をマウントするクライアントで、nfs01/nfs02 が NFS サーバです。
Vagrant.configure(2) do |config|
config.vm.box = "bento/centos-7.2"
config.ssh.insert_key = false
config.vm.define "sv01" do |config|
config.vm.hostname = "sv01"
config.vm.network :private_network, ip: "192.168.33.11", virtualbox__intnet: "ha-nfs"
end
config.vm.define "sv02" do |config|
config.vm.hostname = "sv02"
config.vm.network :private_network, ip: "192.168.33.12", virtualbox__intnet: "ha-nfs"
end
config.vm.define "nfs01" do |config|
config.vm.hostname = "nfs01"
config.vm.network :private_network, ip: "192.168.33.21", virtualbox__intnet: "ha-nfs"
end
config.vm.define "nfs02" do |config|
config.vm.hostname = "nfs02"
config.vm.network :private_network, ip: "192.168.33.22", virtualbox__intnet: "ha-nfs"
end
config.vm.provider :virtualbox do |v|
v.linked_clone = true
end
end
vagrant up
の後、名前で解決できるように hosts を作成しておきます。
sudo tee /etc/hosts <<EOS
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.33.11 sv01
192.168.33.12 sv02
192.168.33.21 nfs01
192.168.33.22 nfs02
192.168.33.20 nfsvip
EOS
nfsvip は Floating IP で付ける予定の NFS サーバのアクティブ側の仮想アドレスです。
パッケージのインストール
必要なパッケージを yum でインストールします。DRBD は ELrepo からインストールします。
# [nfs01/nfs02]
sudo yum -y install http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
sudo yum -y install kmod-drbd84 pacemaker corosync resource-agents pcs nfs-utils
DRBD
DRBD の設定を行います。Vagrant 環境での検証なので簡単化のためにループバックデバイスを使います。
# [nfs01/nfs02]
sudo fallocate -l 1G /var/opt/drbddisk
sudo losetup /dev/loop0 /var/opt/drbddisk
sudo tee /etc/drbd.d/nfs.res <<EOS
resource nfs
{
protocol C;
device /dev/drbd0;
meta-disk internal;
on nfs01 {
address 192.168.33.21:7791;
disk /dev/loop0;
}
on nfs02 {
address 192.168.33.22:7791;
disk /dev/loop0;
}
}
EOS
メタデータを作成して開始します。
# [nfs01/nfs02]
sudo drbdadm create-md nfs
sudo systemctl start drbd.service
sudo systemctl status drbd.service
開始直後なので Inconsistent になっています。
# [nfs01/nfs02]
cat /proc/drbd
# (...snip...)
# 0: cs:Connected ro:Secondary/Secondary ds:Inconsistent/Inconsistent C r-----
# ns:0 nr:0 dw:0 dr:0 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:1048508
nfs01 をプライマリにします。
# [nfs01]
sudo drbdadm primary nfs --force
--force
したので同期も開始します。
# [nfs01]
cat /proc/drbd
# (...snip...)
# 0: cs:SyncSource ro:Primary/Secondary ds:UpToDate/Inconsistent C r-----
# ns:1720 nr:0 dw:0 dr:2632 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:1046788
# [>....................] sync'ed: 0.4% (1046788/1048508)K
# finish: 0:08:43 speed: 1,720 (1,720) K/sec
DRBD のブロックデバイスを xfs でフォーマットします。
# [nfs01]
sudo mkfs.xfs /dev/drbd/by-res/nfs
マウントポイントを作成してマウントします。
# [nfs01]
sudo mkdir -p /drbd/nfs
sudo mount /dev/drbd/by-res/nfs /drbd/nfs
必要なディレクトリを作成します。
それぞれ exportfs するディレクトリと /var/lib/nfs
に mount --bind
するディレクトリです(ocf:heartbeat:nfsserver
が勝手にやります)。
# [nfs01]
sudo mkdir -p /drbd/nfs/nfsroot
sudo mkdir -p /drbd/nfs/var-lib-nfs
アンマウントしてセカンダリに戻します。
# [nfs01]
sudo umount /drbd/nfs
sudo drbdadm secondary nfs
次は nfs02 で作業します。もうそろそろ同期も終わっているでしょう。
# [nfs02]
cat /proc/drbd
# (...snip...)
# 0: cs:Connected ro:Secondary/Secondary ds:UpToDate/UpToDate C r-----
# ns:728889 nr:0 dw:1054333 dr:725379 al:12 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:0
DRBD リソースをプライマリにします。
# [nfs02]
sudo drbdadm primary nfs
マウントポイントを作成してマウントします。
# [nfs02]
sudo mkdir -p /drbd/nfs
sudo mount /dev/drbd/by-res/nfs /drbd/nfs
nfs01 で作成したディレクトリが見えていることを確認します。
# [nfs02]
ls /drbd/nfs
# nfsroot var-lib-nfs
アンマウントしてセカンダリに戻ります。
# [nfs02]
sudo umount /drbd/nfs
sudo drbdadm secondary nfs
DRBD の作業は終わりなので停止しておきます(DRBD は Pacemaker から開始される)。
# [nfs01/nfs02]
sudo systemctl stop drbd.service
sudo systemctl status drbd.service
Corosync
Corosync の設定ファイルを作成します。下記のような感じで作成します。2ノードなので quorum で two_node: 1
が必要です。
# [nfs01/nfs02]
sudo tee /etc/corosync/corosync.conf <<EOS
totem {
version: 2
crypto_cipher: none
crypto_hash: none
token: 4000
rrp_mode: active
transport: udpu
interface {
ringnumber: 0
bindnetaddr: 192.168.33.0
mcastaddr: 239.255.1.1
mcastport: 5405
}
}
logging {
timestamp: on
debug: off
to_stderr: no
to_syslog: no
to_logfile: yes
logfile: /var/log/cluster/corosync.log
}
nodelist {
node {
ring0_addr: 192.168.33.21
nodeid: 1
}
node {
ring0_addr: 192.168.33.22
nodeid: 2
}
}
quorum {
provider: corosync_votequorum
two_node: 1
}
EOS
開始します。pacemaker を開始すると corosync も開始するので pacemaker だけで良いです。
# [nfs01/nfs02]
sudo systemctl start pacemaker.service
sudo systemctl status pacemaker.service
両方のノードがオンラインになっていることを確認します。
# [nfs01]
sudo crm_mon -1
# (...snip...)
#
# Online: [ nfs01 nfs02 ]
#
# (...snip...)
Pacemaker
Pacemaker のリソース定義を行います。
まずはプロパティやリソースデフォルトを設定します。
# [nfs01]
sudo pcs property set stonith-enabled="false"
sudo pcs property set no-quorum-policy="ignore"
sudo pcs property set start-failure-is-fatal="false"
sudo pcs resource defaults migration-threshold="5"
sudo pcs resource defaults resource-stickiness="INFINITY"
sudo pcs resource defaults failure-timeout="3600s"
CIB をファイルに出力します。
# [nfs01]
sudo pcs cluster cib output.cib
出力した CIB ファイルを対象にリソースを作成していきます。
まずは DRBD のリソース。これは Master/Slave リソースです。
# [nfs01]
sudo pcs -f output.cib resource create drbd ocf:linbit:drbd \
drbd_resource="nfs" drbdconf="/etc/drbd.conf" \
op start timeout="240s" on-fail="restart" \
op stop timeout="100s" on-fail="block" \
op monitor interval="20s" timeout="20s" start-delay="1m" on-fail="restart" \
--master meta notify="true"
DRBD デバイスのマウント、仮想アドレスの付与、NFS サーバ、exportfs のリソースを作成します。これらは通常のリソースです。
# [nfs01]
sudo pcs -f output.cib resource create fs ocf:heartbeat:Filesystem \
device="/dev/drbd/by-res/nfs" directory="/drbd/nfs" fstype="xfs" \
op start timeout="60s" on-fail="restart" \
op stop timeout="60s" on-fail="block" \
op monitor interval="10s" timeout="60s" on-fail="restart"
sudo pcs -f output.cib resource create vip ocf:heartbeat:IPaddr2 \
ip="192.168.33.20" cidr_netmask="24" nic="enp0s8" \
op start timeout="20s" on-fail="restart" \
op stop timeout="20s" on-fail="block" \
op monitor interval="10s" timeout="20s" on-fail="restart"
sudo pcs -f output.cib resource create nfsserver ocf:heartbeat:nfsserver \
nfs_shared_infodir="/drbd/nfs/var-lib-nfs" \
op start timeout="20s" \
op stop timeout="20s" \
op monitor interval="10s" timeout="10s" on-fail="restart" start-delay="20s"
sudo pcs -f output.cib resource create exportfs ocf:heartbeat:exportfs \
clientspec="192.168.33.0/24" options="rw,no_root_squash" directory="/drbd/nfs/nfsroot" fsid="root"
グループにします。
# [nfs01]
sudo pcs -f output.cib resource group add group-nfs fs vip nfsserver exportfs
drbd リソースと↑のグループとの制約を作成します。↑のグループは drbd のマスターと同じノードでなければならないし、drbd のプロモートが先に行われる必要があります。
# [nfs01]
sudo pcs -f output.cib constraint colocation add master drbd-master with group-nfs INFINITY
sudo pcs -f output.cib constraint order promote drbd-master then start group-nfs
CIB ファイルの変更を適用します。
# [nfs01]
sudo pcs cluster cib-push output.cib
ステータスを表示させてそれっぽくなっていれば OK です。
# [nfs01]
sudo pcs status
# (...snip...)
#
# Online: [ nfs01 nfs02 ]
#
# Full list of resources:
#
# Master/Slave Set: drbd-master [drbd]
# Masters: [ nfs01 ]
# Slaves: [ nfs02 ]
# Resource Group: group-nfs
# fs (ocf::heartbeat:Filesystem): Started nfs01
# vip (ocf::heartbeat:IPaddr2): Started nfs01
# nfsserver (ocf::heartbeat:nfsserver): Started nfs01
# exportfs (ocf::heartbeat:exportfs): Started nfs01
#
# (...snip...)
クライアントからマウント
sv01 からマウントして適当にファイルを書いてみます。
# [sv01]
sudo mount -t nfs nfsvip:/drbd/nfs/nfsroot /mnt -o rw,rsize=8192,wsize=8192,soft,intr,timeo=20,retrans=3
uname -n | sudo tee /mnt/$(uname -n)
sv02 からもマウントして適当にファイルを書いてみます。
# [sv02]
sudo mount -t nfs nfsvip:/drbd/nfs/nfsroot /mnt -o rw,rsize=8192,wsize=8192,soft,intr,timeo=20,retrans=3
uname -n | sudo tee /mnt/$(uname -n)
相互に読めることを確認します。
# [sv01]
cat /mnt/sv02
# [sv02]
cat /mnt/sv01
障害発生
NFS のノードで障害が発生したときの動作を確認します。
sv01 でマウント先のファイルに連続して書き込みまくります。
# [sv01]
sudo touch /mnt/date.txt
sudo chown $USER: /mnt/date.txt
while sleep 0.1; do date 1>&2; date > /mnt/date.txt; done
おもむろにアクティブ側のノードを強制終了させます。
# [nfs01]
sudo halt -f
20秒ぐらい書き込みが止まった後、I/O error になって書き込みが再開しました。
# [sv01]
# (...snip...)
# Tue Jun 7 01:47:23 UTC 2016
# Tue Jun 7 01:47:23 UTC 2016
# -bash: /mnt/date.txt: Input/output error
# Tue Jun 7 01:47:40 UTC 2016
# Tue Jun 7 01:47:40 UTC 2016
# (...snip...)
実際の時間や動作は Pacemaker/Corosync の設定や NFS のマウントオプションによって変わります。
なお、DRBD のディスクにループバックデバイスを使用しているのと、pacemaker の自動起動を設定していないので、ノードの再起動後は手動でループバックデバイスの作成と pacemaker の起動を行う必要があります。
# [nfs01]
sudo losetup /dev/loop0 /var/opt/drbddisk
sudo systemctl start pacemaker
ノード起動時の monitor で NFS Server が開始してしまう問題
NFS のノードを再起動して pacemaker を起動したときにですが、起動したノードの側で下記のように NFS server が起動した旨のログが記録されました。
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Status: rpcbind
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Status: nfs-mountd
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Starting NFS server ...
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Start: rpcbind i: 10
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Start: v3locking: 0
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Start: nfs-mountd i: 10
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Start: nfs-idmapd i: 10
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Start: rpc-statd i: 10
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: executing sm-notify
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: NFS server started
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Status: nfs-idmapd
nfsserver(nfsserver)[3486]: 2016/06/07_01:51:31 INFO: Status: rpc-statd
この後直ぐに停止されています。
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stopping NFS server ...
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stop: threads
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stop: rpc-statd
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stop: nfs-idmapd
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stop: nfs-mountd
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: Stop: rpcbind
nfsserver(nfsserver)[3782]: 2016/06/07_01:51:32 INFO: NFS server stopped
アクティブ側(DC側でもある)では下記のように nfsserver が両方のノードで開始している旨のログが記録されていました。
Jun 07 01:51:32 [19992] nfs02 pengine: error: native_create_actions: Resource nfsserver (ocf::nfsserver) is active on 2 nodes attempting recovery
Jun 07 01:51:32 [19992] nfs02 pengine: warning: native_create_actions: See http://clusterlabs.org/wiki/FAQ#Resource_is_Too_Active for more information.
この後に両方のノードで NFS server が停止してアクティブ側でのみ再開しているのですが、このためクライアントからは一時的に書き込みが失敗しています。
Tue Jun 7 01:51:32 UTC 2016
Tue Jun 7 01:51:32 UTC 2016
-bash: /mnt/date.txt: Permission denied
Tue Jun 7 01:51:32 UTC 2016
-bash: /mnt/date.txt: Permission denied
Tue Jun 7 01:51:32 UTC 2016
Tue Jun 7 01:51:32 UTC 2016
Tue Jun 7 01:51:39 UTC 2016
Tue Jun 7 01:51:39 UTC 2016
nfsserver の ocf ファイル (/usr/lib/ocf/resource.d/heartbeat/nfsserver
) を見た感じ、
nfsserver_monitor ()
{
# Skip trying to start processes once before failing
# when run from nfsserver_start ()
if [ "$1" == "fromstart" ]; then
ocf_log info "fromstart"
fromstart=1
else
tries=1
fi
:
ocf_log info "Status: rpcbind"
rpcinfo &> /dev/null
rc=$?
if [ "$rc" -ne "0" ]; then
if [ ! "$fromstart" ] && [ "$tries" -gt "0" ]; then
nfsserver_start frommonitor
rc=$?
let tries=$tries-1
fi
if [ "$rc" -ne "0" ]; then
ocf_exit_reason "rpcbind is not running"
return $OCF_NOT_RUNNING
fi
fi
:
}
monitor でリソースの開始が行われてしまうようです・・・?
ocf は resource-agents パッケージに含まれていますが、アップストリームではこのような内容にはなっていませんでした。
CentOS 7 の resource-agents の SRPM を見てみたところ bz1304370-nfsserver-fix-systemd-status-detection.patch でこの変更が行われていましたが・・・
非公開のようです。
monitor でリソースが開始されるのはなにかの間違いだとしか思えません。とりあえず ocf で tries=0
にすれば monitor からリソースの開始は行われなくなるので、下記のようにパッチすることにしました。
sudo cp -a /usr/lib/ocf/resource.d/heartbeat/nfsserver{,.orig}
sudo sed 's/tries=1/tries=0/g' -i /usr/lib/ocf/resource.d/heartbeat/nfsserver
diff -u /usr/lib/ocf/resource.d/heartbeat/nfsserver{.orig,}
--- /usr/lib/ocf/resource.d/heartbeat/nfsserver.orig 2016-05-12 14:02:40.000000000 +0000
+++ /usr/lib/ocf/resource.d/heartbeat/nfsserver 2016-06-07 02:04:09.825775009 +0000
@@ -364,7 +364,7 @@
ocf_log info "fromstart"
fromstart=1
else
- tries=1
+ tries=0
fi
# systemd
nfsserver はクローンリソースでも良い?
下記だと nfsserver は systemd のリソースで定義してクローンリソースにしていました。
それもアリなのかな?、試してみました。まずは DRBD のマウントポイントを exportfs する構成で、この場合は exportfs もクローンリソースにできます。
sudo pcs resource delete exportfs
sudo pcs resource delete nfsserver
sudo pcs resource cleanup exportfs
sudo pcs resource cleanup nfsserver
sudo pcs resource create nfsserver systemd:nfs-server \
op monitor interval="30s"
sudo pcs resource create exportfs ocf:heartbeat:exportfs \
clientspec="192.168.33.0/24" options="rw,no_root_squash" directory="/drbd/nfs" fsid="root" \
op monitor interval="30s"
sudo pcs resource group add group-nfsserver nfsserver exportfs
sudo pcs resource clone group-nfsserver
sudo pcs constraint colocation add group-nfs with group-nfsserver-clone INFINITY
sudo pcs constraint order start group-nfsserver-clone then start group-nfs
次に DRBD のマウントポイントのサブディレクトリを exportfs する構成で、この場合は exportfs は通常のリソースで fs とか vip とかと同じグループにします。
sudo pcs resource delete exportfs
sudo pcs resource delete nfsserver
sudo pcs resource cleanup exportfs
sudo pcs resource cleanup nfsserver
sudo pcs resource create nfsserver systemd:nfs-server \
op monitor interval="30s" --clone
sudo pcs resource create exportfs ocf:heartbeat:exportfs \
clientspec="192.168.33.0/24" options="rw,no_root_squash" directory="/drbd/nfs/nfsroot" fsid="root" \
op monitor interval="30s"
# fs vip exportfs
sudo pcs resource group add group-nfs exportfs
sudo pcs constraint colocation add group-nfs with nfsserver-clone INFINITY
sudo pcs constraint order start nfsserver-clone then start group-nfs
見た感じ、どちらの方法でも問題無さそうでした。/var/lib/nfs/
って同期しなくてもいいものなの?
/var/lib/nfs/
を同期していないとフェイルオーバーしたときにクライアントでマウントしなおす必要があると思ってたんだけどそんなことも無さそう。tcp/udp とか nfs v3/v4 とかによって異なるのだろうか?