ストーリー
「バックアップは取ってありますか?」「…取ってたと思います」
本番データベースの誤操作で大量のデータが消失。バックアップを確認したところ、1週間前のものしかなかった。高橋アーキテクトが厳しい顔で言う。
「バックアップは『あるかどうか』じゃない。『いつの時点に戻せるか』と『復旧にどれくらいかかるか』だ。そしてバックアップはリストアテストなしには信頼できない」
バックアップの基本指標
RPO と RTO
| 指標 | 意味 | 質問 |
|---|---|---|
| RPO(Recovery Point Objective) | 許容できるデータ損失の時間 | 「何時間前までのデータを失っても良いか?」 |
| RTO(Recovery Time Objective) | 許容できる復旧時間 | 「復旧までに何時間かかっても良いか?」 |
データ損失(RPO) ダウンタイム(RTO)
←─────────────→ ←─────────────→
最後のバックアップ 障害発生 サービス復旧
サービスレベル別の指標目安
| サービスレベル | RPO | RTO | 例 |
|---|---|---|---|
| ミッションクリティカル | 0(ゼロ) | 数分 | 金融、決済 |
| ビジネスクリティカル | 1時間以内 | 1時間以内 | EC、SaaS |
| 一般業務 | 24時間以内 | 4時間以内 | 社内ツール |
| 開発・ステージング | 1週間 | 24時間 | 非本番環境 |
バックアップの種類
1. フルバックアップ
データベース全体のコピーを取得する。
# PostgreSQL: フルバックアップ
pg_dump -h localhost -U postgres -d myapp -F c -f backup_20240315.dump
# リストア
pg_restore -h localhost -U postgres -d myapp backup_20240315.dump
2. 差分・増分バックアップ
前回バックアップからの変更分のみ取得する。
# PostgreSQL: 物理バックアップ(pg_basebackup)
pg_basebackup -h localhost -D /backups/base -Ft -z -P
# WAL (Write-Ahead Log) の連続アーカイブ
# postgresql.conf
# archive_mode = on
# archive_command = 'cp %p /backups/wal/%f'
3. Point-in-Time Recovery(PITR)
WALを使って任意の時点に復旧する。
# 特定時刻まで復旧
# recovery.conf
restore_command = 'cp /backups/wal/%f %p'
recovery_target_time = '2024-03-15 14:30:00+09'
recovery_target_action = 'promote'
バックアップ戦略の設計
3-2-1 ルール
3つのコピー: 本番 + バックアップ2つ
2つの異なるメディア: ディスク + クラウドストレージ
1つはオフサイト: 異なるリージョン or データセンター
スケジュール例
// バックアップスケジュールの設計
const backupStrategy = {
// フルバックアップ: 日次
full: {
schedule: '0 2 * * *', // 毎日 AM 2:00
retention: '30 days',
storage: 's3://backups/full/',
},
// WALアーカイブ: 連続
wal: {
mode: 'continuous',
retention: '7 days',
storage: 's3://backups/wal/',
},
// スナップショット: 時間ごと(クラウドDB)
snapshot: {
schedule: '0 * * * *', // 毎時
retention: '48 hours',
},
};
クラウドでのバックアップ
AWS RDS
// AWS RDS の自動バックアップ設定(CDK例)
import * as rds from 'aws-cdk-lib/aws-rds';
const database = new rds.DatabaseInstance(this, 'MyDB', {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_15,
}),
// 自動バックアップ
backupRetention: cdk.Duration.days(30),
preferredBackupWindow: '17:00-18:00', // UTC (JST 02:00-03:00)
// ポイントインタイムリカバリ
// RDS は自動的に5分ごとのトランザクションログを保存
// 削除保護
deletionProtection: true,
// マルチAZ(高可用性)
multiAz: true,
});
手動スナップショット
# AWS CLI でスナップショット取得
aws rds create-db-snapshot \
--db-instance-identifier myapp-db \
--db-snapshot-identifier myapp-db-20240315-pre-migration
# スナップショットからリストア
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier myapp-db-restored \
--db-snapshot-identifier myapp-db-20240315-pre-migration
リストアテスト
バックアップはリストアテストなしには信頼できない。
// リストアテストの自動化
async function weeklyRestoreTest(): Promise<TestResult> {
const steps = [
// 1. 最新バックアップからリストア
async () => {
await restoreFromLatestBackup('test-restore-db');
return { step: 'restore', success: true };
},
// 2. データ整合性チェック
async () => {
const userCount = await testDb.query('SELECT COUNT(*) FROM users');
const orderCount = await testDb.query('SELECT COUNT(*) FROM orders');
return {
step: 'integrity',
success: userCount > 0 && orderCount > 0,
details: { userCount, orderCount },
};
},
// 3. アプリケーション接続テスト
async () => {
const result = await testApp.healthCheck();
return { step: 'app_connection', success: result.ok };
},
// 4. テスト環境のクリーンアップ
async () => {
await destroyTestDatabase('test-restore-db');
return { step: 'cleanup', success: true };
},
];
const results = [];
for (const step of steps) {
results.push(await step());
}
// 結果をSlackに通知
await notifySlack({
channel: '#db-ops',
message: `Weekly restore test: ${results.every(r => r.success) ? 'PASSED' : 'FAILED'}`,
details: results,
});
return { results, allPassed: results.every(r => r.success) };
}
マイグレーション前のバックアップ手順
#!/bin/bash
# pre-migration-backup.sh
set -e
DB_NAME="myapp"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="pre_migration_${TIMESTAMP}.dump"
echo "1. Taking pre-migration backup..."
pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME -F c -f $BACKUP_FILE
echo "2. Uploading to S3..."
aws s3 cp $BACKUP_FILE s3://backups/pre-migration/$BACKUP_FILE
echo "3. Verifying backup..."
pg_restore --list $BACKUP_FILE > /dev/null 2>&1
echo "4. Backup complete: $BACKUP_FILE"
echo " To restore: pg_restore -h \$DB_HOST -U \$DB_USER -d $DB_NAME $BACKUP_FILE"
まとめ
| ポイント | 内容 |
|---|---|
| RPO/RTO | 許容データ損失時間と許容復旧時間を定義 |
| バックアップ種類 | フル、差分/増分、PITR |
| 3-2-1 ルール | 3コピー、2メディア、1オフサイト |
| リストアテスト | 定期的にリストアを検証する |
| マイグレーション前 | 必ずバックアップを取得 |
理解度チェックリスト
- RPO と RTO の違いを説明できる
- バックアップの3種類を説明できる
- 3-2-1 ルールを理解している
- リストアテストの重要性を説明できる
次のステップ
次のレッスンではデータベースのモニタリングを学ぶ。パフォーマンスの可視化、異常検知、アラート設定で運用品質を高めよう。
推定読了時間: 30分