Files

67 lines
2.3 KiB
Bash
Executable File

#!/bin/bash
# tver DBバックアップ(Synology ds923p / DSMタスクスケジューラで日次実行)
#
# 配置先: /volume1/docker/tver-db/backup-db.sh
# DSM設定: コントロールパネル → タスクスケジューラ → 作成
# → スケジュールされたタスク → ユーザー定義のスクリプト
# ユーザーはroot、実行周期は毎日とする。
# 実行コマンド: bash /volume1/docker/tver-db/backup-db.sh
#
# コンテナ内のpg_dumpを使い、サーバーとクライアントのバージョンを合わせる。
# PostgreSQLのローカル接続を使うため、DBパスワードはスクリプトへ書かない。
set -euo pipefail
BACKUP_DIR="${BACKUP_DIR:-/volume1/backups/tver}"
KEEP_DAYS="${KEEP_DAYS:-14}"
CONTAINER="${CONTAINER:-tver-postgres}"
DB_USER="${DB_USER:-tver}"
DB_NAME="${DB_NAME:-tver}"
DOCKER="${DOCKER:-$(command -v docker || printf '/usr/local/bin/docker')}"
STAMP="${STAMP:-$(date +%Y%m%d_%H%M%S)}"
OUT="${BACKUP_DIR}/tver_${STAMP}.sql.gz"
RAW_TMP="${BACKUP_DIR}/.tver_${STAMP}.$$.sql"
GZ_TMP="${OUT}.tmp.$$"
case "$KEEP_DAYS" in
''|*[!0-9]*)
echo "ERROR: KEEP_DAYS must be a non-negative integer: $KEEP_DAYS" >&2
exit 1
;;
esac
mkdir -p "$BACKUP_DIR"
umask 077
cleanup() {
rm -f "$RAW_TMP" "$GZ_TMP"
}
trap cleanup EXIT
# 先に非圧縮の一時ファイルへ出し、pg_dump成功と空でないことを確認する。
"$DOCKER" exec "$CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" > "$RAW_TMP"
if [[ ! -s "$RAW_TMP" ]]; then
echo "ERROR: dump is empty" >&2
exit 1
fi
gzip -9 -c "$RAW_TMP" > "$GZ_TMP"
if [[ ! -s "$GZ_TMP" ]]; then
echo "ERROR: compressed dump is empty" >&2
exit 1
fi
# 完成ファイルは上書きしない。同名が同時に作られても、ハードリンクの
# 作成に成功した一方だけを公開する。一時ファイルは同じディレクトリに
# あるため同一ファイルシステム内でリンクできる。
if ! ln "$GZ_TMP" "$OUT"; then
echo "ERROR: backup already exists or could not be published: $OUT" >&2
exit 1
fi
rm -f "$GZ_TMP" "$RAW_TMP"
# KEEP_DAYS日より古い完了済みバックアップだけを削除する。
find "$BACKUP_DIR" -name 'tver_*.sql.gz' -type f -mtime "+$KEEP_DAYS" -delete
SIZE_BYTES="$(wc -c < "$OUT" | tr -d '[:space:]')"
echo "OK: $OUT ($SIZE_BYTES bytes)"