last update: 27 Apr 2009


Slony-Iによるレプリケーションとオンラインリカバリ


この記事は技術評論社『WEB+DB PRESS』『WEB+DB PRESS Vol.48』2008年12月刊 に掲載された原稿の草稿を、許可を得て公開しているものです。

また、日本PostgreSQLユーザ会仕組み分科会で発表したWALとPITRの資料pgpool-IIのオンラインリカバリに関する資料がありますので、こちらも参照してください。

1.1 はじめに

本章では、Slony-Iによるレプリケーションシステムの構成方法と、オンラインリカバリを含むいくつかの運用テクニックについて説明します。

1.2 Slony-Iとは

Slony-IはPostgreSQL専用の非同期型シングルマスタ・マルチスレーブのレプリケーションソフトです。
2003年頃からJan Wieck氏によって開発が始められ、2004年6月にバージョン1.0がリリースされました。 以降、現在まで順調に開発が進んでおり安定版の最新バージョンは1.2.15です。 開発版は2.0rc3がリリースされています。

Slony-IのレプリケーションはMySQLのそれと同等の機能を提供しています。しかし、後に述べるようにSlony-Iはスレーブやマスターをオンラインリカバリ可能(サービスを停止することなくリカバリできる)です。これはバイナリログという過去の操作履歴をすべて保存しないとオンラインリカバリが困難なMySQLに対して大きなアドバンテージです(脚注1)。

脚注1 MySQLのレプリケーションは、バイナリログと呼ばれる操作履歴ファイルをもとに行います。これは、文字通り操作されたSQL文が列挙されたファイルで、スレーブの初回起動時はマスター側のすべてのバイナリログに記録されたSQL文がスレーブ側で再実行されてデータベースの複製ができあがるという仕組みです。 長期間運用しているとバイナリログの数も非常に多くなるためすべてを保存するのは現実的でなく、またファイルが壊れる可能性もあります。いくつかのバイナリログが失われるか削除されたとすると、マスターとスレーブの初回同期を行うためは、サービスを一旦停止するか、もしくは非常に繁雑且つ高度な操作が必要です。

ここでSlony-Iの動作機構を説明します(図1)。 Slony-Iはslonというデーモンプロセスと、 PostgreSQLに格納された各種テーブルやトリガ関数から成ります。
具体的な例を使ってレプリケーションの仕組みを見てみましょう。
図1 Slony-Iの動作機構

マスター側のPostgreSQLにテーブルtblのUPDATE文が届いたとします。 そのとき、マスター側のPostgreSQLからスレーブ側へデータの更新が伝わる様子を説明します。

(1) 事前に登録されたトリガ関数が、更新情報をデーモンプロセスslonに伝える
(2) マスター側のデーモンプロセスslonからスレーブ側のslonへ、テーブルの更新情報が伝わる
(3)更新情報を受け取ったSlave側のデーモンプロセスslonがPostgreSQLのテーブルを更新する

トリガとは、指定したテーブルが更新(INSERT、UPDATE、DELETE)された場合に起動する仕組みで、 そこで実行される関数をトリガ関数といいます。
予めテーブルの変更をデーモンプロセスslonに伝えるトリガ関数を登録しておくと、 テーブルの更新によってその関数が実行され、テーブルの更新が(マスター側の)slonに伝わります。

Slony-Iのレプリケーションは非同期なので、 マスター側のデーモンプロセスslonが更新情報を受け取ってから、 スレーブ側のPostgreSQLの更新が終了するまで、 数100ミリ秒かそれ以上の時間的遅れが生じます。 しかし、通常はほとんど問題になるような遅れはありません。

1.3 今回のシステム構成

Slony-I独特の用語の説明を兼ねて、今回のシステム構成について解説します(図2)。
図2 システム構成

  • クラスタ
    レプリケーションの一番大きな単位。
  • ノード
    クラスタに含まれるPostgreSQLサーバをノードと呼ぶ。 ノードは1から番号付けする。今回はマスター側をノード1、スレーブ側をノード2となる。
  • セット
    レプリケーションするテーブルの組。
  • オリジン/サブスクライバ
    オリジンとはいわゆるマスターサーバ、サブスクライバとはスレーブサーバのこと。
  • プロバイダ/レシーバ
    テーブルの更新データを送信する側がプロバイダ、データを受信する側がレシーバ。

今回はセットが1つですが、 例えばセットset1はテーブルtbl_1をノード1からノード2にレプリケート、 セット2はテーブルtbl_2をノード1からノード3にレプリケートするなど、細かな設定が可能です。
オリジン/サブスクライバと混同しやすい概念としてプロバイダ/レシーバがあります。 プロバイダ/レシーバは相対的な概念で、ノードがカスケード構成や数珠つなぎになった場合を想像すると理解しやすいでしょう。 特にノードが2つの場合は「マスターノード=オリジン=プロバイダ」、「スレーブノード=サブスクライバ=レシーバ」です。

1.4 インストール

これからSlony-Iのインストールを行いますが、いくつか下準備が必要です。
全サーバに"postgres"というユーザを登録してください。 インストール作業はユーザpostgresで行います。 予め、ユーザpostgresの環境変数PATHにPostgreSQLとSlony-Iのバイナリパス"/usr/local/pgsql/bin"と"/usr/local/slony1/bin"を設定しておいてください。
export PATH=$PATH:/usr/local/pgsql/bin:/usr/local/slony1

なお、以降の説明における、ターミナルで作業する場合のプロンプト表示は次の形式とします。

ユーザ名 @ サーバ名>
例えば、ユーザpostgresがサーバpostgres0で作業する場合は 
postgres@postgres0>
とします。 特に、2台のPostgreSQLサーバpostgres0とpostgres1で共通の作業を行う場合は
postgres@>
と表記します。

    1.4.1 PostgreSQLのインストール

    はじめにPostgreSQLをインストールします。
    PostgreSQLを入手します。最新版は8.3.4です。
    http://www.postgresql.org/ftp/source/v8.3.4/

    PostgreSQLの最新版をダウンロードしたら、適当なディレクトリで展開し、 configureコマンドとmakeコマンドを実行します。
    root@> mkdir /usr/local/pgsql
    root@> chown  postgres:postgres /usr/local/pgsql
    root@> su postgres
    postgres@> tar xvfz postgresql-8.3.4.tar.gz
    postgres@> cd postgresql-8.3.4
    postgres@> ./configure
    postgres@> make && make install
    

    デフォルトのインストールディレクトリは/usr/local/pgsql/です。

    1.4.2 設定

    インストールが終了したら、PostgreSQLを稼働させる2台のサーバ:postgres0とpostgres1で以下の設定を行います。

      1.4.2.1 データベースの初期化

      データベースの初期化を行います。以下のコマンドを実行してください。
      postgres@> initdb -D /usr/local/pgsql/data
      

      これでディレクトリ/usr/local/pgsql/data以下にデータベースが作成されます。 このディレクトリを「ベースディレクトリ」、ベースディレクトリ以下のデータを「データベースクラスタ」とよびます。

      1.4.2.2 アーカイブログディレクトリの作成

      アーカイブログを保存するディレクトリを作成します。 ここでは、ベースディレクトリにarchive_logというディレクトリを作成します。
      postgres@> mkdir /usr/local/pgsql/data/archive_log
      

      1.4.2.3 設定ファイル

      以上の準備が終わったら、2つの設定ファイルpostgresql.confとpg_hba.confの編集を行います。 これらはベースディレクトリ(今回はディレクトリ"/usr/local/pgsql/data")にあります。
      1つ目のファイルはposgtresql.confです。 数多くのパラメータがありますが、今回は次の3つのパラメータを設定してください。
      listen_addresses = '*'
      archive_mode = on
      archive_command = 'cp %p /usr/local/pgsql/data/archive_log/%f'
      
      postgresql.confの抜粋

      パラメータarchive_modeとarchive_commandはオンラインリカバリのために必要です。 同じく行頭の"#"を消去して、それぞれ値を設定してください。 パラメータarchive_commandの設定値にあるディレクトリ"/usr/local/pgsql/data/archive_log/"は 上で作成したアーカイブログの保存ディレクトリを記述してください。

      2つ目のファイルはアクセス制御ファイルpg_hba.confです。 ここでは、ネットワーク192.168.1.0からのアクセスはすべて許可する設定をします。

      host	all	all 192.168.1.0/24	trust
      
pg_hba.confの末尾に追加

1.4.3 PostgreSQLサーバの起動

以上でPostgreSQLの準備が整ったので、PostgreSQLを起動しましょう。 以下のコマンドを実行してください。
postgres@> pg_ctl -D /usr/local/pgsql/data start

次に、これから実験で使うデータベースslonydbを作成しましょう。 createdbコマンドでデータベースを作成します。
postgres@> createdb slonydb

1.4.4 Slony-Iのインストール

以上の準備が終わったら、Slony-Iをインストールしましょう。 以下のURLから最新版のSlony-Iをダウンロードしてください。
http://www.slony.info/
http://www.slony.info/downloads/1.2/source/

2台のPostgreSQLサーバにSlony-Iの最新版をダウンロードしたら、 適当なディレクトリで展開し、configureコマンドとmakeコマンドを実行します。 configureコマンドの"--with-perltools"オプションで、 Slony-Iを簡単に操作するためのコマンド群をインストールするディレクトリを指定します。 "--with-pgconfigdir"オプションには、PostgreSQLの実行ファイルが保存されているディレクトリを指定します。
root@> mkdir /usr/local/slony1
root@> chown postgres:postgres /usr/local/slony1
root@> su postgres
postgres@> tar xvfj slony1-1.2.15.tar.bz2 
cd slony1-1.2.15
./configure --prefix=/usr/local/slony1 \
> --with-perltools=/usr/local/slony1/bin \
> --with-pgconfigdir=/usr/local/pgsql/bin
postgres@> make && make install

1.5 設定

    1.5.1 PostgreSQLの準備

    マスター、スレーブ両方のPostgreSQLが起動していることを確認した後、 今回のテストに使うデータベースとテーブルを準備します。
    まず、マスター側でデータベースslonydbを作成します。 トリガ関数を登録するためにストアドプロシージャPL/pgSQLの設定も必要です。
    postgres@postgres0> createdb slonydb
    postgres@postgres0> createlang plpgsql slonydb
    

    つぎに、今作成したデータベースにログインして3つのテーブルを作成します(図3)。 テーブルtbl_pkeyはプライマリーキー(主キー)を持つテーブル、 テーブルtbl_uniqは列idがNOT NULL且つユニークキー(一意性キー)"tbl_uniq_id_key"であるテーブル、 テーブルtblはプライマリーキーもユニークキーも持たないテーブルです。
    図3 テーブルの作成
    postgres@postgres0> psql slonydb -q
    slonydb=# CREATE TABLE tbl_pkey (id int primary key, data int);
    NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "tbl_pkey_pkey" for table "tbl_pkey"
    CREATE TABLE
    slonydb=# CREATE TABLE tbl_uniq (id int NOT NULL, data int, UNIQUE(id));
    NOTICE:  CREATE TABLE / UNIQUE will create implicit index "tbl_uniq_id_key" for table "tbl_uniq"
    CREATE TABLE
    slonydb=# CREATE TABLE tbl (id int, data int);
    CREATE TABLE
    

    ここで、ユニークキーを持つテーブルtbl_uniqが、暗黙的にインデックス"tbl_uniq_id_key"を張られたことを覚えておいてください。後でSlony-Iの設定を行うときに必要になります。

    マスター側のPostgreSQLの準備が済んだので、データベーススキーマをスレーブ側にコピーします。

    postgres@postgres0> pg_dump -s -C slonydb | ssh 192.168.1.101 psql
    

    ここでSlony-Iの動作をよく理解するために、 サーバpostgres0のテーブルtblにデータを1行挿入して、サーバpostgres0とサーバpostgres1を非同期状態にしておきます。
    postgres@postgres0> psql slonydb -q
    slonydb=# INSERT INTO tbl VALUES (1,1);
    INSERT 0 1
    slonydb=# SELECT * FROM tbl;
     id | data 
    ----+------
      1 |    1 
    (1 row)
    

    1.5.2 Slony-Iの設定

    ここから、いよいよSlony-Iの設定に入ります。
    Slony-Iの制御は、Slony-I独自の命令を組み合わせてslonikというコマンドに入力することで行います。 しかし、Slony-Iの命令は小さなプログラミング言語であり、それを操るのは容易ではありません。
    幸い、Slony-Iの命令を自動生成するperlスクリプトが提供されているので、 ここでそれらを試してみましょう。

      1.5.2.1 設定ファイルslon_tools.conf

      はじめに、基本となる設定ファイルslon_tools.confを作成します。 サンプルファイルslon_tools.conf-sampleがslony-Iをインストールしたディレクトリのサブディレクトリetcにあるので、 それをコピーします。
      postgres@postgres0> cd /usr/local/slony1/etc
      postgres@postgres0> cp slon_tools.conf-sample slon_tools.conf
      

      次にslon_tools.confファイルを今回の環境に合わせて編集します。主要な部分を(リスト1)に示します。
      リスト1 slon_tools.confの抜粋
       1:if ($ENV{"SLONYNODES"}) {
       2:    require $ENV{"SLONYNODES"};
       3:} else {
       4:   $CLUSTER_NAME = 'slony_test';
       5:
       6:   $LOGDIR = '/usr/local/slony1/log';
       7:
       8:   $MASTERNODE = 1;
       9:
      10:   add_node(node     => 1,
      11:	     host     => '192.168.1.100',
      12:	     dbname   => 'slonydb',
      13:	     port     => 5432,
      14:	     user     => 'postgres',
      15:             password => '');
      16:
      17:    add_node(node     => 2,
      18:	     host     => '192.168.1.101',
      19:	     dbname   => 'slonydb',
      20:	     port     => 5432,
      21:	     user     => 'postgres',
      22:             password => '');
      23:}
      24:
      25:$SLONY_SETS = {
      26:    "set1" => {
      27:	"set_id" => 1,
      28:	"origin" => 1,
      29:
      30:	"table_id"    => 1,
      31:	"sequence_id" => 1,
      32:
      33:     "pkeyedtables" => ['tbl_pkey', ],
      34:	"keyedtables" => {'tbl_uniq' => 'tbl_uniq_id_key',},
      35:	"serialtables" => ["tbl"],
      36:    },
      37:};
      38:
      39:# Please do not add or change anything below this point.
      40:1;
      

      簡単にslon_tools.confファイルの中身を説明します。

      作成したslon_tools.confファイルはスレーブ側にも保存しておきます。

      postgres@postgres0> scp slon_tools.conf 192.168.1.101:/usr/local/slony1/etc/
      

      1.5.2.2 クラスタの初期化

      slon_tools.confファイルの編集が終わったら、ディレクトリを移動してSlony-Iの設定をはじめます。 はじめにslonik_init_clusterコマンドでクラスタとノードの設定命令を実行します。
      postgres@postgres0> cd /usr/local/slony1/bin
      postgres@postgres0> slonik_init_cluster | slonik
      

      ここで少し寄り道して、slonik_init_clusterコマンドがどのような命令をslonikコマンドに出力してるのか、覗いてみましょう(図4)。
      postgres@postgres0> slonik_init_cluster
      # INIT CLUSTER
      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
        init cluster (id = 1, comment = 'Node 1 - slonydb@192.168.1.100');
      
      # STORE NODE
        store node (id = 2, event node = 1, comment = 'Node 2 - slonydb@192.168.1.101');
        echo 'Set up replication nodes';
      
      # STORE PATH
        echo 'Next: configure paths for each node/origin';
        store path (server = 1, client = 2, conninfo = 'host=192.168.1.100 dbname=slonydb user=postgres port=5432');
        store path (server = 2, client = 1, conninfo = 'host=192.168.1.101 dbname=slonydb user=postgres port=5432');
        echo 'Replication nodes prepared';
        echo 'Please start a slon replication daemon for each node';
      
      図4 slonik_init_clusterコマンドの出力

      3つに分類されているので、順に説明します。
      1. INIT CLUSTER
        クラスタを初期化。主要な命令は"init cluster"。
      2. STORE NODE
        マスターノード以外のノードを記録。
      3. STORE PATH
        ノード間で通信するための経路(パス)を記録。パスは単方向なので、 ノード1とノード2間で通信するためには、ノート1からノード2のパスと、 ノード2からノード1のパスの2つを記録しなければならない。

      ここで見た命令はほんの一部で、slonikコマンドが理解する命令は全部で37あります。 命令のリファレンスが
      http://www.slony.info/documentation/commandreference.html
      にあるので、興味のある方は覗いてみてください。
      以降、perlスクリプトの生成する命令の解説は行いませんが、 自身でSlony-Iを試すときにはひとつひとつ内容を確認していくと非常に参考になります。

      ところで、ノードやパスの情報はどこに記録されるのでしょうか。 答えは各PostgreSQLサーバのデータベースにです。 各サーバ、つまり各ノードにslony用のスキーマが作成され、 そのスキーマに作られるslony管理用のテーブルに記録されます。 スキーマ名は"_クラスタ名"で、例えば今回はスキーマ名が"slony_test"なので 管理用のスキーマは"_slony_test"となります。

      1.5.2.3 セットset1の初期化

      クラスタの設定後は、slonik_create_setコマンドでセット"set1"に関する設定を行います。
      postgres@postgres0> slonik_create_set set1 | slonik
      

      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
      
      # TABLE ADD KEY
        echo '  Adding unique key to table public.tbl...';
        table add key (
          node id = 1,
          full qualified name='public.tbl'
        );
      
      # CREATE SET
        try {
          create set (id = 1, origin = 1, comment = 'Set 1 for slony_test');
        } on error {
          echo 'Could not create subscription set 1 for slony_test!';
          exit -1;
        }
      
      # SET ADD TABLE
        echo 'Subscription set 1 created';
        echo 'Adding tables to the subscription set';
        set add table (set id = 1, origin = 1, id = 1,
                       full qualified name = 'public.tbl', key=serial,
                       comment = 'Table public.tbl without primary key');
        echo 'Add unkeyed table public.tbl';
        set add table (set id = 1, origin = 1, id = 2,
                       full qualified name = 'public.tbl_pkey',
                       comment = 'Table public.tbl_pkey with primary key');
        echo 'Add primary keyed table public.tbl_pkey';
        set add table (set id = 1, origin = 1, id = 3,
                       full qualified name = 'public.tbl_uniq', key='tbl_uniq_id_key',
                       comment = 'Table public.tbl_uniq with candidate primary key tbl_uniq_id_key');
        echo 'Add candidate primary keyed table public.tbl_uniq';
      
      # SET ADD SEQUENCE
        echo 'Adding sequences to the subscription set';
        echo 'All tables added';
      
      "slonik_create_set set1"の出力

      1.5.2.4 サブスクライバの設定

      最後にslonik_subscribe_setコマンドで、セットset1のサブスクライバの設定を行います。 セットset1のサブスクライバ、つまりスレーブノードは"2"なので、引数は"set1 2"となります。 このコマンドもマスター側のサーバ上で実行してください。
      postgres@postgres0> slonik_subscribe_set set1 2 | slonik
      

      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
        try {
          subscribe set (id = 1, provider = 1, receiver = 2, forward = yes);
        }
        on error {
          exit 1;
        }
        echo 'Subscribed nodes to set 1';
      
      "slonik_subscribe_set set1 2"の出力

    1.5.3 slonの起動

    以上ですべての準備が終わったので、slon_startコマンドでデーモンプロセスslonを起動します。 slonの起動はそれぞれのサーバにログインして行ってください。
    postgres@postgres0> slon_start 1
    postgres@postgres0> ssh 192.168.1.101
    postgres@postgres1> cd /usr/local/slony1/bin
    postgres@postgres1> slon_start 2
    

    これによって、サーバpostgres0のデータがサーバpostgres1に転送されはじめます。 先ほどサーバpostgres0のテーブルtblに挿入したデータも、サーバpostgres1に転送されたはずです。

1.6 動作確認

すべての作業が終わったので、Slony-Iの動作確認を行います。 マスター側のPostgreSQLでテーブルtblにデータを挿入してみます(図5)。
図5 マスター側での動作確認
postgres@postgres0> psql slonydb -q

slonydb=# INSERT INTO tbl VALUES (2,2);
INSERT 0 1
slonydb=# SELECT * FROM tbl;
 id | data | _Slony-I_slony_test_rowID 
----+------+---------------------------
  1 |    1 |          1000000000000001
  2 |    2 |          1000000000000002
(2 rows)

テーブルtblにはユニークキーを定義しなかったので、 Slony-Iが自動的に"_Slony-I_slony_test_rowID"というユニークキーを設定していることがわかります。
ここで、スレーブ側のPostgreSQLにログインしてみましょう(図6)。今挿入されたデータだけでなく、slon起動以前にサーバpostgres0に挿入されたデータもレプリケーションできていることが確認できます。 このように、Slony-Iが正常に起動できていれば、スレーブ側はマスター側に自動的に追い付きます。

図6 スレーブ側での動作確認
postgres@postgres1> psql slonydb -q
slonydb=# SELECT * FROM tbl;
 id | data | _Slony-I_slony_test_rowID 
----+------+---------------------------
  1 |    1 |          1000000000000001
  2 |    2 |          1000000000000002
(2 rows)

slonydb=# UPDATE tbl SET data = 100 WHERE id = 1;
ERROR:  Slony-I: Table tbl is replicated and cannot be modified on a subscriber node

また、検索系SQL(SELECT)は実行できますが、更新系SQL(INSERT、UPDATE、DELETE)はエラーとなって実行できないことがわかります。

1.7 その他の操作

Slony-Iを起動しただけではもの足りないので、実運用で役立ついくつかの方法を説明します。

    1.7.1 スイッチオーバー

    マスターとスレーブを切り替えるには、slonik_move_setコマンドを使います。 セットset1において、マスターをノード1からノード2に切り替えるには、次のようにします。
    postgres@postgres0> slonik_move_set set1 1 2 | slonik
    

    cluster name = slony_test;
     node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
     node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
      echo 'Locking down set 1 on node 1';
      lock set (id = 1, origin = 1);
      echo 'Locked down - moving it';
      move set (id = 1, old origin = 1, new origin = 2);
      echo 'Replication set 1 moved from node 1 to 2.  Remember to';
      echo 'update your configuration file, if necessary, to note the new location';
      echo 'for the set.';
    
    "slonik_move_set set1 1 2"の出力

    これで、先ほどはできなかったノード2でのテーブル更新が可能になります(反対に、ノード1は更新できなくなります)。
    postgres@postgres1> psql slonydb -q
    slonydb=# UPDATE tbl SET data = 100 WHERE id = 1;
    UPDATE 1
    

    1.7.2 フェールオーバー

    不幸にしてマスター側のPostgreSQLがダウンした場合の対処方法を示します。
    まずは障害を起こしましょう。マスター側のPostgreSQLを強制終了してください。
    postgres@postgres0> pg_ctl -D /usr/local/pgsql/data -m immediate stop
    

    障害が起きても慌てずに、スレーブ側でslonik_failoverコマンドを実行し、 ダウンしたノード1からノード2にマスターを切り替えます。 引数は"ダウンしたノード"、次に"代わりにマスターになるノード"の順に指定するので、 "1 2"と指定することになります。
    postgres@postgres1> cd /usr/local/slony1/bin
    postgres@postgres1> slonik_failover 1 2 | slonik
    

    cluster name = slony_test;
     node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
     node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
      try {
          failover (id = 1, backup node = 2);
      } on error {
          echo 'Failure to fail node 1 over to 2';
          exit 1;
      }
      echo 'Replication sets originating on 1 failed over to 2';
    
    "slonik_failover 1 2"の出力

    マスター側のデーモンプロセスslonが起動してるなら、停止させましょう。
    postgres@postgres1> slon_kill 1
    

    これで、ノード2だけの縮退運転モードに入りました。

    1.7.3 スレーブのオンラインリカバリ

    最後に障害を起こしたスレーブをオンラインでリカバリする方法を説明します。

      1.7.3.1 スレーブの停止

      スレーブ側のPostgreSQLサーバとslonデーモンを停止します。
      postgres@postgres1> pg_ctl -D /usr/local/pgsql/data -m immediate stop
      postgres@postgres1> slon_kill 2
      

      1.7.3.2 ノードの削除

      マスター側でノード2を削除します。
      postgres@postgres0> slonik_drop_node 2 | slonik
      

      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
        try {
            drop node (id = 2, event node = 1);
        } on error {
            echo 'Failed to drop node 2 from cluster';
            exit 1;
        }
        echo 'dropped node 2 cluster';
      
      "slonik_drop_node 2"の出力

      1.7.3.3 スレーブ側の再構築

      つぎに、改めてデータベースslonydbを作成します。テーブルスキーマもすべて再作成してください。
      postgres@postgres1> pg_ctl -D /usr/local/pgsql/data start
      postgres@postgres1> dropdb slonydb
      postgres@postgres1> createdb slonydb
      postgres@postgres1> createlang plpgsql slonydb
      postgres@postgres1> psql slonydb -q
      slonydb=# CREATE TABLE tbl_pkey (id int primary key, data int);
      slonydb=# CREATE TABLE tbl_uniq (id int NOT NULL, data int, UNIQUE(id));
      slonydb=# CREATE TABLE tbl (id int, data int);
      

      1.7.3.4 マスター側の作業

      以上の作業が終わったら、ノード2を再設定し、サブスクライバも再設定します。
      postgres@postgres0> slonik_store_node 2 | slonik
      postgres@postgres0> slonik_subscribe_set set1 2 | slonik
      

      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
      
      # STORE NODE
        store node (id = 2, event node = 1, comment = 'Node 2 - slonydb@192.168.1.101');
        echo 'Set up replication nodes';
      
      # STORE PATH
        echo 'Next: configure paths for each node/origin';
        store path (server = 1, client = 2, conninfo = 'host=192.168.1.100 dbname=slonydb user=postgres port=5432');
        store path (server = 2, client = 1, conninfo = 'host=192.168.1.101 dbname=slonydb user=postgres port=5432');
        echo 'Replication nodes prepared';
        echo 'Please start a slon replication daemon for each node';
      
      "slonik_store_node 2"の出力

      1.7.3.5 スレーブ側slonの再起動

      最後にスレーブ側でslonデーモンを再起動すると、スレーブのオンラインリカバリの完了です。
      postgres@postgres1> slon_start 2
      

    1.7.4 マスターのオンラインリカバリ

    最後に、障害を起こしたマスターをオンラインでリカバリする方法を説明します。
    残念なことに今回説明したツールはマスター側で使用することが前提となっています。なので、そのままではマスターのオンラインリカバリできないため、ツールの出力を少し編集する必要があります。

      1.7.4.1 マスターの停止とフェールオーバー

      マスター側のPostgreSQLサーバとslonデーモンを停止します。
      postgres@postgres0> pg_ctl -D /usr/local/pgsql/data -m immediate stop
      postgres@postgres0> slon_kill 1
      

      次に、スレーブ側でフェールオーバーを実行します。
      postgres@postgres1> cd /usr/local/slony1/bin
      postgres@postgres1> slonik_failover 1 2 | slonik
      

      1.7.4.2 ノードの削除

      スレーブ側でノード1を削除します。
      slonik_drop_nodeコマンドはそのままでは使えないので、sedコマンドで多少編集した後にslonikコマンドに送ります。
      postgres@postgres1> slonik_drop_node 1 | sed s/"event node = 1"/"event node = 2"/ | slonik
      

      ここでsed命令は、drop node命令の引数について、 "drop node (id = 1, event node = 1);"を "drop node (id = 1, event node = 2);"に変更します。
      cluster name = slony_test;
       node 1 admin conninfo='host=192.168.1.100 dbname=slonydb user=postgres port=5432';
       node 2 admin conninfo='host=192.168.1.101 dbname=slonydb user=postgres port=5432';
        try {
            drop node (id = 1, event node = 2);
        } on error {
            echo 'Failed to drop node 1 from cluster';
            exit 1;
        }
        echo 'dropped node 1 cluster';
      
      slonikコマンドが受け取る命令

      1.7.4.3 マスター側の再構築

      改めてデータベースslonydbを作成します。テーブルスキーマもすべて再作成してください。
      postgres@postgres0> pg_ctl -D /usr/local/pgsql/data start
      postgres@postgres0> dropdb slonydb
      postgres@postgres0> createdb slonydb
      postgres@postgres0> createlang plpgsql slonydb
      postgres@postgres0> psql slonydb -q
      slonydb=# CREATE TABLE tbl_pkey (id int primary key, data int);
      slonydb=# CREATE TABLE tbl_uniq (id int NOT NULL, data int, UNIQUE(id));
      slonydb=# CREATE TABLE tbl (id int, data int);
      

      1.7.4.4 スレーブ側の作業

      以上の作業が終わったら、ノード1を再設定し、サブスクライバも再設定します。
      slonik_stop_nodeコマンドもそのままでは使えないので、sedコマンドで多少編集した後にslonikコマンドに送ります。
      postgres@postgres1> slonik_store_node 1 | sed s/"event node = 1"/"event node = 2"/ | slonik
      postgres@postgres1> slonik_subscribe_set set1 1 | slonik
      

      slonik_subscribe_setコマンドは作業するホストに依存しないので編集する必要がありません。

      1.7.4.5 マスター側slonの再起動とスイッチオーバー

      マスター側でslonデーモンを再起動すると、マスターのオンラインリカバリが行われます。
      postgres@postgres0> slon_start 1
      

      最後にスイッチオーバーを行えば、作業完了です。
      postgres@postgres0> slonik_move_set set1 2 1 | slonik
      

1.8 まとめ

以上、Slony-Iによるレプリケーションシステム構築といくつかの運用テクニックを解説しました。 Slony-Iの設定はslonik用命令を書き下すのが若干面倒で、それゆえ敷居が高いのも事実ですが、 今回説明したツールを使えば比較的簡単にシステム構築と運用ができますので、 興味のある方は是非試してみてください。