Ubuntu PostgreSQLレプリケーション設定 — ストリーミング同期/非同期の構築

データベース

PostgreSQLのストリーミングレプリケーションは、プライマリのデータをほぼリアルタイムでレプリカ(スタンバイ)に複製する仕組みです。障害時のダウンタイム削減や、読み取り負荷をレプリカに逃がす負荷分散に欠かせません。ただ、設定ファイルの書き方や pg_basebackup の使い方で詰まる方がとても多いです。

結論から言うと、プライマリで wal_level 系を3つ設定し、レプリカ側で pg_basebackup -R を一回叩くだけでストリーミングレプリケーションは動きます。本記事では ubuntu:24.04 公式Dockerイメージ(PostgreSQL 16.14)の1コンテナ内に、プライマリ(ポート5432)とスタンバイ(ポート5433)の2インスタンスを立てて実際に検証した結果を、コマンドの実出力と一緒に載せます。

この記事のポイント

  • 実測環境: ubuntu:24.04 公式イメージ(PostgreSQL 16.14-0ubuntu0.24.04.1)で Primary + Standby を構築(2026-06-14)
  • wal_level = replicamax_wal_senders = 5wal_keep_size = 64MB がプライマリ設定の3点セット
  • pg_basebackup -Fp -Xs -R-R フラグで standby.signalprimary_conninfo が自動生成される
  • pg_stat_replicationstate=streaminglag_bytes=0 でレプリケーション確立を確認
  • 同期モードへの切り替えは synchronous_standby_names = 'walreceiver' の1行(サービス再起動不要)で完了

目次

  1. 前提環境と実測バージョン
  2. ストリーミングレプリケーションの仕組み
  3. プライマリの設定(3ステップ)
  4. pg_basebackupでレプリカを作成する
  5. レプリケーション確立の確認
  6. 同期モードへの切り替え
  7. よくあるエラーと対処法
  8. まとめ

前提環境と実測バージョン

使う PostgreSQL のバージョンは、Ubuntu のリリースによって変わります。実際に ubuntu:22.04ubuntu:24.04 の公式イメージで apt-cache policy postgresql を叩いて確認したところ、次のようになっていました。

UbuntuバージョンとデフォルトPostgreSQLバージョン比較(実測)
UbuntuバージョンとデフォルトPostgreSQLバージョン比較(実測)

Ubuntu 22.04 LTS は PostgreSQL 14系(14.23)、Ubuntu 24.04 LTS は PostgreSQL 16系(16.14) が入ります。本記事は新しい ubuntu:24.04(PostgreSQL 16.14)で検証しています。Ubuntu 22.04 の14系でも手順はほぼ同じですが、設定ファイルのパス(/etc/postgresql/14/main//etc/postgresql/16/main/)が違う点に注意してください。

今回の検証は、1つの ubuntu:24.04 コンテナ内に initdb で2つのデータディレクトリを作り、プライマリを5432番、スタンバイを5433番で起動して行いました。VPSを2台用意する代わりに、同じ仕組みを1台で再現したものです。実際のサーバーでも、IPアドレスとポートを各ホストに合わせれば同じ手順で構築できます。

注意:環境によるパスの違い

apt でインストールした PostgreSQL では、設定ファイルは /etc/postgresql/16/main/、データは /var/lib/postgresql/16/main/ に置かれます(pg_lsclusters で確認できます)。本記事の検証では initdb でデータディレクトリを直接指定しているため、設定ファイルはデータディレクトリ内(postgresql.conf / pg_hba.conf)にあります。お使いの環境のパスに読み替えてください。

ストリーミングレプリケーションの仕組み

PostgreSQL のストリーミングレプリケーションは、WAL(Write-Ahead Log:更新内容を書き込む前に必ず記録するログ)をネットワーク越しにリアルタイム転送する仕組みです。流れは次のとおりです。

  1. プライマリで書き込みが発生すると、まず WAL に記録されます
  2. WAL Sender(walsender)プロセスがスタンバイに WAL を転送します
  3. スタンバイの WAL Receiver(walreceiver)がこれを受け取り、適用します
  4. スタンバイは読み取りクエリ(SELECT)だけを受け付ける「ホットスタンバイ」として動作します

重要なのは非同期モードと同期モードの違いです。デフォルトは非同期で、プライマリはスタンバイへの反映を待たずに COMMIT を返します。同期モードでは COMMIT をスタンバイが確認してから返すため、データ損失ゼロを保証できますが、その分レイテンシが増えます。

同期レプリケーション vs 非同期レプリケーション 特性の概念比較
同期レプリケーション vs 非同期レプリケーション 特性の概念比較

プライマリの設定(3ステップ)

プライマリでは主に2つのファイルを編集します。postgresql.conf(WAL設定)と pg_hba.conf(レプリケーション接続の許可)です。

手順1:postgresql.conf の WAL 設定を変更する

PostgreSQL 16 では wal_level はデフォルトで replica になっています(実測した boot_val でも確認できました)。一方 wal_keep_size はデフォルト 0 なので、レプリカが一時的に接続を失っても古い WAL が消えないよう、64MB 程度を確保しておきます。実際に設定した値とデフォルト値の対応は次のとおりです。

PostgreSQL レプリケーション設定パラメータ一覧(実測値)
PostgreSQL レプリケーション設定パラメータ一覧(実測値)



ubuntu@primary: /etc/postgresql/16/main
# postgresql.conf に以下を設定(apt版なら # を外して値を変更)
listen_addresses = ‘localhost’ # 本番はレプリカが届くIP / ‘*’
wal_level = replica
max_wal_senders = 5
wal_keep_size = 64
synchronous_commit = on
$ sudo -u postgres psql -x -c “SELECT name, setting, unit, boot_val FROM pg_settings WHERE name IN (‘wal_level’,’max_wal_senders’,’wal_keep_size’);”
-[ RECORD 1 ]————–
name | max_wal_senders
setting | 5
boot_val | 10
-[ RECORD 2 ]————–
name | wal_keep_size
setting | 64
unit | MB
boot_val | 0
-[ RECORD 3 ]————–
name | wal_level
setting | replica
boot_val | replica

手順2:pg_hba.conf にレプリケーション接続を許可する

デフォルトの pg_hba.conf にはレプリカからの接続設定がありません。以下の1行を追加します。127.0.0.1/32 の部分は、本番ではレプリカのIPアドレス範囲(例 192.168.0.0/24)に合わせてください。




ubuntu@primary: /etc/postgresql/16/main
# pg_hba.conf の末尾に追加(検証は同一ホストなので 127.0.0.1/32・trust で実行)
$ echo “host replication replicator 127.0.0.1/32 scram-sha-256” >> pg_hba.conf
# 本番では scram-sha-256(パスワード認証)を推奨。ローカル検証なら trust でも可
$ tail -1 pg_hba.conf
host replication replicator 127.0.0.1/32 scram-sha-256

手順3:レプリケーション用ユーザーを作成して再起動する

レプリケーション専用ロールを作成します。REPLICATION 権限を持つユーザーだけが WAL を取得できます。設定変更後はプライマリを再起動して反映させます。




ubuntu@primary: ~
$ sudo -u postgres psql -c “CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD ‘repl_pass’;”
CREATE ROLE
$ sudo -u postgres psql -c “\du replicator”
List of roles
Role name | Attributes
————+————-
replicator | Replication
$ sudo -u postgres psql -c “SHOW wal_level;”
wal_level
———–
replica
(1 row)
プライマリのWAL設定とpg_stat_replicationの確認(Playwright実撮影)
プライマリのWAL設定とpg_stat_replicationの確認(Playwright実撮影)

pg_basebackupでレプリカを作成する

pg_basebackup はプライマリのデータをまるごとコピーして、レプリカの初期データを作るツールです。-R フラグが非常に重要で、これを付けるだけで standby.signal ファイルと postgresql.auto.conf への primary_conninfo 設定が自動生成されます。昔のように recovery.conf を手で書く必要がありません。

pg_basebackupの実行ログ(実測)
pg_basebackupの実行ログ(実測)



ubuntu@replica: ~
# レプリカ側で実行(プライマリのデータをコピー)
$ pg_basebackup -h 127.0.0.1 -p 5432 -U replicator \
-D /var/lib/postgresql/data -Fp -Xs -R -P -v
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/2000028 on timeline 1
pg_basebackup: starting background WAL receiver
30704/30704 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/2000138
pg_basebackup: base backup completed
$ ls -la /var/lib/postgresql/data/standby.signal
-rw——- 1 postgres postgres 0 Jun 14 03:38 standby.signal ← -R で自動作成
$ grep primary_conninfo /var/lib/postgresql/data/postgresql.auto.conf
primary_conninfo = ‘user=replicator … host=127.0.0.1 port=5432 …’

各オプションの意味をおさらいします。

  • -Fp:プレーンフォーマット(通常のディレクトリとして出力)
  • -Xs:バックアップ中の WAL もストリーミングで取得(-X stream と同じ)
  • -Rstandby.signalprimary_conninfo を自動生成
  • -P:進捗を表示
  • -v:詳細なログを表示

レプリカの起動

ベースバックアップが終わったら、レプリカのデータディレクトリを指して PostgreSQL を起動するだけです。standby.signal が存在するため、PostgreSQL は自動的にスタンバイモードで起動します。実測では、起動ログに「entering standby mode」「started streaming WAL from primary at 0/3000000 on timeline 1」が出力され、ストリーミングが始まったことを確認できました。




ubuntu@replica: ~
# standby.log(起動直後の抜粋)
LOG: entering standby mode
LOG: consistent recovery state reached at 0/2000138
LOG: database system is ready to accept read-only connections
LOG: started streaming WAL from primary at 0/3000000 on timeline 1
$ sudo -u postgres psql -p 5433 -c “SELECT pg_is_in_recovery();”
pg_is_in_recovery
——————-
t
(1 row) ← t = true、スタンバイ(リカバリ)モードで動作中

レプリケーション確立の確認

レプリケーションが正しく動いているかはプライマリ側の pg_stat_replication ビューで確認します。今回の実測では、state=streamingsync_state=asynclag_bytes=0 を確認できました。client_addr は同一ホスト検証のため 127.0.0.1 になっています(本番ではレプリカのIPが表示されます)。

pg_stat_replicationの出力(実測)
pg_stat_replicationの出力(実測)



ubuntu@primary: ~
$ sudo -u postgres psql -x -c “SELECT client_addr, application_name, state, sent_lsn, replay_lsn, sync_state FROM pg_stat_replication;”
-[ RECORD 1 ]—-+————
client_addr | 127.0.0.1
application_name | walreceiver
state | streaming
sent_lsn | 0/3002290
replay_lsn | 0/3002290
sync_state | async
$ sudo -u postgres psql -c “SELECT pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes FROM pg_stat_replication;”
lag_bytes
———–
0
(1 row) ← 遅延 = 0バイト(完全に追いついている)

各カラムの意味は次のとおりです。

  • state = streaming:WALをリアルタイムでストリーミング中(正常)
  • sent_lsn = replay_lsn:送信済みの位置とレプリカが適用済みの位置が一致(遅延なし)
  • sync_state = async:非同期モードで動作中(デフォルト)

データが実際に複製されるか確認する

仕上げに、プライマリに3行 INSERT したデータがスタンバイに反映されるかを見てみます。実測では test_row_001003 の3行が、スタンバイ側(ポート5433)で即座に SELECT できました。




ubuntu@primary: ~
$ sudo -u postgres psql -d repl_demo -c “INSERT INTO events (msg) VALUES (‘test_row_001’),(‘test_row_002’),(‘test_row_003’);”
INSERT 0 3



ubuntu@replica: ~ (ポート5433)
$ sudo -u postgres psql -p 5433 -d repl_demo -c “SELECT id, msg FROM events ORDER BY id;”
id | msg
—-+————–
1 | test_row_001
2 | test_row_002
3 | test_row_003
(3 rows) ← プライマリの INSERT がスタンバイに即時反映
スタンバイ側のpg_is_in_recovery確認と複製済みデータ(Playwright実撮影)
スタンバイ側のpg_is_in_recovery確認と複製済みデータ(Playwright実撮影)

同期モードへの切り替え

デフォルトの非同期モードはスループットが高い反面、フェイルオーバー時に未転送の WAL が失われるリスクがあります。金融・決済システムなどデータ損失が許容できない用途では同期モードを使います

同期モードへの切り替え方法

プライマリで synchronous_standby_names にレプリカの application_name を指定するだけです。pg_stat_replication に表示された application_name-R で作成したレプリカのデフォルトは walreceiver)を使います。実測では、設定リロード後に sync_stateasync から sync に変わりました。




ubuntu@primary: ~
# ALTER SYSTEM で設定(postgresql.auto.conf に書き込まれる)
$ sudo -u postgres psql -c “ALTER SYSTEM SET synchronous_standby_names = ‘walreceiver’;”
ALTER SYSTEM
$ sudo -u postgres psql -c “SELECT pg_reload_conf();”
pg_reload_conf
—————-
t
$ sudo -u postgres psql -c “SELECT application_name, sync_state FROM pg_stat_replication;”
application_name | sync_state
——————+————
walreceiver | sync
(1 row) ← async から sync に変わった

注意:同期モードの落とし穴

同期モードでスタンバイが停止すると、プライマリの書き込みもブロックされます。スタンバイがダウンしたままだと COMMIT が返らなくなり、アプリケーションが止まります。本番では複数スタンバイの構成(ANY 1 (replica1, replica2) 記法)でフォールトトレランスを確保してください。

非同期に戻す方法




ubuntu@primary: ~
$ sudo -u postgres psql -c “ALTER SYSTEM SET synchronous_standby_names = ”;”
ALTER SYSTEM
$ sudo -u postgres psql -c “SELECT pg_reload_conf();”
t
# 即座に非同期モードに戻る(サービス再起動は不要)

よくあるエラーと対処法

①pg_basebackup: no pg_hba.conf entry for replication connection

プライマリへのレプリケーション接続が拒否された場合です。pg_hba.confreplication の行が無いか、追加後にリロードしていないのが原因です。




ubuntu@replica: ~
pg_basebackup: error: connection to server … failed: FATAL: no pg_hba.conf entry for replication connection from host “…”, user “replicator”
# 対処: pg_hba.conf に replication 行を追加してプライマリを reload する
$ sudo -u postgres psql -c “SELECT pg_reload_conf();”
t

②FATAL: requested WAL segment has already been removed

レプリカが長時間接続を失い、プライマリが古い WAL を削除してしまった場合に発生します。wal_keep_size を増やすか、レプリケーションスロットを使って WAL を保持させます。




ubuntu@primary: ~
# レプリケーションスロットを作成(WALを保持し続ける)
$ sudo -u postgres psql -c “SELECT * FROM pg_create_physical_replication_slot(‘replica_slot’);”
replica_slot |
# レプリカ側 postgresql.auto.conf に以下を追記して再起動
primary_slot_name = ‘replica_slot’

注意:レプリケーションスロットのディスク枯渇リスク

レプリケーションスロットは、レプリカが接続するまで WAL をディスクに保持し続けます。レプリカがダウンしたまま放置すると、プライマリのディスクが枯渇します。スロット使用時は max_slot_wal_keep_size で上限を設定してください(PostgreSQL 13以降)。

③pg_stat_replication が空(行がない)

プライマリに接続できているはずなのに pg_stat_replication が空の場合、wal_levelminimal になっている可能性があります。設定変更後のプライマリ再起動(リロードではなく restart)が必要です。




ubuntu@primary: ~
$ sudo -u postgres psql -c “SHOW wal_level;”
minimal ← これが原因。postgresql.conf を replica にして再起動
$ sudo systemctl restart postgresql
$ sudo -u postgres psql -c “SHOW wal_level;”
replica

まとめ

今回は ubuntu:24.04 公式イメージ(PostgreSQL 16.14)の Primary + Standby 構成でストリーミングレプリケーションを構築し、pg_stat_replicationstate=streaminglag_bytes=0 を確認しました。プライマリへの INSERT がスタンバイに即時複製されることも実データで確認できました。

  • プライマリの設定は wal_levelmax_wal_senderswal_keep_size の3点が核心
  • pg_basebackup -Fp -Xs -R-R フラグで初期セットアップが大幅に簡略化される(standby.signalprimary_conninfo が自動生成)
  • pg_stat_replicationstate で接続状態、pg_wal_lsn_diff(sent_lsn, replay_lsn) で遅延を確認
  • 同期モードは synchronous_standby_names の1行で切り替え可能(サービス再起動不要)
  • 同期モードはスタンバイ停止でプライマリの書き込みがブロックされる点に注意

同じ手順は VPS の2台構成でもそのまま使えます。学習用に PostgreSQL のレプリケーションを試すなら、東京リージョンで月5ドルから借りられる Vultr が手軽でおすすめです。スナップショットで何度でも作り直せるので、失敗を恐れずに練習できます。

著者アイコン
著者アイコン

正直、pg_basebackup-R フラグはかなり便利です。昔は recovery.conf(PostgreSQL 12以前)に手動で standby_mode = on を書いていましたが、いまは standby.signal という空ファイルが置かれるだけで済みます。実際、検証でも 0バイトstandby.signal が自動生成されていました。

PostgreSQL を VPS で本番運用してみたい方は、サーバー構築の基礎記事も参考にしてみてください。

[LINK_X_ARTICLE]

コメント

タイトルとURLをコピーしました