LXDコンテナでPostgreSQLの非同期リプリケーションを試してみた

ストリーミング・レプリケーションの構築 — Let's PostgresPostgreSQL9.1ためしてみた【非同期レプリケーション編】 - ごろねこ日記 を読んで、2台のLXDコンテナを使ってPostgreSQLの非同期リプリケーションを試してみたのでメモです。

またPostgreSQL Replication - Second Edition | PACKT Booksが $10 と安かったので、買って非同期レプリケーションの章まで読みました。

手順はAnsible playbookとしてまとめました。 hnakamur/postgresql-async-replication-example-playbook

ansible.cfgask_vault_pass = True と指定しているので、プレイブック実行時に Vault password: と聞かれます。パスワードは password です。サンプルなので単純なパスワードにしていますが、実案件でのプレイブックはきちんとしたパスワードをつけています。

テスト環境構築

ホストマシンのディストリビューションはUbuntu 16.04でLXD 2.0.3, curl, jqをインストール済みの状態で試しました。

作業ディレクトリを作って、そこに移動し上記のプレイブックを取得します。

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

lxd_container モジュールを使うため、 github から最新のAnsibleをインストールします。

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

masterとstandbyのコンテナを作成

ansible-playbook launch_containers.yml

実行すると development というインベントリファイルを生成します。初期状態ではコンテナ pgsql1 が master, コンテナ pgsql2 が standby になります。

[development]
pgsql1 postgresql_peer_ipaddr=10.155.92.234 postgressql_master_standby_type=master
pgsql2 postgresql_peer_ipaddr=10.155.92.202 postgressql_master_standby_type=standby

[development:vars]
ansible_connection=lxd

コンテナ内にPostgreSQLの非同期レプリケーションの環境設定

以下のコマンドを実行してセットアップを実行します。

ansible-playbook initial_setup.yml

完了したら、2つ端末を開いて片方で

lxc exec pgsql1 bash
sudo -u postgres -i

を実行し、もう片方で

lxc exec pgsql2 bash
sudo -u postgres -i

を実行し、データベースを作ったり pgbench を動かしたりして変更が同期されるのを確認します。

test というデータベースを作ってpgbenchを実行する手順は以下の通りです。

createdb test
/usr/pgsql-9.5/bin/pgbench -i test
/usr/pgsql-9.5/bin/pgbench -T 180 test

上記の手順を1歩ずつ試し、 test データベースを作る前は pgsql2 では psql test が失敗しますが作った後は成功するなどで同期が確認できます。

レプリケーションの状態確認

master側での確認

以下のコマンドを実行します。

watch -n 0.5 'psql -x -c "SELECT * FROM pg_stat_replication"'

こんな感じで確認できます。

Every 0.5s: psql -x -c "SELECT * FROM pg_stat_replication"         Sat Jul 23 12:47:27 2016

-[ RECORD 1 ]----+------------------------------
pid              | 2160
usesysid         | 16384
usename          | repl_user
application_name | walreceiver
client_addr      | 10.155.92.234
client_hostname  |
client_port      | 44822
backend_start    | 2016-07-23 08:34:43.696331+00
backend_xmin     |
state            | streaming
sent_location    | 0/30031E0
write_location   | 0/30031E0
flush_location   | 0/30031E0
replay_location  | 0/30031E0
sync_priority    | 0
sync_state       | async

standby側での確認

以下のコマンドを実行します。

watch -n 0.5 'ps auxww | grep "[p]ostgres:"'

こんな感じで確認できます。

Every 0.5s: ps auxww | grep "[p]ostgres:"                                   Sat Jul 23 12:49:30 2016
ailabl
postgres  2051  0.0  0.0  86736  3420 ?        Ss   08:34   0:00 postgres: logger process
postgres  2052  0.0  0.0 233948  5996 ?        Ss   08:34   0:00 postgres: startup process   recover
ing 000000010000000000000003
postgres  2071  0.0  0.0 234012  7016 ?        Ss   08:34   0:00 postgres: checkpointer process
postgres  2072  0.0  0.0 233912  5916 ?        Ss   08:34   0:00 postgres: writer processl
postgres  2073  0.0  0.0  88856  3444 ?        Ss   08:34   0:00 postgres: stats collector process

postgres  2078  0.0  0.0 240632  7016 ?        Ss   08:34   0:05 postgres: wal receiver process   st
reaming 0/30031E0

フェイルオーバー

masterのPostgreSQLを停止し、 standbyをmasterにpromote (昇格)させます。

ansible-playbook failover.yml

旧masterを新standbyとして稼働再開

ここでインベントリファイル development 内の postgressql_master_standby_type 変数の masterstandby を入れ替えます。

その後、新standbyのPostgreSQLを起動します。

ansible-playbook start_new_standby.yml

もし復旧できない自体になった場合は、今のstandbyであるpgsql1 のデータディレクトリを退避して一からリプリケーション環境を構築します。

lxc exec pgsql1 -- mv /var/lib/pgsql/9.5/data /var/lib/pgsql/9.5/data.bak
ansible-playbook initial_setup.yml

フェイルバック

masterとstandbyを入れ替えているので、フェイルバックの手順はフェイルオーバーと同じです。

ansible-playbook failover.yml

コンテナの削除

ansible-playbook delete_containers.yml

Ansible vaultを使う際の変数命名規則のtips

ansible-vault encrypt で暗号化したファイルの内容を確認するには ansible-vault decrypt で復号化する必要があります。どんな変数があったかを確認する度に行うのは面倒なので、以下のように暗号化するファイル内で定義する変数を一旦別の変数で受け取ってplaybookではそれを参照するようにしました。

playbookの構成として環境ごとに development, production のようにグループを分けるようにしています(このサンプルでは development だけです)。暗号化するファイルとしないファイルを以下のような配置で作っています。

group_vars/development/secrets.yml
group_vars/development/vars.yml

group_vars/development/secrets.yml では

development:
  secrets:
    postgresql_replication_password: _YOUR_PASSWORD_HERE_

のように定義します。

group_vars/development/vars.yml では

postgresql_replication_password: "{{ development.secrets.postgresql_replication_password }}"

のようにその変数を参照するようにするという具合です。

おわりに

LXDを使えば複数サーバ構成のテスト環境も簡単に作れてとても便利です!