Skip to content

PostgreSQL 9.1 ストリーミングレプリケーション対応 リソースエージェント

Takatoshi MATSUO edited this page May 27, 2013 · 79 revisions

バグ報告はこちら(英語) Mailling list (http://lists.linux-ha.org/mailman/listinfo/) または Issues (https://github.com/ClusterLabs/resource-agents/issues)

日本語は、Linux-HA JapanのML (http://linux-ha.sourceforge.jp/wp/ml) まで。

本機能は、本家(ClusterLabs)のリポジトリにマージされ、resource-agents 3.9.3 でリリースされました。Linux-HA Japan Pacemakerリポジトリパッケージ 1.0.12-1.2 以上には含まれていますが、1.0.12-1.2 に含まれる機能は初期バージョンのため推奨しません。pgsql RA の入れ替えを推奨します。

リポジトリ内最新版ダウンロード先 : https://github.com/ClusterLabs/resource-agents/raw/master/heartbeat/pgsql

PG-REXというプロジェクトでは、検証済みのRAやドキュメント、運用ツールを提供しているようです。http://sourceforge.jp/projects/pg-rex/releases/

概要

PostgreSQL 9.0 の新機能として、非同期レプリケーションがサポートされましたが、さらに9.1では同期レプリケーションがサポートされました。ここでは、PostgreSQL 9.1以上で実装された同期および非同期レプリケーション機能、オープンソースのHAクラスタソフト"Pacemaker"を用いて、クラスタ化するためのリソースエージェント(RA)について解説します。

特徴

RAには主に以下の機能を実装しています。

  • Masterのフェイルオーバ
    • レプリケーション構成において、Masterが故障した際SlaveをMasterに昇格させる機能です。
  • 同期・非同期の自動切替
    • PostgreSQL 9.1 の同期レプリケーションは、Slaveが故障したり、レプリケーション用のLANが切断された場合、Master側のトランザクションが止まってしまいます。よって、Masterのトランザクションが止まる事象が発生した場合、これを検知しPostgreSQLの同期レプリケーションを非同期レプリケーションに自動で切り替えることでトランザクションを再開させます。
  • データの管理
    • 非同期レプリケーションに切り替えた場合、Slave側のデータはMasterより古くなります。これら古いデータのサーバがMasterに昇格しないように、データの新旧を管理したり、データの不整合が発生していると思われる場合はPostgreSQLの起動を抑止したりします。

また、Pacemakerと組み合わせることで、以下のことが実現できます。

  • 参照クエリの負荷分散
    • PostgreSQL単体でも可能ですが、Paceamakerと連携させることで、Slave故障時にMasterに参照クエリを自動で割り振ること可能になります。

用語

まずはじめに、本ドキュメントで使用する用語について整理しておきます。

  • RA
    • リソースエージェント
  • Master, Slave
    • Pacemaker の Master/Slave リソースの各状態
  • PRI
    • PostgreSQLがMasterで動作している状態。PacemakerのMaster/Slaveと区別するため、PostgreSQLのMasterを明示的に指す場合、Primaryと呼びます。PRIでは通常のPostgreSQLと同様にRead/Writeのリクエストを処理可能で、レプリケーション用のデータの送信元になります。基本的にPacemakerのMasterと状態が一致します。
  • HS
    • PostgreSQLがHot Standbyで動作している状態。参照クエリしか処理できません。PRIに接続することで、レプリケーションのデータを受け取ることができます。
  • 非同期モード
    • RAのパラメータ rep_mode に async を設定した場合のモードです。PacemakerはPostgreSQLのレプリケーションを常に非同期で動作せます。HSが正常に非同期レプリケーションで動作している場合のみ、Masterのフェイルオーバが可能です。
  • 同期モード
    • RAのパラメータ rep_mode に sync を設定した場合のモードです。PacemakerはPostgreSQLのレプリケーションが非同期で正常動作していることを確認後、同期レプリケーションに自動で切り替えます。また、PRIがHSの故障を検知した際、非同期レプリケーションに自動で切り替えます。これによりPRIのトランザクションが停止することを防ぎます。HSが同期レプリケーションで動いている場合のみ、Masterのフェイルオーバが可能です。
  • レプリケーションモード
    • 非同期モード、同期モードを総称してレプリケーションモードと呼びます。
  • 同期モード切替
    • 同期モード時に、必要に応じて同期レプリケーションと非同期レプリケーションを切り替える操作を示します。
  • D-LAN
    • データレプリケーションのパケットを流すLAN。冗長化したい場合はOSのbonding機能を使用してください。
  • S-LAN
    • サービスを提供するためのLAN。アプリケーションからの接続を待ち受けるLANです。冗長化したい場合はbondingを使用してください。
  • IC-LAN
    • Pacemakerのハートビート通信のパケットを流すLAN(インターコネクトLAN)。二本以上用意することを推奨します。PacemakerはHeartbeatスタック、Corosyncスタックどちらでも冗長化機能を有しているので、bondingを使う必要はありません。
  • STONITH-LAN
    • STONITH用のLAN。本ドキュメントでは使用していませんが、商用利用時は使用することを強く勧めます。

制限事項

  • PostgreSQLはPRIからHSへ直接遷移できないため、スイッチオーバー (MasterとSlaveの入れ替え)は簡単にできません。Master停止後、Slaveが新Masterに昇格するので、その後手動で旧MasterをSlaveとして組み込む必要があります。
  • PostgreSQLの仕様上、PRIとHSのTimelineIDが異なっている場合、レプリケーションできません。TimelineIDを揃えるには、PRIのWALアーカイブをHSへ転送するか共有する必要があります。方法としては、Slave起動後に手動でコピーするか、PRIのpostgresq.confの archive_command の設定で、scpやrsyncを使ってHSにWALアーカイブを転送したり、共有ディスクやNFS等を使ってアーカイブを共有したりする方法が考えられます。任意の方法で共有を行ってください。ただし、共有方式によってはWALアーカイブの整合性が崩れてしまい、最悪データの破壊が考えられますので、PostgreSQLに詳しくない場合、商用で信頼性を第一に考えたい場合は、共有しない方法(手動)をお勧めします。(私もどのソリューションが最適なのか解は持っていません)
    • (注意) PostgreSQL 9.1では、正常にfast shutdownを行っても、全データがHSへ転送されない事象が発生します。そのため、本RAでは、Slave存在時にMasterを停止した場合、Masterのデータに不整合が発生していると判断し、起動抑止用のロックファイルを作成します。
      • ※アプリケーションで書き込み成功したデータがロストするわけではありません。通常fast shutdownはWALを全てHSに転送してから終了しますがこれに失敗します。アプリケーション的には書き込み失敗と見えるので問題ありませんが、PRIにはデータが存在し、HSにはデータが存在しない状態が発生します。改善するには、PostgreSQLのshutdown時に、全データの転送が完了するまで待つモードがあればよいのですが、現在の所存在しません。(求む、PostgreSQL開発者)
  • PostgreSQLはpromote時にTimelineIDがインクリメントされます。WALアーカイブを共有をしない場合、HSのPostgreSQLはPRIのTimelineIDに追いつくことができず、レプリケーション接続できません。よって、HSを起動する前に、PRIの起動確認後、手動でPRIのデータをHSにコピーする必要があります。
  • MasterにはS-LANだけでなく、D-LANにも仮想IPを付与する必要があります。これはMasterの稼働ノードに関係なく、Slaveのレプリケーション接続先を固定するためです。(3ノード以上での使用を想定)

パラメータ

  • 元のpgsqlのパラメータに加えて以下のパラメータが追加されています。なお、元のpgsqlパラメータのmonitor_sqlは、レプリケーションモードでは使用できません。
    • rep_mode
      • none/async/sync から選択します。noneがデフォルトで、noneの場合は今までのpgsql RAと同じ動作です。asyncが非同期モード、syncが同期モードです。これら二つのレプリケーションモード時は、下記のnode_list, master_ip, restore_command のパラメータが必須です。同期モード時は、postgresql.conf に rep_mode.conf ファイルをインクルードする設定が自動で挿入されます。rep_mode.confには、同期モード切替時に設定変更が必要なパラメータ"synchronous_standby_names"がRAにより自動で書き込まれます。そのため、postgresql.confにsynchronous_standby_namesパラメータは記述しないでください。
    • node_list (必須)
      • レプリケーションに参加させる全ノードの一覧です。スペース区切りで全ノード名(uname -n コマンドの結果)を記述します。
    • master_ip (必須)
      • D-LANに使用するMasterの仮想IPを指定します。
    • restore_command (必須)
      • HSで起動する際にrecovery.confに記述するrestore_commandを指定します。
    • repuser
      • SlaveがMasterにレプリケーション接続する際に使用するユーザを指定します。デフォルトは postgres です。
    • primary_conninfo_opt
      • RAは、HS起動時に必要な設定ファイルrecovery.confを自動で作成します。この時recovery.confのprimary_conninfoのhost, port, user,application_nameの値を自動設定します。これら以外に追加パラメータを設定したい場合はここで指定します。
    • tmpdir
      • PostgreSQLの制御に必要な一時ファイルを作成できるディレクトリを指定します。デフォルトは/var/lib/pgsql/tmp ディレクトリです。本ディレクトリが存在しない場合、自動で作成されます。異常シャットダウン時にPostgreSQLの起動を抑止するための"PGSQL.lock"ファイルや、同期・非同期モードを切り替えるための"rep_mode.conf"ファイル、データの新旧を判別するための一時ファイル等が置かれます。
    • xlog_check_count
      • 初期起動時に自分のデータの位置(xlog location)をチェックする回数(1回のmonitorで1回チェック)を指定します。デフォルトは3(回)です。
    • stop_escalate_in_slave
      • Slave停止時にfast shutdownからimmediate shutdownにエスカレーションするまでのタイムアウト値。デフォルトは30(秒)です。Master停止は、既存のパラメータstop_escalateを使用してください。0に設定した場合、fastは実行されず、一気にimmediateで停止させます。

インストール

ここでは本RA特有の設定について主に記述しています。PostgreSQLやPacemakerのインストール、基本操作等は他のドキュメントを参考にしてください。

本ドキュメントでの想定構成は以下とします。

  • レプリケーションモードは同期モードとする。
  • WALアーカイブは共有しない。
  • ノード名はpm01,pm02とする。
  • S-LAN IPは、192.168.0.1, 192.168.0.2 とする。
  • 仮想IP(Master)は192.168.0.201とする
  • 仮想IP(Slave)は192.168.0.202とする
  • IC-LANは、192.168.1.1, 192.168.1.2 と、192.168.2.1, 192.168.2.2 とする。
  • D-LANは、192.168.3.1, 192.168.3.2 とする。
  • 仮想IP(D-LANのMaster)は、192.168.3.200とする。
  • PostgreSQLのインストール先は/usr/local/pgsql/とします。
  • PostgreSQLデータ領域は/var/lib/pgsql/9.1/data/とし、アーカイブ領域は/var/lib/pgsql/9.1/data/pg_archiveとします。
  • 説明の都合上STONITHは設定しませんが、商用利用時にはSTONITHを使うことを強くお勧めします。

構成図 図

インストール方法は以下の通りです。

PotgreSQLの設定 (重要な点のみ)

  • postgresql.conf 設定のポイント * レプリケーションの設定をする前に、必ず一回、正常にPostgreSQLが起動するかを確認してください。一度も起動せずに下記の設定を行うとPostgreSQLを起動できない可能性があります。 * "synchronous_standby_names"パラメータがある場合は、削除します。 * listen_addressに固定のIPを書くことはできません。"*" を指定してください。 * replication_timeout は、レプリケーション用LANが切断した場合の検知時間で、wal_receiver_status_interval は、HSがPRIに接続を試みる間隔です。デフォルトでは検知に時間がかかってしまうため、検知を短くしたい場合は短く指定します。 * Slaveの監視クエリがキャンセルされるのを回避するために max_standby_streaming_delayとmax_standby_archive_delayは"-1"にします。 * 同期モード時、RAは自動的に最終行にinclude文を追加します。これは、同期モード切替に使用される設定です。構成を非同期モードに変更する場合、このinclude文を削除してください。

以下、主な設定箇所の抜粋です。その他のパラメータの設定については、PostgreSQLのマニュアルを参考にして構築し、PostgreSQL単体で起動して、レプリケーションが可能であることまで事前に確認しておいてください。 ※PostgreSQLはあまり詳しくないので、他にも設定しておいた方がよいパラメータがある場合はLinux-HA JapanのMLまでお願いします。

レプリケーション参考

pm01のpostgresql.conf抜粋

listen_addresses = '*'
wal_level = hot_standby
synchronous_commit = on
archive_mode = on
archive_command = 'cp %p /var/lib/pgsql/9.1/data/pg_archive/%f'
max_wal_senders=5
wal_keep_segments = 32
hot_standby = on
restart_after_crash = off
replication_timeout = 5000         # mseconds
wal_receiver_status_interval = 2   # seconds
max_standby_streaming_delay = -1
max_standby_archive_delay = -1
synchronous_commit = on
restart_after_crash = off
hot_standby_feedback = on

  • pg_hba.conf * ※セキュリティは考慮していません:-)

host    all             all     127.0.0.1/32        trust
host    replication     all     192.168.3.0/24      trust
  • recovery.conf
    • 必要ありません。RAが自動で作りますので、あれば削除します。

設定後、PostgreSQLを起動し、レプリケーションが正常にできることを確認してください。手動でrecovery.confを作ってHSの動作確認する場合は、以下のようなファイルを作る必要があります。

standby_mode = 'on'
primary_conninfo = 'host=192.168.3.1 port=5432 user=postgres application_name=pm02'
restore_command = 'cp /var/lib/pgsql/9.1/data/pg_archive/%f %p'
recovery_target_timeline = 'latest'

Pacemaker

  • Linux-HA Japan 提供のPacemakerをインストールします。(Pacemakerリポジトリパッケージ 1.0.12以上を使用しないとはまります)
  • Pacemakerリポジトリパッケージ1.0.12-1.1以下には、本機能は含まれていません。/usr/lib/ocf/resource.d/heartbeat/pgsql ファイルをこのファイル https://github.com/ClusterLabs/resource-agents/blob/master/heartbeat/pgsql と入れ替えてください。(RAWをクリック)
  • Pacemaker起動後、本ドキュメントの最後に記述しているサンプルの設定CRMファイルを自分の環境にあわせて変更し、流し込みます。

状態について

RAは以下の状態をPacemakerのノード属性値として定義します。 ※属性は"crm_mon -A"で見ることができます。

pgsql-status

PRI,HSどちらのノードでも表示される属性値で、PostgreSQLの現在の状態を示します。

  • STOP
    • PostgreSQLが停止している状態です。
  • UNKNOWN
    • PostgreSQLの停止に失敗し状態不明な状態です。
  • HS:alone
    • HSとして動作しているが、PRIに接続していない状態です。
  • HS:connected
    • HSとして動作し、PRIに接続しているが、正常なレプリケーション状態になっていない(PRIにデータが追いついていない)状態です。
    • 同期モードでは、PRIでの"select state,sync_state from pg_stat_replication" の実行結果が STREAMING|SYNC, STREAMING|ASYNC STREAMING|POTENTIAL以外の場合、非同期モードではSTREAMING|ASYNC以外の場合に表示されます。
  • HS:async
    • HSとして動作し、非同期レプリケーション状態です。非同期モードでRAを使用している場合、Masterに昇格できます。
  • HS:sync
    • HSとして動作し、同期レプリケーション状態です。同期モードの場合、Masterに昇格できます。
  • HS:potential
    • 3ノード以上において、同期モードで動かした場合(Master x1, Slave x2)に表示されます。PostgreSQLの仕様で、実際に同期レプリケーションとして動かすことができるHSは1ノードのみで、残りはこのPOTENTIALというモードになります。
  • PRI
    • PRIで動作している状態です。

pgsql-data-status

データの遷移状態を示します。本状態は、次回起動時に、データが最新かどうか判断するために使用されます。

  • DISCONNECT
    • HSの故障やD-LAN故障等で、PRIからHSが認識できなくなった場合に遷移します。
  • {state}|{sync_state}
    • HSがPRIに接続している場合に遷移します。
    • PRI上での"select state,sync_state from pg_stat_replication"の結果がそのまま属性値として表示されます。
    • {state}には、INIT, CATCHUP, STREAMING が、{sync_state}には、ASYNC, POTENTIAL, SYNCが表示されます。(これで全部?)
  • LATEST
    • Master(PRI)になった場合に遷移します。

これらの状態は、最終的なデータの遷移状態を示すもので、実際のデータの状態と必ずしも一致するわけではありません。 例えばPRIで動作している時は"LATEST"という状態に遷移しますが、ノード停止やダウン時もこの"LATEST"という状態が保持されます。つまり自分で"DISCONNECT"に遷移することはありません。この後、他のノードが新しくMasterになった場合、この新しいMasterが旧Masterの状態を"DISCONNECT"に変更します。逆に誰もMasterになれない場合は、この"LATEST"が保持され続けることになります。

よって、次回起動時に自分のデータが"LATEST"ならば、自分が最も新しいデータを持っていると判断できますし、"DISCONNECT"ならば、自分のデータは古いと判断できます。これにより、古いデータでのサービス再開を防ぐことができます。

pgsql-master-baseline

Masterノードに表示される属性値で、promote 直前のxlog receive locationの値です。Master停止(demote)時も、全ノードに一時的に表示されます。

pgsql-xlog-loc

起動時にMasterがどこにも存在しない場合に表示されます。自分がMasterになれるかどうかを決定するために、自分のlast xlog replay locationかlast xlog receive locationの新しい方の値を他のノードに提示するために設定されます。他のノードと自分のノードのこの値を比較し、自分が最も新しい場合にMaster(PRI)になるための権限を得ることができます。ただし上記のpgsql-data-statusの状態で、データが古いと判断できた場合は、Masterになることはできません。

crm_mon の表示例

正常に同期レプリケーションで動作している場合

============
Last updated: Wed Jul 11 11:11:11 2011
Stack: Heartbeat
Current DC: pm02 (11111111-1111-1111-1111-111111111111) - partition with quorum
Version: 1.0.12-1554a83db0d3c3e546cfd3aaff6af1184f79ee87
2 Nodes configured, unknown expected votes
5 Resources configured.
============

Online: [ pm01 pm02 ]

vip-master      (ocf::heartbeat:IPaddr2):       Started pm01
vip-rep (ocf::heartbeat:IPaddr2):       Started pm01
vip-slave       (ocf::heartbeat:IPaddr2):       Started pm02
 Master/Slave Set: msPostgresql
     Masters: [ pm01 ]
     Slaves: [ pm02 ]
 Clone Set: clnPingCheck
     Started: [ pm01 pm02 ]

Node Attributes:
* Node pm01:
    + default_ping_set                  : 100
    + master-pgsql:0                    : 1000
    + pgsql-data-status                 : LATEST
    + pgsql-status                      : PRI
* Node pm02:
    + default_ping_set                  : 100
    + master-pgsql:1                    : 100
    + pgsql-data-status                 : STREAMING|SYNC
    + pgsql-status                      : HS:sync

Migration summary:
* Node pm02:
* Node pm01:

設定CRMサンプル(同期モード使用)

※Pacemaker 1.0.x 上での簡易的な設定例です。商用で使用する場合は、STONITHやディスクチェック機能等を導入すること。

Master/Slave 2台構成

property \
	no-quorum-policy="ignore" \
	stonith-enabled="false" \
    crmd-transition-delay="0s"

rsc_defaults \
	resource-stickiness="INFINITY" \
	migration-threshold="1"

ms msPostgresql pgsql \
	meta \
		master-max="1" \
		master-node-max="1" \
		clone-max="2" \
		clone-node-max="1" \
		notify="true"

clone clnPingCheck pingCheck
group master-group \
      vip-master \
      vip-rep \
      meta \
          ordered="false"

primitive vip-master ocf:heartbeat:IPaddr2 \
	params \
		ip="192.168.0.201" \
		nic="eth0" \
		cidr_netmask="24" \
	op start   timeout="60s" interval="0s"  on-fail="restart" \
	op monitor timeout="60s" interval="10s" on-fail="restart" \
	op stop    timeout="60s" interval="0s"  on-fail="block"

primitive vip-rep ocf:heartbeat:IPaddr2 \
	params \
		ip="192.168.3.200" \
		nic="eth3" \
		cidr_netmask="24" \
    meta \
            migration-threshold="0" \
	op start   timeout="60s" interval="0s"  on-fail="stop" \
	op monitor timeout="60s" interval="10s" on-fail="restart" \
	op stop    timeout="60s" interval="0s"  on-fail="block"

primitive vip-slave ocf:heartbeat:IPaddr2 \
	params \
		ip="192.168.0.202" \
		nic="eth0" \
		cidr_netmask="24" \
    meta \
        resource-stickiness="1" \
	op start   timeout="60s" interval="0s"  on-fail="restart" \
	op monitor timeout="60s" interval="10s" on-fail="restart" \
	op stop    timeout="60s" interval="0s"  on-fail="block"

primitive pgsql ocf:heartbeat:pgsql \
	params \
		pgctl="/usr/local/pgsql/bin/pg_ctl" \
		psql="/usr/local/pgsql/bin/psql" \
		pgdata="/var/lib/pgsql/9.1/data/" \
		start_opt="-p 5432" \
		rep_mode="sync" \
		node_list="pm01 pm02" \
		restore_command="cp /var/lib/pgsql/9.1/data/pg_archive/%f %p" \
		primary_conninfo_opt="keepalives_idle=60 keepalives_interval=5 keepalives_count=5" \
		master_ip="192.168.3.200" \
		stop_escalate="0" \
	op start   timeout="60s" interval="0s"  on-fail="restart" \
	op monitor timeout="60s" interval="7s" on-fail="restart" \
	op monitor timeout="60s" interval="2s"  on-fail="restart" role="Master" \
	op promote timeout="60s" interval="0s"  on-fail="restart" \
	op demote  timeout="60s" interval="0s"  on-fail="stop" \
	op stop    timeout="60s" interval="0s"  on-fail="block" \
	op notify  timeout="60s" interval="0s"

primitive pingCheck ocf:pacemaker:pingd \
	params \
		name="default_ping_set" \
		host_list="192.168.0.254" \
		multiplier="100" \
	op start   timeout="60s" interval="0s"  on-fail="restart" \
	op monitor timeout="60s" interval="10s" on-fail="restart" \
	op stop    timeout="60s" interval="0s"  on-fail="ignore"

location rsc_location-1 vip-slave \
	rule  200: pgsql-status eq "HS:sync" \
	rule  100: pgsql-status eq "PRI" \
	rule  -inf: not_defined pgsql-status \
	rule  -inf: pgsql-status ne "HS:sync" and pgsql-status ne "PRI"

location rsc_location-2 msPostgresql \
	rule -inf: not_defined default_ping_set or default_ping_set lt 100

colocation rsc_colocation-1 inf: msPostgresql        clnPingCheck
colocation rsc_colocation-2 inf: master-group        msPostgresql:Master

order rsc_order-1 0: clnPingCheck          msPostgresql
order rsc_order-2 0: msPostgresql:promote  master-group:start   symmetrical=false
order rsc_order-3 0: msPostgresql:demote   master-group:stop    symmetrical=false

運用時のQ&A

  • 両ノードのpgsql-data-statusが"DISCONNECT"状態になって、Masterになれない場合は?
    • ログ等を確認し、古いデータの方のPacemakerを停止します。
    • 停止後、新しいデータの方のPacemakerのpgsql-data-statusを"LATEST"に変更します。
      • crm_attribute -l forever -N ノード名 -n "pgsql-data-status" -v "LATEST"

  • rsyncでデータ同期する場合のオプションは?
    • --delete オプションを必ずつけましょう。このオプションをつけないと不要なファイルが削除されず、データベースが異常な状態になってしまいます。
      • (例) rsync -avr --delete 192.168.3.2:/var/lib/pgsql/9.1/data/ /var/lib/pgsql/9.1/data/
        • sync元、sync先のパスはディレクトリを指定すること。
  • ロックファイル(/var/lib/pgsql/tmp/PGSQL.lock)ファイルはどのような時に残るの?
    • ロックファイルは、promote時に作成され、demote時にSlaveがいない場合に削除されます。つまり、Master停止時にSlaveがいるか、もしくはMasterが故障で正常にシャットダウン処理されなかった場合にロックファイルが残ります。ロックファイルが残っている場合、そのサーバーのPostgreSQLのデータが不整合を起こしている可能性があります。現在動いているMasterとデータを同期しPacemakerを起動(もしくはフェイルカウントクリア)してください。
  • 全サーバー停止の順番は?
    • Masterから停止すると、上記のロックファイルが残って余分な手間が増えるため、Slave停止完了後Masterの停止をお勧めします。

その他運用例については、こちらもご参考ください。(運用のドキュメントはあまり綺麗にまとまっていなくてすみません・・・)