転載・引用について

ユーザ用ツール

サイト用ツール


os:freebsd:hast:old

FreeBSDでHAST/NFS Serverを立てる(Old)

Mail Serverなどに供給するFilesystemは安定性と冗長性が重要である。 しかし、一般にFilesystemの冗長化は非常に難しく、なかなかFreeに利用できる良い実装はない。

Linuxを利用する場合は、DRBDやlsyncd/rsyncを用いた同期が可能である。

  • DRBD: Block DeviceとしてのHDDを同期する。今回のHASTと考え方はよく似ている。
  • lsyncd/rsync: Linux kernelのinotifyを利用した、ファイルの書き換え検出(lsyncd)と、検出されたファイルの同期(rsync)の組み合わせで実装する、ファイル単位の同期

しかし、FreeBSDを利用する場合、そのために用いる手法としては、実質HASTしかない。また、このHASTはNetBSDやOpenBSDには存在しない。

今時、MailServerを構築する際には、Lock問題が発生しないように、Maildirを利用しHome Directoryにファイル単位で受信したメールを保存するのが一般的である。この時、SMTP Serverは冗長性を確保したいので、Home DirectoryはNFSでshareしたいという要望がある。

以上より、FreeBSD 10.1を用いてHASTでHome Directoryを冗長化することにした。

当初 NAS4Free で行こうと思ったのだが、以下の理由で FreeBSD + HAST を用いてNFS Server を組むことにした。

  • NFS「しか」使わないこと
  • CentOS7でNFSで謎の挙動に出会ってはまったこと
    • *BSDではそのような謎の挙動を観測したことがないこと
  • XenServer 上で GuestVM として構築すること
    • つまりはxe-guest-utilitiesを入れたいということ。
    • NAS4Freeは現在の段階でも、まだFreeBSD 9系統なので、Full Virtual でしか動作しない
  • NAS4Free の HAST は Still Experimental であって、それなら FreeBSD 10.1 でも同じであること

仮想基盤の Storage が NAS4Free で RAID-Z2 であることと、HAST で筐体間冗長を構成することを考慮し、NFS Server 単体では冗長構成は構築しない。もし、冗長構成を取るなら

  • 仮想 HDD を 2 個にし、ZFS Mirror/snapshot を利用
  • 仮想 HDD を 3 個にし、RAID-Z1/snapshot を利用

する手も考えられるが、今回は容量も50G程度なので、UFS(FFS)を利用する。

なお、当然のことだが、HASTの冗長は「片側を書き換えたら即逆側も書き換わる」冗長なので、いわゆるBackupではない。冗長である。なので、当然Backupは定期的に取るに越したことはない。NAS4Free側でsnapshotを取るのも悪くないが、やはりちゃんとbackupを取るべきであろう。

なお、UFSでのsnapshotの取得については、ここを参照のこと

設定

今回は、以下の構成で構築する

  • /dev/ada1 を HAST で冗長化する
  • hast の resource 名は home とする
  • hast で共有する Filesystem は /hast/[resource] (今回は /hast/home) に mount する
  • NFSはv3を利用する
    • これは、NFSv4で頑張るために必要な account 情報をまだ一元管理していないため
    • NFSv2での接続は禁止する。そのために、vfs.nfsd.server_min_nfsvers=3 を設定する
    • NFSv3ではTCP/UDP共に許可する
  • NFS Serverとしてのアドレスは CARP のものを利用する
  • NFS で export するDirectoryは /hast 下のもののみとする

とにかく FreeBSD を Install

諸元

OS FreeBSD 10.1
vCPU 1
Memory 1024MB
HDD Boot 20GB
HDD Export 50GB
NIC 2

とりあえず、普通にInstall

  • ports/src のみを Installする (srcは不要かもしれない)
  • Disk は以下のように設定(自動で作成し、HDDはada0のみを選択する)
    • ada1も同時に構築しようとすると、正しくDiskが構築されない。これはおそらくFreeBSDの不具合
ada0 GPT 備考
ada0p1 Freebsd-boot 512KB FreeBSD Boot code上の制限
ada0p2 Freebsd-ufs 19GB
ada0p3 Freebsd-swap 1GB
  • sshd/ntpd/powerd/dumpdev を設定
  • 追加 Account を作成

初期設定

  • ntp Server関連の設定
  • sshd関連の設定
  • 追加NICの設定 (/etc/rc.conf)
  • fstabの修正(procfsを追加)
    • proc /proc procfs rw 0 0
  • OSのUpdate
    • freebsd-update fetch
    • freebsd-update install
    • portsnap fetch
    • portsnap update
  • XenServer 上で PV Guest にする
    • pkg install xe-guest-utilities
    • echo 'xenguest_enable=“YES”' » /etc/rc.conf.local
  • /etc/sysctl.confに以下を追加
    • vfs.nfsd.server_min_nfsvers=3
    • net.inet.carp.allow=1
    • net.inet.carp.preempt=1

ここまで出来たら、shutdownして、VMをCopyし、2台目の設定を実施する。

HAST 設定

HASTが動作するためには、GEOM_GATE(geom_gate.ko)がkernelにlinkされている必要がある。 通常は、hastdが起動されるタイミングでLKMとして読み込まれる(kernelにlinkされる)が、staticにkernelにlinkするならkernelを再構築する。その際 OPTIONS GEOM_GATE を追加すること

HASTの設定は、/etc/hast.confに記載する。

#
# HAST: Highly Available STorage configuration.
#

# General configuration
listen		tcp4://0.0.0.0:8457
replication	fullsync
compression	lzf
timeout		10

on nfs001 {
	listen	tcp4://10.1.101.3:8457
}
on nfs002 {
	listen	tcp4://10.1.101.4:8457
}

# node configuration
resource home {
	local	/dev/ada1
	on nfs001 {
		remote	tcp4://10.1.101.4:8457
	}
	on nfs002 {
		remote	tcp4://10.1.101.3:8457
	}
}
  1. HASTで同期する両サーバーで以下を実行
    1. /etc/hast.conf を作成する
    2. hastctl create home を実行
    3. service hastd onestart を実行
  2. HASTのPrimary側で以下を実行
    1. hastctl role primary home
  3. HASTのSecondary側で以下を実行
    1. hastctl role secondary home
  4. 両方で以下を実行
    1. hastctl status home
      • この結果のstatusが complete であるなら、同期は完了している
      • この結果のstatusが degraded であるなら、設定の何かが間違っているので確認する
      • hastctl list -d homeを実行すると、詳細な情報が出力される。この時dirtyが0であることを確認する
  5. HASTのPrimary側で以下を実行
    1. ファイルシステムを構築する : newfs -O2 -U -j /dev/hast/home
      • 今回は、UFS/Soft-update(-U)/soft-update journaling(SUJ)(-j)を生かす設定にする。
      • gjournalを利用する場合、-jを-Jに変える。
      • snapshotを取得できるようにするため、-nは指定しない。
    2. mkdir /home
    3. mount /dev/hast/home /home
      • これで、HASTで同期しているFilesystemがPrimary側でmountされた。
    4. echo 'hastd_enable=“YES”' » /etc/rc.conf.local
      • これで再起動時に自動的にHASTdが動作する
  6. /etc/fstabに必要な情報を記載する

HASTの制御コマンドは以下

  • hastctl status [resource] : HAST resource [resource] の状態を見る
  • hastctl status all : HASTに登録されている全ての[resource]の状態を見る
  • hastctl role primary [resource] [resource]をPrimaryにする
    • このタイミングで、/dev/hast/[resource]が生える
  • hastctl role secondary [resource] [resource]をSecondaryにする
    • このタイミングで、/dev/hast/[resource]が消える
  • hastctl list [resource] : [resource]の詳細情報を表示する

hastdがSplit-Brainを通知する場合

hastで共有しているDiskの状態がおかしくなったので、以下を実施する

  1. まず、secondary側でhastctl create [resource]してみる。
    • これでエラーが出なければ、primary側でdirtyを確認し、dirtyが0になればOK
    • これでダメな場合、以下を実行する
  2. とにかくhastの同期を切り離す (片方(仮にBとする)のI/Fをdownするのが手っ取り早い)
  3. Bで、以下の作業を実施する
    1. hastctl role primary して、FSをmountする
    2. DataのBackupを取得する
    3. Bをhastctl role init [resource] → hastctl create [resource] → hastctl role secondary [resource] して初期化する
  4. HASTの同期を再開する (I/Fをあげるのが良い)
  5. primary側がDegradedになり、同期が開始される。
  6. primary側のdirtyが0になり完全に同期するのを待つ
  7. primary側にBから取得したバックアップから必要なファイルを書き込む

とりあえず、これが一番手っ取り早い。

split-brain状態になっていることを検出し、勝手にhastctl createするようにする方策を考えなければならない

CARPの設定

ここまでで、HAST deviceは作成できた。

ここからは、CARPとdevdを利用して、Failoverの設定を行う

  1. echo 'carp_load=“YES”' » /boot/loader.conf を実行
    • 初期設定する場合、rebootするのは面倒なので、kldload carpを実行する
    • kernelにstatic linkしておくには、devide CARPを追加する
  2. /etc/rc.confに以下を追加
    1. Primaryマシンに ifconfig_xn1_alias0=“vhid 2 advskew 10 pass UltraSecret alias xxx.xxx.xxx.xxx/32”
    2. Secondaryマシンに ifconfig_xn1_alias0=“vhid 2 advskew 10 pass UltraSecret alias xxx.xxx.xxx.xxx/32”

これでCARPの設定は終了。

CARPのI/FをBACKUPに切り替えるには、ifconfig xn1 vhid 2 state backup でOK

ifconfig state backup で CARP の state を変化させた場合、相手側の I/F の state は変化しない。

従って、host-1 でifconfig xn1 vhid 2 state backup を実行した場合、host-2 で ifconfig xn1 vhid 2 state master を実行すること

VHIDは、KeepalivedやVRRPなどのVHIDと衝突しないように設定する必要があるので、注意

FreeBSDのCARPはVRRPと異なり、自分がBACKUPの時でも共有IPアドレスが自身に割り当てられているものとして扱われる。つまり、host-1がCARP MASTER、host-2がCARP BACKUPであって、共有IP AddressがAddr(s)であるとすると、Addr(s)への通信に返事を返すのはhost-1のみであるが、host-2のInterface Address TableにもAddr(s)が載る。

devdの設定

前項のCARPまで設定が終了すれば、HASTで同期しているFilesystemを公開する準備が整っている。 この時利用できるProtocolとしては、SAMBAやNFSがある。iSCSIは動作するか微妙。

そこで、Interfaceの状態が変化した場合にhastのroleを変化させる設定を行う。 この設定は、devdを利用する。

まず、/etc/dev.confの末尾に、以下を追記する。

notify 100 {
	match "system" "CARP";
	match "subsystem" "[0-9]+@[0-9a-z]+";
	match "type" "(MASTER|BACKUP|INIT)";
	action "/usr/local/sbin/carp-hast-switch $subsystem $type";
};

その後、service devd restartを実行する

次に、/usr/local/sbin/carp-hast-switchを作成する。

  • 2015/04/16 Release版
  • 2015/11/22 更新: Boot時の問題を解決。
carp-hast-switch
#! /bin/sh
#
# carp-hast-switch: shell script for change hast role when carp
#                   status is changed.
#
 
# Original script by Freddie Cash <fjwcash@gmail.com>
# Modified by Michael W. Lucas <mwlucas@BlackHelicopters.org>
# and Viktor Petersson <vpetersson@wireload.net>
# and HEO SeonMeyong <seirios@seirios.org>
# Last modified 2015/11/10 HEO SeonMeyong <seirios@seirios.org>
 
# ***WARNINGS***
# Need net.inet.carp.preempt=1 and same of advskew on carp.
# Currently HAST device must formatted by UFS
# ZFS code is impremented but not checked.
#     This script is assumed to match the ZFS pool name and HAST resource name 
 
##############################################################################
# Setting Variables and parse Arguments.
 
#DEBUG=1
SYSLOG_FACILITY="user.notice"
SYSLOG_TAG="carp-hast"
 
IF=${1%@*}
VHID=${1#*@}
ACTION=$2
 
# Work around for boot time. devd execute this script before start hastd.
[ ! `/bin/pgrep hastd` ] && exit
 
case "${ACTION}" in
    MASTER|BACKUP|INIT)
	/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		        "State Changed. I/F: ${IF} VHID: ${VHID} state: ${ACTION}"
	;;
    *)
	/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                        "FATAL: ${ACTION} is not yet implemented"
	exit 1
	;;
esac
 
##############################################################################
# Get resources.
 
HASTDEV=`/sbin/hastctl dump all | /usr/bin/grep resource | /usr/bin/sed -e 's/^.*:\ *//'`
[ "x"$DEBUG != "x" ] && echo "HASTDEV = ${HASTDEV}"
[ -z "${HASTDEV}" ] && exit 0		# no hast device.
 
# get all carp interfaces
ifs=`/sbin/ifconfig -l`
for i in ${ifs}; do
    no_of_carp=`/sbin/ifconfig $i | /usr/bin/grep -c carp`
    [ "x"$DEBUG != "x" ] && echo "Interface $i has ${no_of_carp} CARP configuration"
    [ ${no_of_carp} != "0" ] && carps="${carps} $i"
done
[ "x"$DEBUG != "x" ] && echo "CARP I/F = ${carps}"
[ -z "${carps}" ] && exit 0		# no carp I/F.
 
PREEMPTION=`/sbin/sysctl net.inet.carp.preempt | /usr/bin/awk '{print $2}'`
[ "x"$DEBUG != "x" ] && echo "CARP preemption = ${PREEMPTION}"
[ ${PREEMPTION} != "1" ] && exit 0	# No carp preemption. May cause failure.
 
##############################################################################
# Main.
 
case "${ACTION}" in
    "MASTER")
	# make sure all carp is master.
	if [ -n "${carps}" ]; then
	    for if in ${carps}; do
		vhid=`/sbin/ifconfig ${if} | /usr/bin/grep carp | /usr/bin/awk '{print $3 " " $4}'`
		/sbin/ifconfig ${if} ${vhid} state master
	    done
	fi
 
	for disk in ${HASTDEV}; do
            # If there is secondary worker process, it means that remote primary process is
            # still running. We have to wait for it to terminate.
            for i in `jot 30`; do
                /bin/pgrep -f "hastd: ${disk} \(secondary\)" >/dev/null 2>&1 || break
		sleep 1
	    done
            if pgrep -f "hastd: ${disk} \(secondary\)" >/dev/null 2>&1; then
	        /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		                "FATAL: Secondary process for resource ${disk} is still running after 30 seconds."
                exit 1
            fi
 
	    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		            "Role for HAST resources ${disk} switched to primary."
	    /sbin/hastctl role primary ${disk}
	    if [ $? -ne 0 ]; then
		/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			        "FATAL: Unable to change role to primary for resource ${disk}."
		exit 1
	    fi
	done
 
	# Wait for the /dev/hast/* devices to appear
	for disk in ${HASTDEV}; do
	    for loop in $( jot 120 ); do
		[ -c "/dev/hast/${disk}" ] && break
		sleep 0.5
	    done
 
	    if [ ! -c "/dev/hast/${disk}" ]; then
		/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			        "FATAL: GEOM provider /dev/hast/${disk} did not appear."
		exit 1
	    fi
            FSFMT=`file -bs /dev/hast/${disk}`
            if [ $? -ne 0 ]; then
		/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			        "FATAL: /dev/hast/${disk} cannot define FS format."
		exit 1
            fi
            FSFMT=`echo ${FSFMT} | /usr/bin/awk '{print $1 " " $2}'`
            case ${FSFMT} in
                "Unix Fast")
                    /sbin/fsck -y -t ufs /dev/hast/${disk} >/dev/null 2>&1
                    if [ $? -ne 0 ]; then
                        /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                        "FATAL: UFS fsck /dev/hast/${disk} failed."
                        exit 1
                    fi
                    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                    "fsck /dev/hast/${disk} finished."
 	            e_code=`/sbin/mount /dev/hast/${disk} /hast/${disk} 2>&1`
                    if [ $? -ne 0 ]; then
	                /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		                        "FATAL: UFS mount for resource ${disk} failed: ${e_code}."
                        exit 1
                    fi
                    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                    "UFS /dev/hast/${disk} is mounted."
                    ;;
                *)
                    # If not UFS, Assume that filesystem is ZFS.
                    e_code=`/sbin/zpool import -f ${disk} 2>&1`
                    if [ $? -ne 0 ]; then
	                /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                        "FATAL: ZFS import for resource ${disk} failed: ${e_code}."
                        exit 1
                    fi
                    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                    "ZFS ${disk} is imported."
                    ;;
            esac
	done
        # NFS Service ( run nfsd and mountd. )
	/usr/sbin/service rpcbind restart
	/usr/sbin/service statd restart
	/usr/sbin/service lockd restart
        /usr/sbin/service nfsd restart
        /usr/sbin/service mountd restart
	/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			"NFS started."
 
	# iSCSI service
	# /etc/rc.d/iscsi_target start
	;;
 
    "BACKUP"|"INIT")
	# make sure all carp is backup
	if [ -n "${carps}" ]; then
	    for if in ${carps}; do
		vhid=`/sbin/ifconfig ${if} | /usr/bin/grep carp | /usr/bin/awk '{print $3 " " $4}'`
		/sbin/ifconfig ${if} ${vhid} state backup
	    done
	fi
 
        # stop iSCSI service
	# /etc/rc.d/iscsi_target forcestop
 
        # NFS Service ( stop nfsd and mountd. ) 
        /usr/sbin/service mountd forcestop
        /usr/sbin/service nfsd forcestop
	/usr/sbin/service lockd forcestop
	/usr/sbin/service statd forcestop
	/usr/sbin/service rpcbind forcestop
	/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			"NFS stoped."
 
	# Switch roles for the HAST resources
	for disk in ${HASTDEV}; do
	    sleep 1
	    # First of all, set hast status to secondary.
	    # Do not touch device before hast status become secondary.
	    # This work protects to become split-brain status.
	    /sbin/hastctl role secondary ${disk} 2>&1
	    if [ $? -ne 0 ]; then
		/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
			        "FATAL: Unable to switch role to secondary for resource ${disk}."
		exit 1
	    fi
	    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		            "Role switched to secondary for resource ${disk}."
	    # Unmount UFS
            for i in `/sbin/mount | /usr/bin/awk '{print $1 ":" substr($4,2)}' | /usr/bin/grep hast`; do
                if [ ${i%:*} = "/dev/hast/${disk}" ]; then
                    if [ ${i#*:} = "ufs," ]; then
                        e_code=`/sbin/umount /hast/${disk} 2>&1`
                        if [ $? -ne 0 ]; then
			    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
					    "FATAL: UFS unmount of resource ${disk} failed: ${e_code}"
                            exit 1
                        fi
			/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
					"UFS /dev/hast/${disk} is unmounted"
                    fi
                fi
            done
 
	    # Export ZFS
            zpool list | egrep -q "^${disk} "
            if [ $? -eq 0 ]; then
                # Force export ZFS pool.
                e_code=`/sbin/zpool export -f ${disk} 2>&1`
	        if [ $? -ne 0 ]; then
		    /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                    "FATAL: ZFS export of resource ${disk} failed: ${e_code}."
		    exit 1
	        fi
                /usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
                                "ZFS ${disk} is exported."
            fi
	done
	;;
 
    *)
	/usr/bin/logger -p ${SYSLOG_FACILITY} -t ${SYSLOG_TAG} \
		        "Unknown CARP state. ${STATE}"
	;;
esac
 
exit 0
##############################################################################
# Local Variables:
# coding: utf-8
# mode: sh
# sh-basic-offset: 4
# sh-indentation: 4
# End:

このファイルは「shell script」なので、chmod 750 carp-hast-switchchown root:wheel carp-hast-switchを実行しておくこと。

iscsi target関連の設定はコメントアウトしている。(2015/04/14)

FilesystemをmountするDirectoryが/hastにhardcodeされている。(2015/04/14)

ZFSのcodeは組み込んであるが試験をしていない。また、zfsのpool名がhastのresource名と同じであることを仮定している

最後に、/hastを作成する。mkdir /hast

これで、hastがprimaryの時には、/hast/homeが作成される。

NFS Server 設定

NFSサーバーの設定を行う。

  1. /etc/rc.conf.localに以下を記述
    • rpc_lockd_enable="YES"
      rpc_statd_enable="YES"
      rpcbind_enable="YES"
      portmap_enable="YES"
      mountd_enable="YES"
      mountd_flags="-r"
      nfs_server_enable="YES"
      nfs_client_enable="YES"
    • mountd_flagsに関してはmanを読むこと。Diskless workstationを利用する場合には“-r”が必要
  2. /etc/exportsを記述する
    • /hast/home -network xxx.xxx.xxx.xxx/xx -maproot=root

今回はHome Directoryをshareすることを前提としているので、-maproot=root とか -maproot=0:0を記載しているが、root権限のファイルがNFSでshareされること自体は十分に検討する必要があるので注意すること

ここまで設定すれば NFS Server が Filesystem を export できるようになっている。 そこで以下のコマンドを実行する

# kill -HUP `cat /var/run/mountd.pid`
# showmount -e
/hast/home			xxx.xxx.xxx.xxx/xx
#

NFS client 設定

NFS Client側では、/etc/fstabに必要な設定を投入すれば、FilesystemをNFSでmountできる。

host-carp:/hast/home	/home	nfs	rw,noinet6,tcp,soft,noatime,nfsv3,bg,wsize=32768,rsize=32768	0 0
#host-carp:/hast/home	/home	nfs	rw,noinet6,tcp,hard,intr,noatime,nfsv3,bg,wsize=32768,rsize=32768	0 0

/usrなどの「必須filesystem」をmountする場合には、hard,intrを、/homeのような「システムに必須ではない」ファイルシステムはsoftを指定すると良い。

hard,intr : NFSサーバーが落ちている時にファイルシステムにアクセスすると、アクセスができるまで停止する。signalを送ることで割り込み(intr)がかかりエラーが帰る

soft : NFSサーバーが落ちている時にファイルシステムにアクセスすると即時エラーが帰る

読み込み・書き込みの最大転送サイズを変更する場合には、wsize=32768,rsize=32768をつける。

非同期書き込みで良い場合にはasyncをつける

起動時の処理について

本節の問題は、carp-hast-switchを書き換えることで、とりあえず解決したはず。 以下は、過去の記録として残すのみで、考慮する必要は(現時点では)ないはず

サービスでHASTを利用する場合、事前に考えておくべきことが多い。 特に起動時の問題が大きい。定常状態まで行けば、あとはHASTdがSplit-Brainになった場合の対処だけとも言える。

起動時のhastの問題は、

  1. hastdは起動されるとresourceをinit状態にする
  2. 従って、CARPのstatusなどを鑑みて、自身のroleを設定しなければならない
  3. しかし、下手するとsplit brainになるからlocalcntとremotecntを確認しなければならない

ことである。むしろ、勝手に判断されるよりはましであるとも言える。

上記の問題を考えると、HASTdを起動するタイミングが問題になる。

本記事中に掲載した “carp-hast-switch” はCARPのstateの変化をdevd経由で取得しHASTdのroleを設定している。この時、NFSの挙動も制御しているため、定常状態では十分なのだが、OSの起動時には問題が発生する。

FreeBSDの起動順序では、

  1. devdを起動
  2. NICが設定される
    • ここでCARPも設定される
  3. rpc関連を起動
  4. NFS Client関連の起動
  5. hastd 起動
  6. NFS Server関連の起動

となっている。

この起動順序では、以下の問題が発生する

  1. devdがNIC設定より前に起動されているため、NIC設定時点でcarp-hast-switchが呼ばれることがある
  2. hastdが起動された時には、すでにNICが設定されているため、先のcarp-hast-switchは呼び出されない
    • そのため、hastdが起動されたのちにhastdのroleを設定する方策がない
  3. hastd起動時点でNFS関連の設定をしても直後のNFS Serverの設定時点で元の木阿弥になる

この問題は、要するに、「hastdの制御」と「hastdを用いたファイルシステムのexport」を混同しているから発生する。従って、この問題に対処するには、結局以下の方策のいずれかを採用するしかない。

  1. /etc/rc.localにhast関連の設定を行うscriptを記述する
    • 結局devからhastdが起動される前にcarp-hast-switchを呼び出す問題は解決しない
  2. /usr/local/etc/rc.dになんらかのscriptを設置し、起動時に一度だけ読みだすようにする
  3. hastdのscriptを修正し、hastdのscriptで処理を行う
    • この場合、nfs関連のscriptもおそらく修正が必要
      • 最終的には本案で解決した。(2015/11/22)

上記案のいずれを採用するか決める前に、方針の決定と、問題点を抽出する。

方針は

  • system関連のscriptはいじらない
    • OSのUpdateなどで更新が入った場合に、問題が読めなくなる
    • Update時に気を使わなければならないシステムはミスの温床になる。
  • できる限り単純に実装する
    • 将来なんらかの問題が発生した時に、scriptがprimitiveであることが可読性を上げる
  • 対象をNFSに絞る
    • おそらくiSCSIもSAMBAも制御可能だと思うが、今はそこまで考えない
    • pfによる制御(filteroutなど)は実行しない
  • このサーバーは、あくまでFile Serverとしてしか動作させない

ものとする

現時点で把握している問題をいかに挙げる。

  1. hastdは、hastdを起動してからresourceが/dev/hastに現れる他での時間が長い
    • (下手すると3sec近くかかる)
    • 従って、単純にNFSでexportして、ファイルシステムが公開されるまでの間にNFSでアクセスされるとfile not foundになる。これは、Mail ServerのSpoolなどでは致命的な問題を引き起こす可能性がある
    • 従って、nfsサービスを停止すれば、アクセスできないからwaitがかかるので都合がいい
      • ただし、HARD mountすることが前提になる。softだと、NFSでの接続ができない段階でerrorになるため
  2. hastdが起動し、INIT状態である段階でNFSが一度上がってしまう
    • この問題の回避だけならば、nfs_server_enableやmountd_enableをNOにする手がある
      • この手を使うと、rc.conf.localの設定の一貫性を壊す
        • 他人がscriptを見て何してるかわからなくなる
      • 他のscriptから判断することも困難になる
      • 従って、この手法は採用できない。
    • hastdが立ち上がり、nfsd/mountdが起動した後、nfsを停止するまでの短時間は、どうしてもNFSによるアクセスによってエラーになる可能性がある。
      • この問題は、この問題が発生する頻度が低いこと(両NFS Serverが同時に再起動している時にのみ発生する)と問題になる時間が短いこと(nfsdが起動されてからnfsdを停止するまでの時間概ね1秒未満)から、許容範囲のリスクであると考えるしかない
  3. Daemonの監視・復旧が少し複雑になる
    • localcntとremotecntを比較して、split-brain出ないことを確認しなければならない
    • split-brainである場合、即座にCARPをBACKUPにし、hastdを停止しなければならない
    • CARP StatusとHAST stateを監視し、必要に応じて同期しなければならない
      • この問題は、devdが動作していない時に発生する可能性がある
      • 従って、devdの死活の監視も必須になる

以上から、実装としては、 - carp-hast-nfs-boot を作成し、/usr/local/etcに設置 * /etc/rc.confにcarp-hast-nfs-boot_enable=“YES”を記述 - /etc/rc.localに直に記載する の2案が考えられるが、現時点では、後者のrc.localによる実装を採用する。 (2015/11/22)

結論としては、carp-hast-switchで

  • hastdが起動していない場合には「何もしない」

ように実装を修正することで、問題を解決した。

この手法で問題が解決できる理由は、CARPのステータスが「MASTER」になった際に、必ずcarp-hast-switchが呼ばれるため、最終的に正しく設定がなされることが期待出来るからである。

rc.local
# Obsolete. Do not use this.(2015/11/22)
#----- HAST initiate (Only boot time needed)
if checkyesno hastd_enable; then
  echo "hast initializing"
 
  for if in `/sbin/ifconfig -l`; do
    if [ "0" != `/sbin/ifconfig ${if}|/usr/bin/grep -c carp` ]; then
      if [ -n "${if}" ]; then
        state=`/sbin/ifconfig ${if}|/usr/bin/grep carp|/usr/bin/awk '{print $2 " " $4}'`
        [ -x /usr/local/sbin/carp-hast-switch ] && \
            /usr/local/sbin/carp-hast-switch ${state##* }@${if} ${state%% *}
        break
      else
        echo -n " no carp I/F"
      fi
    fi
  done
 
  unset if state
  echo " ... done"
fi

注意点

FreeBSD 10.1 の段階では、以下の挙動が確認されている。

  • host-1 と host-2 が /dev/ada1 を HAST で共有し、/hast/home に mount されているとする。
  • 初期状態では、hast-1 が CARP MASTER/HAST Primary であるとする。

この状況において、

  1. host-1 がなんらかの事情(OSのUpdateなど)で停止した(もしくは通信不能な状況になった)とする
  2. host-1 では、secondaryになったことを受け、filesystemをunmountした
  3. これを受けて、host-2が CARP MASTER/HAST Primary に変化した
  4. なんらかの作業の結果、/hast/home でファイルの変更(fsckをかける、fileを削除など)が発生したとする
    • これは、/homeとしてNFS Exportし、かつMailを受信したなどの作業を実施した状況と同様
  5. host-1が起動(もしくは、通信が復活)し、CARP BACKUP/HAST Secondaryとして復活した

という流れが発生したとする。

この時、HASTは、Dirtyが小さい状況であっても「Split-Brain状態になった」と認識し、同期が外れた状況となってしまう。

状況 host-1 host-2
HAST localcnt remotecnt HAST localcnt remotecnt
0 初期状態 primary 0 0 secondary 0 0
1 host-1 切断 primary 0 0 secondary 0 0
2 host-1 unmount primary 1 0 secondary 0 0
3 host-2 primary secondary 1 0 primary 0 0
4 host-2でfsck実行 secondary 1 0 primary 1 0
5 host-2 mount secondary 1 0 primary 1 0
6 host-1 復帰 secondary 1 0 primary 1 0
7 split-brain secondary 1 0 primary 1 0
8 hastctl create home secondary 0 0 primary 1 0
9 最後 secondary 0 0 primary 1 0

これを見ると、host-1でroleがprimaryの状況のままでunmountしたことが、Degrade、ひいてはSplit-brainの引き金になっているように見える。当初のcarp-hast-switchは、このような実装であったため、ちょっとした試験で頻繁にSplit-Brainとなってしまっていた。なぜumountでsplit-brainが発生するかは不明だが、MemoryとDiskのSyncに起因すると推測している。

この問題は、要するにsecondaryになる前にumountしているのが問題なのだと考えられることから、シナリオを変更して、以下のようにする。

状況 host-1 host-2
HAST localcnt remotecnt HAST localcnt remotecnt
0 初期状態 primary 0 0 secondary 0 0
1 host-1 切断 primary 0 0 secondary 0 0
2 host-2 primary secondary 1 0 primary 0 0
3 host-1 unmount secondary 0 0 primary 0 0
4 host-2でfsck実行 secondary 0 0 primary 1 0
5 host-2 mount secondary 0 0 primary 1 0
6 host-1 復帰 secondary 0 0 primary 0 0
7 最後 secondary 0 0 primary 1 0

要するに、当初シナリオの2と3をひっくり返した。その結果、Split-Brainが発生しなくなった。

というわけで、carp-hast-switchを書き換えて問題は解決した。(現在掲示しているcarp-hast-switchはこの問題を修正済みのものである)

このウェブサイトはクッキーを使用しています。 Webサイトを使用することで、あなたはあなたのコンピュータにクッキーを保存することに同意します。 また、あなたはあなたが私たちのプライバシーポリシーを読んで理解したことを認めます。 同意しない場合はウェブサイトを離れてください。クッキーに関する詳細情報
os/freebsd/hast/old.txt · 最終更新: 2018/05/14 13:31 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki