Skip to content

PostgreSQLのオンラインバックアップとリカバリ

2016/3/3

PostgreSQLはオンラインバックアップに対応している。
ということで、オンラインバックアップをしてみたのと、リカバリまで試して記録とまとめ。

バックアップの方式としてはpg_dumpコマンドを用いたものもオンラインでできるが、WALアーカイブを用いたバックアップを試してみることにした。

簡単に仕組みを説明

  • Postgesはデータベースの更新時、トランザクションのログ、WALを残してから実行される。
  • そうすることで、データベースがクラッシュしてもWALを追うことで、リカバリできる仕組みになっている。
  • WALアーカイブとは、一定のタイミングでそのWALを別の場所にアーカイブしておく仕組み。
  • 例えば、データベースの実態が入っているディスクとは別のディスクにWALアーカイブを出力しておけば、最悪データベースが入っているディスクが完全に破損しても、あるポイントまでリカバリできるようになる。

ちなみに以下はPostgres Ver.9.4で検証している。

Postgresの設定

postgresql.conf

# walアーカイブを出力する設定
wal_level = archive

# walアーカイブを出力するときに実行するコマンド。WALをcpコマンドで所定の場所にコピーしている。
# %pはwalファイル名、%fはwalアーカイブファイル名が自動で入力される
archive_command = 'cp %p /var/lib/pgsql/9.4/wal/%f' 

# 同時に稼動するWAL送信プロセスの最大値
max_wal_senders = 1

ph_hba.conf

# ローカルのpostgresユーザにレプリケーション権限を与える。
local   replication     postgres  peer

バックアップ

バックアップの流れ

  • ベースバックアップを定期的に取る
  • WALアーカイブを安全な場所に出力しておく

ベースバックアップ

ベースバックアップとは、postgresの設定やら実際のデータベースの中身やらをまるまるバックアップする仕組み。
以下のコマンドで実行できる(※)。

pg_basebackup -D [dirpath] -F t -x -z

※Ver.9.1からこのコマンドが使えるとのこと。

コマンドはデータベース管理ユーザ(デフォルトだとpostgres)で実行すること。

オプションの説明

-D :ベースバックアップ出力先パスを指定。出力先は空でなければならない。
  ディレクトリが存在しなければ作ってくれる。  
-F t :出力する形式。tはtarで出力する。  
-x :ベースバックアップ処理中、データベースが更新された際もファイルシステム上の  
  ファイルは更新しないようにする。出力中はメモリ上で頑張る。
  (そうすることで、出力ファイルが変な状態になることを防ぐ)
-z :gzipで圧縮した状態にする

こちらのコマンドを実行すると、base.tar.gzというファイルが生成される。 それを展開すると以下のものが入っている。

-rw-------. 1 postgres postgres     4  3月  1 21:49 PG_VERSION
-rw-------. 1 postgres postgres   206  3月  2 21:01 backup_label
drwx------. 6 postgres postgres    50  3月  1 21:50 base
drwx------. 2 postgres postgres  4096  3月  2 21:01 global
drwx------. 2 postgres postgres    17  3月  1 21:49 pg_clog
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_dynshmem
-rw-------. 1 postgres postgres  4218  3月  1 21:50 pg_hba.conf
-rw-------. 1 postgres postgres  1636  3月  1 21:49 pg_ident.conf
drwx------. 2 postgres postgres     6  3月  1 21:50 pg_log
drwx------. 4 postgres postgres    37  3月  1 21:49 pg_logical
drwx------. 4 postgres postgres    34  3月  1 21:49 pg_multixact
drwx------. 2 postgres postgres    17  3月  2 20:40 pg_notify
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_replslot
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_serial
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_snapshots
drwx------. 2 postgres postgres     6  3月  2 20:40 pg_stat
drwx------. 2 postgres postgres     6  3月  2 21:00 pg_stat_tmp
drwx------. 2 postgres postgres    17  3月  1 21:49 pg_subtrans
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_tblspc
drwx------. 2 postgres postgres     6  3月  1 21:49 pg_twophase
drwx------. 3 postgres postgres    58  3月  2 21:01 pg_xlog
-rw-------. 1 postgres postgres    88  3月  1 21:49 postgresql.auto.conf
-rw-------. 1 postgres postgres 21331  3月  1 21:50 postgresql.conf

postgresを触ったことがある人であれば、まるまるバックアップされていることがわかるかと。

WALアーカイブ

WALアーカイブ出力先を覗いてみると以下のようなファイルが出力されている。

-rw-------. 1 postgres postgres 16777216  3月  1 21:59 000000010000000000000001
-rw-------. 1 postgres postgres 16777216  3月  2 03:56 000000010000000000000002
-rw-------. 1 postgres postgres 16777216  3月  2 21:01 000000010000000000000003
-rw-------. 1 postgres postgres 16777216  3月  2 21:01 000000010000000000000004
-rw-------. 1 postgres postgres      302  3月  2 21:01 000000010000000000000004.00000028.backup

デフォルトだと、WALが16MBごとにアーカイブを残すようになっている。
なので、16MBのファイルがいっぱいある。

「〜.00000028.backup」について、これはベースバックアップ実行時に生成されるファイル。
バックアップ履歴ファイルと言い、チェックポイント等、ベースバックアップ取得時の情報が入っている。 中身は以下のとおり。

START WAL LOCATION: 0/4000028 (file 000000010000000000000004)
STOP WAL LOCATION: 0/40000B8 (file 000000010000000000000004)
CHECKPOINT LOCATION: 0/4000028
BACKUP METHOD: streamed
BACKUP FROM: master
START TIME: 2016-03-02 21:01:03 JST
LABEL: pg_basebackup base backup
STOP TIME: 2016-03-02 21:01:05 JST

〜.backの前半部分と同じファイル名の 000000010000000000000004 が一番最初に必要なファイルとなる。
000000010000000000000004 とそれ以降生成されるWALアーカイブをリカバリのために取っておく必要がある。
その前のファイルは消してしまっても問題なし。
(といっても、余裕があれば取っておいたほうが安心。)

まとめると、リカバリに必要なのは以下のファイル。

  • ベースバックアップ(base.tar.gz)
  • バックアップ履歴ファイル(000000010000000000000004.00000028.backup)
  • .backupが付いている、元のファイル名以降のWALアーカイブ
    (000000010000000000000004以降のWALアーカイブ)

リカバリ

リカバリの流れ

  • postgresのサービスを停止する
  • データベースクラスタを消去する
  • ベースバックアップファイルをデータベースクラスタの場所に展開する
  • ベースバックアップの中身に入っているWALは一旦削除する
  • WALアーカイブを適当な場所に配置する
  • recovery.confファイルを作成する
  • postgresのサービスを起動する

postgresのサービス停止

RHEL7系のsystemdであれば以下のとおり。

systemctl stop postgresql-9.4.service

データベースクラスタの消去

データベースクラスタとはデータベースが入ってる場所。
デフォルトだと、 /var/lib/pgsql/[バージョン]/data
この中身をまるまる削除する。 (ディスクに余裕があれば、削除ではなく別の場所に移動する等のほうが良い)

rm -rf /var/lib/pgsql/9.4/data/*

ベースバックアップファイルの展開

データベースクラスタのディレクトリ内にベースバックアップを展開します。

tar xvf  base.tar.gz

ベースバックアップの中のWALの削除

ベースバックアップ取得時のWALは最新のものではないため削除する。
(こちらもディスクに余裕があれば、削除ではなく別の場所に移動する等のほうが良い)

pg_xlogの中身を削除。

rm -rf /var/lib/pgsql/9.4/data/pg_xlog/*

WALアーカイブを適当な場所に配置

WALアーカイブの配置はこの後の設定で場所を指定するので、どこでも良い。
postgresユーザが読み取り権限をもっている場所である必要あり。

recovery.confファイルの作成

データベースクラスタの場所に作成。

vim /var/lib/pgsql/9.4/data/recovery.conf

recovery.confの中身

# 以下の1行のみでOK
restore_command = 'cp /tmp/wal/%f %p'

★WALアーカイブが置いてある場所はここで指定する。
上記の場合、WALアーカイブの配置先として"/tmp/wal"を指定している。

postgresのサービス起動

あとはサービスを起動するだけとなる。

systemctl start postgresql-9.4.service

postgresのサービス起動時にWALを読み込み、自動でリカバリをしてくれる。
postgresのログを見たところ以下の通りログが出力されていた。

2016-03-02 21:39:20.008 JST 2713 56d6def8.a99-1 0 LOG:  データベースシステムは中断されました: 今回は 2016-03-02 21:01:03 JST までは到達しています

2016-03-02 21:39:20.008 JST 2713 56d6def8.a99-2 0 LOG:  見つからなかった WAL ディレクトリ "pg_xlog/archive_status" を作成しています ... 

2016-03-02 21:39:20.245 JST 2713 56d6def8.a99-3 0 LOG:  アーカイブリカバリを開始しています

2016-03-02 21:39:20.269 JST 2713 56d6def8.a99-4 0 LOG:  ログファイル"000000010000000000000004"をアーカイブからリストアしました

2016-03-02 21:39:20.352 JST 2713 56d6def8.a99-5 0 LOG:  0/4000090のREDOを開始します

2016-03-02 21:39:20.436 JST 2713 56d6def8.a99-6 0 LOG:  0/40000B8 でリカバリー状態の整合が取れました

cp: `/tmp/wal/000000010000000000000005' を stat できません: そのようなファイルやディレクトリはありません

2016-03-02 21:39:20.441 JST 2713 56d6def8.a99-7 0 LOG:  0/40000B8のREDOが終わりました

2016-03-02 21:39:20.464 JST 2713 56d6def8.a99-8 0 LOG:  ログファイル"000000010000000000000004"をアーカイブからリストアしました

cp: `/tmp/wal/00000002.history'  stat できません: そのようなファイルやディレクトリはありません

2016-03-02 21:39:20.468 JST 2713 56d6def8.a99-9 0 LOG:  選択された新しいタイムラインID: 2

cp: `/tmp/wal/00000001.history'  stat できません: そのようなファイルやディレクトリはありません

2016-03-02 21:39:20.597 JST 2721 56d6def8.aa1-1 0 (postgres, postgres, [local], [unknown])FATAL:  データベースシステムは起動しています

2016-03-02 21:39:21.119 JST 2713 56d6def8.a99-10 0 LOG:  アーカイブリカバリが完了しました

2016-03-02 21:39:21.598 JST 2722 56d6def9.aa2-1 0 (postgres, postgres, [local], [unknown])FATAL:  データベースシステムは起動しています

2016-03-02 21:39:22.003 JST 2713 56d6def8.a99-11 0 LOG:  MultiXact member wraparound protections are now enabled

2016-03-02 21:39:22.008 JST 2711 56d6def7.a97-3 0 LOG:  データベースシステムの接続受付準備が整いました

2016-03-02 21:39:22.008 JST 2724 56d6defa.aa4-1 0 LOG:  自動バキュームランチャプロセス

リカバリ、つまりWALからREDOしているのがわかるかと。

以上で終わり。結構簡単。