PostgreSQL PITR復旧訓練ガイド: バックアップがあるのに戻せないを防ぐ実践手順

PostgreSQL運用で最も危険なのは「バックアップがある」という安心感です。実際の障害では、バックアップ自体より 復旧手順の不整合 で時間を失います。たとえば、WAL保管期間が足りず目標時刻に戻せない、暗号鍵が見つからず復号できない、復旧後の整合性確認が曖昧で再開判断ができない、といった問題です。

本記事では、PostgreSQLの Point-in-Time Recovery(PITR)を、机上ではなく本番レベルで回すための実装手順を解説します。pgBackRest を例にしていますが、考え方は他ツールでも共通です。

1. PITRの前提: 3つ揃わないと復旧できない

PITRは次の3要素で成立します。

  1. ベースバックアップ(フルまたは差分)
  2. WALアーカイブ(継続的)
  3. 目標時刻情報(いつまで戻すか)

どれか1つでも欠けると成立しません。特に本番で多いのは「WALが途中で消えていた」ケースです。S3保存していても、ライフサイクル設定や権限変更で欠落することがあります。

2. まず決めるべきRTO/RPO

技術論の前に、業務要件を決めます。

  • RTO(復旧に許容される時間): 例 60分
  • RPO(失ってよいデータ時間): 例 5分

この2つで設計が変わります。

  • RPO 5分以内ならWALアーカイブ遅延監視が必須
  • RTO 60分以内なら復旧訓練を定期実施し、手順を自動化する必要あり

要件不明のまま「毎日バックアップ」だけ実施しても、障害時に役立たないことが多いです。

3. 推奨アーキテクチャ(単一リージョンの最小構成)

  • DBサーバ: PostgreSQL 15/16
  • バックアップツール: pgBackRest
  • 保存先: S3互換ストレージ(バージョニングON)
  • 監視: Prometheus + Alertmanager
  • 復旧先: 別ホスト(本番と同一ネットワーク)

重要なのは、本番DBと別ホストで実際に復旧できること を定期検証する点です。

4. 実装手順(pgBackRest)

4.1 PostgreSQL設定

postgresql.conf 例:

wal_level = replica
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'
max_wal_senders = 10
wal_compression = on

archive_command は失敗時に非0を返す必要があります。ここが曖昧だとWAL欠落に気づけません。

4.2 pgBackRest設定

/etc/pgbackrest/pgbackrest.conf 例:

[global]
repo1-type=s3
repo1-path=/pgbackrest
repo1-s3-bucket=prod-db-backup
repo1-s3-endpoint=s3.ap-northeast-1.amazonaws.com
repo1-s3-region=ap-northeast-1
repo1-retention-full=14
start-fast=y
process-max=4
compress-type=zst

[main]
pg1-path=/var/lib/postgresql/16/main

初期化:

1
2
pgbackrest --stanza=main stanza-create
pgbackrest --stanza=main --type=full backup

4.3 WAL到達の監視

  • pg_stat_archiverfailed_count
  • 最終成功時刻と現在時刻の差分
  • repoの最新WALタイムスタンプ

例: 10分以上WALが更新されない場合にCriticalアラート。

5. 復旧訓練(Drill)を標準化する

運用で差が出るのはここです。訓練の目的は「復旧できること」ではなく、予測可能な時間で安全に復旧できること です。

5.1 月次ドリル手順(テンプレート)

  1. 目標時刻を決める(例: 当日 08:35:00 JST)
  2. 復旧専用ホストを初期化
  3. バックアップ + WALからリストア
  4. DB起動後、整合性チェックを実行
  5. アプリのスモークテストを実行
  6. RTO実測値と課題を記録

5.2 実コマンド例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 復旧先でデータディレクトリを準備
systemctl stop postgresql
rm -rf /var/lib/postgresql/16/main/*

# 指定時刻までリストア
pgbackrest --stanza=main \
  --type=time \
  --target="2026-03-06 08:35:00+09" \
  --delta \
  restore

# 起動
systemctl start postgresql

PostgreSQL 12+では recovery.conf ではなく postgresql.auto.conf + standby.signal 形式に変わっている点に注意してください。

6. 復旧後の整合性チェック項目

「起動したからOK」は危険です。最低限、次を確認します。

  • 主要テーブル件数(基準値との差分)
  • 直近トランザクション時刻
  • 重要集計値(売上、注文、在庫など)
  • アプリの read/write スモークテスト
  • レプリカ再構築可否

SQL例:

1
2
3
SELECT now();
SELECT max(created_at) FROM orders;
SELECT count(*) FROM users;

件数だけでなく「業務上重要な指標」を入れるのが実務的です。

7. 失敗しがちなポイントと対策

7.1 WAL保持期間が短すぎる

  • 対策: RPO/RTOに基づき、最低でも7〜14日保持
  • 削除はバックアップ整合性確認後に実行

7.2 暗号鍵・認証情報の保管ミス

  • 対策: KMS/Secret Managerに分離保管
  • 緊急時アクセス手順をRunbook化

7.3 手順が個人依存

  • 対策: RunbookをGit管理
  • ドリル時に“当番以外”が実行して再現性を検証

7.4 復旧はできるが遅すぎる

  • 対策: 差分バックアップ頻度見直し
  • 復旧先マシンスペックの最低保証
  • リストア並列度(process-max)調整

8. 自動化の実装例(CIでドリルを回す)

本番同等データを使えない場合は、匿名化済みスナップショットで定期検証します。

  • 毎週日曜 03:00 に復旧ジョブ起動
  • 復旧後にSQLチェック + APIスモーク
  • 結果をSlack/Discordへ通知
  • 失敗時は翌営業日のSRE定例でレビュー

この「半自動ドリル」を導入すると、障害時の初動が劇的に安定します。

9. 監査対応のための証跡

監査や顧客説明で必要になるのは、次の証跡です。

  • バックアップ成功ログ(日次)
  • WAL連続性の証跡
  • 復旧ドリルの実行記録(日時、担当、RTO実測)
  • 改善アクション履歴

「やっています」ではなく「この月にこの結果でした」と示せる状態を作っておきましょう。

10. 現場向けチェックリスト

  • フルバックアップ成功率 99%以上
  • WAL遅延アラートが有効
  • 目標時刻指定の復旧手順がRunbook化
  • 月次ドリル実施済み
  • 復旧後整合性チェックSQLが整備済み
  • KMS/鍵管理手順が文書化済み

このチェックリストを満たして初めて「PITR対応」と言えます。

まとめ

PITRは設定項目の話ではなく、復旧可能性を継続的に検証する運用 です。

  • RTO/RPOを先に決める
  • バックアップ + WAL + 監視をセットで設計する
  • 月次ドリルで実測し、Runbookを改善する
  • 復旧後整合性チェックまで標準化する

バックアップがあることはゴールではありません。障害時に「何分で、どこまで戻せるか」を言える状態こそが、プロダクション品質です。

付録: 実運用で使えるPITRドリル手順(90分版)

以下は、現場でそのまま回せる最小ドリル手順です。月次で固定化すると、障害対応の心理的負荷を下げられます。

  1. 開始宣言(5分)
    • 担当者、開始時刻、目標RTO/RPOをチケットに記録
  2. 復旧環境準備(15分)
    • 復旧先PostgreSQLを起動
    • バックアップ世代と目標復旧時刻(例: 10:32:00 JST)を確定
  3. リストア実行(25分)
    • pg_restore もしくはベースバックアップ展開
    • WAL適用完了ログを保存
  4. 整合性検証(20分)
    • 件数チェックSQL、外部キー整合性、直近注文IDの連続性を確認
    • APIスモーク(ログイン/一覧/作成)を実施
  5. 振り返り(25分)
    • 実測RTO/RPOを記録
    • 失敗ポイントをRunbookに即反映

重要なのは「成功したか」だけではなく、どこで何分消費したかを毎回残すことです。これを3回続けるだけで、復旧手順のボトルネックがかなり明確になります。