スプリットブレインの解消
スプリットブレインとは
例えば、レプリケーション構成のボリュームにおいて次のような操作をすると、レプリカされたファイルの内容にノード間で矛盾が生じて、GlusterFSは、どちらの内容が正しいかを判断することができなくなります。
※ここでは、「ボリューム構成手順」で作成したボリューム「vol02」を想定します。これは、gluster01とgluster02でレプリケーションされた構成のボリュームです。
gluster01を停止
↓
クライアントからファイルを更新(gluster02のみに書き込まれる)
↓
gluster02を停止後、gluster01を再起動
↓
クライアントからファイルを更新(gluster01のみに書き込まれる)
↓
gluster02を再起動
このような状況を「スプリットブレイン」と呼びます。この時、これ以上ファイルの内容が破壊されないように、GlusterFSはクライアントから該当ファイルへのアクセスを禁止します。クライアントから該当ファイルにアクセスすると「I/Oエラー」になります。
スプリットブレイン状態のファイルを回復するには、管理者が各ノードのファイルの内容を直接確認して、不要と判断した方のファイルを手動で削除した上で再レプリケーションを行う必要があります。
スプリットブレインの発生例
意図的にスプリットブレイン状態のファイルを作り出してみます。
「ボリューム構成手順」で作成したボリューム「vol02」を/mntにマウントしたクライアントで下記のシェルスクリプトを実行します。(クライントからglsuter01、gluster02のrootユーザにssh公開鍵認証を設定した後に実行します。)
#!/bin/sh -v
echo "volume is healthy." > /mnt/file01.txt
ssh gluster01 "pkill glusterfs"
sleep 10
echo "gluster01 is dead." >> /mnt/file01.txt
ssh gluster02 "pkill glusterfs"
ssh gluster01 "service glusterd restart"
sleep 10
echo "gluster02 is dead." >> /mnt/file01.txt
ssh gluster02 "service glusterd restart"
sleep 10
cat /mnt/file01.txt
実行の様子は次のようになります。
# ./splitbrain.sh
#!/bin/sh -v
echo "volume is healthy." > /mnt/file01.txt
ssh gluster01 "pkill glusterfs"
sleep 10
echo "gluster01 is dead." >> /mnt/file01.txt
ssh gluster02 "pkill glusterfs"
ssh gluster01 "service glusterd restart"
Stopping glusterd:[ OK ]
Starting glusterd:[ OK ]
sleep 10
echo "gluster02 is dead." >> /mnt/file01.txt
ssh gluster02 "service glusterd restart"
Stopping glusterd:[ OK ]
Starting glusterd:[ OK ]
sleep 10
cat /mnt/file01.txt
cat: /mnt/file01.txt: 入力/出力エラーです
最後に「file01.txt」へのアクセスがI/Oエラーになっていることが分かります。次のように、gluster01とgluster02のブリック内のファイルの内容が相互に異なることが分かります。
# ssh gluster01 cat /data/brick02/file01.txt
volume is healthy.
gluster02 is dead.
# ssh gluster02 cat /data/brick02/file01.txt
volume is healthy.
gluster01 is dead.
この状態でSelf-heal daemonによる再レプリケーションを行なっても次のように「split-brain」を検出してfile01.txtの再レプリケーションに失敗します。
[root@gluster01 ~]# gluster vol heal vol02 full
Heal operation on volume vol02 has been successful
[root@gluster01 ~]# tail /var/log/glusterfs/glustershd.log
・・・
[2012-09-09 16:40:05.086235] W [afr-self-heal-data.c:831:afr_lookup_select_read_child_by_txn_type] 0-vol02-replicate-0: /file01.txt: Possible split-brain
[2012-09-09 16:40:05.086273] W [afr-common.c:1226:afr_detect_self_heal_by_lookup_status] 0-vol02-replicate-0: split brain detected during lookup of /file01.txt.
[2012-09-09 16:40:05.086286] I [afr-common.c:1340:afr_launch_self_heal] 0-vol02-replicate-0: background data gfid self-heal triggered. path: /file01.txt, reason: lookup detected pending operations
[2012-09-09 16:40:05.086887] I [afr-self-heal-common.c:1318:afr_sh_missing_entries_lookup_done] 0-vol02-replicate-0: No sources for dir of /file01.txt, in missing entry self-heal, continuing with the rest of the self-heals
[2012-09-09 16:40:05.087138] I [afr-self-heal-common.c:994:afr_sh_missing_entries_done] 0-vol02-replicate-0: split brain found, aborting selfheal of /file01.txt
[2012-09-09 16:40:05.087155] E [afr-self-heal-common.c:2156:afr_self_heal_completion_cbk] 0-vol02-replicate-0: background data gfid self-heal failed on /file01.txt
スプリットブレインの解消手順
上述のfile01.txtのスプリットブレイン状態を解消します。ここでは、gluster01のブリック内のファイルを削除して、gluster02のブリック内のファイルを再レプリケーションします。
gluster01において、次の手順でブリック内のfile01.txtを削除します。
[root@gluster01 ~]# gluster vol stop vol02
Stopping volume will make its data inaccessible. Do you want to continue? (y/n) y
Stopping volume vol02 has been successful
[root@gluster01 ~]# getfattr -d -m trusted.gfid -e hex /data/brick02/file01.txt
getfattr: Removing leading '/' from absolute path names
# file: data/brick02/file01.txt
trusted.gfid=0xc53b6eef92974d928dcb48744aa0231e
[root@gluster01 ~]# rm -f /data/brick02/file01.txt
[root@gluster01 ~]# rm -f /data/brick02/.glusterfs/c5/3b/c53b6eef-9297-4d92-8dcb-48744aa0231e
[root@gluster01 ~]# gluster vol start vol02
Starting volume vol02 has been successful
はじめに、vol02を停止しています。次の「getfattr」コマンドでは、削除対象ファイルのGFID(GlusterFSが内部的に割り当てるID番号)を確認しています。その上で、該当ファイルと、隠しディレクトリ「.glusterfs」の下にある、対応するGFIDファイル(実態は、file01.txtへのハードリンクです)を削除しています。最後にvol02を再度、開始しています。
この後、vol02の再レプリケーションを行うと、次のようにfile01.txtが正常に再レプリケーションされます。
[root@gluster01 ~]# gluster vol heal vol02 full
Heal operation on volume vol02 has been successful
[root@gluster01 ~]# tail /var/log/glusterfs/glustershd.log
・・・
[2012-09-09 16:48:05.873435] I [afr-common.c:1340:afr_launch_self_heal] 0-vol02-replicate-0: entry self-heal triggered. path: /, reason: checksums of directory differ
[2012-09-09 16:48:05.878198] E [afr-self-heal-common.c:1087:afr_sh_common_lookup_resp_handler] 0-vol02-replicate-0: path /file01.txt on subvolume vol02-client-0 => -1 (No such file or directory)
[2012-09-09 16:48:05.879628] I [afr-self-heal-common.c:2159:afr_self_heal_completion_cbk] 0-vol02-replicate-0: background entry self-heal completed on /
[2012-09-09 16:48:05.880925] I [afr-common.c:1189:afr_detect_self_heal_by_iatt] 0-vol02-replicate-0: size differs for /file01.txt
[2012-09-09 16:48:05.880943] I [afr-common.c:1340:afr_launch_self_heal] 0-vol02-replicate-0: background meta-data data self-heal triggered. path: /file01.txt, reason: lookup detected pending operations
[2012-09-09 16:48:05.885937] I [afr-self-heal-algorithm.c:116:sh_loop_driver_done] 0-vol02-replicate-0: full self-heal completed on /file01.txt
[2012-09-09 16:48:05.887655] I [afr-self-heal-common.c:2159:afr_self_heal_completion_cbk] 0-vol02-replicate-0: background meta-data data self-heal completed on /file01.txt
クライアントからfile01.txtに正常にアクセスするには、ファイルシステムの再マウントが必要になります。
# umount /mnt
# mount -t glusterfs gluster01:/vol02 /mnt
# cat /mnt/file01.txt
volume is healthy.
gluster01 is dead.
参考までに、下記はブリック内のファイルとハードリンクをまとめて削除するスクリプトです。
#!/bin/sh
#
# delfile.sh - Delete file and hardlink in bricks
#
function usage {
echo "Usage: $0 <brick dir> <file path>"
exit 1
}
[[ -z $1 || -z $2 ]] && usage
brick=$1
file=$2
if [[ ! -f ${file} ]]; then
echo "File ${file} is not found."
usage
fi
gfid=$(getfattr -n trusted.gfid --absolute-names -e hex ${file} | grep 0x | cut -d'x' -f2)
hlink=${brick}/.glusterfs/${gfid:0:2}/${gfid:2:2}/${gfid:0:8}-${gfid:8:4}-${gfid:12:4}-${gfid:16:4}-${gfid:20:12}
if [[ ! -f ${hlink} ]]; then
echo "Hardlink ${hlink} is not found."
usage
fi
echo "Delete file: ${file}"
echo "Delete hardlink: ${hlink}"
rm -f ${file} ${hlink}
次のようにブリックのディレクトリとファイルを指定して実行します。
# ./delfile.sh
Usage: ./delfile.sh <brick dir> <file path>
# ./delfile.sh /data/brick01 /data/brick01/dir01/file01.txt
Delete file: /data/brick01/dir01/file01.txt
Delete hardlink: /data/brick01/.glusterfs/14/bb/14bb8bab-5e79-4413-a7c0-adf2994b9634