FreeBSDのHASTを触ってみた。

FreeBSDのHASTを触ってみた。

 こういうのは最近ならQiitaあたりに書いた方がいいんだろうけど。

目次

特徴など


インストール・設定

  • インストールは特に必要なし(FreeBSD 11.0)
  • 設定ファイル: /etc/hast.conf
	replication memsync
	resource shared1 {
	    local /dev/vtbd1p1
	    on host1 {
	        remote 192.168.0.102
	    }
	    on host2 {
	        remote 192.168.0.101
	    }
	}
	resource shared2 {
	    local /var/hast/shared.img
	    on host1 {
	        remote 192.168.0.102
	    }
	    on host2 {
	        remote 192.168.0.101
	    }
	}
    • replicationはmemsyncがデフォルト、fullsyncが安全で遅い
    • execでイベント発生時にプログラムの実行が可能
  • ブロックデバイスを利用する場合はそのまま指定する
    • パーティションは切っておいたほうがよいと思われる
      • サイズを一致させるため
      • ストレージのサイズが大きくなった時(仮想環境など)に後ろのブロックを利用するため
	# gpart create -s gpt vtbd1
	# gpart add -t freebsd-ufs -s 10G vtbd1
	# gpart show vtbd1
  • ファイルを使う場合は必要なサイズのファイルを作る
	# mkdir /var/hast
	# dd if=/dev/zero of=/var/hast/shared.img bs=10m count=1024
    • ※ただし、ファイルを使った場合に問題が生じるケースがある模様
      • HASTのターゲットファイルがufs上の時は問題が起こったが、ZFS上であれば問題なかった
      • HASTのターゲットファイルがufs上の時でも、HASTデバイスZFSで扱っている時は問題なかった
      • /var/log/messages に「Unable to flush disk cache on activemap update: Inappropriate ioctl for device.」が残る(この時に問題があるかどうかは不明。今のところは問題なく同期できているようだった)
  • ブロックデバイスにしろファイルにしろ、サイズが不一致の場合は同期できない
    • /var/log/messagesにエラーが残る
    • 逆にサイズさえ合っていればブロックデバイスとファイルとの同期もされる模様

hastctl による操作

  • 初期化
	# hastctl create shared1
    • create でリソースを初期化する
    • 初めてアクセスする前に実行する
    • 問題が生じて再初期化時にも利用
  • 状態変更
	# hastctl role primary shared1
    • role primary 時に /dev/hast/{リソース名} にアクセス可能になる
    • 同時に両方primaryにできる(排他制御などはしてない模様)
	# hastctl role secondary shared1
    • role secondary にすることで、primaryと同期される
    • hastd起動時はrole init なので、secondaryにする必要がある
  • 状態確認
	# hastctl status shared1
	# hastctl list shared1
    • 同期の状態などを確認可能
  • Split-brainになった場合
    • /var/log/messages にSplit-brainを検知した旨と対象のリソース名が残る(Primary Secondary共)
      • Split-brain時にconfのexecで指定したものが起動されるので、そこで対応は可能(ただしSplit-brain時以外にも呼ばれる)
    • セカンダリ側で下記を実行すると復旧する:
	# hastctrl role init {リソース名}
	# hastctrl create {リソース名}
	# hastctrl role secondary {リソース名}

状態について

  • roleはinit, primary, secondaryの3種類
  • role initの時は動作していない
  • role primaryの時に /dev/hast/{リソース名} として参照可能になる
  • role secondaryの時にprimaryからの同期データを受信している
  • role primaryの排他制御などはしていないらしく、同時にprimaryにしてしまうことができる
    • これを回避する方法は特にない?(primaryに変えるときに注意するしかない?)
    • 切替え時には両方をsecondaryにしてから、片方をprimaryにする必要がある
      • 両方primaryにするとhastdが刺さった状態になる模様。プロセスをkillする必要がある。
		# kill -KILL `cat /var/run/hastd.pid`
  • hastctl statusでcompleteになっていても、同期は完了していない模様
    • hastctl listで表示されるdirtyが0になった時に完了している?
  • HASTデバイスを利用するサービスはrc.confで有効にしないほうがよい
    • role primary & mount後にアクセスする必要があるため
    • 上記でアクセス可能になったあとでservice {サービス名} onestart で起動
  • 起動時はrole initのため、なんらかの方法でrole secondaryにする必要がある
    • CARPを使った事例ではcarpの状態変更に合わせてroleを変更する
    • pacemaker + corosyncでの対応もおそらく可能
      • ※corosyncのports/pkgが動かなくて未検証

ファイルシステムについて

  • ufsよりZFSのほうがパフォーマンスがよさそう
  • HASTで同期している領域を拡張することはできない模様
    • 領域を増やしたい場合はhast.confに別途追加する形になりそう
    • ZFSでアクセスするようにしたほうがよいかもしれない
      • zpool replaceで大きいパーティションに変更可能
      • zpool addで領域を増やすことも可能
      • ただし、こういう運用をする場合は管理が煩雑になる可能性がある
  • ZFSは運用に注意点がある(後述)

ufsで利用する場合
  • 初期化
	# newfs -U -j /dev/hast/shared1
  • ufsの場合はmountする前にfsckでチェックする
	# hastctl role primary shared1
	# fsck -y -t ufs /dev/hast/shared1
	# mount -o noatime /dev/hast/shared1 /mnt/shared
	# application_start

ZFSで利用する場合
  • 初期化
	# zpool create hast-zfs /dev/hast/shared2
	# zfs create -o mountpoint=/mnt/shared hast-zfs/shared
  • role prymary以外にする時にzpool exportしておかないと問題が生じる
    • また、zpool exportしておかないとshutdownに失敗する(どこかで刺さっている?)
	# zpool export hast-zfs
	# hastctl role secondary shared2
  • zpool import時は正常にexportされていない可能性があるため(ダウン時のリカバリ等)、-fオプションを常に付けたほうがよいかもしれない
	# hastctl role primary shared2
	# zpool import -f -d /dev/hast/ hast-zfs
  • 異常終了時はzpool exportされていないため、zpool statusを見るとstate:UNAVAILになっている
    • この時はzpool exportしておいたほうがよいかもしれない
    • exportしていないときにrole primaryになると自動的にONLINEになる(mountはされない)

簡易ツール

dirtry値のチェック
#!/bin/sh

# check options
while getopts aq opt
do
  case $opt in
    a) option_all=1 ;;
    q) option_quiet=1 ;;
  esac
done

# target resources
shift $(($OPTIND - 1))
if [ "$*" ]; then
  resources="$*"
else
  resources=$(/sbin/hastctl dump | /usr/bin/awk '/^ *resource: / {print $2}')
fi

# view dirty
dirtycount=0
for res in $resources; do
  dirty=$(/sbin/hastctl list $res | /usr/bin/awk '/^ *dirty: / {print $2}')
  [ $dirty -ne 0 ] && dirtycount=$(($dirtycount + 1))
  [ $option_quiet ] && continue

  if [ $dirty -ne 0 -o "$option_all" ]; then
    echo "$res $dirty"
  fi
done

exit $dirtycount

  • オプションなしの場合、dirty値があるリソースとそのdirty値が表示される
  • オプション-aでdirty値がないリソース(とそのdirty値)も表示される
  • オプション-qで何も表示しない
  • リソース名を渡すと、そのリソースのみチェックする
  • 終了ステータスにdirty値があるリソースの数を返す

Sprit-brain時の同期状態リセット
#!/bin/sh

syslog_facility="user.notice"
syslog_tag="hast-event"

logger="/usr/bin/logger -p $syslog_facility -t $syslog_tag"

resource=$1

[ $resource ] || exit 1

# check role secondaryj
roletype=$( /sbin/hastctl list $resource | /usr/bin/awk '/^[\t ]*role:[\t ]+/ {print $2}' )
[ "$roletype" = "primary" ] && exit

# recover
$logger "HAST [$resource] recover"
/sbin/hastctl role init $resource
/sbin/hastctl create $resource
/sbin/hastctl role $roletype $resource

  • リソース名を渡すと、そのリソースの同期状態をリセットする
    • ただし、role primaryの時は何もしない
    • role init の時はrole secondaryに変更しないため、そのままでは同期されない(role secondaryに変更する必要がある)