Pacemakerを使ってPostgreSQLのアクティブ・スタンバイ(1+1構成)を試してみた

はじめに

STONITH無し、quorum無しのアクティブ・スタンバイ(1+1構成)がとりあえず動くところまでは来たので、一旦メモです。

参考資料

以下の資料と連載記事がわかりやすくて非常に参考になりました。ありがとうございます!

さらに以下の記事と電子書籍も参考にしました。

テスト用のAnsible playbook

https://github.com/hnakamur/postgresql-pacemaker-example-playbook に置きました。

LXD をセットアップ済みの Ubuntu 16.04 上で試しました。

セットアップの事前準備

上記のplaybookを取得します。

git clone https://github.com/hnakamur/postgresql-pacemaker-example-playbook
cd postgresql-pacemaker-example-playbook

Ansibleの lxd_container モジュールを使うので、virtualenvで仮想環境を作ってAnsibleのmaster版をインストールします。

virtualenv venv
source venv/bin/activate
pip install git+https://github.com/ansible/ansible

今回はコンテナのIPアドレスをDHCPではなく静的アドレスを使うようにしてみました。

/etc/default/lxd-bridgeLXD_IPV4_DHCP_RANGE に DHCP のアドレス範囲が設定されているので、ファイルを編集して範囲を狭めます。私の環境では以下のようにしました。

## IPv4 network (e.g. 10.0.8.0/24)
LXD_IPV4_NETWORK="10.155.92.1/24"

## IPv4 DHCP range (e.g. 10.0.8.2,10.0.8.254)
LXD_IPV4_DHCP_RANGE="10.155.92.200,10.155.92.254"

LXDをインストールしたときに LXD_IPV4_NETWORK はランダムなアドレスになるかあるいは自分で指定しますので、それに応じた値に適宜変更してください。

変更したら lxd-bridge を再起動して変更を反映します。

sudo systemctl restart lxd-bridge

group_vars/development/vars.yml ファイル内のIPアドレスも適宜変更します。

また、 group_vars/development/secrets.yml 内にパスワードやsshの鍵ペアなどが含まれています。これを違う値に変更したい場合は以下のようにします。

まず、以下のコマンドを実行して一旦復号化します。

ansible-vault decrypt group_vars/development/secrets.yml

vaultのパスワードを聞かれますので入力します。この例では password としています。これはあくまで例なのでこういう弱いパスワードにしていますが、実際の案件で使うときは、もっと強いパスワードを指定してください。

group_vars/development/secrets.yml 内の変数を適宜変更したら、以下のコマンドを実行して暗号化します。

ansible-vault encrypt group_vars/development/secrets.yml

vaultの新しいパスワードを聞かれますので入力してください。

コンテナの作成

以下のコマンドを実行して node1node2 という2つのコンテナを作成します。

$ ansible-playbook launch_containers.yml -D -v

vaultのパスワードを聞かれますので入力してください。

コンテナ内にPostgreSQLとPacemakerをセットアップ

以下のコマンドを実行して、コンテナ内にPostgreSQLとPacemakerをセットアップします。

$ ansible-playbook setup_containers.yml -D -v

ここでは、セットアップ完了後、アクティブスタンバイ構成が開始するまでの時間を図りたいので、以下のように date -u コマンドも実行するようにします。

$ ansible-playbook setup_containers.yml -D -v; date -u
…(略)…
Sun Aug 21 13:51:21 UTC 2016

以下のコマンドを実行して node2 コンテナに入ります。

$ lxc exec node2 bash

以下のコマンドを実行して、クラスタの状態をモニターします。 node1, node2 が両方 Slaves の状態を経て、 node1 が Master になり master-ip が node1 につくまで待ちます。

Last updated: Sun Aug 21 13:52:07 2016          Last change: Sun Aug 21 13:52:03 2016 by root via crm_attribute on node1
Stack: corosync
Current DC: node1 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node1 node2 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node1 ]
     Slaves: [ node2 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node1

Node Attributes:
* Node node1:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 0000000003000098
    + pgsql-status                      : PRI
    + pgsql-xlog-loc                    : 0000000003000098
* Node node2:
    + master-pgsql                      : -INFINITY
    + pgsql-data-status                 : STREAMING|ASYNC
    + pgsql-status                      : HS:async
    + pgsql-xlog-loc                    : 0000000003000000

Migration Summary:
* Node node2:
* Node node1:

この端末は開いたままにしておきます。

node1 コンテナを強制停止してフェールオーバのテスト

別の端末を開いて以下のコマンドを実行し、 node1 コンテナを強制停止し時刻を記録します。

$ lxc stop -f node1; date -u
Sun Aug 21 13:52:57 UTC 2016

しばらくすると crm_mon -fA の出力が以下のようになります。

Last updated: Sun Aug 21 13:53:11 2016          Last change: Sun Aug 21 13:53:05 2016 by root via crm_attribute on node2
Stack: corosync
Current DC: node2 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node2 ]
OFFLINE: [ node1 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node2 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node2

Node Attributes:
* Node node2:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 00000000030001A8
    + pgsql-status                      : PRI
    + pgsql-xlog-loc                    : 0000000003000000

Migration Summary:
* Node node2:

LXDホストで以下のコマンドを実行して node1 を起動します。

$ lxc start node1; date -u
Sun Aug 21 13:53:58 UTC 2016

起動後しばらくしても node1 はオフラインのままですが、これは意図した挙動です。実際のケースではディスク障害などが起きているかもしれないので、マシンの状況を確認してから手動でクラスタに復帰させることになるためです。

以下のコマンドで node1 コンテナに入ります。

$ lxc exec node1 bash

PacemakerがPostgreSQLのロックファイルを作っているのでそれを削除します。

[root@node1 ~]# ll /var/run/postgresql/
total 4
-rw-r----- 1 root     root      0 Aug 21 13:52 PGSQL.lock
-rw-r----- 1 postgres postgres 36 Aug 21 13:52 rep_mode.conf
[root@node1 ~]# rm /var/run/postgresql/PGSQL.lock
rm: remove regular empty file '/var/run/postgresql/PGSQL.lock'? y

以下のコマンドで node1 をクラスタに復帰させ、時刻を記録します。

[root@node1 ~]# pcs cluster start node1; date -u
node1: Starting Cluster...
Sun Aug 21 13:55:30 UTC 2016

15秒後、 crm_mon -fA の画面で node1 の PostgreSQL が Slaves に追加されました。

Last updated: Sun Aug 21 13:55:45 2016          Last change: Sun Aug 21 13:55:42 2016 by root via crm_attribute on node2
Stack: corosync
Current DC: node2 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node1 node2 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node2 ]
     Slaves: [ node1 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node2

Node Attributes:
* Node node1:
    + master-pgsql                      : 100
    + pgsql-data-status                 : STREAMING|SYNC
    + pgsql-status                      : HS:sync
* Node node2:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 00000000030001A8
    + pgsql-status                      : PRI
    + pgsql-xlog-loc                    : 0000000003000000

Migration Summary:
* Node node2:
* Node node1:

ここで、 node2crm_mon -fA を実行していた端末で Control-C を入力してモニターを終了します。

PostgreSQLのプロセスを強制終了してフェールオーバのテスト

今度は node2 の PostgreSQL のプロセスを強制終了してフェールオーバしてみます。

経過を見るために node1 で以下のコマンドを実行して、その端末を開いたままにしておきます。

[root@node1 ~]# crm_mon -fA

開始時点では以下のような出力になっていました。

Last updated: Sun Aug 21 13:57:17 2016          Last change: Sun Aug 21 13:55:42 2016 by root via crm_attribute on node2
Stack: corosync
Current DC: node2 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node1 node2 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node2 ]
     Slaves: [ node1 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node2

Node Attributes:
* Node node1:
    + master-pgsql                      : 100
    + pgsql-data-status                 : STREAMING|SYNC
    + pgsql-status                      : HS:sync
* Node node2:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 00000000030001A8
    + pgsql-status                      : PRI
    + pgsql-xlog-loc                    : 0000000003000000

Migration Summary:
* Node node2:
* Node node1:

node2 で以下のコマンドを実行して PostgreSQL のプロセスを強制終了し、時刻を記録します。

[root@node2 ~]# kill -KILL `head -1 /var/lib/pgsql/9.5/data/postmaster.pid`; date -u
Sun Aug 21 13:58:20 UTC 2016

11秒後 node1 の PostgreSQL が Masterに昇格されました。

Last updated: Sun Aug 21 13:58:31 2016          Last change: Sun Aug 21 13:58:27 2016 by root via crm_attribute on node1
Stack: corosync
Current DC: node2 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node1 node2 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node1 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node1

Node Attributes:
* Node node1:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 0000000003000398
    + pgsql-status                      : PRI
* Node node2:
    + master-pgsql                      : -INFINITY
    + pgsql-data-status                 : DISCONNECT
    + pgsql-status                      : STOP

Migration Summary:
* Node node2:
   pgsql: migration-threshold=2 fail-count=1000000 last-failure='Sun Aug 21 13:58:23 2016'
* Node node1:

Failed Actions:
* pgsql_start_0 on node2 'unknown error' (1): call=23, status=complete, exitreason='My data may be inconsistent. You have to remove /va
r/run/postgresql/PGSQL.lock file to force start.',
    last-rc-change='Sun Aug 21 13:58:23 2016', queued=0ms, exec=383ms

次に、 node2 の PostgreSQL を再び稼働してスタンバイにさせてみます。

まず Pacemaker が作成した PostgreSQL のロックファイル /var/run/postgresql/PGSQL.lock を削除します。

[root@node2 ~]# ll /var/run/postgresql/
total 4
-rw-r----- 1 root     root      0 Aug 21 13:53 PGSQL.lock
-rw-r----- 1 postgres postgres 31 Aug 21 13:58 rep_mode.conf
[root@node2 ~]# \rm /var/run/postgresql/PGSQL.lock

次に以下のコマンドを実行して node2 のPostgreSQL の failcount をリセットし、時刻を記録します。

[root@node2 ~]# pcs resource failcount reset pgsql node2; date -u
Sun Aug 21 14:00:04 UTC 2016

9秒後、 node1 での crm_mon -fA の出力を見ると node2 がスタンバイになりました。

Last updated: Sun Aug 21 14:00:13 2016          Last change: Sun Aug 21 14:00:10 2016 by root via crm_attribute on node1
Stack: corosync
Current DC: node2 (version 1.1.13-10.el7_2.4-44eb2dd) - partition with quorum
2 nodes and 3 resources configured

Online: [ node1 node2 ]

 Master/Slave Set: pgsql-master [pgsql]
     Masters: [ node1 ]
     Slaves: [ node2 ]
master-ip       (ocf::heartbeat:IPaddr2):       Started node1

Node Attributes:
* Node node1:
    + master-pgsql                      : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-master-baseline             : 0000000003000398
    + pgsql-status                      : PRI
* Node node2:
    + master-pgsql                      : 100
    + pgsql-data-status                 : STREAMING|SYNC
    + pgsql-status                      : HS:sync

Migration Summary:
* Node node2:
* Node node1:

Failed Actions:
* pgsql_start_0 on node2 'unknown error' (1): call=23, status=complete, exitreason='My data may be inconsistent. You have to remove /va
r/run/postgresql/PGSQL.lock file to force start.',
    last-rc-change='Sun Aug 21 13:58:23 2016', queued=0ms, exec=383ms

おわりに

STONITH無し、quorum無しという簡易構成ですが、アクティブ・スタンバイ(1+1構成)でフフェールオーバする検証ができました。本番運用するにはSTONITHやquorumも重要そうなので、そちらも調べて行きたいです。