MySQL + Heartbeat で fail-over cluster な DB サーバーを構築してみる

普段にも増してくだらない番組しかやってないので、第 9 を聞きながらブログを書いてやる。

ネタは MySQL + Heartbeat で 2 ノードフェイルオーバークラスターな DB サーバーを構築する。昼間の仕事で必要になりそうだからまとめたんだけど、なんかトンチンカンなことやってる気がする。だからこのエントリーには続きがある予定。

Prerequisites

このエントリーが想定しているソフトウェア環境は以下のとおり。

  • CentOS 5.1
  • MySQL 5.0.22 (RPM: mysql-server-5.0.22-2.2.el5_1.1)
  • Heartbeat 2.1.2 (RPM: heartbeat-2.1.2-3.el5.centos)

また、エントリーで扱うのは Heartbeat の設定からなので、システムとか MySQL とかの設定は各自やっておくこと。
登場するホストは以下のとおり。/etc/hosts には書いておくべし。

ホスト名 IP アドレス 備考
* 192.168.108.30 DB サーバーの代表アドレス
linux01.test.vm.dacci.org 192.168.108.31 DB クライアント
linux02.test.vm.dacci.org 192.168.108.32 DB ノード 1
linux03.test.vm.dacci.org 192.168.108.33 DB ノード 2

設定

クラスターの各メンバーノードすべてで、同じ内容に設定すること。

/etc/ha.d/ha.cf

クラスターの基本的な設定を書くファイル。詳細は /usr/share/doc/heartbeat-2.1.2/ha.cf などを参照されたし。

logfacility local0
keepalive 2
deadtime 60
warntime 10
initdead 30
udpport 694
bcast eth0
auto_failback off
# member nodes
node linux02.test.vm.dacci.org
node linux03.test.vm.dacci.org
crm on
use_logd yes
/etc/ha.d/authkeys

ノードの認証に関する設定ファイル。パーミッションを 600 にしないとエラーになる。これも詳細は /usr/share/doc/heartbeat-2.1.2/authkeys を見てくだしあ。

auth 1
1 sha1 test.vm.dacci.org
/var/lib/heartbeat/crm/cib.xml

クラスターの詳細な動作を決める設定ファイル。XML って読めない。。。/usr/share/heartbeat/crm.dtd がこのファイルの DTD で、コメントで色々説明が書いてあるので読んでw。ちょっと長いけど以下のとおり。

<?xml version="1.0" encoding="UTF-8"?>
<cib admin_epoch="0" epoch="1" num_updates="1" have_quorum="1">
  <configuration>
    <crm_config/>
    <nodes>
      <!-- メンバーになるノード -->
      <node id="nd_linux02" uname="linux02.test.vm.dacci.org" type="normal"/>
      <node id="nd_linux03" uname="linux03.test.vm.dacci.org" type="normal"/>
    </nodes>
    <resources>
      <!-- IP Aliasing で振られるアドレス -->
      <primitive id="rc_IPaddr" class="ocf" type="IPaddr" provider="heartbeat">
        <instance_attributes id="ia_IPaddr">
          <attributes>
            <nvpair id="ip" name="ip" value="192.168.108.30"/>
            <nvpair id="cidr_netmask" name="cidr_netmask" value="255.255.255.0"/>
            <!-- エイリアスを振る NIC -->
            <nvpair id="nic" name="nic" value="eth0"/>
          </attributes>
        </instance_attributes>
      </primitive>
      <!-- MySQL なリソース (1: マスター風) -->
      <primitive id="rc_mysql_1" class="ocf" type="mysql" provider="heartbeat">
        <instance_attributes id="ia_mysql_1">
          <attributes>
            <!-- DB のヘルスチェックに使うアカウント -->
            <nvpair id="test_user_1" name="test_user" value="root"/>
            <nvpair id="test_passwd_1" name="test_passwd" value="mysqladmin"/>
          </attributes>
        </instance_attributes>
        <operations>
          <!-- モニタ (ヘルスチェッカー) -->
          <!-- ダウン時には他のノードに振り分ける -->
          <op id="monitor_1" name="monitor" interval="10" timeout="30" start_delay="10" on_fail="fence"/>
        </operations>
      </primitive>
      <!-- MySQL なリソース (2: スレーブ風) -->
      <primitive id="rc_mysql_2" class="ocf" type="mysql" provider="heartbeat">
        <instance_attributes id="ia_mysql_2">
          <attributes>
            <nvpair id="test_user_2" name="test_user" value="root"/>
            <nvpair id="test_passwd_2" name="test_passwd" value="mysqladmin"/>
          </attributes>
        </instance_attributes>
        <operations>
          <!-- ダウンしたらそのまま停止 (振り分けない) -->
          <op id="monitor_2" name="monitor" interval="10" timeout="30" start_delay="10" on_fail="stop"/>
        </operations>
      </primitive>
    </resources>
    <constraints>
      <!-- rc_mysql_1 は、rc_IPaddr と同じノードに割り当てる -->
      <rsc_colocation id="colocation_1" from="rc_IPaddr" to="rc_mysql_1" score="INFINITY"/>
    </constraints>
  </configuration>
  <status/>
</cib>

ここで肝になるのは、たぶん MySQL なリソースがダウンした時の動作と、代表アドレスが常にマスター風リソースのあるノードに割り当てられること *1。スレーブ風リソースがダウンした時は特に何も起こらないけど、マスター風リソースがダウンした時は、、、

  1. マスター風リソースがダウン
  2. on_fail="fence" なのでノードが OFFLINE になる
  3. 代表アドレスがスレーブ風のリソースが乗っているノードに割り当てられる
  4. Co-location の制約で、マスター風リソースが割り当てなおされる

っていう順序で系の切り替わりが発生する (はず)。実際の切り替わりには 30 秒もかかってない感じなので、運が良ければユーザーに切り替わりを意識させずにすむかも。
ただ、切り替わったあとは 1 つのノードにマスター風リソースとスレーブ風リソースが同居している状態で、いまのところきれいに切り戻すことができない。さぁどうしよう・・・

*1:ホントは逆で、代表アドレスの割り当てられたノードにマスター風リソースが割り当てられる。