めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

GlusterFSでHAクラスタ

何をするかというと。

共有ディスクを使わずにDRBDでローカルディスクのデータを同期させて、HAクラスタを組むというテクニックがあります。それはそれでありなのですが、障害発生後の再同期に時間がかかるなど、なかなか運用が大変なのも事実です。

で、DRBDの代わりに、GlusterFSのレプリケーション機能を使ってローカルディスクのファイルを同期させたらどうだろう、と思って試してみました。

GlusterFSについては、下記の資料を参照ください。

はじめてのGlusterFS

GlusterFSと組み合わせるHAは、RHEL6 High Availability Add-Onです。High Availability Add-Onについては、下記を参照。

30分でRHEL6 High Availability Add-Onを超絶的に理解しよう!

RHEL6 High Availability Add-On Technical Guide

構成

物理サーバを用意するのが面倒なので、KVM仮想マシンで組みました。

High Availability Add-Onの基本セットアップ手順は下記を参照。今回は、共有ディスクは使わないので、iSCSIのセットアップは不要です。

KVMでHAクラスタの機能検証環境を構築

GlusterFSの導入・設定

Glusterの導入:両ノードで実施

# yum groupinstall "Development tools"
# wget http://download.gluster.com/pub/gluster/glusterfs/qa-releases/3.3-beta-2/glusterfs-3.3beta2.tar.gz
# tar -xvzf glusterfs-3.3beta2.tar.gz
# cd glusterfs-3.3beta2
# ./configure && make
# make install

# chkconfig glusterd on
# service glusterd start

ディレクトリの準備:両ノードで実施

# mkdir /glvol00
# mkdir /gldata00

/glvol00は、この後で定義するボリュームのバックエンド領域(Brick)として使います。本来はルートファイルシステムとは別に専用のファイルシステムをマウントしておくのがよいでしょう。/gldata00は共有ファイルシステムのマウントポイントです。

PeerとVolumeの定義:node01のみで実施

# gluster peer probe node02h

# gluster volume create data00 replica 2 transport tcp node01m:/glvol00 node02m:/glvol00
# gluster volume start data00

# gluster volume info data00
 
Volume Name: data00
Type: Replicate
Status: Started
Number of Bricks: 2
Transport-type: tcp
Bricks:
Brick1: node01m:/glvol00
Brick2: node02m:/glvol00

共有ファイルシステムをマウントするためのスクリプトを用意します。両ノードに共通で配置してください。

/etc/init.d/glmount

#!/bin/bash

MOUNTPOINT=/gldata00    # Mount Point
GLVOL=localhost:data00  # Gluster Volume

start()
{
    status
    rc=$?
    if [[ $rc -eq 0 ]]; then
        RETVAL=0
    else
        mount -t glusterfs -o log-level=WARNING,log-file=/var/log/gluster.log $GLVOL $MOUNTPOINT
        RETVAL=$?
    fi
}

stop()
{
    status
    rc=$?
    if [[ $rc -eq 0 ]]; then
        umount $MOUNTPOINT
#        RETVAL=$?
        RETVAL=0 # ignore umount failure
    else
        RETVAL=0
    fi
}

status() {
    mount | grep $MOUNTPOINT >/dev/null 2>&1
    return $?
}

restart() {
    stop
    start
}

selfheal() {
    status
    rc=$?
    if [[ $rc -eq 0 ]]; then
        echo "You should unmount the shared fs first."      
        RETVAL=1
    else
        start
        find $MOUNTPOINT -noleaf -print0 | xargs --null stat
        stop
        RETVAL=0
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    selfheal)
        selfheal
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart|status|selfheal}"
        RETVAL=2
esac
exit $RETVAL

オプションは次の通り

オプション 説明
start マウントする
stop アンマウントする
status マウントしてたら0を返す。マウントしてなかったら1を返す
selfheal 障害回復後にレプリケーションを再同期する。アンマウントした状態で実行する

PostgreSQLの導入

サンプルアプリケーションとして PostgreSQL を使用します。

node01で実施

# yum install postgresql-server
# echo "PGDATA=/gldata00/pgsql" > /etc/sysconfig/pgsql/postgresql 
# chkconfig postgresql off

# /etc/init.d/glmount start
# service postgresql initdb

#### /gldata00/pgsql/pg_hba.conf, postgresql.confを適宜編集後、起動確認

# service postgresql start
postgresql サービスを開始中:                               [  OK  ]
# service postgresql stop
postgresql サービスを停止中:                               [  OK  ]

# /etc/init.d/glmount stop

node02で実施

# yum install postgresql-server
# chkconfig postgresql off
# echo "PGDATA=/gldata00/pgsql" > /etc/sysconfig/pgsql/postgresql 

#### node01でinitdbしたもので、PostgreSQLが起動することを確認

# /etc/init.d/glmount start

# service postgresql start
postgresql サービスを開始中:                               [  OK  ]
# service postgresql stop
postgresql サービスを停止中:                               [  OK  ]

# /etc/init.d/glmount stop

HAクラスタの構成

/etc/cluster/cluster.conf

<?xml version="1.0"?>
<cluster config_version="1" name="cluster01">
  <cman expected_votes="1" two_node="1"/>
  <clusternodes>
    <clusternode name="node01h" nodeid="1" votes="1">
      <fence>
        <method name="virsh_reboot">
          <device name="kvmhost01" port="RHCSvm01-gluster"/>
        </method>
      </fence>
    </clusternode>
    <clusternode name="node02h" nodeid="2" votes="1">
      <fence>
        <method name="virsh_reboot">
          <device name="kvmhost01" port="RHCSvm02-gluster"/>
        </method>
      </fence>
    </clusternode>
  </clusternodes>
  <totem token="20000"/>
  <fencedevices>
    <fencedevice agent="fence_virsh" identity_file="/etc/cluster/fence_virsh_key" ipaddr="192.168.8.1" login="root" name="kvmhost01" option="reboot"/>
  </fencedevices>
  <rm>
    <failoverdomains>
      <failoverdomain name="dom01" ordered="0" restricted="1">
        <failoverdomainnode name="node01h" priority="1"/>
        <failoverdomainnode name="node02h" priority="2"/>
      </failoverdomain>
    </failoverdomains>

    <service autostart="0" domain="dom01" name="service01">
      <ip address="192.168.7.99" monitor_link="on"/>
      <script file="/etc/init.d/glmount" name="glmount">
          <script file="/etc/init.d/postgresql" name="pgsql"/>
      </script>
    </service>
  </rm>
</cluster>

注目点は、サービスリソースの定義部分で、下記の3種類のリソースを定義しています。

    <service autostart="0" domain="dom01" name="service01">

# サービスIPアドレス
      <ip address="192.168.7.99" monitor_link="on"/>

# 共有ファイルシステムのマウント
      <script file="/etc/init.d/glmount" name="glmount">

# PostgreSQLの起動
          <script file="/etc/init.d/postgresql" name="pgsql"/>
      </script>
    </service>

動作テスト

クラスタの起動とサービスの開始:node01で実施

# clstart_all 
Starting cluster: 
   Checking if cluster has been disabled at boot... [  OK  ]
   Checking Network Manager... Starting cluster: 
   Checking if cluster has been disabled at boot... [  OK  ]
   Checking Network Manager... [  OK  ]
   Global setup... [  OK  ]
   Loading kernel modules... [  OK  ]
   Global setup... [  OK  ]
   Loading kernel modules... [  OK  ]
   Mounting configfs... [  OK  ]
   Mounting configfs... [  OK  ]
   Starting cman... [  OK  ]
   Starting cman... [  OK  ]
[  OK  ]
   Waiting for quorum...    Waiting for quorum... [  OK  ]
[  OK  ]
   Starting fenced...    Starting fenced... [  OK  ]
   Starting dlm_controld... [  OK  ]
   Starting dlm_controld... [  OK  ]
   Starting gfs_controld... [  OK  ]
   Starting gfs_controld... [  OK  ]
   Unfencing self... [  OK  ]
   Unfencing self... [  OK  ]
   Joining fence domain... [  OK  ]
   Joining fence domain... [  OK  ]
[  OK  ]
Starting Cluster Service Manager: Starting Cluster Service Manager: [  OK  ]
[  OK  ]

# clustat 
Cluster Status for cluster01 @ Thu Sep 15 14:36:40 2011
Member Status: Quorate

 Member Name                             ID   Status
 ------ ----                             ---- ------
 node01h                                     1 Online, Local, rgmanager
 node02h                                     2 Online, rgmanager

 Service Name                   Owner (Last)                   State         
 ------- ----                   ----- ------                   -----         
 service:service01              (none)                         disabled      

# clusvcadm -e service01
Local machine trying to enable service:service01...Success
service:service01 is now running on node01h

# clustat 
Cluster Status for cluster01 @ Thu Sep 15 14:37:15 2011
Member Status: Quorate

 Member Name                             ID   Status
 ------ ----                             ---- ------
 node01h                                     1 Online, Local, rgmanager
 node02h                                     2 Online, rgmanager

 Service Name                   Owner (Last)                   State         
 ------- ----                   ----- ------                   -----         
 service:service01              node01h                        started

# df
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/vda2              6047492   3318144   2422148  58% /
tmpfs                   510788     22556    488232   5% /dev/shm
/dev/vda1               198337     62649    125448  34% /boot
localhost:data00       6047488   3318144   2422144  58% /gldata00

# su - postgres -c "psql -l"
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |      アクセス権       
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
(3 行)

成功。ちゃんと共有ファイルシステムもマウントされて、PostgreSQLも起動しています。

node01で擬似障害発生:node01で実施

# pkill -9 corosync

node02で引継ぎ確認:node02で実施

# clustat 
Cluster Status for cluster01 @ Thu Sep 15 14:42:48 2011
Member Status: Quorate

 Member Name                             ID   Status
 ------ ----                             ---- ------
 node01h                                     1 Offline
 node02h                                     2 Online, Local, rgmanager

 Service Name                   Owner (Last)                   State         
 ------- ----                   ----- ------                   -----         
 service:service01              node02h                        started       

# df
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/vda2              6047492   3306196   2434096  58% /
tmpfs                   510788     22556    488232   5% /dev/shm
/dev/vda1               198337     47989    140108  26% /boot
localhost:data00       6047488   3306240   2434048  58% /gldata00

# su - postgres
$ createdb testdb
$ psql -l
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |      アクセス権       
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
 testdb    | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
(4 行)

オッケーです。最後に、node01へのresyncがうまくいくかみるために新しいDB(testdb)を作成してます。

node01の再起動後に回復処理実施:node01で実施

### まずはGlusterdの稼働を確認

# gluster peer status
Number of Peers: 1

Hostname: node02m
Uuid: a4b9d549-5c0f-498d-b33a-426ba50f8bf7
State: Peer in Cluster (Connected)

# gluster volume info data00
 
Volume Name: data00
Type: Replicate
Status: Started
Number of Bricks: 2
Transport-type: tcp
Bricks:
Brick1: node01m:/glvol00
Brick2: node02m:/glvol00

###レプリケーションの再同期を実施
# /etc/init.d/glmount selfheal
(中略)
  File: `/gldata00/pgsql/global/1260_fsm'
  Size: 24576     	Blocks: 56         IO Block: 131072 通常ファイル
Device: 14h/20d	Inode: 18446744073076288545  Links: 1
Access: (0600/-rw-------)  Uid: (   26/postgres)   Gid: (   26/postgres)
Access: 2011-09-15 14:47:24.686158005 +0900
Modify: 2011-09-15 14:26:33.050339178 +0900
Change: 2011-09-15 14:26:33.050339178 +0900
  File: `/gldata00/pgsql/pg_ident.conf'
  Size: 1631      	Blocks: 16         IO Block: 131072 通常ファイル
Device: 14h/20d	Inode: 18446744072504937921  Links: 1
Access: (0600/-rw-------)  Uid: (   26/postgres)   Gid: (   26/postgres)
Access: 2011-09-15 14:28:36.965551698 +0900
Modify: 2011-09-15 14:26:22.529132482 +0900
Change: 2011-09-15 14:26:22.532127898 +0900

### HAクラスタの起動とサービスのTake Back
# clstart
Starting cluster: 
   Checking if cluster has been disabled at boot...        [  OK  ]
   Checking Network Manager...                             [  OK  ]
   Global setup...                                         [  OK  ]
   Loading kernel modules...                               [  OK  ]
   Mounting configfs...                                    [  OK  ]
   Starting cman...                                        [  OK  ]
   Waiting for quorum...                                   [  OK  ]
   Starting fenced...                                      [  OK  ]
   Starting dlm_controld...                                [  OK  ]
   Starting gfs_controld...                                [  OK  ]
   Unfencing self...                                       [  OK  ]
   Joining fence domain...                                 [  OK  ]
Starting Cluster Service Manager:                          [  OK  ]

# clustat 
Cluster Status for cluster01 @ Thu Sep 15 14:58:34 2011
Member Status: Quorate

 Member Name                             ID   Status
 ------ ----                             ---- ------
 node01h                                     1 Online, Local, rgmanager
 node02h                                     2 Online, rgmanager

 Service Name                   Owner (Last)                   State         
 ------- ----                   ----- ------                   -----         
 service:service01              node02h                        started       

# clusvcadm -r service01 -m node01h
Trying to relocate service:service01 to node01h...Success
service:service01 is now running on node01h

# clustat 
Cluster Status for cluster01 @ Thu Sep 15 14:59:10 2011
Member Status: Quorate

 Member Name                             ID   Status
 ------ ----                             ---- ------
 node01h                                     1 Online, Local, rgmanager
 node02h                                     2 Online, rgmanager

 Service Name                   Owner (Last)                   State         
 ------- ----                   ----- ------                   -----         
 service:service01              node01h                        started       

# su - postgres -c "psql -l"
                                         データベース一覧
   名前    |  所有者  | エンコーディング |  照合順序   | Ctype(変換演算子) |      アクセス権       
-----------+----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
 template0 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
 template1 | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres
                                                                           : postgres=CTc/postgres
 testdb    | postgres | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
(4 行)

オッケーです。

まとめ

まずは普通に構成できることは確認できました。あと、GlusterFSのアクセスタイムアウト時間などに合わせて、障害検知時間のチューニングなどが必要になると思います。

あとは、みんな気になるパフォーマンスですが、こちらは、別環境でじっくりと確認する予定です。