OSS Fan ~OSSでLinuxサーバ構築~

作成日: 2014/01/14

OSSでLinuxサーバ構築

PostgreSQL 9.0でPITR(Point In Time Recovery)を実行

トップページOSSでLinuxサーバ構築 > PostgreSQL 9.0でPITR(Point In Time Recovery)を実行
このエントリーをはてなブックマークに追加

概要

 PostgreSQL 9.0でPITR(Point In Time Recovery)を実行します。 あえてバージョンを明記しているのは、9.1以降では実行するコマンドが少し変わるためです。 以下の流れでバックアップとリカバリを実行します。
(1) PITRのためのベースバックアップの取得
(2) データの更新
※このタイミングでデータファイルが破損した想定
(3) PostgreSQLインスタンスの停止
(4) ベースバックアップの復元
(5) 最新のWALログの復元
(6) リカバリ設定ファイルの作成
(7) PostgreSQLインスタンスの起動

構成

想定環境

 本バックアップ・リカバリ手順では下記ディレクトリ・ファイル構成であることを前提に説明します。

データベースクラスタ($PGDATA)/data/pgdata1/
WALディレクトリ/data/pgdata1/pg_xlog/
WALアーカイブディレクトリ/bkup/pgdata1/pg_arch/
PostgreSQLログ/data/pgdata1/pg_log/
バックアップラベル20131218_Backup
ベースバックアップファイル/bkup/20131218_pgdata1.tar.gz
リカバリ設定ファイル/data/pgdata1/recovery.conf

サーバ構成

OSバージョン

Red Hat Enterprise Linux 5.9 x86_64

ソフトウェア・パッケージ一覧

postgresql90-9.0.8-1PGDG.rhel6.x86_64.rpm
postgresql90-libs-9.0.8-1PGDG.rhel6.x86_64.rpm
postgresql90-server-9.0.8-1PGDG.rhel6.x86_64.rpm

手順

バックアップ

(1) PITRのためのベースバックアップ取得

 まず始めにデータベースクラスタをリストアする際のベースとなるバックアップを取得します。 ベースバックアップは任意の時点でのデータベースクラスタの静止点です。 このベースバックアップに対してWAL(データベースの更新情報)を適用してデータベースを 復旧していくことになります。

 ベースバックアップはPostgreSQLインスタンスが起動した状態で取得します。 そのため、バックアップ中にデータベースファイル(ディスク)に対して更新が されないような仕組みが導入されています。

 psqlでpg_start_backup()関数を実行すると、内部的にcheckpointが実行されます。 これによりその時点でのバッファ(メモリ)上の更新情報がディスクに反映されます。 また、それ以降の更新情報はディスクに反映されなくなります。 これでデータベースの静止点が取れます。

 pg_start_backup()の引数にはラベルを指定します。 ラベルには何を指定しても良いのですが、ベースバックアップ取得時の 日付や日時などを指定するのが一般的なようです。 このラベルは特に覚えておく必要はありません。

-bash-3.2$ psql -c "select pg_start_backup('20131218_Backup')"
 pg_start_backup
-----------------
 0/1D000020
(1 行)

 続いてデータベースクラスタのバックアップを取得します。 バックアップにはOSコマンドを使用します。 cpやtarコマンドで別の同じサーバのディスクにコピーしても良いですし、 scpやrsyncコマンドなどで別のサーバにコピーしても良いです。 ここではPostgreSQLインスタンスが起動しているのと同じサーバ上の 別のディスクに対してtarコマンドでバックアップします。 バックアップ対象はデータベースクラスタ全体($PGDATA環境変数に設定してあるディレクトリ)です、

-bash-3.2$ cd /data/
-bash-3.2$ tar zcvf /bkup/20131218_pgdata1.tar.gz ./pgdata1
./pgdata1/
./pgdata1/postmaster.opts
./pgdata1/pg_hba.conf
./pgdata1/pg_notify/
./pgdata1/pg_notify/0000
(中略)
./pgdata1/pg_multixact/members/0000
./pgdata1/pg_multixact/offsets/
./pgdata1/pg_multixact/offsets/0000
./pgdata1/pg_clog/
./pgdata1/pg_clog/0000
-bash-3.2$ ls -l /bkup/20131218_pgdata1.tar.gz
-rw-r--r-- 1 postgres postgres 2819055 12月 18 02:47 /bkup/20131218_pgdata1.tar.gz

 OSコマンド(上の例ではtar)でのデータベースクラスタのコピーが終了したら、 ベースバックアップ取得完了をPostgreSQLへ伝える必要があります。 psqlでpg_stop_backup()関数を実行します。 引数は不要です。 するとベースバックアップ取得中に停止されていたデータベースの更新をディスクへ反映する 処理が開始され、通常の状態に戻ります。

-bash-3.2$ psql -c "select pg_stop_backup()"
NOTICE:  pg_stop_backup complete, all required WAL segments have been archived
 pg_stop_backup
----------------
 0/1D0000D8
(1 行)

-bash-3.2$

(2) データの更新

 ベースバックアップ取得後にデータベースに対して追加、更新、削除などが実行されるかと思います。 その後、操作ミスなどによりデータベースファイルを誤って消してしまったことを想定します。

リカバリ

(3) PostgreSQLインスタンスの停止

 データベースクラスタの復旧を行うに当たり、まずはPostgreSQLインスタンスを停止します。 (データベースファイルが消失した場合、PostgreSQLインスタンスはダウンしてしまうかもしれませんが。。。) 正常に停止しない場合はOSのkillコマンドでSIGKILLシグナルを投げて、強制的に停止させてください。

-bash-3.2$ pg_ctl stop -m fast
サーバ停止処理の完了を待っています....完了
サーバは停止しました
-bash-3.2$ ps -ef | grep postgres
avahi     3201     1  0 Dec17 ?        00:00:00 avahi-daemon: running [postgresql.local]
root      6823  6555  0 19:59 pts/1    00:00:00 su - postgres
postgres  6824  6823  0 19:59 pts/1    00:00:00 -bash
postgres  6868  6824  0 19:59 pts/1    00:00:00 ps -ef
postgres  6869  6824  0 19:59 pts/1    00:00:00 grep postgres
-bash-3.2$

(4) ベースバックアップの復元

 PostgreSQLインスタンスが停止したら、バックアップしておいたベースバックアップ (今回の例ではデータベースクラスタのtarファイル)を復元します。 その際に、破損してしまったデータベースクラスタのディレクトリは消さずにとっておきます。 全てとっておく必要はないのですが、ディスクの空き容量に余裕があれば念のため全てとっておきます。

-bash-3.2$ cd $PGDATA/..
-bash-3.2$ ls -l
合計 16
drwx------ 13 postgres postgres 4096 12月 18 02:48 pgdata1
-bash-3.2$ mv pgdata1 pgdata1.bk20131218
-bash-3.2$ ls -l
合計 12
drwx------ 14 postgres postgres 4096 12月 18 20:00 pgdata1.bk20131218

 ベースバックアップのtarファイルをデータベースクラスタのパスに復元します。

-bash-3.2$ tar zxvf /bkup/20131218_pgdata1.tar.gz
./pgdata1/
./pgdata1/postmaster.opts
./pgdata1/pg_hba.conf
./pgdata1/pg_notify/
./pgdata1/pg_notify/0000
(中略)
./pgdata1/pg_multixact/members/0000
./pgdata1/pg_multixact/offsets/
./pgdata1/pg_multixact/offsets/0000
./pgdata1/pg_clog/
./pgdata1/pg_clog/0000
-bash-3.2$ ls -l
合計 12
drwx------ 13 postgres postgres 4096 12月 18 02:47 pgdata1 ←ベースバックアップの復元
drwx------ 14 postgres postgres 4096 12月 18 20:00 pgdata1.bk20131218
-bash-3.2$

(5) 最新のWALログの復元

 ベースバックアップを復元すると、WALもベースバックアップを取得した時の状態に戻ってしまいます。 データベースを最新の状態に復旧するためには、最新のWALが必要となりますので、 退避しておいた破損したデータベースクラスタのディレクトリ内から、最新のWALを復元します。

-bash-3.2$ rm -rf $PGDATA/pg_xlog
-bash-3.2$ cp -pivr $PGDATA/../pgdata1.bk20131218/pg_xlog $PGDATA/
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog' -> `/data/pgdata1/pg_xlog'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/000000010000000000000022' -> `/data/pgdata1/pg_xlog/000000010000000000000022'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/000000010000000000000020' -> `/data/pgdata1/pg_xlog/000000010000000000000020'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/00000001000000000000001F' -> `/data/pgdata1/pg_xlog/00000001000000000000001F'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/000000010000000000000023' -> `/data/pgdata1/pg_xlog/000000010000000000000023'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/00000001000000000000001E' -> `/data/pgdata1/pg_xlog/00000001000000000000001E'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/archive_status' -> `/data/pgdata1/pg_xlog/archive_status'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/archive_status/00000001000000000000001E.done' -> `/data/pgdata1/pg_xlog/archive_status/00000001000000000000001E.done'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/archive_status/00000001000000000000001D.00000020.backup.done' -> `/data/pgdata1/pg_xlog/archive_status/00000001000000000000001D.00000020.backup.done'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/00000001000000000000001D.00000020.backup' -> `/data/pgdata1/pg_xlog/00000001000000000000001D.00000020.backup'
`/data/pgdata1/../pgdata1.bk20131218/pg_xlog/000000010000000000000021' -> `/data/pgdata1/pg_xlog/000000010000000000000021'
-bash-3.2$

(6) リカバリ設定ファイルの作成

 最後にリカバリ設定ファイルを作成します。 リカバリ設定ファイルがない状態でPostgreSQLインスタンスを起動すると、 データベースに対するリカバリは実行されません。 PostgreSQLインスタンス起動時に $PGDATA/recovery.conf ファイルが存在する場合のみ リカバリが実行されます。 リカバリ設定ファイル(recovery.conf)はデフォルトでは準備されていませんので、新規に作成します。

 PostgreSQLは recovery.conf の「restore_command」にはWALアーカイブファイル名(%f) をWALファイル(%p)にコピーするコマンドを記述します。 ちょうど postgresql.conf の「archive_command」と逆方向のコピーをする形になります。

-bash-3.2$ cd $PGDATA
-bash-3.2$ vi recovery.conf
ファイル名:$PGDATA/recovery.conf
restore_command = 'cp /bkup/pgdata1/pg_arch/%f %p'

(7) PostgreSQLインスタンスの起動

 ここまででリカバリの準備が整いましたので、PostgreSQLインスタンスを起動します。 起動時に自動でリカバリが実行され、PostgreSQLインスタンスが起動します。

-bash-3.2$ pg_ctl start -w
pg_ctl: 他のサーバが動作中の可能性がありますが、とにかくpostmasterの起動を試みます。
サーバの起動完了を待っています...完了
サーバ起動完了
-bash-3.2$ ps -ef | grep postgres
avahi     3201     1  0 Dec17 ?        00:00:00 avahi-daemon: running [postgresql.local]
root      7321  6555  0 21:04 pts/1    00:00:00 su - postgres
postgres  7322  7321  0 21:04 pts/1    00:00:00 -bash
postgres  7368     1  0 21:05 pts/1    00:00:00 /usr/pgsql-9.0/bin/postgres
postgres  7369  7368  0 21:05 ?        00:00:00 postgres: logger process
postgres  7372  7368  0 21:05 ?        00:00:00 postgres: writer process
postgres  7376  7368  0 21:05 ?        00:00:00 postgres: wal writer process
postgres  7377  7368  0 21:05 ?        00:00:00 postgres: autovacuum launcher process
postgres  7378  7368  0 21:05 ?        00:00:00 postgres: archiver process   last was 00000002.history
postgres  7380  7368  0 21:05 ?        00:00:00 postgres: stats collector process
postgres  7393  7322  0 21:09 pts/1    00:00:00 ps -ef
postgres  7394  7322  0 21:09 pts/1    00:00:00 grep postgres
-bash-3.2$

 ログを確認して「archive recovery complete」と表示されていれば復旧完了です。

 ※以下の表示例は結果がイマイチかもしれません。すみません。。。

-bash-3.2$ tail -n 30 /data/pgdata1/pg_log/postgresql-20131218.log
(中略)
2013-12-18 21:05:04 JST [7370] LOG:  database system was interrupted; last known up at 2013-12-18 20:59:16 JST
2013-12-18 21:05:04 JST [7370] LOG:  starting archive recovery
2013-12-18 21:05:05 JST [7370] LOG:  restored log file "000000010000000000000020" from archive
2013-12-18 21:05:05 JST [7370] LOG:  redo starts at 0/20000020
2013-12-18 21:05:05 JST [7370] LOG:  consistent recovery state reached at 0/200000D8
cp: cannot stat `/bkup/pgdata1/pg-arch/000000010000000000000021': No such file or directory
2013-12-18 21:05:05 JST [7370] LOG:  record with zero length at 0/2101CE98
2013-12-18 21:05:05 JST [7370] LOG:  redo done at 0/2101CBB8
2013-12-18 21:05:05 JST [7370] LOG:  last completed transaction was at log time 2013-12-18 21:01:40.291329+09
cp: cannot stat `/bkup/pgdata1/pg-arch/00000002.history': No such file or directory
2013-12-18 21:05:05 JST [7370] LOG:  selected new timeline ID: 2
cp: cannot stat `/bkup/pgdata1/pg-arch/00000001.history': No such file or directory
2013-12-18 21:05:05 JST [7370] LOG:  archive recovery complete
2013-12-18 21:05:05 JST [7377] LOG:  autovacuum launcher started
2013-12-18 21:05:05 JST [7368] LOG:  database system is ready to accept connections
-bash-3.2$

 リカバリに使われた $PGDATA/recovery.conf ファイルは、 リカバリ終了後に自動で $PGDATA/recovery.done にリネームされます。 そのため、次回PostgreSQLインスタンス起動時に、またリカバリが 実行されてしまうということはありません。

-bash-3.2$ ls -l $PGDATA/recovery*
-rw-r--r-- 1 postgres postgres 51 12月 18 20:38 /data/pgdata1/recovery.done
-bash-3.2$

 以上でリカバリは終了です。

プロフィール

らのっち

損害保険会社のIT企画部に勤務するSEです。OSSを勉強中です。

<所属>
日本PostgreSQLユーザ会とくしまOSS普及協議会


第000414号