noellabo's tech blog

@noellaboの技術ブログ

予備のサーバにPostgreSQLをレプリケーションしよう

Mastodon管理者のためのレプリケーション

レプリケーションのススメ

Mastodonサーバ管理者(鯖缶)のみなさん、レプリケーションしてますか?

ざっくり言うと、

  1. 元のPostgreSQLの設定にレプリケーションに必要な設定項目を追加する
  2. レプリケーション用のユーザーを作って、外から繋げられるように許可する
  3. レプリケーション先のサーバで、pg_basebackupでデータベースをコピーする
  4. レプリケーション先のサーバを起動すると、同期を開始する(完了!)

という感じです。簡単でしょ?

何をしているか

  • PostgreSQLは、データベースファイルに対する変更を実際に反映する前に、WAL(Write Ahead Logging)という変更内容のログを書き出している
  • この仕組みにより、突然クラッシュした場合でも、データベースを正しい状態に保ちながら、安全に変更を反映して再開できる
  • ある時点のデータベースファイルを保存しておいて、そこにWALを順次適用していけば、最新のデータベースを再現できる
  • レプリケーションでは、WALをマスターからスタンバイに転送して、それをスタンバイ側で適用することで、同一のデータベースの複製を作っている
  • 通常のWALだとレプリケーションに必要な情報が足りないので、少し内容を充実させたWALを書き出すように変更が必要
  • スタンバイの最初のデータベースの複製はpg_basebackupでネットワーク越しに簡単にできる。recovery.confも自動的に作成される
  • スタンバイは、recovery.confの指示にしたがって、リカバリをし続けている状態
  • そのときが来たら、レプリケーションを終了して、こちらを本番サーバ(マスター)に昇格できる pg_ctl promote
  • WALをアーカイブしておくことで、任意の時点を再現(復元)することができる。別の場所に保存するとより強固になる
    参考:WAL-GでオブジェクトストレージにPostgreSQLをバックアップしよう

メリット

  • 待機(スタンバイ)サーバが実現でき、データベースサーバにトラブルがあった際に最短時間でサービスを復旧できる
  • サーバを移転する際も、ギリギリまでサービスを継続しておいて、安全に最短時間で切り替えができる
  • スタンバイに対して、参照のみのアクセスが可能なので、書き込みの不要なアクセスをそちらに振り向けることで負荷分散が可能
    • mastodon-streaming(タイムラインのリアルタイム更新通知)は、読み取りだけでOK

設定手順

postgresql.confに設定する

  • listen_addressespg_hba.condでアクセス制限を行う前提で*に設定しているが、localhost,xxx.xxx.xxx.xxxなどと具体的に書いても良い
  • hot_standby = onはレプリケーション先に行う設定だが、後ほどpg_basebackupする際にコピーされて有効になるのでここに書いておくと楽
# for replication master server
listen_addresses = '*'
synchronous_commit = off
wal_level = replica
max_wal_senders = 3

# for replication standby server
hot_standby = on

replication_userを作る

パスワードを決めて、わかるようにしておくこと

sudo -u postgres psql -c "CREATE ROLE replication_user LOGIN REPLICATION PASSWORD 'xxxxxxxxx'";

(xxxxxxxxxの部分にパスワードを指定)

pg_hba.confに追記

host    replication     replication_user xxx.xxx.xxx.xxx/32     md5
host    replication     replication_user xxxx:xxxx::xxxx/128     md5

(xxx.xxx.xxx.xxxの部分にレプリケーション先のサーバのIPv4アドレスを指定) (xxxx:xxxx::xxxxの部分にレプリケーション先のサーバのIPv6アドレスを指定)

ここで一度PostgreSQLを再起動する。

sudo -u postgres pg_ctl restart

ファイアウォールで接続を許可

PostgreSQLのポートに、レプリケーション先のサーバから接続できるように許可する。ここでは5432で実行しているものと仮定する。

firewall-cmdufwの例をあげておく。

firewall-cmd

firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=xxx.xxx.xxx.xxx/32 port port=5432 protocol=tcp accept' --permanent
firewall-cmd --zone=public --add-rich-rule='rule family=ipv6 source address=xxxx:xxxx::xxxx/128 port port=5432 protocol=tcp accept' --permanent
firewall-cmd --reload

ufw

ufw allow from xxx.xxx.xxx.xxx to any port 5432 proto tcp
ufw allow from xxxx:xxxx::xxxx to any port 5432 proto tcp

空のクラスタを作成しておく

レプリケーション先となるサーバで、PostgreSQLをインストールして、データディレクトリを空にしておく。PostgreSQLは同じバージョンを使用すること。(違うバージョンでレプリケーションする方法もあるが、ここでは説明しない)

だいたい/var/lib/pgsql/9.2/dataとか/var/lib/postgresql/11/mainとか。

今回の目的以外でPostgreSQLを使っていないなら、実行を止めて、削除を行う。 既に利用中の場合は、新しいクラスタを作成すること。

sudo -u postgres pg_ctl stop
sudo -u postgres rm -rf /var/lib/postgresql/11/main

pg_basebackupでまるごと複製してくる

レプリケーション先となるサーバから実行。

ここでの xxx.xxx.xxx.xxx はマスターのIPアドレス

umask 0077
sudo -u postgres pg_basebackup -h xxx.xxx.xxx.xxx -D /var/lib/postgresql/11/main -U replication_user -R -P
umask 0002

設定に問題がなければ、接続して複製が開始される。

5432でサービスを実行していないなどと言われる場合は、pg_hba.confの設定、ファイアウォールの設定などで、マスターに接続できる条件が整っていない。

postgresql.confに設定する

レプリケーション先となるサーバのpostgresql.confにホットスタンバイの設定を追記する。

Debian / Ubuntu などではデータディレクトリ内のpostgresql.confではなく/etc/postgresql/11/main/postgresql.conf(ディレクトリ名はバージョンとクラスタ名による)が使われるので、ホットスタンバイに限らず、必要な設定は移行しておくこと。

# for replication standby server
hot_standby = on

ついでにpg_hba.confも見直しておく

前項のようにデータディレクトリの中のpg_hba.confを読み込むとは限らない。/etc/postgresql/〜の場合はそちらにコピーして今後はそちらで管理するようにするか、下記を参照して設定し直すこと。

ここでは、サーバ移転(引っ越し)や、待機サーバを本番に昇格することを想定し、Mastodonの実行に必要な設定と、マスターとしてレプリケーションを受け付けるための設定を合わせて記載している。

なお、データベース名はmastodon_productionとしているが、Mastodonの設置が初期の頃の場合はmastodonだったりするので、/home/mastodon/live/.env.productionDB_NAMEを参照するなどして自分の環境に合わせて設定すること。

  • postgresユーザーがソケットにより接続した場合だけ色々変更できるようにする。外部からは許可しない
  • mastodonユーザーがローカルのTCP接続でパスワード認証した場合にだけ、mastodon_productionデータベースへ接続を許可する
  • replication_userがレプリケーション先のサーバからTCP接続でパスワード認証した場合にだけ、レプリケーション(同期とかバックアップ)を許可する
  • 初期設定はゆるゆるなので、できるだけ厳しく設定して、必要なものだけ許可する
    • TYPE localはソケット接続なので、同一マシンからの接続専用
    • TYPE hostはTCP接続になるので、接続元のIPアドレス指定次第で外部からも接続できる
      • 同一マシンからの接続(loalhost)は、IPv4では127.0.0.1/32、IPv6では::1/128と書く
    • METHODpeerにすると、ログインしているUNIXユーザーとPostgreSQLユーザーが一致すれば認証する
    • METHODmd5では指定したユーザーを(md5でハッシュにした)パスワードで認証する
    • METHODtrustは誰でも信用して全部許可しちゃうので論外
# TYPE  DATABASE            USER             ADDRESS             METHOD
local   all                 postgres                             peer
host    mastodon_production mastodon         127.0.0.1/32        md5
host    mastodon_production mastodon         ::1/128             md5
host    replication         replication_user xxx.xxx.xxx.xxx/32  md5
host    replication         replication_user xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/128  md5
_EOS_

レプリケーション先のサーバを起動する

ここまで来たら、あとは起動するだけで完了。自動的にrecovery.confに基づいて、マスターからWALを受け取り、レプリケーションを開始する。

sudo -u postgres pg_ctl start

本番サーバに切り替える

障害の時は不要だが、マスターがまだ動いている場合は停止する。

マスターと同期の完了したスタンバイを、本番サーバに切り替える。

sudo -u postgres pg_ctl promote

切り替えが完了すると、recovery.confrecovery.doneにリネームされる。

余談

  • pg_basebackupが優秀なので、コイツを使って、手元のマシンにバックアップだけとるという手もある。
  • レプリケーションは複数ぶら下げたり、多段にカスケードすることもできる。
  • 今回、スタンバイの更新を待って動作する同期レプリケーション、待たない非同期、WALがシンクロするのを待つか、など細かい設定については省略したので、各自で調べられたし。
  • バージョン違いでレプリケーションしたい場合は、ロジカルレプリケーションという方法がある。

参考資料

アーキテクチャから理解するPostgreSQLのレプリケーション