PostgreSQLのダンプから暗号化・S3自動転送までをこなすShellバックアップスクリプトの設計
目次
開閉
「開発サーバーのハードディスクがクラッシュし、直近1週間分のデータが吹き飛んだ……」
インフラやデータベースを運用するエンジニアにとって、バックアップの未設定や失敗はもっとも恐ろしい災厄の一つです。「あとで設定しよう」と思っているうちに限って、障害や操作ミスによるデータ消失トラブルは発生します。
今回は、PostgreSQL(またはMySQL)のデータを自動でダンプ圧縮し、暗号化処理を施したうえで AWS S3(Amazon Simple Storage Service)へ安全にアップロードするBashスクリプト の構築方法を解説します。
バックアップスクリプト設計で満たすべき4つの要件
単に pg_dump を実行するだけでは、プロダクション運用には耐えられません。運用に耐えうるシェルスクリプトには、以下の要素が必須です。
- ディスク容量圧迫を防ぐ圧縮とローテーション:
gzipで圧縮し、ローカルの古いファイルは自動削除する。 - クラウドストレージ(S3)への二重化退避: サーバー自体の破損に備え、地理的に離れた場所に保管する。
- 世代管理(ライフサイクルルール): S3側でも30日経過した古いバックアップを自動削除・Glacier退避させる。
- 異常発生時の即時通知: ダンプ処理や通信が失敗した場合、管理者のSlackやメールへアラートを発報する。
バックアップ処理フロー
graph TD
A["cron による夜間自動起動 (例: 午前3時)"] --> B["pg_dump でDBダンプ出力"]
B --> C["gzip によるデータ高圧縮 (.sql.gz)"]
C --> D["日時スタンプ付きファイル名の生成"]
D --> E["AWS CLI による暗号化付き S3 転送 (--sse AES256)"]
E --> F{"転送およびコマンド成功?"}
F -- "成功" --> G["ローカルの古い一時ファイルを削除"]
F -- "失敗" --> H["エラー詳細を管理者 Slack へ通知"]検証環境と前提ツール
本記事で解説するスクリプトと検証ログは、以下の環境で動作確認を行っています。
- OS: Ubuntu 24.04 LTS(※macOS Sonoma 14.5 / GNU bash 5.2 でも動作確認済み)
- シェル: GNU bash 5.2.21
- データベース: PostgreSQL 16.2 (Dockerコンテナ運用)
- CLIツール: AWS CLI v2.15.30
- ストレージ: Amazon S3(ライフサイクル設定によるGlacier自動移行)
プロダクション仕様の Bash バックアップスクリプト
以下は、PostgreSQLデータベースを対象とした実際の Bash スクリプト(backup_to_s3.sh)です。
#!/usr/bin/env bash
set -euo pipefail
# ==========================================
# cron実行時の環境変数対策(PATHの明示)
# ==========================================
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
# ==========================================
# 設定項目
# ==========================================
DB_CONTAINER_NAME="postgres_db"
DB_NAME="app_production"
DB_USER="postgres"
S3_BUCKET="s3://my-app-db-backups-bucket/postgres"
RETENTION_DAYS=7
LOG_FILE="/var/log/db_backup.log"
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
# 日時フォーマットの定義
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/tmp/db_backups"
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.sql.gz"
mkdir -p "${BACKUP_DIR}"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}
notify_slack_error() {
local message="$1"
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\": \"❌ *【バックアップ失敗通知】*: ${message}\"}" \
"${SLACK_WEBHOOK_URL}" || true
}
log "=== データベースバックアップ処理を開始します ==="
# 1. DBダンプ & 圧縮実行 (Dockerコンテナ経由を想定)
if docker exec "${DB_CONTAINER_NAME}" pg_dump -U "${DB_USER}" "${DB_NAME}" | gzip > "${BACKUP_FILE}"; then
log "DBダンプと圧縮に成功しました: ${BACKUP_FILE}"
else
log "ERROR: DBダンプの実行に失敗しました。"
notify_slack_error "pg_dump コマンドの実行に失敗しました。"
exit 1
fi
# 2. AWS S3 へのアップロード(保管時暗号化 SSE-S3 を明示)
if aws s3 cp "${BACKUP_FILE}" "${S3_BUCKET}/${DB_NAME}_${TIMESTAMP}.sql.gz" --sse AES256; then
log "S3への暗号化アップロードに成功しました: ${S3_BUCKET}"
else
log "ERROR: S3へのアップロードに失敗しました。"
notify_slack_error "AWS S3 へのファイル転送に失敗しました。"
exit 1
fi
# 3. ローカルの古いバックアップファイルの削除
find "${BACKUP_DIR}" -type f -name "*.sql.gz" -mtime +${RETENTION_DAYS} -exec rm -f {} \;
log "ローカルの保持期間(${RETENTION_DAYS}日)を過ぎた古いバックアップを清掃しました。"
log "=== 全てのバックアップ処理が正常に完了しました ==="スクリプトの堅牢性とセキュリティを高めるポイント
set -euo pipefailによるエラー即時検知: 未定義変数の参照やパイプライン途中でのエラーを検知し、スクリプトを即座に安全停止させます。特にpg_dump ... | gzipのようなパイプラインでは、pg_dump側が認証失敗やDB停止でエラーを吐いても、後続のgzipが空ファイルを生成して終了コード0を返してしまうリスクがあります。pipefailを有効化しておくことで、パイプライン内のいずれかのコマンドが失敗した時点で確実にトラップできます。- AWS S3 サーバーサイド暗号化(
--sse AES256)の明示: データベースダンプには顧客情報やアプリケーションの機密データが含まれます。aws s3 cpに--sse AES256オプションを指定し、S3側の保管時暗号化(256ビット高度暗号化規格 AES-256)を適用した状態で格納します。組織のセキュリティ要件に応じて、AWS KMS管理キー(--sse aws:kms --sse-kms-key-id <Key-ID>)を指定する構成にもそのまま拡張できます。 cron実行環境における PATH の落とし穴: cronデーモンは、ユーザーのログインシェルとは異なり最小限の環境変数(/usr/bin:/bin等)で実行されます。そのため、/usr/local/bin配下に配置されたawsやdockerコマンドが見つからず、夜間にサイレントエラーを起こすトラブルが現場で頻発します。スクリプト冒頭でexport PATH="/usr/local/bin:/usr/bin:/bin:$PATH"を定義しておくことで、cron環境下でも確実にコマンドを解決できます。
実際のバックアップ実行ログ
手元のUbuntu検証環境でスクリプトを手動実行した際の標準出力および /var/log/db_backup.log の記録ログです。
$ sudo /usr/local/bin/backup_to_s3.sh
[2026-08-20 03:00:01] === データベースバックアップ処理を開始します ===
[2026-08-20 03:00:07] DBダンプと圧縮に成功しました: /tmp/db_backups/app_production_20260820_030001.sql.gz
upload: ../tmp/db_backups/app_production_20260820_030001.sql.gz to s3://my-app-db-backups-bucket/postgres/app_production_20260820_030001.sql.gz
[2026-08-20 03:00:12] S3への暗号化アップロードに成功しました: s3://my-app-db-backups-bucket/postgres
[2026-08-20 03:00:12] ローカルの保持期間(7日)を過ぎた古いバックアップを清掃しました。
[2026-08-20 03:00:12] === 全てのバックアップ処理が正常に完了しました ===
AWS CLIでS3バケット内を確認すると、暗号化属性が付与されたダンプファイルが正常に格納されていることが確認できます。
$ aws s3 ls s3://my-app-db-backups-bucket/postgres/ --human-readable
2026-08-20 03:00:12 42.8 MiB app_production_20260820_030001.sql.gz実務で必須:リストア(復元)の検証手順とデータ整合性確認
バックアップを運用するうえで最も危険なのは、「取得できていると思っていたバックアップファイルが、実は中身が破損していて復元できなかった」という事態です。定期的にステージング環境や検証用コンテナへリストアし、データ整合性を確かめる手順を用意しておきます。
# 1. S3から対象日時のバックアップファイルを検証領域へ取得
aws s3 cp s3://my-app-db-backups-bucket/postgres/app_production_20260820_030001.sql.gz /tmp/restore_test.sql.gz
# 2. 検証用DBコンテナへ展開しながら直接リストア(解凍と投入をパイプラインで一括実行)
gunzip -c /tmp/restore_test.sql.gz | docker exec -i postgres_staging psql -U postgres -d app_staging
リストア実行後、主要テーブルのレコード件数や最新トランザクションの整合性をクエリで確認します。
-- ステージングDBでのデータ件数確認クエリ
SELECT count(*) FROM users;
-- count
-- --------
-- 148520
-- (1 row)
SELECT max(created_at) FROM orders;
-- max
-- -----------------------------
-- 2026-08-20 02:58:44.112035+00
-- (1 row)
ダンプ直前のトランザクション時刻と一致していることが確認できれば、バックアップと復元パイプラインの健全性が実証されます。
OS差異と運用時の注意点
- 改行コード(CRLF)トラブル:
Windows環境(WSLやPowerShell、VS Code等)でスクリプトを作成・編集してLinuxサーバーへ配置した場合、改行コードが CRLF になってしまい
bash: ./backup_to_s3.sh: /bin/bash^M: bad interpreter: No such file or directoryというエラーで停止することがあります。サーバー配置前に必ず LF(UNIX改行)であることを確認してください。 - Dockerソケットのアクセス権限:
cronを root 以外の専用一般ユーザーで実行する場合、ユーザーが
dockerグループに属していないとGot permission denied while trying to connect to the Docker daemon socketが発生します。事前にsudo usermod -aG docker <username>でグループ権限を付与しておくか、rootユーザーのcrontabで運用する設計とします。
運用で実感した自動化の価値
- 「データが守られている」という安心感: サーバーのメンテナンスやバージョンアップ作業を行う際も、常に最新のバックアップがS3に存在するため、不安なく作業に臨めるようになりました。
- リストア検証の容易さ:
S3から最新の
.sql.gzファイルをダウンロードしてgunzipするだけでローカル環境に本番相当のデータを復元できるため、バグ調査の効率が飛躍的に上がりました。 - コストを最小限に抑制: ダンプファイルを圧縮してS3標準ストレージやGlacierに保存することで、月額数万件のレコードを持つデータベースでも保管コストは数十円程度で収まります。
まとめ:バックアップは「復元テスト」までセットで考える
バックアップシステムを作って満足してはいけません。本当に重要なのは、「実際にそのバックアップファイルからデータを元通りに復元(リストア)できるか」を事前に試しておくことです。
スクリプトを作成したら、一度ローカルやステージングのテストDBに復元する手順まで実行してみてください。バックアップの全自動化は、エンジニアの平和な睡眠を守る最高の投資です。
シェルスクリプトによる自動バックアップ運用と並行して、日々のスクリプト実行やバッチ処理のログを堅牢に記録する設計については以下の記事もご覧ください。