スプリットブレインの解消

スプリットブレインとは

例えば、レプリケーション構成のボリュームにおいて次のような操作をすると、レプリカされたファイルの内容にノード間で矛盾が生じて、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