PostgreSQL + Slony-I: 3 台構成
PostgreSQL 用の、トリガベースのレプリケーションシステムである Slony-I は、非同期でありながらクラスタ全体がインテリジェントに連動して動く素晴らしいシステムなのですが、いかんせん敷居が高いです。構築の定形パターンを作っておこうと思ったので、メモに残します。
PostgreSQL のインストール、設定、起動
PostgreSQL のインストールをします。
[root@node1 ~]# yum install -y postgresql84 postgresql84-server (中略) Installed: postgresql84.x86_64 0:8.4.4-1.el5_5.1 postgresql84-server.x86_64 0:8.4.4-1.el5_5.1 Dependency Installed: postgresql84-libs.x86_64 0:8.4.4-1.el5_5.1 Complete! [root@node1 ~]#
データベースを初期化、起動し、システム起動時に起動するようにします。
[root@node1 ~]# service postgresql initdb データベースを初期化中: [ OK ] [root@node1 ~]# service postgresql start postgresql サービスを開始中: [ OK ] [root@node1 ~]# chkconfig postgresql on [root@node1 ~]#
CentOS のデフォルトだと、PostgreSQL は ident 認証になっていますので、”postgres” ユーザに su します。とりあえずロケールと文字エンコーディングを確認し、リモートからアクセスできるようにしておきます。pg_hba.conf のアクセス制御のネットワークの指定については、環境ごとに適宜読み替えてください。
[root@node1 ~]# su - postgres
-bash-3.2$ psql template1 -c "select datname, datcollate,
datctype, pg_encoding_to_char(encoding) from pg_database"
datname | datcollate | datctype | pg_encoding_to_char
-----------+-------------+-------------+---------------------
template1 | ja_JP.UTF-8 | ja_JP.UTF-8 | UTF8
template0 | ja_JP.UTF-8 | ja_JP.UTF-8 | UTF8
postgres | ja_JP.UTF-8 | ja_JP.UTF-8 | UTF8
(3 行)
-bash-3.2$ cp /var/lib/pgsql/data/postgresql.conf \
/var/lib/pgsql/data/postgresql.conf.orig
-bash-3.2$ vi /var/lib/pgsql/data/postgresql.conf
-bash-3.2$ diff -uNr /var/lib/pgsql/data/postgresql.conf.orig \
/var/lib/pgsql/data/postgresql.conf
--- /var/lib/pgsql/data/postgresql.conf.orig 2010-09-28 16:58:35.000000000 +0900
+++ /var/lib/pgsql/data/postgresql.conf 2010-09-28 17:01:38.000000000 +0900
@@ -56,7 +56,7 @@
# - Connection Settings -
-#listen_addresses = 'localhost' # what IP address(es) to listen on;
+listen_addresses = '*' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
# (change requires restart)
-bash-3.2$ cp /var/lib/pgsql/data/pg_hba.conf \
/var/lib/pgsql/data/pg_hba.conf.orig
-bash-3.2$ vi /var/lib/pgsql/data/pg_hba.conf
-bash-3.2$ diff -uNr /var/lib/pgsql/data/pg_hba.conf.orig \
/var/lib/pgsql/data/pg_hba.conf
--- /var/lib/pgsql/data/pg_hba.conf.orig 2010-09-28 17:09:08.000000000 +0900
+++ /var/lib/pgsql/data/pg_hba.conf 2010-09-28 17:09:51.000000000 +0900
@@ -72,3 +72,5 @@
host all all 127.0.0.1/32 ident
# IPv6 local connections:
host all all ::1/128 ident
+# IPv4 local network connections:
+host postgres postgres 192.168.1.0/24 md5
-bash-3.2$ exit
logout
[root@node1 ~]# service postgresql restart
postgresql サービスを停止中: [ OK ]
postgresql サービスを開始中: [ OK ]
[root@node1 ~]# iptables -I INPUT -p tcp --dport 5432 -j ACCEPT
[root@node1 ~]# service iptables save
ファイアウォールのルールを /etc/sysconfig/iptables に保存中[ OK ]
[root@node1 ~]#
サンプルデータの作成
ある程度 1 台で運用していたと見立てて、テスト用のテーブルとデータを作成します。
[root@node1 ~]# su - postgres
-bash-3.2$ psql -c "
create table foo (k integer primary key, v integer);
insert into foo (
select 1, (random() * 100)::integer
union all select 2, (random() * 100)::integer
union all select 3, (random() * 100)::integer )"
NOTICE: CREATE TABLE / PRIMARY KEYはテーブル"foo"に暗黙的なインデックス"foo_pkey"を作成します
INSERT 0 3
-bash-3.2$ psql -c "select * from foo;"
k | v
---+----
1 | 26
2 | 34
3 | 53
(3 行)
-bash-3.2$
Slony-I のインストール、設定、起動
CentOS 用の Slony-I パッケージを、yum でインストールします。PostgreSQL 8.4 用の最新の、yum 設定ファイルの RPM をインストールしてください。CentOS-5.5 で提供される postgres84-* とコンフリクトするファイルが多いので、Slony-I のインストールを終えたら、yum 設定ファイルはアンインストールしておきます。
[root@node1 ~]# rpm \ -Uvh http://www.pgrpms.org/reporpms/8.4/pgdg-centos-8.4-2.noarch.rpm http://www.pgrpms.org/reporpms/8.4/pgdg-centos-8.4-2.noarch.rpm を取得中 準備中... ########################################### [100%] 1:pgdg-centos ########################################### [100%] [root@node1 ~]# yum install -y slony1-II (中略) Installed: slony1-II.x86_64 0:2.0.3-2.rhel5 Dependency Installed: perl-DBD-Pg.x86_64 0:1.49-2.el5_3.1 perl-DBI.x86_64 0:1.52-2.el5 Complete! [root@node1 ~]# rpm -e pgdg-centos [root@node1 ~]#
PL/pgSQL 言語サポートを導入し、Slony-I 用の DB ロールを作成します。
[root@node1 ~]# su - postgres -bash-3.2$ createlang plpgsql -bash-3.2$ createuser --encrypted --pwprompt --superuser slony 新しいロールのパスワード:<パスワード> もう一度入力してください:<パスワード> -bash-3.2$ psql -c \ "update pg_authid set rolcatupdate = 't' where rolname = 'slony'" UPDATE 1 -bash-3.2$
slon デーモンがデータベースにアクセスできるようにします。
[root@node1 ~]# su - postgres -bash-3.2$ cp /var/lib/pgsql/data/pg_hba.conf \ /var/lib/pgsql/data/pg_hba.conf.orig2 -bash-3.2$ vi /var/lib/pgsql/data/pg_hba.conf -bash-3.2$ diff -uNr /var/lib/pgsql/data/pg_hba.conf.orig2 \ /var/lib/pgsql/data/pg_hba.conf --- /var/lib/pgsql/data/pg_hba.conf.orig2 2010-09-29 10:43:56.000000000 +0900 +++ /var/lib/pgsql/data/pg_hba.conf 2010-09-29 10:45:38.000000000 +0900 @@ -67,10 +67,13 @@ # TYPE DATABASE USER CIDR-ADDRESS METHOD # "local" is for Unix domain socket connections only +local postgres slony md5 local all all ident # IPv4 local connections: +host postgres slony 127.0.0.1/32 md5 host all all 127.0.0.1/32 ident # IPv6 local connections: host all all ::1/128 ident # IPv4 local network connections: +host postgres slony 192.168.1.0/24 md5 host postgres postgres 192.168.1.0/24 md5 -bash-3.2$ exit [root@node1 ~]# service postgresql restart postgresql サービスを停止中: [ OK ] postgresql サービスを開始中: [ OK ] [root@node1 ~]# cp /etc/slon.conf /etc/slon.conf.orig [root@node1 ~]# vi /etc/slon.conf [root@node1 ~]# diff -uNr /etc/slon.conf.orig /etc/slon.conf --- /etc/slon.conf.orig 2010-09-28 15:12:00.000000000 +0900 +++ /etc/slon.conf 2010-09-28 15:15:04.000000000 +0900 @@ -86,10 +86,10 @@ # Set the cluster name that this instance of slon is running against # default is to read it off the command line -#cluster_name='sloncluster' +cluster_name='testcluster' # Set slon's connection info, default is to read it off the command line -#conn_info='host=/tmp port=5432 user=slony' +conn_info='host=localhost port=5432 dbname=postgres user=slony password=slony' # maximum time planned for grouped SYNCs # If replication is behind, slon will try to increase numbers of [root@node1 ~]# service slony1-II start slony1-II サービスを開始中: [ OK ] [root@node2 ~]# chkconfig slony1-II on [root@node1 ~]#
slon デーモンのログで、まずは slony ロールでの接続ができていることを確認します。まだクラスタの初期化をしていないので Slony-I としての動作ではエラーが出ていますが、問題ありません。
[root@node1 ~]# tail /var/log/slony
2010-09-29 10:49:18 JSTCONFIG main: String option lag_interval = [NULL]
2010-09-29 10:49:18 JSTCONFIG main: String option command_on_logarchive = [NULL]
2010-09-29 10:49:18 JSTCONFIG main: String option syslog_facility = LOCAL0
2010-09-29 10:49:18 JSTCONFIG main: String option syslog_ident = slon
2010-09-29 10:49:18 JSTCONFIG main: String option cleanup_interval = 10 minutes
2010-09-29 10:49:18 JSTCONFIG slon: worker process created - pid = 4728
2010-09-29 10:49:18 JSTERROR cannot get sl_local_node_id -
ERROR: スキーマ"_testcluster"は存在しません
LINE 1: select last_value::int4 from "_testcluster".sl_local_node_id
^
2010-09-29 10:49:18 JSTFATAL main: Node is not initialized properly - sleep 10s
[root@node1 ~]#
Slony-I クラスタの 1st ノードをセットアップ
[root@node1 ~]# mv /etc/slon_tools.conf /etc/slon_tools.conf.orig
[root@node1 ~]# vi /etc/slon_tools.conf
[root@node1 ~]# cat /etc/slon_tools.conf
$CLUSTER_NAME = 'testcluster';
$LOGDIR = '/var/log/slony1';
$DEBUGLEVEL = 2;
add_node(
node => 1,
host => 'node1.priv',
port => 5432,
dbname => 'postgres',
user => 'slony',
password => 'slony' );
$MASTERNODE = 1;
$SLONY_SETS = {
"set1" => {
"set_id" => 1,
"table_id" => 1,
"sequence_id" => 1,
"pkeyedtables" => [
'foo',
],
},
};
1;
[root@node1 ~]# slonik_init_cluster | slonik
<stdin>:8: Set up replication nodes
<stdin>:11: Next: configure paths for each node/origin
<stdin>:12: Replication nodes prepared
<stdin>:13: Please start a slon replication daemon for each node
[root@node1 ~]# slonik_create_set set1 | slonik
<stdin>:15: Subscription set 1 created
<stdin>:16: Adding tables to the subscription set
<stdin>:20: Add primary keyed table public.foo
<stdin>:23: Adding sequences to the subscription set
<stdin>:24: All tables added
[root@node1 ~]#
何か不具合が起きたら、”DROP SCHEMA _<クラスタ名> CASCADE” で、スキーマごとドロップしてからやり直してください。
ログに、”configuration complete” が出ていれば、slon デーモンが正常に Slony-I の管理データにアクセスできています。
[root@node1 ~]# less /var/log/slony ... 2010-09-29 10:54:48 JSTCONFIG main: loading current cluster configuration 2010-09-29 10:54:48 JSTCONFIG main: last local event sequence = 5000000001 2010-09-29 10:54:48 JSTCONFIG main: configuration complete - starting threads 2010-09-29 10:54:48 JSTINFO localListenThread: thread starts 2010-09-29 10:54:48 JSTCONFIG version for "host=localhost port=5432 dbname=postgres user=slony password=slony" is 80404 ...
Slony-I スレーブノードの追加
2 台目のサーバ上で、「PostgreSQL のインストール、設定、起動」と「Slony-I のインストール、設定、起動」を実行します。
slon tools の設定ファイルに、2 つ目のノードを追加します。
[root@node1 ~]# cp /etc/slon_tools.conf /etc/slon_tools.conf.orig2
[root@node1 ~]# vi /etc/slon_tools.conf
[root@node1 ~]# diff -uNr /etc/slon_tools.conf.orig2 /etc/slon_tools.conf
--- /etc/slon_tools.conf.orig2 2010-09-28 17:24:20.000000000 +0900
+++ /etc/slon_tools.conf 2010-09-28 17:23:36.000000000 +0900
@@ -10,6 +10,14 @@
user => 'slony',
password => 'slony' );
+add_node(
+ node => 2,
+ host => 'node2.priv',
+ port => 5432,
+ dbname => 'postgres',
+ user => 'slony',
+ password => 'slony' );
+
$MASTERNODE = 1;
$SLONY_SETS = {
[root@node1 ~]#
対象スキーマをコピーしてからノードを Slony-I クラスタへ store し、購読設定をします。ここで注意としては、スキーマをコピーした後で、ノードを store することです。コピーの際に、node2 上にはまだ Slony-I 管理情報の名前空間が無いので、ここで node1 のテーブルに仕掛けてある Slony-I のトリガが落ちます (下記の通り、エラーになります)。その後の "store node" で名前空間が、"subscribe set" でトリガが追加されます。
[root@node1 ~]# pg_dump -s -c "dbname=postgres user=slony password=slony" \ -n public | psql "host=node2.priv dbname=postgres user=slony password=slony" SET SET SET SET SET SET SET ERROR: リレーション"public.foo"は存在しません ERROR: リレーション"public.foo"は存在しません ERROR: リレーション"public.foo"は存在しません ERROR: テーブル"foo"は存在しません DROP SCHEMA CREATE SCHEMA ALTER SCHEMA COMMENT SET SET SET CREATE TABLE ALTER TABLE ALTER TABLE ERROR: スキーマ"_testcluster"は存在しません ERROR: テーブル"foo"のトリガ"_testcluster_denyaccess"は存在しません ERROR: スキーマ"_testcluster"は存在しません REVOKE REVOKE GRANT GRANT [root@node1 ~]# slonik_store_node 2 | slonik <stdin>:7: Set up replication nodes <stdin>:10: Next: configure paths for each node/origin <stdin>:13: Replication nodes prepared <stdin>:14: Please start a slon replication daemon for each node [root@node1 ~]# slonik_subscribe_set set1 2 | slonik <stdin>:4: NOTICE: subscribe set: omit_copy=f <stdin>:4: NOTICE: subscribe set: omit_copy=f CONTEXT: SQL statement "SELECT "_testcluster".subscribeSet_int( $1 , $2 , $3 , $4 , $5 )" PL/pgSQL 関数 "subscribeset" の 68 行目の型 PERFORM <stdin>:10: Subscribed nodes to set 1 [root@node1 ~]#
この時点で、node2 側の slon ログに "configuration complete" が出ていれば、追加ノードの slon は、正しく Slony-I データにアクセスできています。
スイッチオーバー
購読セットのマスターを、他のノードに移します。この際、旧マスターと新マスターの役割は入れ替わりますが、旧マスターとそれ以外のスレーブとの間のプロバイダ - レシーバの関係は変わりません。
[root@node1 ~]# psql -U slony postgres -c "select * from _testcluster.sl_subscribe"
ユーザ slony のパスワード:
sub_set | sub_provider | sub_receiver | sub_forward | sub_active
---------+--------------+--------------+-------------+------------
1 | 1 | 3 | t | t
1 | 1 | 2 | t | t
(2 行)
[root@node1 ~]# slonik_move_set set1 1 2 | slonik
<stdin>:5: Locking down set 1 on node 1
<stdin>:9: Locked down - moving it
<stdin>:11: Replication set 1 moved from node 1 to 2. Remember to
<stdin>:12: update your configuration file, if necessary, to note the new location
<stdin>:13: for the set.
[root@node1 ~]# psql -U slony postgres -c "select * from _testcluster.sl_subscribe"
ユーザ slony のパスワード:
sub_set | sub_provider | sub_receiver | sub_forward | sub_active
---------+--------------+--------------+-------------+------------
1 | 1 | 3 | t | t
1 | 2 | 1 | t | t
(2 行)
[root@node1 ~]#
ですので、例えば { node1 → node2, node1 → node3, node1 → node4 } という構成でマスターを node1 から node2 に移したら、{ node2 → node1, node1 → node3, node1 → node4 } になります。
スイッチバックすれば、元に戻ります。
[root@node1 ~]# slonik_move_set set1 2 1 | slonik
<stdin>:5: Locking down set 1 on node 2
<stdin>:9: Locked down - moving it
<stdin>:11: Replication set 1 moved from node 2 to 1. Remember to
<stdin>:12: update your configuration file, if necessary, to note the new location
<stdin>:13: for the set.
[root@node1 ~]# psql -U slony postgres -c "select * from _testcluster.sl_subscribe"
ユーザ slony のパスワード:
sub_set | sub_provider | sub_receiver | sub_forward | sub_active
---------+--------------+--------------+-------------+------------
1 | 1 | 3 | t | t
1 | 1 | 2 | t | t
(2 行)
[root@node1 ~]#
node1 を、メンテナンスのために、Slony-I クラスタから取り除きたいと思います。では、node2 → node1 → node3 の時、node1 の unsubscribe をしたらどうなるのでしょうか?
[root@node2 ~]# psql -U slony postgres -c "select * from _testcluster.sl_subscribe" ユーザ slony のパスワード: sub_set | sub_provider | sub_receiver | sub_forward | sub_active ---------+--------------+--------------+-------------+------------ 1 | 1 | 3 | t | t 1 | 2 | 1 | t | t (2 行) [root@node2 ~]# slonik_unsubscribe_set set1 1 | slonik <stdin>:5: PGRES_FATAL_ERROR select "_testcluster".unsubscribeSet(1, 1); - ERROR: Slony-I: Cannot unsubscribe set 1 while being provider <stdin>:8: Failed to unsubscribe node 1 from set 1 [root@node2 ~]#はい、できません。事前にプロバイダを変更し、node1 を、クラスタのリーフにしておく必要があります。再度、unsubscribe をせずに、プロバイダを変更して subscribe します。下記の作業は、新マスターである node2 で行なっています。
[root@node2 ~]# cp /etc/slon_tools.conf /etc/slon_tools.conf.orig3 [root@node2 ~]# vi /etc/slon_tools.conf [root@node2 ~]# diff -uNr /etc/slon_tools.conf.orig3 /etc/slon_tools.conf --- /etc/slon_tools.conf.orig3 2010-09-30 12:33:15.000000000 +0900 +++ /etc/slon_tools.conf 2010-09-30 12:33:40.000000000 +0900 @@ -26,7 +26,7 @@ user => 'slony', password => 'slony' ); -$MASTERNODE = 1; +$MASTERNODE = 2; $SLONY_SETS = { "set1" => { [root@node2 ~]# slonik_subscribe_set set1 3 | slonik <stdin>:5: NOTICE: subscribe set: omit_copy=f <stdin>:5: NOTICE: subscribe set: omit_copy=f CONTEXT: SQL statement "SELECT "_testcluster".subscribeSet_int( $1 , $2 , $3 , $4 , $5 )" PL/pgSQL 関数 "subscribeset" の 68 行目の型 PERFORM <stdin>:11: Subscribed nodes to set 1 [root@node2 ~]# psql -U slony postgres \ -c "select * from _testcluster.sl_subscribe" ユーザ slony のパスワード: sub_set | sub_provider | sub_receiver | sub_forward | sub_active ---------+--------------+--------------+-------------+------------ 1 | 2 | 1 | t | t 1 | 2 | 3 | t | t (2 行) [root@node2 bin]# slonik_unsubscribe_set set1 1 | slonik <stdin>:12: unsubscribed node 1 from set 1 [root@node2 bin]# psql -U slony postgres -c \ "select * from _testcluster.sl_subscribe" ユーザ slony のパスワード: sub_set | sub_provider | sub_receiver | sub_forward | sub_active ---------+--------------+--------------+-------------+------------ 1 | 2 | 3 | t | t (1 行) [root@node2 bin]#slon_tools.conf を各ノードに配置し、それぞれの $MASTERNODE には、それぞれのノード名を指定しておいて、管理作業は現マスター上で行なう、とするのが良いかも知れません。
参考:
障害を起こしたスレーブのドロップ
障害に見たてて、OS の終了処理をせずに、node3 の電源を切ります。手順的には、通常のドロップと同じです。
[root@node1 ~]# slonik_drop_node 3 | slonik <stdin>:11: dropped node 3 cluster [root@node1 ~]#
/etc/slon_tools.conf の設定も消しておきましょう。
マスター障害に伴うフェイルオーバ
障害に見たてて、OS の終了処理をせずに、node1 の電源を切ります。マスターを node2 に移すべく、node2 で作業をします。先に書いた通り、/etc/slon_tools.conf の $MASTERNODE は 2 です。
[root@node2 ~]# slonik_failover 1 2 | slonik
<stdin>:5: NOTICE: failedNode: set 1 has other direct receivers - change providers only
<stdin>:5: NOTICE: failedNode: set 1 has other direct receivers - change providers only
IMPORTANT: Last known SYNC for set 1 = 5000004784
<stdin>:11: Replication sets originating on 1 failed over to 2
[root@node2 ~]# psql -U slony postgres -c "select * from _testcluster.sl_node"
ユーザ slony のパスワード:
no_id | no_active | no_comment
-------+-----------+------------------------------
1 | t | Node 1 - postgres@node1.priv
2 | t | Node 2 - postgres@node2.priv
3 | t | Node 3 - postgres@node3.priv
(3 行)
[root@node2 ~]# psql -U slony postgres -c "select * from _testcluster.sl_subscribe"
ユーザ slony のパスワード:
sub_set | sub_provider | sub_receiver | sub_forward | sub_active
---------+--------------+--------------+-------------+------------
1 | 2 | 3 | t | t
(1 行)
[root@node2 ~]# slonik_drop_node 1 | slonik
<stdin>:11: dropped node 1 cluster
[root@node2 ~]# psql -U slony postgres -c "select * from _testcluster.sl_node"
ユーザ slony のパスワード:
no_id | no_active | no_comment
-------+-----------+------------------------------
2 | t | Node 2 - postgres@node2.priv
3 | t | Node 3 - postgres@node3.priv
(2 行)
[root@node2 ~]#
slon_tools.conf の node1 の設定も消しておきましょう。
後は、復旧→スレーブ追加を行ない、どうしても元に戻したければ、さらにスイッチオーバ→subscribe し直し、です。




