高可用性クラスタへの応用

2011年11月2日(水)
那賀 樹一郎(なか きいちろう)

2 台で非同期 HA を構築

まず最初に、2 台のホストを設定し、非同期レプリケーションを用いた構成をしてみましょう。

図2:非同期 2 台の構成例(クリックで拡大)

構成は上記のようになります。今回は説明を分かりやすくするためにホスト名で記述していますが、実環境では、DNS が不調になることも考えられますので、IP アドレスで指定した方が良いでしょう。

まずは動作するところまで一気に構築をしてから、個々の設定については後で説明して行こうと思います。最初に、各ホストで共通の設定を行います。

/etc/keepalived/keepalived.conf を以下のように設定します。

01vrrp_instance pgsql {
02  garp_master_delay 5
03  virtual_router_id 200
04  advert_int 1
05  state BACKUP
06  priority 100
07  nopreempt
08  interface eth0
09  authentication {
10    auth_type PASS
11    auth_pass foobar
12  }
13  virtual_ipaddress {
14    192.168.12.23/24 dev eth0
15  }
16  notify "/var/lib/pgsql/pgdata/notify"
17}

前回までの環境を流用するならば、使用するポートがかぶってしまうので、前回動かしていたインスタンスを止めておきます。

1[root@node1 ~]# su - postgres
2-bash-4.1$ export PATH=/usr/pgsql-9.1/bin/:$PATH
3-bash-4.1$ pg_ctl -D ~/pgdata-prim/ status && pg_ctl -D ~/pgdata-prim/ stop -m fast
4pg_ctl: サーバが動作していません
5-bash-4.1$ pg_ctl -D ~/pgdata-stby/ status && pg_ctl -D ~/pgdata-stby/ stop -m fast
6pg_ctl: サーバが動作していません
7-bash-4.1$

他ホストから RSH でログインするための ~/.rhost と、psql クライアントで VIP に対してデータベース接続をするための ~/.pgpass を設定します。

1-bash-4.1$ echo node1 postgres > ~/.rhosts
2-bash-4.1$ echo node2 postgres >> ~/.rhosts
3-bash-4.1$ echo spare postgres >> ~/.rhosts
4-bash-4.1$ chmod 0600 ~/.rhosts
5-bash-4.1$ echo vip:5432:*:reprole:reppass > ~/.pgpass
6-bash-4.1$ echo vip:5432:postgres:postgres:postgres >> ~/.pgpass
7-bash-4.1$ chmod 0600 ~/.pgpass
8-bash-4.1$

以上が各ホストに共通の設定となり、ここから先がプライマリの設定となります。データディレクトリ下のファイルは pg_basebackup でレプリケーション・ベースをコピーする際に同時にコピーされますので、可能な限りプライマリ上で前もって設定をし、後でそれらをスタンバイ側へコピーするようにします。

プライマリ上にデータ領域を作成して仮起動し、レプリケーション用のユーザを作成します。

01-bash-4.1$ initdb -D ~/pgdata/ --encoding=UTF-8 --no-locale --pwprompt --auth=md5
02(中略)
03新しいスーパーユーザのパスワードを入力してください: postgres
04再入力してください: postgres
05(中略)
06成功しました。以下を使用してデータベースサーバを起動することができます。
07 
08    postmaster -D /var/lib/pgsql/pgdata
09または
10    pg_ctl -D /var/lib/pgsql/pgdata -l logfile start
11-bash-4.1$ pg_ctl -D ~/pgdata/ start
12サーバは起動中です。
13-bash-4.1$ psql -c "CREATE ROLE reprole REPLICATION LOGIN PASSWORD 'reppass'"
14CREATE ROLE
15-bash-4.1$

~postgres/pgdata/postgresql.conf を設定します。内容は前回と同じですので、変更箇所のみ記載します。

01## '*' を指定することで PostgreSQL はアドレス 0.0.0.0 に対して listen を
02## します。すると、後で付与された VIP 上でも listen することができます。
03listen_addresses = '*'
04 
05port = 5432
06wal_level = hot_standby
07max_wal_senders = 10
08wal_keep_segments = 1000
09synchronous_standby_names = ''
10hot_standby = on

~postgres/pgdata/pg_hba.conf に以下を追加します。今回はネットワークごしでのアクセスが必要ですので、ネットワークアドレス (ここでは 192.168.0.0/16) を指定します。

1host  replication     reprole         192.168.0.0/16          md5
2host  postgres        reprole         192.168.0.0/16          md5
3host  postgres        postgres        192.168.0.0/16          md5

スタンバイに降格した際に用いる recovery.conf のひな形として、~postgres/pgdata/recovery.conf.in を作成しておきます。実際には、"application_name" の値をホスト名 ("node1" や "node2" といった名前) に置換して使用されます。

1standby_mode = 'on'
2primary_conninfo = 'host=vip user=reprole password=reppass application_name=@hostname@'
3recovery_target_timeline = 'latest'
4restore_command = 'rcp vip:/var/lib/pgsql/pgdata/pg_xlog/%f "%p" 2> /dev/null'

次に、Keepalived がマスターやバックアップに遷移する際に呼ばれるスクリプトファイルである ~postgres/pgdata/notify を設置します。

01#!/bin/sh
02vhost=vip
03dbuser=postgres
04pgdata=/var/lib/pgsql/pgdata/
05reprole=reprole
06repdb=postgres
07export PATH=$PATH:/usr/pgsql-9.1/bin/
08# --------------------------------------------------------------------
09# root で実行されていたら、DB ユーザに切り替えて再実行します
10if test $UID = 0
11then
12  abspath=$(cd $(dirname $0); pwd)/$(basename $0)
13  exec su - $dbuser $abspath "$@"
14  false
15fi
16type=$1
17name=$2
18stat=$3
19prio=$4
20case $stat in
21  MASTER)
22    # PostgreSQL が起動していなければ起動します
23    pg_ctl -D $pgdata status || pg_ctl -D $pgdata start
24    # スタンバイであれば、プライマリに昇格させます
25    if test -e $pgdata/recovery.conf
26    then
27      pg_ctl -D $pgdata promote
28      while ! psql -h $vhost -U $reprole $repdb -c "SELECT pg_switch_xlog()"
29      do
30        sleep 1
31      done
32      psql -h $vhost -U $reprole $repdb -c "CHECKPOINT"
33    fi
34    ;;
35  BACKUP)
36    # PostgreSQL を停止し、プライマリであればスタンバイに降格させます
37    if ! test -e $pgdata/recovery.conf && \
38     psql -h $vhost -U $reprole $repdb -l
39    then
40      pg_ctl -D $pgdata status && pg_ctl -D $pgdata -m immediate stop
41      sed -e "s/@hostname@/$(hostname)/" < $pgdata/recovery.conf.in \
42       > $pgdata/recovery.conf
43    fi
44    # 起動していなければ、起動します
45    pg_ctl -D $pgdata status || pg_ctl -D $pgdata start
46    ;;
47esac

以上で設定は終わりですので、仮稼働させていた PostgreSQL を停止させます。

1-bash-4.1$ chmod 755 ~/pgdata/notify
2-bash-4.1$ pg_ctl -D ~/pgdata/ stop -m fast

root ユーザへ戻り、Keepalived を起動させます。Keepalived はまずバックアップで起動しますが、まだネットワーク上に自分自身しか Keepalived ホストが存在しないので、すぐにマスターに遷移します。そして上記の "notify" のスクリプトを経由して、PostgreSQL のインスタンスをプライマリとして起動させます。

1-bash-4.1$ exit
2[root@node1 ~]# service keepalived start
3keepalived を起動中:                                       [  OK  ]
4[root@node1 ~]# chkconfig keepalived on

以上が、プライマリの構築手順でした。次に、スタンバイの設定を行います。必要なファイル群はベースのコピーとしてプライマリから取得できるので、こちらの手順は簡単です。なお、フェイルオーバーの際に WAL の取りこぼしが発生して旧プライマリではレプリケーションが継続できなくなった場合には、スタンバイの再構築が必要です。これは、その際の手順でもあります。

1[root@node2 ~]# su - postgres
2-bash-4.1$ export PATH=/usr/pgsql-9.1/bin/:$PATH
3-bash-4.1$ rm -fr ~/pgdata/
4-bash-4.1$ pg_basebackup -x -h vip -U reprole -D ~/pgdata/

root ユーザへ戻り、keepalived を起動させます。設定が正しくなされていれば、"notify" のスクリプトを経由して、PostgreSQL のインスタンスが、こちらはスタンバイとして起動されるはずです。

1-bash-4.1$ exit
2[root@node2 ~]# service keepalived start
3keepalived を起動中:                                       [  OK  ]
4[root@node2 ~]# chkconfig keepalived on

以上です。下記のように、プライマリ上でレプリケーションの状態を確認し、プライマリの現在の WAL 位置とスタンバイの flush_location の位置が一致しているようであれば、試しにプライマリのホストの電源を、終了処理を経ずに落としてみましょう。

01-bash-4.1$ psql -h vip -x -c "SELECT * FROM pg_current_xlog_location()"
02-[ RECORD 1 ]------------+----------
03pg_current_xlog_location | 0/40000B0 ← これと
04 
05-bash-4.1$ psql -h vip -x -c "SELECT * FROM pg_stat_replication"
06-[ RECORD 1 ]----+------------------------------
07procpid          | 8432
08usesysid         | 16384
09usename          | reprole
10application_name | node2
11client_addr      | 192.168.12.25
12client_hostname  |
13client_port      | 33320
14backend_start    | 2011-XX-XX 18:29:08.142613+09
15state            | streaming
16sent_location    | 0/40000B0
17write_location   | 0/40000B0
18flush_location   | 0/40000B0 ← これが一致
19replay_location  | 0/40000B0
20sync_priority    | 0
21sync_state       | async
22 
23-bash-4.1$

その結果、Keepalived によって node2 がマスター (プライマリ) に昇格し、引き続き "vip" に対してデータベースへのアクセスができるはずです。その後 node1 を起動すれば、今度は node1 がバックアップ (スタンバイ) としてレプリケーションを受けることになります。

なお、HA クラスタ全体を停止させる際には、スタンバイ→プライマリの順に停止してください。

著者
那賀 樹一郎(なか きいちろう)

サイオステクノロジー株式会社にて、PostgreSQL/Postgres Plus の製品担当として、サポートサービス、コンサルティング、トレーニング等を行う。Linux ディストリビューションベンダーで培った、OSS プロダクトのソースコードレベルでのソフトウェア解析技術を生かして業務にあたる。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています