Pacemaker

2015/10/26更新

対応バージョン: Vagrant 1.4.3, Pacemaker 1.0.13, Heartbeat 3.0.5, Apache 2.2.15, Tomcat 7.0.56

以下で作成したApacheの2ノードクラスタにTomcatを組み込む手順を示す。

関連資料・記事

要件は以下の通りとする。

Tomcat用にVIP(192.168.1.102)を用意し、稼動系・待機系どちらにも接続できるようにする
コンテンツはSANやNASなどの共有領域には置かずに各サーバのローカルに置くものとする
待ち受けポートはデフォルトのままとし、Webサーバへのアクセスは8080、AJP接続は8009とする
Apacheに/app/でアクセスしてきた場合にVIPの8009ポートにAJP接続する
AJP接続がうまくいかない場合に備えてWebサーバ用のポート8080は閉じずに残しておく

Vagrantfile作成

基本的な設定は今までと同様とし、プロビジョニング用のスクリプトでTomcat環境を構築する。

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.define :s1 do |v|
    v.vm.box = "centos65_64"
    v.vm.hostname = "s1" 
    v.vm.network :private_network, ip: "192.168.1.121"
    v.vm.network :private_network, ip: "192.168.9.121"
    v.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2122
    v.vm.provision :shell, :path => "heartbeat.sh"
    v.vm.provision :shell, :path => "apache.sh"
    v.vm.provision :shell, :path => "tomcat.sh"
    v.vm.provision :shell, :path => "httpd-proxy.sh"
  end

  config.vm.define :s2 do |v|
    v.vm.box = "centos65_64"
    v.vm.hostname = "s2" 
    v.vm.network :private_network, ip: "192.168.1.122"
    v.vm.network :private_network, ip: "192.168.9.122"
    v.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2222
    v.vm.provision :shell, :path => "heartbeat.sh"
    v.vm.provision :shell, :path => "apache.sh"
    v.vm.provision :shell, :path => "tomcat.sh"
    v.vm.provision :shell, :path => "httpd-proxy.sh"
    v.vm.provision :shell, :path => "pacemaker.sh"
  end
end

前回のheartbeat.shからApacheの環境構築部分を切り出してapache.shとし、Tomcatの構築(tomcat.sh)とAJP接続の設定(httpd-proxy.sh)をそれぞれ新規作成する。

pacemaker.shはtomcatのリソース定義を追加する。

heartbeat.sh, apache.sh

heartbeat.shの前回作成ファイルから以下のApacheの環境構築部分を削除してapache.shに移動する。

### install httpd(Apache)
yum -y install httpd
cp /vagrant/index.html /var/www/html

tomcat.sh

#!/bin/sh

today=`date "+%Y%m%d"`

### install
yum -y install java-1.7.0-openjdk

useradd -s /sbin/nologin -u 700 tomcat

cd /tmp

wget http://ftp.riken.jp/net/apache/tomcat/tomcat-7/v7.0.56/bin/apache-tomcat-7.0.56.tar.gz

tar zxvf apache-tomcat-7.0.56.tar.gz
rm -f apache-tomcat-7.0.56.tar.gz
mv apache-tomcat-7.0.56 /usr/local
cd /usr/local
chown -R tomcat:tomcat apache-tomcat-7.0.56
ln -s apache-tomcat-7.0.56 tomcat

install -m 0755 /vagrant/tomcat /etc/init.d
chkconfig --add tomcat
chkconfig tomcat off

### setting
cp -p /etc/profile /etc/profile.${today}

JAVA_PATH=`readlink -e $(which java)`

cat << EOD >> /etc/profile

JAVA_HOME=${JAVA_PATH}
CATALINA_HOME=/usr/local/tomcat
export JAVA_HOME CATALINA_HOME
EOD

source /etc/profile

### firewall
cd /etc/sysconfig

cp -p iptables iptables.${today}

iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 8009 -j ACCEPT

service iptables save
service iptables restart
### install

Tomcatに先立ちOpenJDKをインストールする。

実行ユーザのtomcatを作成する2台のサーバでそれぞれ作成するが、UIDの付与をシステムに任せておくと2台で別々のUIDが割り当てられる可能性があるので-uオプションでUIDを指定しておく。今回の構成のようにコンテンツが各ローカルにあれば問題ないが、共有領域に両サーバからアクセスする構成にする場合にUIDが異なっているとファイルの書き込みができなくなる。

Tomcatのバイナリを任意のミラーサイトからダウンロードして/usr/local配下に配置する。

/vagrantに用意した起動スクリプトtomcatを/etc/init.dにインストールし、システム起動時の自動起動はOFFにする。

(tomcat)

#!/bin/bash
#
# tomcat       Startup script for the Tomcat Servlet Container
#
# chkconfig: 2345 35 65
# description: Tomcat is the servlet container that is used in the official \
#              Reference Implementation for the Java Servlet and JavaServer \
#              Pages technologies

TOMCAT_USER=tomcat
CATALINA_HOME=/usr/local/tomcat
 
. /etc/rc.d/init.d/functions
prog=tomcat

start() {
    echo -n $"Starting $prog: "
    daemon --user $TOMCAT_USER $CATALINA_HOME/bin/startup.sh > /dev/null
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
        echo_success
    else
        echo_failure
    fi
    echo
    [ $RETVAL = 0 ] && touch /var/lock/subsys/$prog
    return $RETVAL
}
stop() {
    echo -n $"Stopping $prog: "
    daemon --user $TOMCAT_USER $CATALINA_HOME/bin/shutdown.sh > /dev/null
    RETVAL=$?
    if [ $RETVAL -eq 0 ]; then
        echo_success
    else
        echo_failure
    fi
    echo
    [ $RETVAL = 0 ] && rm -f /var/lock/subsys/$prog
    return $RETVAL
}
 
# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    stop
    start
    ;;
  status)
    INSTANCES=`ps --columns 512 -aef|grep java|grep tomcat|grep org.apache.catalina.startup.Bootstrap|wc -l`
    if [ $INSTANCES -eq 0 ]; then
        echo $prog is stopped
        RETVAL=3
    else
        if [ $INSTANCES -eq 1 ]; then
            echo $prog is running 1 instance...
        else
            echo $prog is running $INSTANCES instances...
        fi
        RETVAL=0
    fi
    ;;
  *)
    echo $"Usage: $prog {start|stop|restart|status|help}"
    exit 1
esac
 
exit $RETVAL
### setting

/etc/profileにTomcatが動作するための環境変数JAVA_HOME,CATALINA_HOMEを定義する。

### firewall

2つの待受ポート(8080,8009)へのアクセスを許可する。

httpd-proxy.sh

### connect Apache - Tomcat
cp /vagrant/httpd-proxy.conf /etc/httpd/conf.d/
### connect Apache - Tomcat

/vagrantに用意したApache - TomcatのAJP接続用のファイルを配置する。

(httpd-proxy.conf)

<Location /app/>
  ProxyPass ajp://192.168.1.102:8009/
</Location>

pacemaker.sh

前回の設定にTomcatのリソース定義を追加する。

:
### setting resouce - Tomcat
JAVA_PATH="/usr"

crm configure primitive vip_tomcat ocf:heartbeat:IPaddr2 params \
ip="192.168.1.102" nic="eth1" cidr_netmask="24" op monitor interval="10"

crm configure primitive tomcat ocf:heartbeat:tomcat params \
java_home="${JAVA_PATH}" catalina_home="/usr/local/tomcat" \
tomcat_user="tomcat" statusurl="http://192.168.1.102:8080/" \
op start interval="0" timeout="90" on-fail="restart" \
op monitor interval="10" timeout="60" on-fail="restart" \
op stop interval="0" timeout="300" on-fail="block"

crm configure group webapp vip_tomcat tomcat
### setting resouce - Tomcat

java_homeに指定するのはjavaプログラムそのものではなく2つ上のディレクトリ。RA(/usr/lib/ocf/resource.d/heartbeat/tomcat)の中でjavaのパスを以下のように決めているので注意が必要である。

(tomcat)

:
586 JAVA_HOME="${OCF_RESKEY_java_home}"
:
644 JAVA=${JAVA_HOME}/bin/java
:

仮想マシン起動

以上の設定が終わったら仮想マシンを起動し、以下のURLにアクセスしてどちらもTomcatのトップ画面が出ればOKである。

http://192.168.1.102:8080/ ... TomcatのWebサーバ
http://192.168.1.101/app/ .... Apache - Tomcat AJP接続

リソースの状態は以下のようになっているはずである。

# crm_mon -f
:
Online: [ s1 s2 ]

 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):  Started s1
     httpd      (ocf::heartbeat:apache):   Started s1
 Resource Group: webapp
     vip_tomcat (ocf::heartbeat:IPaddr2):  Started s1
     tomcat     (ocf::heartbeat:tomcat):   Started s1

Migration summary:
* Node s1: 
* Node s2: 

2015/10/07更新

対応バージョン: Vagrant 1.4.3, Pacemaker 1.0.13, Heartbeat 3.0.5

Vagrantで用意したCentOSのBOXを使って2つの仮想マシン上にPacemaker + Heartbeatによる2ノードクラスタを構築する手順を示す。ホストOSはUbuntu14.04。

関連資料・記事

クラスタ環境は上記で作成した構成を踏襲するが、以下の点が異なっている。

NICのインタフェース名

eth1: サービス提供用(上記資料ではem1)

eth2: インターコネクト(上記資料ではp4p1)

リソース管理するサービスはApacheのみ(上記資料ではApacheとSamba)
rsyslogの設定は変更しない

仮想マシン作成

まず2台の仮想マシンを作成する。

% cd
% mkdir -p vagrant/centos65
% cd vagrant/centos65
% vagrant init centos65_64
% ls
Vagrantfile

各仮想マシンの設定が異なるのでVagrantfileを以下のように記述する。

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.define :s1 do |v|
    v.vm.box = "centos65_64"
    v.vm.hostname = "s1" 
    v.vm.network :private_network, ip: "192.168.1.121"
    v.vm.network :private_network, ip: "192.168.9.121"
    v.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2122
    v.vm.provision :shell, :path => "heartbeat.sh"
  end

  config.vm.define :s2 do |v|
    v.vm.box = "centos65_64"
    v.vm.hostname = "s2"
    v.vm.network :private_network, ip: "192.168.1.122"
    v.vm.network :private_network, ip: "192.168.9.122"
    v.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2222
    v.vm.provision :shell, :path => "heartbeat.sh"
    v.vm.provision :shell, :path => "pacemaker.sh"
  end
end

初回起動時のプロビジョニングとして各仮想マシンでHeatbeatの設定を行い(heartbeat.sh)、2台がHA構成になったあと2台目の仮想マシンでPacemakerの設定を行う(pacemaker.sh)。

heartbeat.shとpacemaker.shはそれぞれ以下のように記述し、Vagrantfileと同じパスに格納する。説明は後述する。

heartbeat.sh

#!/bin/sh

yum update
yum -y install wget

### install pacemaker
cd /tmp
tar xvzf /vagrant/pacemaker-1.0.13-2.1.el6.x86_64.repo.tar.gz
cd pacemaker-1.0.13-2.1.el6.x86_64.repo
yum -y -c pacemaker.repo install pacemaker-1.0.13-2.el6.x86_64 heartbeat-3.0.5-1.1.el6.x86_64 pm_extras-1.5-1.el6.x86_64

### install httpd(Apache)
yum -y install httpd
cp /vagrant/index.html /var/www/html

### add iptables
iptables -A INPUT -p udp -m udp --dport 694 -j ACCEPT
service iptables save

### add hosts
cat /vagrant/hosts_add >> /etc/hosts

### setting heartbeat
install /vagrant/ha.cf /etc/ha.d
install -m 0600 /vagrant/authkeys /etc/ha.d

### start heartbeat
service heartbeat start
### install pacemaker

ホストOS側の共有ディレクトリ(Vagrantfileと同じパス)に格納したPacemakerのバイナリを展開してインストールする。

Pacemakerのバイナリは以下から入手できる。

http://sourceforge.jp/projects/linux-ha/releases/

### install httpd(Apache)

Apacheをインストールする。設定はとりあえずデフォルトのまま。

Apacheの動作をモニタするために内部的にwgetでHTTPアクセスをするのでwgetもインストールしておく。

また上記アクセスにおいてHTMLファイルにアクセスできる必要があるので以下のダミーHTMLを共有ディレクトリ(Vagrantfileと同じパス)に用意しておきApacheのドキュメントルートにコピーする。

(index.html)

<html>
</html>

これをやっておかないとApacheのリソースが起動してもすぐ終了してしまう。

関連資料・記事

### add iptables

インターコネクト通信用ポート(694/udp)の接続を許可する。

### add hosts

サーバ2台分のホスト情報を追加する。

このファイルもダミーHTML同様、共有ディレクトリに用意しておく。

(hosts_add)

192.168.1.121 s1
192.168.1.122 s2
### setting heartbeat

Heartbeat用の設定ファイル(/etc/ha.d/ha.cf、/etc/ha.d/authkeys)を用意する。

このファイルも共有ディレクトリに用意しておく。

(ha.cf)

pacemaker on

debug 0
udpport 694
keepalive 2
warntime 7
deadtime 10
initdead 10
logfacility local1

ucast eth2 192.168.9.121
ucast eth2 192.168.9.122

ping 192.168.1.1

node s1
node s2

watchdog /dev/watchdog
respawn root /usr/lib64/heartbeat/ifcheckd

(authkeys)

auth 1
1 sha1 xxxxx <--- インターコネクト通信用の任意のパスフレーズ
### start heartbeat

heartbeatのサービスを起動する。

pacemaker.sh

#!/bin/sh

### wait for being ready cluster
while [ 1 ]
do
  crm node status > /dev/null 2>&1
  if [ $? -eq 0 ]; then
    break
  fi
  sleep 1
done

sleep 5

### setting option
crm configure property stonith-enabled="false"
crm configure property no-quorum-policy="ignore"

### setting resouce
crm configure primitive vip_httpd ocf:heartbeat:IPaddr2 params \
ip="192.168.1.101" nic="eth1" cidr_netmask="24" op monitor interval="10"

crm configure primitive httpd ocf:heartbeat:apache params \
configfile="/etc/httpd/conf/httpd.conf" statusurl="http://192.168.1.101/" \
op start interval="0" timeout="90" on-fail="restart" \
op monitor interval="10" timeout="60" on-fail="restart" \
op stop interval="0" timeout="300" on-fail="block"

### grouping
crm configure group web vip_httpd httpd
### wait for being ready cluster

クラスタが動作可能になってリソース制御ができるようになるまで待つ。

### setting option

各種オプションを設定する。

### setting resouce

リソースとしてVIP(vip_httpd)とApache(httpd)を定義する。

### grouping

上記で定義したリソースをグルーピングして一つのリソースグループとする。

仮想マシン起動

以上の設定が終わったら仮想マシンを起動する。

% vagrant up
Bringing machine 's1' up with 'virtualbox' provider...
Bringing machine 's2' up with 'virtualbox' provider...
[s1] Importing base box 'centos65_64'...
[s1] Matching MAC address for NAT networking...
[s1] Setting the name of the VM...
[s1] Clearing any previously set forwarded ports...
[s1] Clearing any previously set network interfaces...
[s1] Preparing network interfaces based on configuration...
[s1] Forwarding ports...
[s1] -- 22 => 2122 (adapter 1)
[s1] Booting VM...
[s1] Waiting for machine to boot. This may take a few minutes...
[s1] Machine booted and ready!
[s1] Setting hostname...
[s1] Configuring and enabling network interfaces...
[s1] Mounting shared folders...
[s1] -- /vagrant
[s1] Running provisioner: shell...
[s1] Running: /tmp/vagrant-shell20141007-20822-ag7wdv
Loaded plugins: fastestmirror
:
(ここからs1のプロビジョニング)
:
Starting High-Availability services: Done.

[s2] Importing base box 'centos65_64'...
[s2] Matching MAC address for NAT networking...
[s2] Setting the name of the VM...
[s2] Clearing any previously set forwarded ports...
[s2] Clearing any previously set network interfaces...
[s2] Preparing network interfaces based on configuration...
[s2] Forwarding ports...
[s2] -- 22 => 2222 (adapter 1)
[s2] Booting VM...
[s2] Waiting for machine to boot. This may take a few minutes...
[s2] Machine booted and ready!
[s2] Setting hostname...
[s2] Configuring and enabling network interfaces...
[s2] Mounting shared folders...
[s2] -- /vagrant
[s2] Running provisioner: shell...
[s2] Running: /tmp/vagrant-shell20141007-20822-1pyt36a
Loaded plugins: fastestmirror
:
(ここからs2のプロビジョニング)
:
Starting High-Availability services: Done.

[s2] Running provisioner: shell...
[s2] Running: /tmp/vagrant-shell20141007-20822-12eoo2m

初回起動時のみheartbeat.sh及びpacemaker.shが動き(プロビジョニング)、各種ソフトウェアのインストールと設定が行われる。

試しにs1(サーバ1)に接続してcrm_monを実行すると各ノードとリソースが正常に動作しているのが確認できる。この状態でhttp://192.168.1.101/にアクセスするとApacheのテスト画面が表示される。

% vagrant ssh s1

[vagrant@s1 ~]$ sudo crm_mon -A
============
Last updated: Mon Oct  6 17:36:38 2014
Stack: Heartbeat
Current DC: s1 (0ae70a20-2a06-4775-98d8-4e6cdddfb170) - partition with quorum
Version: 1.0.13-a83fae5
2 Nodes configured, unknown expected votes
1 Resources configured.
============

Online: [ s1 s2 ]

 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):   Started s1
     httpd      (ocf::heartbeat:apache):    Started s1

Node Attributes:
* Node s1:
    + s2-eth2                           : up
* Node s2:
    + s1-eth2                           : up

このプロビジョニングは初回起動時だけ動作すればいいので2回目以降は動かない。

もしスクリプトに変更を加えたい時などは以下のようにいったん仮想マシンを削除して再作成すればよい。

% vagrant destroy
Are you sure you want to destroy the 's2' VM? [y/N] y
[s2] Forcing shutdown of VM...
[s2] Destroying VM and associated drives...
[s2] Running cleanup tasks for 'shell' provisioner...
Are you sure you want to destroy the 's1' VM? [y/N] y
[s1] Forcing shutdown of VM...
[s1] Destroying VM and associated drives...
[s1] Running cleanup tasks for 'shell' provisioner...

% vagrant up
:

2013/12/4更新

対応バージョン: Pacemaker 1.0.13, Heartbeat 3.0.5

Pacemaker + Heartbeatを使って2ノードのクラスタを構築する手順を示す。導入OSはCentOS 6.4。

PacemakerとHeartbeatはそれぞれ以下の役割を担い、協調して動作する。

Pacemaker

仮想IP、ApacheやMySQL等といった各種サービスをリソースとして扱い、起動/停止、動作状態の監視を行う。何らかの異常を検知すると自動的に再起動やフェールオーバなどの制御を行う。

Heartbeat

メンバーノードをクラスタとして動作させる。電源不良やKernel Panicといった低レイヤに関する障害が発生した場合にノードの切り離しや再起動などの制御を行う。

構築環境

ここでは以下の環境を構築する。

提供サービス

Apache
Samba

片系のノードで両サービスを提供し、障害時やメンテナンス時はサービス単位で停止/起動・フェールオーバ可能とする。

ノード

サーバ1: s1 (em1: 192.168.1.121, p4p1: 192.168.9.121)
サーバ2: s2 (em1: 192.168.1.122, p4p1: 192.168.9.122)

・em1: サービス提供用

・p4p1: インターコネクト(ハートビート)用

GWは192.168.1.1とする。

仮想IP

Apache用: 192.168.1.101
Samba用: 192.168.1.100

以降の設定にあたっては、クラスタが構築し終わるまではそれぞれのノードで同じ作業を行う。

インターコネクト用NIC設定

em1用の設定をコピーしてインターコネクト用NICの設定を行う。

# cd /etc/sysconfig/network-scripts
# cp ifcfg-em1 ifcfg-p4p1
# vi ifcfg-p4p1

以下を削除

GATEWAY=192.168.1.1
DNS1=xxx.xxx.xxx.xxx
DEFROUTE=yes

以下を変更

DEVICE=p4p1
HWADDR=xx:xx:xx:xx:xx:xx
IPADDR=192.168.9.xxx

networkサービス再起動

# service network restart

相手のインターコネクト用NICが見えることを確認

# ping 192.168.9.xxx

Pacemaker, Heartbeatインストール

Linux-HA Japanよりパッケージを取得してインストールする。

pacemaker-1.0.13-1.1.el6.x86_64.repo.tar.gz (64bit用)
pacemaker-1.0.13-1.1.el6.i686.repo.tar.gz (32bit用)

ここでは64bit用をインストールする。

# tar xvzf pacemaker-1.0.13-1.1.el6.x86_64.repo.tar.gz
# mv pacemaker-1.0.13-1.1.el6.x86_64.repo /tmp
# cd /tmp/pacemaker-1.0.13-1.1.el6.x86_64.repo
# yum -c pacemaker.repo install pacemaker-1.0.13-1.el6.x86_64 heartbeat-3.0.5-1.1.el6.x86_64 pm_extras-1.3-1.el6.x86_64
# yum list installed | grep pacemaker
cluster-glue.x86_64     1.0.11-1.el6    @pacemaker
                        1.0.11-1.el6    @pacemaker
corosync.x86_64         1.4.5-1.el6     @pacemaker
corosynclib.x86_64      1.4.5-1.el6     @pacemaker
heartbeat.x86_64        3.0.5-1.1.el6   @pacemaker
heartbeat-libs.x86_64   3.0.5-1.1.el6   @pacemaker
libesmtp.x86_64         1.0.4-16.el6    @pacemaker
pacemaker.x86_64        1.0.13-1.el6    @pacemaker
pacemaker-libs.x86_64   1.0.13-1.el6    @pacemaker
pm_extras.x86_64        1.3-1.el6       @pacemaker
resource-agents.x86_64  3.9.5-1.el6     @pacemaker

(*) pm_extrasをインストールするとインターコネクトLAN故障が標準ツールで見えるようになる。

ファイアウォール設定

インターコネクト通信用に694/udpを許可する。

# cd /etc/sysconfig
# vi iptables

以下の行を追加

-A INPUT -p udp -m udp --dport 694 -j ACCEPT

iptables再起動

# service iptables restart

Heartbeat設定

まず低レイヤを制御するHeartbeatを設定する。

# vi /etc/ha.d/ha.cf
pacemaker on

debug 0
udpport 694
keepalive 2
warntime 7
deadtime 10
initdead 10
logfacility local1

ucast p4p1 192.168.9.121
ucast p4p1 192.168.9.122

ping 192.168.1.1

node s1
node s2

watchdog /dev/watchdog
respawn root /usr/lib64/heartbeat/ifcheckd

以下、主なパラメータについて説明する。

pacemaker

リソース制御にPacemakerを使用するかどうか

udpport

インターコネクト通信で使用するudpポート

keepalive

インターコネクト通信の送信間隔(秒)

warntime
deadtime

インターコネクト通信途絶後に警告(warn)あるいは故障(dead)と判断するまでの時間(秒)

initdead

初期起動時に他のサーバの起動を待つ時間(秒)

logfacility

syslogのファシリティ

ucast

インターコネクト通信に使用するデバイスとIPアドレスの指定

ping

ネットワーク監視用に各ノードからpingを送信するIPアドレス。一般的にはルータなどを指定する。

node

クラスタに参加するノード名

watchdog

kernel提供のsoftdogデバイス名

respawn

起動するサブプロセスの指定

(*) respawnで指定しているifcheckdはpm_extrasに含まれるLinux-HA Japanオリジナルパッケージで、これを使うとインターコネクト通信が正常かどうかを見えるようになる。

続いて各ノードがクラスタメンバーとして動作できるように認証ファイルを配置する。

# vi /etc/ha.d/authkeys
auth 1
1 sha1 xxxxxxxx ← 任意のパスフレーズ
# chmod 0600 /etc/ha.d/authkeys

またPacemakerはログ量が多いので、必須ではないが別ファイルに切り出したほうがよい。

# cd /etc
# cp -p rsyslog.conf rsyslog.conf.org
# vi rsyslog.conf
:
local1.*        /var/log/ha-log

(*) syslogのファシリティは前述のha.cfで変更可能

syslog再起動

# service rsyslog restart

Heartbeat起動

これでクラスタ化の準備ができたのでメンバーノード毎にHeartbeatを起動していく。

まずサーバ1(s1)。

# service heartbeat start

# crm_mon -A
============
Last updated: Fri Sep  6 21:26:14 2013
Stack: Heartbeat
Current DC: s1 (655c8a37-f03a-4b07-bece-d32b39adc80a) - partition with quorum
Version: 1.0.13-30bb726
1 Nodes configured, unknown expected votes
0 Resources configured.
============

Online: [ s1 ]


Node Attributes:
* Node s1:

サーバ1がオンラインになるので別ターミナルを開いてサーバ2(s2)でもHeartbeatを起動する。

# service heartbeat start

これによりサーバ1,2ともオンラインになる。

# crm_mon -A
============
Last updated: Tue Sep 10 21:10:29 2013
Stack: Heartbeat
Current DC: s1 (b7cf0247-85a1-472a-8430-fb38728488fd) - partition with quorum
Version: 1.0.13-30bb726
2 Nodes configured, unknown expected votes
0 Resources configured.
============

Online: [ s1 s2 ]


Node Attributes:
* Node s1:
    + s2-p4p1                           : up
* Node s2:
    + s1-p4p1                           : up

STONITH機能無効化

STONITHとは「Shoot The Other Node In The Head」の略で、相手ノードが不安定になった時にそのノードをOSごと再起動/停止する機能である。

この機能はデフォルトで有効になっているが、リソースを何も設定しない現状では以下のエラーになる。

# crm_verify -LV
crm_verify[24723]: 2013/09/09_21:39:21 ERROR: unpack_resources: Resource start-up disabled since no STONITH resources have been defined
crm_verify[24723]: 2013/09/09_21:39:21 ERROR: unpack_resources: Either configure some or disable STONITH with the stonith-enabled option
crm_verify[24723]: 2013/09/09_21:39:21 ERROR: unpack_resources: NOTE: Clusters with shared data need STONITH to ensure data integrity
Errors found during check: config not valid

そこでひとまず無効にしておく。

既にクラスタが構成されているので設定変更は任意のノードで1回だけ実行すれば自動的にメンバーノードに反映される。

# crm configure show
node $id="b7cf0247-85a1-472a-8430-fb38728488fd" s1
node $id="e959ca5c-76e4-43c4-8641-6c6c71ac8fbc" s2
property $id="cib-bootstrap-options" \
        dc-version="1.0.13-30bb726" \
        cluster-infrastructure="Heartbeat"

# crm configure property stonith-enabled="false"

# crm_verify -LV
(エラーなし)

# crm configure show
node $id="b7cf0247-85a1-472a-8430-fb38728488fd" s1
node $id="e959ca5c-76e4-43c4-8641-6c6c71ac8fbc" s2
property $id="cib-bootstrap-options" \
        dc-version="1.0.13-30bb726" \
        cluster-infrastructure="Heartbeat" \
        stonith-enabled="false"

また、メンバーノードが2台の場合はquorumによる管理が不要になるのでcrm_attribute(*)をあわせて無効にしておく。

(*) クラスタに参加するメンバーノードが過半数に満たない場合の動作ポリシー

# crm configure property no-quorum-policy="ignore"

Pacemaker設定

Heartbeatの設定が終わったら次はPacemakerでリソースの定義を行う。

まずサービス提供用の仮想IPを定義する。

RA(Resource Agent)としてIPaddr2を使用し、以下のパラメータを設定する。

ip

仮想IPアドレス

nic

使用NIC

cidr_netmask

ネットマスク

# crm configure primitive vip_httpd ocf:heartbeat:IPaddr2 params \
ip="192.168.1.101" nic="em1" cidr_netmask="24" op monitor interval="10s"

# crm configure primitive vip_samba ocf:heartbeat:IPaddr2 params \
ip="192.168.1.100" nic="em1" cidr_netmask="24" op monitor interval="10s"

リソースを定義するとすぐ有効になる。

# crm configure show vip_httpd
primitive vip_httpd ocf:heartbeat:IPaddr2 \
        params ip="192.168.1.101" nic="em1" cidr_netmask="24" \
        op monitor interval="10s" \
        meta target-role="Started"

# crm configure show vip_samba
primitive vip_samba ocf:heartbeat:IPaddr2 \
        params ip="192.168.1.100" nic="em1" cidr_netmask="24" \
        op monitor interval="10s"

# crm_mon -A
============
Last updated: Tue Sep 10 22:19:44 2013
Stack: Heartbeat
Current DC: s1 (b7cf0247-85a1-472a-8430-fb38728488fd) - partition with quorum
Version: 1.0.13-30bb726
2 Nodes configured, unknown expected votes
1 Resources configured.
============

Online: [ s1 s2 ]

vip_httpd     (ocf::heartbeat:IPaddr2):       Started s1
vip_samba     (ocf::heartbeat:IPaddr2):       Started s1

Node Attributes:
* Node s1:
    + s2-p4p1                           : up
* Node s2:
    + s1-p4p1                           : up

仮想IPが使えるようになっていることをpingで確認する。

# ping 192.168.1.101
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
64 bytes from 192.168.1.101: icmp_seq=1 ttl=64 time=0.404 ms
64 bytes from 192.168.1.101: icmp_seq=2 ttl=64 time=0.403 ms
64 bytes from 192.168.1.101: icmp_seq=3 ttl=64 time=0.401 ms
^C

# ping 192.168.1.100
PING 192.168.1.100 (192.168.1.100) 56(84) bytes of data.
64 bytes from 192.168.1.100: icmp_seq=1 ttl=64 time=0.324 ms
64 bytes from 192.168.1.100: icmp_seq=2 ttl=64 time=0.323 ms
64 bytes from 192.168.1.100: icmp_seq=3 ttl=64 time=0.320 ms
^C

続いて各サービスを定義する。

まずApache。RAはapacheを使用する。

# crm configure primitive httpd ocf:heartbeat:apache params \
configfile="/etc/httpd/conf/httpd.conf" statusurl="http://192.168.1.101:443/" \
op start interval="0" timeout="90" on-fail="restart" \
op monitor interval="10" timeout="60" on-fail="restart" \
op stop interval="0" timeout="300" on-fail="block"

(*) statusurlはRAの仕様上「http://」で始まらなければならない。SSLサイトの場合は「http://xxx.xxx.xxx.xxx:443/」のように指定する。

次にSamba。RAは用意されていないのでSamba付属のinitスクリプト(LSB)を使用する。

# crm configure primitive nmb lsb:nmb \
op start interval="0" timeout="15" on-fail="restart" \
op monitor interval="15" timeout="15" on-fail="restart" \
op stop interval="0" timeout="15" on-fail="block"

# crm configure primitive smb lsb:smb \
op start interval="0" timeout="15" on-fail="restart" \
op monitor interval="15" timeout="15" on-fail="restart" \
op stop interval="0" timeout="15" on-fail="block"

Pacemakerで規定する故障には以下の3種類がある。

start

起動失敗

stop

停止失敗

monitor

監視による検出

故障が発生した際はon-fail="xxx"の設定に応じた動作を行う。指定できる動作には以下のものがある。

block

故障したリソースの管理を停止し待機する。

fence

リソース故障が発生したメンバーノードをSTONITHによって再起動し、フェールオーバする。

ignore

何の処理も行わない。

stop

故障したリソースを停止し、他のメンバーノードへフェールオーバさせない。

restart

故障したリソースを他のメンバーノードへフェールオーバする(デフォルト)。

仮想IPとサービスの定義が終わったのでサービス毎のグルーピングを行う。グループ名には任意の名前が指定できる。

web

Apache用仮想IPとApacheのグルーピング

# crm configure group web vip_httpd httpd
samba

Samba用仮想IPとSambaのグルーピング

# crm configure group samba vip_samba nmb smb

これでグループ単位でフェールオーバやオンライン/オフラインの制御ができるようになる。

# crm_mon -A
============
Last updated: Mon Oct  7 20:46:35 2013
Stack: Heartbeat
Current DC: s1 (b7cf0247-85a1-472a-8430-fb38728488fd) - partition with quorum
Version: 1.0.13-30bb726
2 Nodes configured, unknown expected votes
Online: [ s1 s2 ]

 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):       Started s1
     httpd      (ocf::heartbeat:apache):        Started s1
 Resource Group: samba
     vip_samba  (ocf::heartbeat:IPaddr2):       Started s1
     nmb        (lsb:nmb):      Started s1
     smb        (lsb:smb):      Started s1

Node Attributes:
* Node s1:
    + s2-p4p1                           : up
* Node s2:
    + s1-p4p1                           : up

グループやリソース、ノードの制御はcrmコマンドで行う。以下に一例を示す。

フェールオーバ
# crm resource move web s2 force
グループ停止
# crm resource stop web
リソースに故障履歴(Faied actions)が付いた場合のクリーンアップ
# crm resource cleanup httpd s1
フェールオーバ/フェールバック先に移動禁止フラグが付いた場合のクリア
# crm resource unmove httpd
ノードスタンバイ(当該ノード上でリソースが動作している場合フェールオーバする)
# crm node standby s1

(*)ノードをメンテナンスする場合などに使用

ノードオンライン
# crm node online s1

参考資料

今回のようにシンプルな構成であれば比較的簡単に構築ができるが、複雑な構成の場合は構築や障害発生時などに対応に時間がかかることが考えられるので、以下に参考資料を紹介しておく。

2017/08/26更新

対応バージョン: Vagrant 1.4.3, Pacemaker 1.0.13, Heartbeat 3.0.5, Apache 2.2.15, Tomcat 7.0.56

Pacemakerでリソースグループが自ホストで稼動している時のみ処理を行いたい場合は以下のようなスクリプトを書けばよい。

myhost=`uname -n`

runhost=`crm resource status <リソースグループ名> | awk '{print $NF}'`

if [ ${myhost} = ${runhost} ]; then
  (任意の処理)
fi

例)

2台のクラスタ構成において稼動系のホストでRedmineのリマインドメールを出すスクリプト

# crm_mon -f
:
Online: [ s1 s2 ]
 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):       Started s1
     httpd      (ocf::heartbeat:apache):        Started s1
:

# cat redmine.sh
#!/bin/sh

PATH=${PATH}:/usr/sbin

#
# check user
#
user=`whoami`

if [ ${user} != "root" ]; then
  echo "execute root user"
  exit 100
fi

#
# check host & send mail
#
myhost=`uname -n`

runhost=`crm resource status web | awk '{print $NF}'`

if [ ${myhost} = ${runhost} ]; then
  cd /nas/contents/www/redmine-2.5.2
  PATH=${PATH}:/usr/local/rbenv/shims
  rake redmine:send_reminders days=3 RAILS_ENV=production
fi

2015/10/07更新

対応バージョン: Pacemaker 1.0.13, Heartbeat 3.0.5

以下のようなApacheのリソース定義がある(VIP定義とのグルーピング)。

# crm configure show
:
primitive httpd ocf:heartbeat:apache \
        params configfile="/etc/httpd/conf/httpd.conf" statusurl="http://192.168.1.101/" \
        op start interval="0" timeout="90" on-fail="restart" \
        op monitor interval="10" timeout="60" on-fail="restart" \
        op stop interval="0" timeout="300" on-fail="block"
primitive vip_httpd ocf:heartbeat:IPaddr2 \
        params ip="192.168.1.101" nic="eth1" cidr_netmask="24" \
        op monitor interval="10"
group web vip_httpd httpd \
        meta target-role="Started"
:

Apacheのサービスをserviceコマンドで普通に起動した場合はうまく動作するが、Pacemakerのリソースとして起動した場合は一瞬起動してすぐに停止してしまう。

正常
# service httpd start
Starting httpd: [  OK  ]

# tail /var/log/httpd/error_log
:
[notice] Apache/2.2.15 (Unix) DAV/2 configured -- resuming normal operations
起動後すぐに停止
# crm resource start web

# crm_mon -f
:
Online: [ s1 s2 ]

 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):  Started s1
     httpd      (ocf::heartbeat:apache):   Stopped

Migration summary:
* Node s1: 
   httpd: migration-threshold=1000000 fail-count=1000000
* Node s2: 

Failed actions:
    httpd_start_0 (node=s1, call=281, rc=1, status=complete): unknown error

# tail /var/log/httpd/error_log
:
[notice] Apache/2.2.15 (Unix) DAV/2 configured -- resuming normal operations
[error] [client 127.0.0.1] Directory index forbidden by Options directive: /var/www/html/
[notice] caught SIGWINCH, shutting down gracefully

これはPacemakerのApache Resource Agent(/usr/lib/ocf/resource.d/heartbeat/apache)がApacheの動作をモニタする際に以下の動きをするからである。

1.apache_monitor_basic()においてリソース定義のstatusurl=に指定したURLにアクセスし(358行目)、HTMLファイルが取得できないと次の処理に移り、363行目で呼び出したattempt_index_monitor_request()関数内において$OCF_RESKEY_statusurlに上記URLが設定されている(340行目)ために1を返し、結果的に371行目でエラー($OCF_ERR_GENERIC)を返す。

328 attempt_index_monitor_request() {
:
340     if [ -n "$OCF_RESKEY_statusurl" ]; then
341         return 1;
342     fi
:
357 apache_monitor_basic() {
358     if ${ourhttpclient}_func "$STATUSURL" | grep -Ei "$TESTREGEX" > /dev/null
359     then
360         return $OCF_SUCCESS
361     fi
362 
363     attempt_index_monitor_request
364     if [ $? -eq 0 ]; then
365         return $OCF_SUCCESS
366     fi
367 
368     if ! ocf_is_probe; then
369         ocf_log err "Failed to access httpd status page."
370     fi
371     return $OCF_ERR_GENERIC
372 }

2.エラーを受け取った上位の関数はapache_stop()を呼び出しApacheを停止する。

191     while : # wait until the user set timeout
192     do
193         apache_monitor
194         ec=$?
195         if [ $ec -eq $OCF_NOT_RUNNING ]
196         then
197             tries=`expr $tries + 1`
198             ocf_log info "waiting for apache $CONFIGFILE to come up"
199             sleep 1
200         else
201             break
202         fi
203     done
204 
205     if [ $ec -ne 0 ] && silent_status; then
206         apache_stop
207     fi

これを回避するにはドキュメントルート(httpd.confのDocumentRootで指定したディレクトリ、/var/www/html等)にダミーのindex.htmlを置いておけばよい。

ダミーなので以下のような内容で構わない。

<html>
</html>

この対処を行えばリソースは正常に起動する。

# crm resource cleanup httpd s1

# crm_mon -f
:
Online: [ s1 s2 ]

 Resource Group: web
     vip_httpd  (ocf::heartbeat:IPaddr2):  Started s1
     httpd      (ocf::heartbeat:apache):   Started s1
:

関連資料・記事