CTDBによるHA NFS

参考資料

クラスタ全体像の説明

下記のネットワーク構成で、GlusterFSのボリュームをNFSで公開します。

-------------

| NFS Cient |

-------------

|

| Service Network

---------------------------------------------------------

| 192.168.122.101 | 192.168.122.102 | 192.168.122.103 |192.168.122.104

| eth0 | eth0 | eth0 | eth0

------------- ------------- ------------- -------------

| gluster01 | | gluster02 | | gluster03 | | gluster04 |

------------- ------------- ------------- -------------

| eth1 | eth1 | eth1 | eth1

| 192.168.1.101 | 192.168.1.102 | 192.168.1.103 | 192.168.1.104

---------------------------------------------------------

Internal Network

この際、Service NetworkのIPアドレスとして、CTDBによるFloating IPをアサインして、ノード障害時にFloating IPの引き継ぎを行う構成を行います。

Internal Networkは、glusterデーモンの通信、および、CTDBデーモンのハートビート通信に使用します。

なお、CTDBは、元々は、Sambaのクラスタ構成におけるロックマネージャとして開発されましたが、その後、Floating IPの引き継ぎ機能(IP Failover機能)などが追加され、Samba以外のクラスタ型の共有ファイルシステムとも組み合わせて利用できるようになりました。ここでの構成手順は、Configuring CTDBに記載の手順をベースに、GlusterFSに対応するように修正を加えています。

CTDBのクラスタ監視機構

具体的な構成手順の前に、CTDBのクラスタ監視機構を簡単に説明しておきます。

ノード間の死活監視

ノード間の死活確認は、Internal Networkによるハートビートを通じて行います。ここでは、簡易的に、glusterデーモンの通信経路(ノード間のデータ転送経路)とハートビート経路を共有しています。ただし、後述の注意にあるように、本番環境ではハートビートは専用のネットワーク経路を用意することをお勧めします。

Split-brainへの対応

共有ファイルシステム上にロックファイルを用意して、クラスタマスタ(Recovery Master)の選出を行います。該当ファイルにPOSIX Lockを取得することで、排他的にクラスタマスタが選出されます。ネットワーク障害でハートビートが分断して、クラスタとしての Split-brain状態になった場合、クラスタマスタのいる「島」がFloating IPを引き継いで、サービスを継続します。ロックファイルを配置する共有ファイルシステムとしては、小さなGlusterFSボリュームを用意して、各ノードでマウントして利用します。

カスタムスクリプトによるノード監視

各ノードで定期的に監視スクリプトを実行して、何らかの問題を検知した場合は、該当ノードのFloating IPを強制的に他ノードに引き継ぎます。デフォルトで用意されている監視スクリプトでは、Floating IPをアサインしたNICのLink Downの検知などを行なっていますので、Service NetworkのNIC障害が発生した場合なども、自動的にFloating IPの引き継ぎが行われます。

(注意)

Internal Networkとハートビート経路を共有する場合、Internal Networkの障害時にハードビート経路切断と(GlusterFSボリューム上のロックファイルによる)ロック情報の共有不可のニ重障害となり、クラスタの動作が不安定になる危険性があります。一方、Service Networkとハートビート経路を共有する構成の場合、Service NetworkのLink Downが発生すると、上述のノード監視によって、Floating IPの引き継ぎが発生しますが、ハートビート切断が同時に発生するため、該当ノードがクラスタマスタの場合、引き継ぎ処理に失敗する場合があります。

ハートビートには、専用の独立したネットワーク経路を使用することで、このような複合障害を回避することができます。もしくは、ハートビートを他のネットワーク経路と共有する場合は、各ネットワーク経路はBondingで冗長化しておき、CTDBによるIP Failoverはノード障害の対応のみに使用するという方法が考えられます。

ハートビート経路をService Networkと共有する場合は、カスタム監視スクリプトで問題を回避する方法も考えられます。カスタム監視スクリプト「Ping Check」を参照ください。

GlusterFSとボリュームの構成

前述のネットワーク構成を持った4ノードの GlusterFSクラスタを構成します。ここでは、次の/etc/hostsを使用して、Internal NetworkのIPがホストネームに一致するようにした状態で、ホストネームを指定して「gluster peer probe」を実行してます。これにより、Internal Networkがglusterデーモンの通信経路として使用されるようになります。

# cat /etc/hosts

192.168.1.101 gluster01

192.168.1.102 gluster02

192.168.1.103 gluster03

192.168.1.104 gluster04

CTDBのハートビートは、TCP4379を使用するので、このポートをiptablesで許可しておきます。

# cat /etc/sysconfig/iptables | grep 4379

-A INPUT -m state --state NEW -m tcp -p tcp --dport 4379 -j ACCEPT

続いて、ロックファイルを配置するためのボリュームを作成します。ここでは、LVMで最小サイズ(4MB)のLVを作成して、それをブリック(/brick/lock)としたボリュームlockvolを作成しています。冗長性を最大限とするため、4ノード全体でのレプリカ構成としています。

# gluster vol info lockvol

Volume Name: lockvol

Type: Replicate

Volume ID: 2b1787e9-dfda-4837-8ac9-d7e30106e9c9

Status: Started

Number of Bricks: 1 x 4 = 4

Transport-type: tcp

Bricks:

Brick1: gluster01:/brick/lock

Brick2: gluster02:/brick/lock

Brick3: gluster03:/brick/lock

Brick4: gluster04:/brick/lock

NFSで公開するボリュームは、2x2のReplica - Distributed構成としています。

# gluster vol info nfsvol

Volume Name: nfsvol

Type: Distributed-Replicate

Volume ID: a8dfc606-5a47-49c4-8e75-ba0b386588a8

Status: Started

Number of Bricks: 2 x 2 = 4

Transport-type: tcp

Bricks:

Brick1: gluster01:/brick/vol00

Brick2: gluster02:/brick/vol00

Brick3: gluster03:/brick/vol00

Brick4: gluster04:/brick/vol00

CTDBの導入と構成

各ノードでctdbパッケージをインストールします。(RHEL6の場合、ctdbパッケージは、Resilient Storage Add-onのリポジトリに含まれています。)

# yum isntall ctdb

ロックボリューム内に全ノード共通の設定ファイルを用意します。これは、gluster01のみで実施します。

ロックボリュームをマウントします。

# mkdir -p /gluster/lock

# mount -t glusterfs localhost:/lockvol /gluster/lock

各ノードのハートビート経路のIPアドレスを列挙したファイルを用意します。

# cat /gluster/lock/nodes

192.168.1.101

192.168.1.102

192.168.1.103

192.168.1.104

CTDBデーモンの設定ファイルを用意します。

# cat /gluster/lock/ctdb

CTDB_RECOVERY_LOCK=/gluster/lock/lockfile

CTDB_PUBLIC_ADDRESSES=/etc/ctdb/public_addresses

CTDB_NODES=/etc/ctdb/nodes

# tunables

CTDB_SET_DeterministicIPs=1

CTDB_SET_RecoveryBanPeriod=120

CTDB_SET_KeepaliveInterval=5

CTDB_SET_KeepaliveLimit=5

CTDB_SET_MonitorInterval=15

最後の5行の設定は必須ではありませんが、それぞれ、次の意味のオプションになります。

  • DeterministicIPs : 各ノードに対するFloating IPの割り当てを固定化します。全ノードが正常稼働している場合、各ノードには必ず同じFloating IPが割り当てられます。ただし、割り当て順をユーザが明示することはできません。CTDBの内部ロジックで決定されます。

  • ReoveryBanPeriod : 障害検知されてクラスタから除外されたノードは、除外発生後、この時間(秒)は再度、クラスタに参加することが許可されません。障害が繰り返して、クラスタからの除外と参加が頻繁に繰り返すことを防止するパラメータです。デフォルトは300ですが、ここでは、障害テストを円滑にするために少し小さな値に変更しています。

    • KeepaliveInterval / KeepaliveLimit : ハートビートの通信間隔(秒)とハートビート切断と判断するまでの通信失敗回数です。これらの積がハートビート切断検知時間になります。

    • MonitorInterval : 後述の「monitor」イベントの発行間隔(秒)です。

ここで、一旦、ロックボリュームをアンマウントしておきます。

# umount /gluster/lock

ここからは、全ノードで共通の作業です。

ロックボリュームのマウントポイント作成と、ロックボリューム上の設定ファイルへのシンボリックリンクを作成します。

# mkdir -p /gluster/lock

# mv /etc/sysconfig/ctdb /etc/sysconfig/ctdb.orig

# ln -s /gluster/lock/ctdb /etc/sysconfig/ctdb

# ln -s /gluster/lock/nodes /etc/ctdb/nodes

このノードにアサイン可能なFloating IPを列挙したファイルを作成します。ここでは、全ノードでお互いにFloating IPを引き継げるように、全ノードで共通に、すべてのFloating IPを記述しておきます。

# cat /etc/ctdb/public_addresses

192.168.122.201/24 eth0

192.168.122.202/24 eth0

192.168.122.203/24 eth0

192.168.122.204/24 eth0

glusterデーモンとCTDBデーモンは連携して起動する必要があるので、自動起動は停止しておきます。

# service glusterd stop

# chkconfig glusterd off

# chkconfig ctdb off

CTDB起動/停止スクリプトの用意

gluster01からgluster01〜gluster04への公開鍵SSH認証を設定しておいて、次のスクリプトを用意します。

ctdb_start.sh

#!/bin/sh -x

ssh gluster01 service glusterd start

ssh gluster02 service glusterd start

ssh gluster03 service glusterd start

ssh gluster04 service glusterd start

sleep 5

ssh gluster01 mount -t glusterfs localhost:/lockvol /gluster/lock

ssh gluster02 mount -t glusterfs localhost:/lockvol /gluster/lock

ssh gluster03 mount -t glusterfs localhost:/lockvol /gluster/lock

ssh gluster04 mount -t glusterfs localhost:/lockvol /gluster/lock

sleep 5

ssh gluster01 service ctdb start

ssh gluster02 service ctdb start

ssh gluster03 service ctdb start

ssh gluster04 service ctdb start

ctdb_stop.sh

#!/bin/sh -x

ssh gluster01 service ctdb stop

ssh gluster02 service ctdb stop

ssh gluster03 service ctdb stop

ssh gluster04 service ctdb stop

sleep 5

ssh gluster01 umount /gluster/lock

ssh gluster02 umount /gluster/lock

ssh gluster03 umount /gluster/lock

ssh gluster04 umount /gluster/lock

sleep 5

ssh gluster01 service glusterd stop

ssh gluster02 service glusterd stop

ssh gluster03 service glusterd stop

ssh gluster04 service glusterd stop

それぞれ、「glusterデーモン起動、ロックボリュームのマウント、CTDBデーモンの起動」を全ノードで順次実施/停止しています。あくまでサンプルですので、例外処理などは入れていません。

注)GlusterFSのEvent Hook機能を利用して、glusterデーモン起動時に自動的にこれらを実施することも可能です。RHS2.0ではそのためのスクリプトが事前に用意されています。

CTDBの起動と動作確認

gluster01で起動スクリプトを実行します。

# ./ctdb_start.sh

+ ssh gluster01 service glusterd start

Starting glusterd: [ OK ]

+ ssh gluster02 service glusterd start

Starting glusterd: [ OK ]

+ ssh gluster03 service glusterd start

Starting glusterd: [ OK ]

+ ssh gluster04 service glusterd start

Starting glusterd: [ OK ]

+ sleep 5

+ ssh gluster01 mount -t glusterfs localhost:/lockvol /gluster/lock

+ ssh gluster02 mount -t glusterfs localhost:/lockvol /gluster/lock

+ ssh gluster03 mount -t glusterfs localhost:/lockvol /gluster/lock

+ ssh gluster04 mount -t glusterfs localhost:/lockvol /gluster/lock

+ sleep 5

+ ssh gluster01 service ctdb start

Starting ctdbd service: [ OK ]

+ ssh gluster02 service ctdb start

Starting ctdbd service: [ OK ]

+ ssh gluster03 service ctdb start

Starting ctdbd service: [ OK ]

+ ssh gluster04 service ctdb start

Starting ctdbd service: [ OK ]

しばらく待つと、次のコマンドで全ノードのステータスがOKになります。

# ctdb status

Number of nodes:4

pnn:0 192.168.1.101 OK (THIS NODE)

pnn:1 192.168.1.102 OK

pnn:2 192.168.1.103 OK

pnn:3 192.168.1.104 OK

Generation:515898200

Size:4

hash:0 lmaster:0

hash:1 lmaster:1

hash:2 lmaster:2

hash:3 lmaster:3

Recovery mode:NORMAL (0)

Recovery master:0

Recovery master(この例では、pnn:0 192.168.1.101)は、ロックファイルで選出されたクラスタマスタを表します。

次のコマンドでFloating IPの割り当て状況が確認できます。

# ctdb ip

Public IPs on node 0

192.168.122.201 node[3] active[] available[eth0] configured[eth0]

192.168.122.202 node[2] active[] available[eth0] configured[eth0]

192.168.122.203 node[1] active[] available[eth0] configured[eth0]

192.168.122.204 node[0] active[eth0] available[eth0] configured[eth0]

各ノードの実際のIPは、ipコマンドで確認します。

# ip addr show eth0

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

link/ether 52:54:00:b6:e2:a4 brd ff:ff:ff:ff:ff:ff

inet 192.168.122.101/24 brd 192.168.122.255 scope global eth0

inet 192.168.122.204/24 brd 192.168.122.255 scope global secondary eth0

inet6 fe80::5054:ff:feb6:e2a4/64 scope link

valid_lft forever preferred_lft forever

特定ノード(この例ではgluster02)を再起動すると、該当ノードのFloating IPは他のノードに引き継がれます。CTDBデーモンのログは/var/log/log.ctdbに出力されています。

# ssh gluster02 reboot

# ctdb status

Number of nodes:4

pnn:0 192.168.1.101 OK (THIS NODE)

pnn:1 192.168.1.102 DISCONNECTED|UNHEALTHY|INACTIVE

pnn:2 192.168.1.103 OK

pnn:3 192.168.1.104 OK

Generation:1290868

Size:3

hash:0 lmaster:0

hash:1 lmaster:2

hash:2 lmaster:3

Recovery mode:NORMAL (0)

Recovery master:0

# ctdb ip

Public IPs on node 0

192.168.122.201 node[3] active[] available[eth0] configured[eth0]

192.168.122.202 node[2] active[] available[eth0] configured[eth0]

192.168.122.203 node[0] active[eth0] available[eth0] configured[eth0]

192.168.122.204 node[0] active[eth0] available[eth0] configured[eth0]

今回の設定では、NFSクライアントからのアクセスは約1分間停止した後、エラーなど発生せずにアクセスが再開しました。

gluster02で、起動処理を行うと、Floating IPは元の状態に戻ります。切り戻し時のNFSクライアントからのアクセス停止はありませんでした。

# ssh gluster02 service glusterd start

Starting glusterd: [ OK ]

# ssh gluster02 mount -t glusterfs localhost:/lockvol /gluster/lock

# ssh gluster02 service ctdb start

Starting ctdbd service: [ OK ]

# ctdb ip

Public IPs on node 0

192.168.122.201 node[3] active[] available[eth0] configured[eth0]

192.168.122.202 node[2] active[] available[eth0] configured[eth0]

192.168.122.203 node[1] active[] available[eth0] configured[eth0]

192.168.122.204 node[0] active[eth0] available[eth0] configured[eth0]

前述のDeterministicIPsを設定しているので、必ず、最初にCTDBデーモンを起動した時と同じ配置になります。この設定がない場合、障害/復旧のパターンによっては、Floating IPの配置が最初と変わる場合があります。

監視スクリプトのカスタマイズ

CTDBの動作に伴う各種イベントが事前に定義されており、イベントが発生するごとに、下記のスクリプトが番号順にすべて実行されます。

# ls /etc/ctdb/events.d/

00.ctdb 11.natgw 20.multipathd 41.httpd 61.nfstickle

01.reclock 11.routing 31.clamd 50.samba 70.iscsi

10.interface 13.per_ip_routing 40.vsftpd 60.nfs 91.lvs

各スクリプトは、イベントに対応したハンドラを実装しており、イベントに伴う自動化を実現します。イベントの種類とハンドラの書き方は、READMEを参照してください。

特に、15秒ごとに「monitor」イベントが発行されるようになっており、このイベントをノードの定期監視に利用できます。たとえば、「10.interface」では、ethtoolコマンドで、Floating IPを持つNICのLink Statusを確認して、Link Downを検知すると、該当ノードを強制的に「UNHEALTHY」ステータスにして、Floating IPの引き継ぎを発生します。

gluster02のeth0をLink Downした場合の例

# ctdb status

Number of nodes:4

pnn:0 192.168.1.101 OK (THIS NODE)

pnn:1 192.168.1.102 UNHEALTHY

pnn:2 192.168.1.103 OK

pnn:3 192.168.1.104 OK

Generation:1068935853

Size:4

hash:0 lmaster:0

hash:1 lmaster:1

hash:2 lmaster:2

hash:3 lmaster:3

Recovery mode:NORMAL (0)

Recovery master:0

この他に、独自にmonitorイベントのハンドラを追加して、glutsterデーモンの障害を検知してIP Failoverを発生させるなどの作りこみも可能と思われます。

monitorイベントの発行間隔は、/gluster/lock/ctdbのパラメータで変更可能です。

CTDB_SET_MonitorInterval=15