【pgBackRest 紹介】PostgreSQL のバックアップを取ってみよう

こんにちは。サイオステクノロジーの橋本です。
今回は PostgreSQL のバックアップ管理ツールである pgBackRest を紹介します。

コミュニティページ : pgBackRest

pgBackRest は非常に多機能で細かい制御ができる反面、
設定項目が多い点と日本語ドキュメントが少ないため、導入のハードルが高いかもしれません。

今回は少し冗長ですが、機能や設定、扱い方を詳細に説明します。
最後まで読んでいただければ、インストール~バックアップ取得~リストアまでできるレベルで解説します。

〇目次〇
用語説明
できること、機能紹介
要件
今回の環境
インストール
 - PostgerSQL の設定
 - pgBackRestの設定
 - stanza の作成とチェック
バックアップ取得
バックアップの世代管理について
リポジトリのディレクトリ構造
リストアについて
pgBackRest の欠点

〇用語説明〇
pgBackRest では 2 つの用語があります。
本記事でもその用語を用いて説明を進めていきます。

・stanza (スタンザ)
 pgBackRest をインストールしたサーバが1台で複数台の Postgres サーバのバックアップ取得が可能です。
 また、それぞれの Postgres サーバごとに細かくバックアップ内容を変えることが可能です。
 Postgres サーバのバックアップ方法などを定義した内容を stanza といいます。
 *stanza は一区切りを示す単語で日本語だと節や回が近いニュアンスらしいです。

・リポジトリ
 ディレクトリ、オブジェクトストレージといったバックアップを保存する場所、
 WALセグメントをアーカイブする場所を合わせてリポジトリと言います。

・アーカイブ WAL
 pgbackrest の用語ではないですが、本記事ではarchive_commandで退避されたWAL ファイルを
 全てアーカイブ WAL と呼んでいます。

〇できること、機能紹介〇
pgBackRest は非常に高機能で細かい制御が可能です。
反面、パラメータが多いため設定の理解に時間がかかるかもしれません。

pgBackRest でできることを主なことは以下になります。
☆マークがついているものは、後程実際に検証します。

– 取得できるバックアップ種別
 ・完全バックアップ ☆
 ・増分バックアップ ☆
 ・差分バックアップ ☆

– 取得できるバックアップ
 ・リモートバックアップ ☆
 ・ローカルバックアップ
 ・アーカイブ WAL ファイル ☆
  *アーカイブされていない WAL ファイルはバックアップされません

– 機能 複数の PostgreSQL サーババックアップ ☆
 ・バックアップコンテンツの圧縮 ☆
  バックアップコンテンツそのものを gzip 圧縮します。
 ・バックアップコンテンツの並列圧縮 ☆
  複数コアを用いて並列的にファイル圧縮が可能です。
 ・バックアップの保持期限設定 (バックアップローテション) ☆
  それぞれのバックアップの保持期限や何世代保持するか設定が可能です。
 ・バックアップ整合性検証
  バックアップ最中にファイルのチェックサムの検証が可能です。
 ・バックアップの中断と再開
 ・デルタ復元
  DB の復元時、Postgresサーバに乗り込んでDBコンテンツを手動で削除する必要はないです。
 ・S3、Azure、およびGCS互換のオブジェクトストアのサポート
  Azure
  S3
  GCS

 ・リポジトリ(バックアップそのもの)の暗号化
 

〇要件〇
細かい要件は省略しますが、以下のポイントは特に気を付ける必要があります。

 pgBackRest サーバから PostgreSQL サーバに対しパスワードなしで SSH ログインできること
 PostgreSQL サーバから pgBackRest サーバに対しパスワードなしで SSH ログインできること
 PostgreSQL サーバにも pgBackRest がインストールされていること

SSH ログイン設定の話は、SSH の話なので今回は割愛します。
設定方法はコミュニティマニュアルを参考にしてください。

〇今回の環境〇
ここからは実際にコマンドや設定、バックアップの取得を例示します。
前提として今回は以下の環境で検証しました。

OS や kernel , Pgbackrest のバージョンは全サーバ共通です。

OS CentOS 7.7.1908
kernel kernel-3.10.0-1062.12.1.el7.x86_64
Pgbackrest pgbackrest-2.36-1.rhel7.x86_64

PostgerSQL サーバは以下の2つバージョンのサーバを用意しました。

PostgreSQL postgresql14-14.1-1PGDG.rhel7.x86_64
postgresql10-10.19-1PGDG.rhel7.x86_64
PGDATA /pgdb/data
PostgreSQL プロセスオーナー pg-user

また、前提として 1秒に 1レコード、現在時刻を挿入するスクリプトを実装しています。
これを PostgreSQL のリストア時に、いつの時点にリカバリができたか判断に利用します。

〇インストール〇
PostgreSQL のリポジトリで配布されています
そのため、以下の 2 コマンドでインストールが可能です。

yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum -y install pgbackrest

– pgBackRestの設定

pgBackRest は非常に柔軟な設定が可能です。
主に 4 箇所で設定、指定が可能です。

1: デフォルト値
2: 設定ファイルの global セクション
3: 設定ファイルの各 stanza 内
4: コマンド実行時の付与するオプション

1 の設定が最も弱く、数字が大きい箇所の設定ほど優先 (上書き) されます。

・設定サンプル (/etc/pgbackrest.conf)

[server1]
pg1-host = server1    # ←サーバを指定
pg1-path = /pgdb/data   # ← $PGDATAを指定
pg1-host-user = pg-user # ←ユーザを指定

repo1-retention-full = 2 # ←保持する完全バックアップの数を指定
start-fast = y           # ←チェックポイントを高速に実行する
compress-level = 9    # ←ファイルの圧縮レベルを指定
log-level-file = detail # ←ファイルに出力するログレベルを指定

[server2]
pg1-host = server2
pg1-host-user = pg-user
pg1-path = /pgdb/data

[global]
repo1-path = /var/lib/pgbackrest # ←バックアップとアーカイブが保存されるパス。
log-level-file = warn

上記は設定例をサンプルに説明します。
server1 と server2 の 2 つのサーバのバックアップが設定されています。
pgbackrest の言葉で言うと stanza が 2 つあるわけです。

オプション log-level-file に着目して設定の優先度について説明します。
log-level-file はファイルに出力するログレベルを指定します。

デフォルト値は info です。
つまり設定ファイルに log-level-file がない場合は info レベルのログが出力されます。

上記例では global セクションに log-level-file の設定があります。
server2 の stanza では log-level-file の設定はないため、
global セクションの log-level-file 設定が引き継がれます。

対し、server1 の stanza では log-level-file は detail が指定されます。
そのため server1 では detail レベルのログが出力されるわけです。

コマンド実行時にもオプションを付与することができます。
そして、コマンド実行時に付与したオプションが最も優先されます。

究極的には設定ファイルは編集せず、
すべてコマンド実行時のオプションとして付与することでバックアップを取得することもできます。
しかし、コミュニティは基本的に設定ファイル内部で諸々設定することが推奨しています。

– PostgerSQL の設定
アーカイブされたログを保存するために以下のように設定します。
*<stanza name>は適宜変える必要があります

archive_mode = on
archive_command = 'pgbackrest --stanza=<stanza name> archive-push %p'

この設定により WAL ファイルをアーカイブする際リモートリポジトリにアーカイブしてくれます。

PostgreSQL は PITR するためには WAL ファイルとアーカイブ WAL が必要になります。
この設定をすることでアーカイブ WAL はリモートサーバである pg_backrest サーバに保管してくれます。

– pgBackRest の設定
pgBackRest サーバの設定
設定ファイル : /etc/pgbackrest.conf

[server1]
pg1-host = server1
pg1-path = /pgdb/data
pg1-host-user = pg-user

repo1-retention-full = 2
start-fast = y
compress-level = 9
log-level-file = detail

[server2]
pg1-host = server2
pg1-host-user = pg-user
pg1-path = /pgdb/data

[global]
repo1-path = /var/lib/pgbackrest
log-level-file = warn

server1 の設定
設定ファイル : /etc/pgbackrest.conf

[server1]
pg1-path=/pgdb/data

[global]
repo1-path=/var/lib/pgbackrest
repo1-host=backup_server
repo1-host-user=pg-user

server2 の設定
設定ファイル : /etc/pgbackrest.conf

[server2]
pg1-path=/pgdb/data

[global]
repo1-path=/var/lib/pgbackrest
repo1-host=backup_server
repo1-host-user=pg-user

– stanza の作成
デフォルトではコンソールには何も表示されません。

$ pgbackrest --stanza=server1 stanza-create
$ pgbackrest --stanza=server2 stanza-create

– 設定内容のチェック
以下のコマンドで設定に誤りがないかチェックできます。
PostgreSQL サーバおよび pgBackRest サーバで実行しましょう。

$ pgbackrest --stanza=server1 check
$ pgbackrest --stanza=server2 check

このコマンドもデフォルトでは何も表示されません。
ちょっと不安なので、もっとたくさんログを表示させてみましょう

$ pgbackrest --stanza=server1 --log-level-console=info check
2021-12-07 14:56:25.861 P00 INFO: check command begin 2.36: --exec-id=2488-96ce3a49 --log-level-console=info --log-level-file=detail --pg1-host=server1 --pg1-host-user=pg-user --pg1-path=/pgdb/data --repo1-path=/var/lib/pgbackrest --stanza=server1
2021-12-07 14:56:26.625 P00 INFO: check repo1 configuration (primary)
2021-12-07 14:56:26.830 P00 INFO: check repo1 archive for WAL (primary)
2021-12-07 14:56:27.231 P00 INFO: WAL segment 000000010000000000000040 successfully archived to '/var/lib/pgbackrest/archive/server1/14-1/0000000100000000/000000010000000000000040-b05178b362a3433ca05965aef1e664b6360b380d.gz' on repo1
2021-12-07 14:56:27.334 P00 INFO: check command end: completed successfully (1474ms)
$

コンソールに出力されるログレベルは warn ですが、
コマンド実行時に info レベルを指定することで詳細なログを出力させています。

successfully と出てくれたので安心感がありますね。

〇バックアップの取得〇
以下のコマンドを pgBackRest サーバで実行することでバックアップ取得が可能です。
pgbackrest –stanza=<stanza name> –type=full backup

ですが、初回なのでちょっと詳細にログを出力してみましょう。

$ pgbackrest --stanza=server1 --log-level-console=info --type=full backup
2021-12-07 15:02:39.789 P00 INFO: backup command begin 2.36: --compress-level=9 --exec-id=2536-7156e055 --log-level-console=info --log-level-file=detail --pg1-host=server1 --pg1-host-user=pg-user --pg1-path=/pgdb/data --repo1-path=/var/lib/pgbackrest --repo1-retention-diff=1 --repo1-retention-full=2 --stanza=server1 --start-fast --type=full
2021-12-07 15:02:40.656 P00 INFO: execute non-exclusive pg_start_backup(): backup begins after the requested immediate checkpoint completes
2021-12-07 15:02:41.172 P00 INFO: backup start archive = 000000010000000000000043, lsn = 0/43000028
2021-12-07 15:02:56.879 P00 INFO: execute non-exclusive pg_stop_backup() and wait for all WAL segments to archive
2021-12-07 15:02:57.081 P00 INFO: backup stop archive = 000000010000000000000043, lsn = 0/43003BF8
2021-12-07 15:02:57.086 P00 INFO: check archive for segment(s) 000000010000000000000043:000000010000000000000043
2021-12-07 15:02:57.403 P00 INFO: new backup label = 20211207-150241F
2021-12-07 15:02:57.439 P00 INFO: full backup size = 46.8MB, file total = 1259
2021-12-07 15:02:57.439 P00 INFO: backup command end: completed successfully (17651ms)
2021-12-07 15:02:57.439 P00 INFO: expire command begin 2.36: --exec-id=2536-7156e055 --log-level-console=info --log-level-file=detail --repo1-path=/var/lib/pgbackrest --repo1-retention-diff=1 --repo1-retention-full=2 --stanza=server1
2021-12-07 15:02:57.444 P00 INFO: expire command end: completed successfully (5ms)
$
$ pgbackrest --stanza=server2 --log-level-console=info --type=full backup
2021-12-07 15:04:00.120 P00 INFO: backup command begin 2.36: --exec-id=2579-64b21d1d --log-level-console=info --log-level-file=warn --pg1-host=server2 --pg1-host-user=pg-user --pg1-path=/pgdb/data --repo1-path=/var/lib/pgbackrest --repo1-retention-diff=1 --repo1-retention-full=2 --stanza=server2 --type=full
2021-12-07 15:04:00.987 P00 INFO: execute non-exclusive pg_start_backup(): backup begins after the next regular checkpoint completes
2021-12-07 15:04:01.512 P00 INFO: backup start archive = 000000010000000000000009, lsn = 0/9000028
2021-12-07 15:04:06.500 P00 INFO: execute non-exclusive pg_stop_backup() and wait for all WAL segments to archive
2021-12-07 15:04:06.711 P00 INFO: backup stop archive = 000000010000000000000009, lsn = 0/9000130
2021-12-07 15:04:06.723 P00 INFO: check archive for segment(s) 000000010000000000000009:000000010000000000000009
2021-12-07 15:04:07.039 P00 INFO: new backup label = 20211207-150401F
2021-12-07 15:04:07.072 P00 INFO: full backup size = 23.3MB, file total = 952
2021-12-07 15:04:07.072 P00 INFO: backup command end: completed successfully (6953ms)
2021-12-07 15:04:07.072 P00 INFO: expire command begin 2.36: --exec-id=2579-64b21d1d --log-level-console=info --log-level-file=warn --repo1-path=/var/lib/pgbackrest --repo1-retention-diff=1 --repo1-retention-full=2 --stanza=server2
2021-12-07 15:04:07.083 P00 INFO: expire command end: completed successfully (11ms)
$

server1、server2 ともに問題なくバックアップを取得できたみたいです。

取得したバックアップの内容は以下のコマンドで確認可能です
pgbackrest –stanza=<stanza name> info

$ pgbackrest --stanza=server1 info
stanza: server1
status: ok
cipher: none

db (current)
wal archive min/max (14): 000000010000000000000037/000000010000000000000043

full backup: 20211207-150241F
timestamp start/stop: 2021-12-07 15:02:41 / 2021-12-07 15:02:57
wal start/stop: 000000010000000000000043 / 000000010000000000000043
database size: 46.8MB, database backup size: 46.8MB
repo1: backup set size: 5.7MB, backup size: 5.7MB
$
$ pgbackrest --stanza=server2 info
stanza: server2
status: ok
cipher: none

db (current)
wal archive min/max (10): 000000010000000000000004/000000010000000000000009

full backup: 20211207-150401F
timestamp start/stop: 2021-12-07 15:04:01 / 2021-12-07 15:04:06
wal start/stop: 000000010000000000000009 / 000000010000000000000009
database size: 23.3MB, database backup size: 23.3MB
repo1: backup set size: 2.7MB, backup size: 2.7MB
$

〇バックアップの世代管理について〇

完全バックアップの取得ができたので差分バックアップを取得してみましょう

$ pgbackrest --stanza=server1 --type=diff backup
$ pgbackrest --stanza=server1 --type=diff backup

連続して2 回実行しました。バックアップを確認してみましょう。

$ pgbackrest --stanza=server1 info
stanza: server1
status: ok
cipher: none

db (current)
wal archive min/max (14): 000000010000000000000049/000000010000000000000051

full backup: 20211207-151931F
timestamp start/stop: 2021-12-07 15:19:31 / 2021-12-07 15:19:47
wal start/stop: 00000001000000000000004B / 00000001000000000000004B
database size: 46.9MB, database backup size: 46.9MB
repo1: backup set size: 5.7MB, backup size: 5.7MB

diff backup: 20211207-151931F_20211207-152024D
timestamp start/stop: 2021-12-07 15:20:24 / 2021-12-07 15:20:30
wal start/stop: 000000010000000000000051 / 000000010000000000000051
database size: 46.9MB, database backup size: 11MB
repo1: backup set size: 5.7MB, backup size: 1.4MB
backup reference list: 20211207-151931F
$

しかし、どうも差分バックアップは 1回しかとられていないようです。

「/etc/pgbackrest.conf」内で「repo1-retention-diff = 1」と定義しているため、
1 世代しか保存せず、古い世代は自動で削除してくれたわけです。

なお、増分バックアップは –type=incr を指定することで取得可能です。

$ pgbackrest --stanza=server1 --type=incr backup
$
$ pgbackrest --stanza=server1 info
stanza: server1
status: ok
cipher: none

db (current)
wal archive min/max (14): 000000010000000000000049/000000010000000000000051

full backup: 20211207-151931F
timestamp start/stop: 2021-12-07 15:19:31 / 2021-12-07 15:19:47
wal start/stop: 00000001000000000000004B / 00000001000000000000004B
database size: 46.9MB, database backup size: 46.9MB
repo1: backup set size: 5.7MB, backup size: 5.7MB

diff backup: 20211207-151931F_20211207-152024D
timestamp start/stop: 2021-12-07 15:20:24 / 2021-12-07 15:20:30
wal start/stop: 000000010000000000000051 / 000000010000000000000051
database size: 46.9MB, database backup size: 11MB
repo1: backup set size: 5.7MB, backup size: 1.4MB
backup reference list: 20211207-151931F

incr backup: 20211207-151931F_20211207-152522I
timestamp start/stop: 2021-12-07 15:25:22 / 2021-12-07 15:25:28
wal start/stop: 000000010000000000000053 / 000000010000000000000053
database size: 46.9MB, database backup size: 11.1MB
repo1: backup set size: 5.7MB, backup size: 1.4MB
backup reference list: 20211207-151931F
$

増分バックアップ、差分バックアップともに紐づいている完全バックアップが削除されたら、
併せて削除されます。pgbackrest 側で柔軟にやってくれるのはうれしいポイントです

〇リポジトリのディレクトリ構造〇
リポジトリの構造は以下のようになっています。
なお、通常運用する中ではバックアップされたコンテンツを直接見るケースはほとんどないと思います。

それぞれの stanza ごとにディレクトリを作ってくれるので、
それぞれのバックアップが混ざる心配はありません。

repo1-path
┣ archive
┃ ┗ <stanza name>
┃  ┗ アーカイブ WAL

┗ backup
 ┗ <stanza name>
  ┗ バックアップ

実際に中を見てみると gz 形式で圧縮されていることがわかります。

$ ls -la /var/lib/pgbackrest/backup/server1/20211207-150241F/pg_data/base/1 |more
合計 2104
drwxr-x--- 2 pg-user pg-user 8192 12月 7 15:02 .
drwxr-x--- 6 pg-user pg-user 54 12月 7 15:02 ..
-rw-r----- 1 pg-user pg-user 80 12月 7 15:02 112.gz
-rw-r----- 1 pg-user pg-user 79 12月 7 15:02 113.gz
-rw-r----- 1 pg-user pg-user 12415 12月 7 15:02 1247.gz
-rw-r----- 1 pg-user pg-user 141 12月 7 15:02 1247_fsm.gz
-rw-r----- 1 pg-user pg-user 68 12月 7 15:02 1247_vm.gz
~以下略~

$ ls -la /var/lib/pgbackrest/archive/server1/14-1/0000000100000000/
合計 916
drwxr-x--- 2 pg-user pg-user 4096 12月 7 15:02 .
drwxr-x--- 3 pg-user pg-user 30 12月 7 12:44 ..
-rw-r----- 1 pg-user pg-user 21967 12月 7 12:44 000000010000000000000037-693af810f86df48ba56ef84e8c835d55ed806a40.gz
-rw-r----- 1 pg-user pg-user 21471 12月 7 12:44 000000010000000000000038-52a5a7f24ff3fd115504d451a2769ee4616d2e43.gz
-rw-r----- 1 pg-user pg-user 21319 12月 7 12:44 000000010000000000000039-944421ed6cfa487ba0331d4ac997a200876afdb2.gz
~以下略~

〇リストアについて〇
– 単純な PITR の実現

pgBackRest ではアーカイブ WAL は pgBackRest サーバに退避してくれますが、
そうでない再生中の WAL は管理外です。
そのため、PostgreSQL 標準のコマンド pg_switch_wal を用いて WAL ファイルを切り替えます。

$ date ; psql -d postgres -c "select pg_switch_wal();" ;date
2021年 12月 7日 火曜日 16:02:09 JST
pg_switch_wal
---------------
0/57012910
(1 行)

2021年 12月 7日 火曜日 16:02:09 JST
$

少なくとも 16:02:08 まで復元ができるはずです。

疑似的に障害を発生させてみましょう。

$ date ; kill -9 $(head -1 ${PGDATA}/postmaster.pid) ;date
2021年 12月 7日 火曜日 16:03:18 JST
2021年 12月 7日 火曜日 16:03:18 JST
$

リストアは以下のコマンドで行ないます。
pgbackrest –stanza=<stanza name> –delta restore

$ rm /pgdb/data/postmaster.pid
$ pgbackrest --stanza=server1 --log-level-console=info --delta restore
2021-12-07 16:08:13.495 P00 INFO: restore command begin 2.36: --delta --exec-id=3825-1ab8779d --log-level-console=info --pg1-path=/pgdb/data --repo1-host=backup_server --repo1-host-user=pg-user --repo1-path=/var/lib/pgbackrest --stanza=server1
2021-12-07 16:08:13.774 P00 INFO: repo1: restore backup set 20211207-151931F_20211207-152522I, recovery will start at 2021-12-07 15:25:22
2021-12-07 16:08:13.775 P00 INFO: remove invalid files/links/paths from '/pgdb/data'
2021-12-07 16:08:15.085 P00 INFO: write updated /pgdb/data/postgresql.auto.conf
2021-12-07 16:08:15.088 P00 INFO: restore global/pg_control (performed last to ensure aborted restores cannot be started)
2021-12-07 16:08:15.089 P00 INFO: restore size = 46.9MB, file total = 1259
2021-12-07 16:08:15.089 P00 INFO: restore command end: completed successfully (1595ms)
$

ポイントとしては 2 つあります。
1: PID ファイルの削除
 PID ファイルがあると pgBackRest は PostgreSQL 起動中と判断してしまいます。
 そのため、PostgreSQL が異常終了している場合などは PID ファイルを削除しましょう

2: –delta オプション
 このオプションを付与すると $PGDATA 配下の掃除を pgBackRest が勝手にやってくれます。
 rm $PGDATA を行うリスクがないので安心感があります。

リストアができたので、PostgreSQL を起動し、DB の中を覗いてみましょう

# systemctl start postgresql-14.service
#

$ psql -d INFDB -c "select * from test ORDER BY id DESC FETCH FIRST 5 ROWS ONLY;"
id | date | hostname
--------+---------------------+-------------------------------------------------
102142 | 2021-12-07 16:02:09 | ip-172-31-5-111.ap-northeast-1.compute.internal
102141 | 2021-12-07 16:02:08 | ip-172-31-5-111.ap-northeast-1.compute.internal
102140 | 2021-12-07 16:02:07 | ip-172-31-5-111.ap-northeast-1.compute.internal
102139 | 2021-12-07 16:02:06 | ip-172-31-5-111.ap-northeast-1.compute.internal
102138 | 2021-12-07 16:02:05 | ip-172-31-5-111.ap-northeast-1.compute.internal
(5 行)

$

最後に取得したバックアップは 2021-12-07 15:25:28 に取得した増分バックアップです。
15:25:28 から 16:02:09 までの情報はアーカイブ WAL から復元をしてくれたわけです。

16:02:09 から 16:03:18 のデータは再生中の WAL ファイルに含まれているデータのため消失しています。

〇pgBackRest の欠点〇
再生中の WAL ファイルは退避してくれません。
例えば PostgreSQL の更新が少ない時間が続くと WAL のアーカイブはされません。
その状態でストレージ障害が発生すると PostgreSQL は数時間前までの状態にしかリストアできません。

システム要件によっては RPO(Recovery Point Objective) は XX 分以内とあるかもしれません。
この場合、PostgreSQL の更新が少ない時間帯は pg_switch_wal を定期的に実行するなどの工夫が必要になります。

 

いかがでしたでしょうか。
pg_backrest は細かい制御ができる反面、設定が複雑です (おまけに日本語ドキュメントが少ない!)。
ご参考になればと思いまうす。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です