database-backups について
PostgreSQL、MySQL、MongoDB の構築や運用に携わるチーム向けのバックアップと復旧のスキルです。定期バックアップ、リモートストレージ、保持設定、復元テストの例を提供します。
主な機能
- 01論理・物理バックアップの方式
- 02PostgreSQL、MySQL、MongoDB の例
- 03S3 アップロードと保持設定の指針
- 04Restic の暗号化と重複排除
- 05スケジュール設定と復旧テスト
活用シーン
- 01定期的なデータベースバックアップの設定
- 02S3 ストレージへのオフサイトコピー保存
- 03災害復旧手順書の準備
- 04テスト復元によるバックアップの検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill database-backupsインストール後、Claude Code で/database-backupsを入力して読み込まれたか確認します。
詳細
データベースのバックアップ
包括的なデータベースバックアップ戦略を実装し、検証済みの復旧手順を整備します。
使用する場面
- 新しいデータベースをデプロイする際に、初日からバックアップ計画が必要な場合。
- PostgreSQL、MySQL、MongoDB のバックアップを毎晩または毎時、自動化したい場合。
- 保持ポリシーを設定して、バックアップを S3 互換のオブジェクトストレージに送信したい場合。
- 災害復旧の手順書を作成または検証している場合。
前提条件
- データベースクライアントツールがインストールされていること(
pg_dump、mysqldump、mongodump)。 - リモートストレージ用の AWS CLI または
restic。 - スケジュール設定用の
cronまたは systemd タイマー。 - オフサイトバックアップ用の S3 バケット(または S3 互換エンドポイント)。
バックアップの種類
| 種類 | 説明 | 頻度 | 用途 |
|---|---|---|---|
| フル | データベース全体のコピー | 毎週 | 復元のベースライン |
| 増分 | 前回のバックアップ以降の変更 | 毎日 | ストレージ容量と所要時間の削減 |
| トランザクションログ/WAL | ログの継続的な転送 | 継続的 | ポイントインタイムリカバリ(PITR) |
| スナップショット | ストレージレベルのスナップショット(EBS、ZFS) | 毎日 | フルリストアの高速化 |
PostgreSQL のバックアップ
pg_dump による論理バックアップ
#!/bin/bash
# pg_backup.sh — PostgreSQL logical backup
set -euo pipefail
DB_NAME="mydb"
DB_USER="backup_user"
DB_HOST="localhost"
BACKUP_DIR="/backups/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="${BACKUP_DIR}/${DB_NAME}_${DATE}.dump"
mkdir -p "$BACKUP_DIR"
# Custom compressed format (recommended for selective restore)
pg_dump -h "$DB_HOST" -U "$DB_USER" -Fc -Z6 "$DB_NAME" > "$FILENAME"
echo "[$(date)] PostgreSQL backup complete: $FILENAME ($(du -h "$FILENAME" | cut -f1))"
pg_basebackup による物理バックアップ
#!/bin/bash
# pg_basebackup.sh — PostgreSQL physical backup for PITR
set -euo pipefail
BACKUP_DIR="/backups/postgres/base_$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
pg_basebackup \
-h localhost \
-U replicator \
-D "$BACKUP_DIR" \
--wal-method=stream \
--checkpoint=fast \
--progress \
--verbose
echo "[$(date)] Base backup complete: $BACKUP_DIR"
PostgreSQL の復元
# Restore from custom-format dump
pg_restore -h localhost -U myapp -d mydb --clean --if-exists /backups/postgres/mydb_20250115_020000.dump
# Restore a single table
pg_restore -h localhost -U myapp -d mydb -t orders /backups/postgres/mydb_20250115_020000.dump
# Restore from plain SQL
psql -h localhost -U myapp -d mydb < /backups/postgres/mydb_20250115.sql
MySQL のバックアップ
mysqldump による論理バックアップ
#!/bin/bash
# mysql_backup.sh — MySQL logical backup
set -euo pipefail
DB_NAME="mydb"
DB_USER="backup_user"
DB_PASS="${MYSQL_BACKUP_PASSWORD}"
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz"
mkdir -p "$BACKUP_DIR"
mysqldump -u "$DB_USER" -p"$DB_PASS" \
--single-transaction \
--routines \
--triggers \
--events \
"$DB_NAME" | gzip > "$FILENAME"
echo "[$(date)] MySQL backup complete: $FILENAME ($(du -h "$FILENAME" | cut -f1))"
Percona XtraBackup による物理バックアップ
#!/bin/bash
# xtrabackup.sh — MySQL physical backup
set -euo pipefail
BACKUP_DIR="/backups/mysql/full_$(date +%Y%m%d)"
xtrabackup --backup \
--user=backup_user \
--password="${MYSQL_BACKUP_PASSWORD}" \
--target-dir="$BACKUP_DIR"
xtrabackup --prepare --target-dir="$BACKUP_DIR"
echo "[$(date)] XtraBackup complete: $BACKUP_DIR"
MySQL の復元
# Restore from compressed mysqldump
gunzip < /backups/mysql/mydb_20250115_020000.sql.gz | mysql -u root -p mydb
# Restore from XtraBackup
sudo systemctl stop mysql
sudo rm -rf /var/lib/mysql/*
xtrabackup --move-back --target-dir=/backups/mysql/full_20250115
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql
MongoDB のバックアップ
mongodump による論理バックアップ
#!/bin/bash
# mongo_backup.sh — MongoDB backup
set -euo pipefail
MONGO_URI="mongodb://backup_user:${MONGO_BACKUP_PASSWORD}@localhost:27017"
BACKUP_DIR="/backups/mongodb"
DATE=$(date +%Y%m%d_%H%M%S)
TARGET="${BACKUP_DIR}/${DATE}"
mkdir -p "$BACKUP_DIR"
# Full backup with compression
mongodump --uri="$MONGO_URI" --gzip --out="$TARGET"
echo "[$(date)] MongoDB backup complete: $TARGET"
MongoDB の復元
# Restore all databases
mongorestore --uri="mongodb://admin:secret@localhost:27017" \
--gzip --drop /backups/mongodb/20250115_020000/
# Restore a single database
mongorestore --uri="mongodb://admin:secret@localhost:27017" \
--gzip --drop --db mydb /backups/mongodb/20250115_020000/mydb/
# Restore a single collection
mongorestore --uri="mongodb://admin:secret@localhost:27017" \
--gzip --drop --db mydb --collection users \
/backups/mongodb/20250115_020000/mydb/users.bson.gz
S3 へのアップロード
#!/bin/bash
# s3_upload.sh — Upload backups to S3
set -euo pipefail
S3_BUCKET="s3://my-backups"
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d)
# Upload PostgreSQL backup
aws s3 cp "${BACKUP_DIR}/postgres/" "${S3_BUCKET}/postgres/${DATE}/" \
--recursive --storage-class STANDARD_IA \
--sse AES256
# Upload MySQL backup
aws s3 cp "${BACKUP_DIR}/mysql/" "${S3_BUCKET}/mysql/${DATE}/" \
--recursive --storage-class STANDARD_IA \
--sse AES256
# Upload MongoDB backup
aws s3 cp "${BACKUP_DIR}/mongodb/" "${S3_BUCKET}/mongodb/${DATE}/" \
--recursive --storage-class STANDARD_IA \
--sse AES256
echo "[$(date)] S3 upload complete for ${DATE}"
保持用の S3 ライフサイクルポリシー
{
"Rules": [
{
"ID": "BackupRetention",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Transitions": [
{ "Days": 30, "StorageClass": "GLACIER" }
],
"Expiration": { "Days": 365 }
}
]
}
aws s3api put-bucket-lifecycle-configuration \
--bucket my-backups \
--lifecycle-configuration file://lifecycle.json
Restic によるバックアップ(暗号化、重複排除)
# Initialize a restic repository on S3
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export RESTIC_PASSWORD="strong_encryption_password"
export RESTIC_REPOSITORY="s3:s3.amazonaws.com/my-backups-restic"
restic init
# Backup the local backup directory
restic backup /backups/postgres /backups/mysql /backups/mongodb
# List snapshots
restic snapshots
# Prune old snapshots — keep 7 daily, 4 weekly, 6 monthly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# Restore a snapshot
restic restore latest --target /restore/
Cron のスケジュール
# /etc/cron.d/db-backups
# PostgreSQL: nightly at 02:00
0 2 * * * backup /opt/scripts/pg_backup.sh >> /var/log/backup-pg.log 2>&1
# MySQL: nightly at 02:30
30 2 * * * backup /opt/scripts/mysql_backup.sh >> /var/log/backup-mysql.log 2>&1
# MongoDB: nightly at 03:00
0 3 * * * backup /opt/scripts/mongo_backup.sh >> /var/log/backup-mongo.log 2>&1
# Upload to S3: daily at 04:00
0 4 * * * backup /opt/scripts/s3_upload.sh >> /var/log/backup-s3.log 2>&1
# Local cleanup: keep 7 days of local backups
0 5 * * * backup find /backups -type f -mtime +7 -delete >> /var/log/backup-cleanup.log 2>&1
# Restic prune: weekly on Sunday at 06:00
0 6 * * 0 backup /opt/scripts/restic_prune.sh >> /var/log/backup-restic.log 2>&1
復旧の自動テスト
#!/bin/bash
# verify_backup.sh — weekly restore test
set -euo pipefail
BACKUP_FILE=$(ls -t /backups/postgres/mydb_*.dump | head -1)
echo "[$(date)] Starting backup verification with $BACKUP_FILE"
# Spin up a temporary PostgreSQL container
docker run -d --name pg-restore-test \
-e POSTGRES_USER=testuser \
-e POSTGRES_PASSWORD=testpass \
-e POSTGRES_DB=testdb \
postgres:16-alpine
# Wait for container to be ready
sleep 5
until docker exec pg-restore-test pg_isready -U testuser; do
sleep 2
done
# Copy backup into container and restore
docker cp "$BACKUP_FILE" pg-restore-test:/tmp/backup.dump
docker exec pg-restore-test pg_restore -U testuser -d testdb --clean --if-exists /tmp/backup.dump
# Run verification queries
USERS_COUNT=$(docker exec pg-restore-test psql -U testuser -d testdb -tAc "SELECT COUNT(*) FROM users;")
ORDERS_COUNT=$(docker exec pg-restore-test psql -U testuser -d testdb -tAc "SELECT COUNT(*) FROM orders;")
echo "[$(date)] Verification: users=$USERS_COUNT, orders=$ORDERS_COUNT"
# Cleanup
docker rm -f pg-restore-test
# Alert on failure
if [ "$USERS_COUNT" -lt 1 ]; then
echo "ALERT: Backup verification failed — users table is empty" >&2
exit 1
fi
echo "[$(date)] Backup verification PASSED"
統合バックアップスクリプト
#!/bin/bash
# backup_all.sh — unified backup orchestrator
set -euo pipefail
LOG="/var/log/backup-all.log"
ALERT_EMAIL="[email protected]"
ERRORS=0
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }
run_backup() {
local name="$1" script="$2"
log "Starting $name backup..."
if bash "$script" >> "$LOG" 2>&1; then
log "$name backup succeeded."
else
log "ERROR: $name backup FAILED."
ERRORS=$((ERRORS + 1))
fi
}
run_backup "PostgreSQL" /opt/scripts/pg_backup.sh
run_backup "MySQL" /opt/scripts/mysql_backup.sh
run_backup "MongoDB" /opt/scripts/mongo_backup.sh
run_backup "S3 Upload" /opt/scripts/s3_upload.sh
if [ "$ERRORS" -gt 0 ]; then
log "Backup run completed with $ERRORS error(s). Sending alert."
mail -s "BACKUP ALERT: $ERRORS failure(s)" "$ALERT_EMAIL" < "$LOG"
exit 1
fi
log "All backups completed successfully."
ベストプラクティス
- 3-2-1 ルール:データのコピーを 3 つ、異なる種類のメディアに 2 つ、オフサイトに 1 つ保管します。
- 保存時のバックアップを暗号化する:暗号化機能を内蔵した
restic、AWS SSE、またはgpgを使用します。 - 定期的に復元をテストする:一度も復元したことのないバックアップは、バックアップではありません。
- バックアップジョブを監視する:失敗があればすぐにアラートを出します。通知のない cron ジョブに頼らないでください。
- RTO と RPO を文書化する:各データベースの目標復旧時間と目標復旧時点を定めます。
- バックアップスクリプトをバージョン管理する:インフラストラクチャのコードと一緒に Git に保存します。
--single-transactionを使用する:一貫性のあるスナップショットを取得するため、MySQL と PostgreSQL の論理バックアップで使用します。- バックアップ用の認証情報を分ける:バックアップ専用の読み取り専用データベースユーザーを使用します。
トラブルシューティング
| 症状 | 考えられる原因 | 解決方法 |
|---|---|---|
pg_dump: too many clients | バックアップ接続がアプリケーションの接続プールと競合している | トラフィックの少ない時間帯に実行します。バックアップユーザー用に max_connections を 5 増やします |
大きなテーブルで mysqldump が停止する | テーブルロックの競合 | --single-transaction(InnoDB)を使用するか、メンテナンス時間帯に実行します |
レプリカ上で mongodump が遅い | 高負荷時にセカンダリから読み取っている | --readPreference=secondaryPreferred を使用し、ピーク時以外に実行します |
| タイムアウトで S3 へのアップロードが失敗する | 低速な接続で大きなファイルを転送している | aws s3 cp --expected-size または aws s3api によるマルチパートアップロードを使用します |
| Restic の prune に数時間かかる | スナップショットが蓄積しすぎている | restic forget --prune をより頻繁に実行し、スナップショット数を制限します |
| 「role does not exist」というエラーで復元に失敗する | バックアップにロール依存のオブジェクトが含まれている | 先にロールを作成するか、復元時に --no-owner --no-privileges を使用します |
関連スキル
- postgresql - PostgreSQL の管理と pg_dump の詳細
- mysql - MySQL の管理と mysqldump の詳細
- mongodb - MongoDB の管理と mongodump の詳細
- redis - Redis の RDB/AOF 永続化とバックアップ
コメント
Markdown 対応