The Sixwish project

Linux Notes / Slony-I

PostgreSQL + Slony-I 実験 1 通常のレプリケーションの場合

PostgreSQL 7.4.x系と、レプリケーションを行うためのSlony-Iを使って、レプリケーションバックアップを行うと言う企画ネタ。
第二回である今回は、実験編の一回目。

ちなみにOSはWhiteBox Linux 3.x(RHEL3.x Clone)を使用しています。
なお、この文書にしたがって生じた損害などについては一切の責任を負いかねますのでご了承ください。

さて、今回の実験対象となるテーブルは、『普通のプライマリキーが一個のテーブル』となります。
また、実験環境その他については、以下にまとめたのでそれを参照のこと。

OS
WhiteBox Enterprise Linux 3(WBEL3)
PostgreSQL
version 7.4.6
./configure --prefix=/usr/local/pgsql CFLAGS='-O2'
initdb --pgdata=/opt/pgsql/data --encoding=EUC_JP --no-locale
Slony-I
version 1.0.5
./configure -with-pgsourcetree=/local/src/postgresql-7.4.6

それから、サーバーの設定関連は以下の通り。

設定 MASTER SLAVE
hostname labgk sixwish
ip address 172.16.1.64 172.16.0.53
database whitecat blackcat

結構忘れがちなのですが、pg_hba.confやPostgreSQLの起動スクリプトを書き換えて、ほかのホストからアクセスできるように設定します。この設定を行わないとSlony-Iが動作しません。
今回は、認証とかをかけると面倒なので、そのあたりを省いた設定でやります。

とりあえず、diffだけ

*** /usr/local/pgsql/share/pg_hba.conf.sample   2004-12-22 11:43:14.000000000 +0900
--- /opt/pgsql/data/pg_hba.conf 2005-01-11 11:56:00.000000000 +0900
***************
*** 58,62 ****
--- 58,63 ----
        local   all         all                                             trust
        # IPv4-style local connections:
        host    all         all         127.0.0.1         255.255.255.255   trust
+       host    all         all         172.16.0.0        255.255.0.0       trust
        # IPv6-style local connections:
        host    all         all         ::1               ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff        trust

関係するサーバーのpg_hba.confを編集後、PostgreSQLのサービスを再起動してください。

普通のプライマリキーが一個のテーブル

Slony-Iで推奨されていると言うか、レプリケーションシステムを使う場合はこの形式がもっとも一般的というテーブル構成っぽいです。何気に面倒なんですが、簡単なので一気に片付けてしまいましょう。

テーブル作成

まずは、対象となるデータベースとテーブルを作成します。

[postgres@labgk postgres]$ createdb whitecat
CREATE DATABASE
[postgres@labgk postgres]$ mkdir sql
[postgres@labgk postgres]$ cd sql
[postgres@labgk sql]$ vi infomation.sql
DROP table infomation;

CREATE TABLE infomation (
   SID     text       not null -- SID
  ,Title   text       not null -- Title
  ,Text    text       not null -- Contents
  ,Update  timestamp  not null -- regist date/time
  ,primary key(SID)
);
[postgres@labgk sql]$ psql -f infomation.sql whitecat
psql:infomation.sql:1: ERROR:  table "infomation" does not exist
psql:infomation.sql:9: NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "infomation_pkey" for table "infomation"
CREATE TABLE
[postgres@labgk sql]$

これでDBとTableの作成が完了しましたので、そのままSLAVEサーバーにDBとTABLEを作成しましょうか。

[postgres@labgk sql]$ createdb -h 172.16.0.53 blackcat
CREATE DATABASE
[postgres@labgk sql]$ psql -h 172.16.0.53 -f infomation.sql blackcat
psql:infomation.sql:1: ERROR:  table "infomation" does not exist
psql:infomation.sql:9: NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "infomation_pkey" for table "infomation"
CREATE TABLE
[postgres@labgk sql]$

Slony-I Slonik Script(MASTERサーバー)

さて、MASTERサーバーに帰ってきまして……ここから本命であるレプリケーション(Slony-I)の設定を行っていくわけです。
この設定ですが、slonikという設定専用のコマンドにスクリプトを流し込んで行います。なので、ここからはそのスクリプト書きになります。

まずは、設定ファイルを詰め込んでおくディレクトリ作成と、基本設定用のファイルを作成します。

[postgres@labgk sql]$ cd ../
[postgres@labgk postgres]$ mkdir slony-master
[postgres@labgk postgres]$ cd slony-master/
[postgres@labgk slony-master]$ vi slony-setup.sh
#!/bin/bash

# define the namespace the replication system
# (レプリケーション マスター・スレーブの関係をまとめるための名前)
CLUSTERNAME=graycat

# define the master database
# (MASTERデータベース名)
MASTERDBNAME=whitecat

# define the slave database
# (SLAVEデータベース名)
SLAVEDBNAME=blackcat

# define master database server address
# (MASTERサーバーのIPアドレス)
# (実行するのがlocalhostだとしても、かならずIPアドレスを打ち込む事。localhost同士の場合は話は別)
MASTERHOST=172.16.1.64

# define slave database server address
# (SLAVEサーバーのIPアドレス)
SLAVEHOST=172.16.0.53

# define replication user(database admin user name)
# (データベースのオーナーユーザー名)
REPLICATIONUSER=postgres

[postgres@labgk slony-master]$ vi slony-master-setup.sh
#!/bin/bash

if [ ! -f ./slony-setup.sh ]; then
    echo 'slony-setup.sh file not found.\n'
    exit -1
fi
. ./slony-setup.sh

slonik <<_EOF_
  cluster name = $CLUSTERNAME;
    
# 対象となるDB情報を * node * と呼ぶ。
# ここでは、それを定義している。
  node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
  node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';

# Initialize the cluster and create the second node
# クラスタの初期化と、スレーブになるDBを設定
  echo 'Initializing $MASTERDBNAME cluster';
  init cluster ( id=1, comment = 'Master node');
  echo 'Node 1 on $MASTERHOST defined';
  
  store node (id=2, comment = 'Slave node');
  echo 'Node 2 on $SLAVEHOST defined';

#
# create paths in both directions
#
  echo 'path from server node 1 to client node 2 created.';
  store path (server = 1, client = 2, conninfo='dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER');
  
  echo 'path from server node 2 to client node 1 created.';
  store path (server = 2, client = 1, conninfo='dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER');
  
#
# make the nodes listen on the paths in both directions
# 管理情報やデータ情報を、どうやって聞けばよいのかを設定する項目
# origin/provider node(情報供給者) --- origin node のデータ・管理情報 ---> receiver(情報取得者)
#
  store listen (origin=1, provider = 1, receiver =2);
  store listen (origin=2, provider = 2, receiver =1);
_EOF_

ここまでが、レプリケーションサーバーについての設定。
Slony-Iは指定したDB丸ごとレプリケーションしてくれるわけではないので、ここからは、対象となるテーブルについての情報を設定します。
なんでいちいちスクリプトファイルを分けているのかと言うと、使いまわしが効くからです。

[postgres@labgk slony-master]$ vi slony-master-add-tbl.sh
#!/bin/bash

if [ ! -f ./slony-setup.sh ]; then
    echo 'slony-setup.sh file not found.\n'

    exit -1
fi
. ./slony-setup.sh

slonik <<_EOF_
  cluster name = $CLUSTERNAME;
    
# 対象となるDB情報を * node * と呼ぶ。
# ここでは、それを定義している。
  node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
  node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';

# Set created
#
echo 'Set created';
create set (id=1, origin=1, comment='$MASTERDBNAME tables');

# Set add tables
#
echo 'Set infomation table';
set add table (set id=1, origin=1, id=1, full qualified name = 'public.infomation', comment='infomation table');

_EOF_

最後に、レプリケーションするという宣言を実行するためのスクリプトを作成します。これを実行しなければ、レプリケーションは開始されません。

[postgres@labgk slony-master]$ vi slony-start.sh

#!/bin/bash

if [ ! -f ./slony-setup.sh ]; then
    echo 'slony-setup.sh file not found.\n'
    exit -1
fi
. ./slony-setup.sh


slonik <<_EOF_
  cluster name = $CLUSTERNAME;
# 
# 
  node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
  node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';

# Node 2 subscribes set 1
# 
  subscribe set ( id = 1,provider = 1,receiver = 2,forward = no);

_EOF_

さて、ここまで書いたところで、これらのスクリプトを実行します。

[postgres@labgk slony-master]$ sh slony-mster-setup.sh
<stdin>:9: Initializing whitecat cluster
<stdin>:11: Node 1 on 172.16.1.64 defined
<stdin>:14: Node 1 on 172.16.0.53 defined
<stdin>:19: path from server node 1 to client node 2 created.
<stdin>:22: path from server node 2 to client node 1 created.
[postgres@labgk slony-master]$ sh slony-master-setup-add-tbl.sh
<stdin>:9: Set created
<stdin>:12: Set infomation table
[postgres@labgk slony-master]$

これでSlony-Iの設定は完了となります。『SLAVE側では設定しなくていいのか?』と疑問に思うかもしれませんが、どうやら、slonyのサービスが起動したときにMASTER側からSLAVE側に情報を送って勝手に設定してくれるようです。

Slony-I slon running(MASTERサーバー)

設定が終わったので、slon(レプリケーションを行うプログラム)を実行します。本来ならば、スクリプトを書いてサービスに組み込んでしまうのが理想なのですが……それは後ほど。

[postgres@labgk slony-master]$ slon graycat "dbname=whitecat user=postgres host=172.16.1.64" &
[1] 18554
[postgres@labgk slony-master]$ CONFIG main: slon version 1.0.5 starting up
CONFIG main: local node id = 1
CONFIG main: loading current cluster configuration
CONFIG storeNode: no_id=2 no_comment='Slave node'
CONFIG storePath: pa_server=2 pa_client=1 pa_conninfo="dbname=blackcat host=172.16.0.53 user=postgres" pa_connretry=10
CONFIG storeListen: li_origin=2 li_receiver=1 li_provider=2
CONFIG storeSet: set_id=1 set_origin=1 set_comment='whitecat tables'
CONFIG main: configuration complete - starting threads
CONFIG enableNode: no_id=2

[postgres@labgk slony-master]$

コマンドプロンプトが返ってきませんが、エンターキーを押してあげると出てきますから心配しないように。
一応、これで起動完了となります。

Slony-I slon running(SLAVEサーバー)

MASTERサーバーが起動したので、SLAVEサーバーも起動させます。

[postgres@sixwish postgres]$ slon graycat "dbname=blackcat user=postgres host=172.16.0.53" &
[1] 30006
CONFIG main: slon version 1.0.5 starting up
[postgres@sixwish postgres]$ CONFIG main: local node id = 2
CONFIG main: loading current cluster configuration
CONFIG storeNode: no_id=1 no_comment='Master Node'
CONFIG storePath: pa_server=1 pa_client=2 pa_conninfo="dbname=whitecat host=172.16.1.64 user=postgres" pa_connretry=10
CONFIG storeListen: li_origin=1 li_receiver=2 li_provider=1
CONFIG storeSet: set_id=1 set_origin=1 set_comment='whitecat tables'

[postgres@sixwish postgres]$

Slony-I slon subscribe(MASTERサーバー)

サーバーが起動したところで、MASTERサーバー側に戻ってきて、レプリケーションの開始を宣言します。

[postgres@labgk slony-master]$ sh slony-start.sh
[postgres@labgk slony-master]$

実行すると、SLAVE側のコンソールに以下のようなメッセージが表示されます。

CONFIG storeSubscribe: sub_set=1 sub_provider=1 sub_forward='f'
CONFIG enableSubscription: sub_set=1

これでレプリケーションが開始したことになります。

役に立たないコラム

既存DBに対して実行した場合、このサブスクライブの処理にやたらと時間が掛かることがあります。
どうやら、内部でトリガなどを設定しているためらしいのですが……
それらの処理が終わったかどうかを確認できるのが、CPU負荷をみるか、SLAVE側のコンソールメッセージだけですね。
知っていても無駄かもしれませんが、知らないと負荷がでかい……と勘違いするかも。

同期確認実験

MASTERサーバー上のinfomationテーブルにデータをInsertして、同期されているかを確認します。

[postgres@labgk slony-master]$ psql whitecat
Welcome to psql 7.4.6, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

whitecat=# INSERT INTO infomation VALUES ( '1', 'test', 'comments', 'now');
INSERT 287167 1
whitecat=# SELECT * From public.infomation ;
 sid | title |   text   |           update
-----+-------+----------+----------------------------
 1   | test  | comments | 2005-07-15 18:10:59.998334
(1 row)

whitecat=# \q
[postgres@labgk slony-master]$ psql -h 172.16.0.53 blackcat
Welcome to psql 7.4.6, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

blackcat=# SELECT * From public.infomation ;
 sid | title |   text   |           update
-----+-------+----------+----------------------------
 1   | test  | comments | 2005-07-15 18:10:59.998334
(1 row)

blackcat=# \q
[postgres@labgk slony-master]$

と言うような感じで同期されていることが確認できると思います。SLAVE側でInsertされていなかった場合は、少し待ってから再度Select文を実行してください。

異常事態時の同期確認実験

以下の手順で実験を行う

  1. MASTERサーバーの電源を落とす
  2. SLAVEサーバーの電源を落とす
  3. MASTER・SLAVEサーバーの電源を入れる
  4. MASTER側のslonyのサービスを起動
  5. SLAVE側のslonyのサービスを起動
  6. MASTER側でデータInsert
  7. 同期されたかを確認

二機とも再起動しなければならなくなったときの実験。

というわけで早速、再起動

[root@labgk root]$ shutdown -r now
[root@sixwish root]$ shutdown -r now

それぞれのサーバーで、サービスを起動する。

まずはMASTER側

[postgres@labgk slony-master]$ slon graycat "dbname=whitecat user=postgres host=172.16.1.64" &
CONFIG main: local node id = 1
CONFIG main: loading current cluster configuration
CONFIG storeNode: no_id=2 no_comment='Slave node'
CONFIG storePath: pa_server=2 pa_client=1 pa_conninfo="dbname=blackcat host=172.16.0.53 user=postgres" pa_connretry=10
CONFIG storeListen: li_origin=2 li_receiver=1 li_provider=2
CONFIG storeSet: set_id=1 set_origin=1 set_comment='whitecat tables'
CONFIG main: configuration complete - starting threads
CONFIG enableNode: no_id=2
[postgres@labgk slony-master]$

次にSLAVE側

[postgres@sixwish postgres]$ slon graycat "dbname=blackcat user=postgres host=172.16.0.53" &
CONFIG main: local node id = 2
CONFIG main: loading current cluster configuration
CONFIG storeNode: no_id=1 no_comment='Master Node'
CONFIG storePath: pa_server=1 pa_client=2 pa_conninfo="dbname=whitecat host=172.16.1.64 user=postgres" pa_connretry=10
CONFIG storeListen: li_origin=1 li_receiver=2 li_provider=1
CONFIG storeSet: set_id=1 set_origin=1 set_comment='whitecat tables'
WARN   remoteWorker_wakeup: node 1 - no worker thread
CONFIG storeSubscribe: sub_set=1 sub_provider=1 sub_forward='f'
WARN   remoteWorker_wakeup: node 1 - no worker thread
CONFIG enableSubscription: sub_set=1
WARN   remoteWorker_wakeup: node 1 - no worker thread
CONFIG main: configuration complete - starting threads
CONFIG enableNode: no_id=1
[postgres@sixwish postgres]$

この時点でWARNが表示されている……てか、「node 1 が起きてませんよ」?
けど、最終的にはconfiguration complete - starting threadsとはどういうこと??

とりあえず、手順通りに進めてみる。

[postgres@labgk slony-master]$ psql whitecat
Welcome to psql 7.4.6, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

whitecat=# INSERT INTO infomation VALUES ( '2', 'test', 'comments', 'now');
INSERT 288548 1
whitecat=# SELECT * From public.infomation ;
 sid | title |   text   |           update
-----+-------+----------+----------------------------
 1   | test  | comments | 2005-07-15 18:10:59.998334
 2   | test  | comments | 2005-07-15 18:39:54.470373
(2 rows)

whitecat=# \q
[postgres@labgk slony-master]$ psql -h 172.16.0.53 blackcat
Welcome to psql 7.4.6, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

blackcat=# SELECT * From public.infomation ;
 sid | title |   text   |           update
-----+-------+----------+----------------------------
 1   | test  | comments | 2005-07-15 18:10:59.998334
 2   | test  | comments | 2005-07-15 18:39:54.470373
(2 rows)

blackcat=#

どうやら問題は無いらしい。