PostgreSQLのダンプから暗号化・S3自動転送までをこなすShellバックアップスクリプトの設計

PostgreSQLのダンプから暗号化・S3自動転送までをこなすShellバックアップスクリプトの設計

2026/08/20
目次
開閉

「開発サーバーのハードディスクがクラッシュし、直近1週間分のデータが吹き飛んだ……」

インフラやデータベースを運用するエンジニアにとって、バックアップの未設定や失敗はもっとも恐ろしい災厄の一つです。「あとで設定しよう」と思っているうちに限って、障害や操作ミスによるデータ消失トラブルは発生します。

今回は、PostgreSQL(またはMySQL)のデータを自動でダンプ圧縮し、暗号化処理を施したうえで AWS S3(Amazon Simple Storage Service)へ安全にアップロードするBashスクリプト の構築方法を解説します。

バックアップスクリプト設計で満たすべき4つの要件

単に pg_dump を実行するだけでは、プロダクション運用には耐えられません。運用に耐えうるシェルスクリプトには、以下の要素が必須です。

  1. ディスク容量圧迫を防ぐ圧縮とローテーション: gzip で圧縮し、ローカルの古いファイルは自動削除する。
  2. クラウドストレージ(S3)への二重化退避: サーバー自体の破損に備え、地理的に離れた場所に保管する。
  3. 世代管理(ライフサイクルルール): S3側でも30日経過した古いバックアップを自動削除・Glacier退避させる。
  4. 異常発生時の即時通知: ダンプ処理や通信が失敗した場合、管理者の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 へ通知"]

検証環境と前提ツール

本記事で解説するスクリプトと検証ログは、以下の環境で動作確認を行っています。

プロダクション仕様の 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 "=== 全てのバックアップ処理が正常に完了しました ==="

スクリプトの堅牢性とセキュリティを高めるポイント

  1. set -euo pipefail によるエラー即時検知: 未定義変数の参照やパイプライン途中でのエラーを検知し、スクリプトを即座に安全停止させます。特に pg_dump ... | gzip のようなパイプラインでは、pg_dump 側が認証失敗やDB停止でエラーを吐いても、後続の gzip が空ファイルを生成して終了コード 0 を返してしまうリスクがあります。pipefail を有効化しておくことで、パイプライン内のいずれかのコマンドが失敗した時点で確実にトラップできます。
  2. AWS S3 サーバーサイド暗号化(--sse AES256)の明示: データベースダンプには顧客情報やアプリケーションの機密データが含まれます。aws s3 cp に --sse AES256 オプションを指定し、S3側の保管時暗号化(256ビット高度暗号化規格 AES-256)を適用した状態で格納します。組織のセキュリティ要件に応じて、AWS KMS管理キー(--sse aws:kms --sse-kms-key-id <Key-ID>)を指定する構成にもそのまま拡張できます。
  3. 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差異と運用時の注意点

  1. 改行コード(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改行)であることを確認してください。
  2. Dockerソケットのアクセス権限: cronを root 以外の専用一般ユーザーで実行する場合、ユーザーが docker グループに属していないと Got permission denied while trying to connect to the Docker daemon socket が発生します。事前に sudo usermod -aG docker <username> でグループ権限を付与しておくか、rootユーザーのcrontabで運用する設計とします。

運用で実感した自動化の価値

  1. 「データが守られている」という安心感: サーバーのメンテナンスやバージョンアップ作業を行う際も、常に最新のバックアップがS3に存在するため、不安なく作業に臨めるようになりました。
  2. リストア検証の容易さ: S3から最新の .sql.gz ファイルをダウンロードして gunzip するだけでローカル環境に本番相当のデータを復元できるため、バグ調査の効率が飛躍的に上がりました。
  3. コストを最小限に抑制: ダンプファイルを圧縮してS3標準ストレージやGlacierに保存することで、月額数万件のレコードを持つデータベースでも保管コストは数十円程度で収まります。

まとめ:バックアップは「復元テスト」までセットで考える

バックアップシステムを作って満足してはいけません。本当に重要なのは、「実際にそのバックアップファイルからデータを元通りに復元(リストア)できるか」を事前に試しておくことです。

スクリプトを作成したら、一度ローカルやステージングのテストDBに復元する手順まで実行してみてください。バックアップの全自動化は、エンジニアの平和な睡眠を守る最高の投資です。

シェルスクリプトによる自動バックアップ運用と並行して、日々のスクリプト実行やバッチ処理のログを堅牢に記録する設計については以下の記事もご覧ください。

Lowom 編集長
この記事を書いた人:Lowom 編集長 現役Webエンジニア

都内IT企業に勤める業界20年のWebエンジニア。業務効率化・自動化スクリプトや快適な開発環境の構築、厳選したツール・ガジェットの活用法など、手元で実際に検証したリアルな一次情報をお届けします。

運営者プロフィール詳細を見る