シェルスクリプトでmysqldumpの自動バックアップを設計する方法|世代管理・エラートラップ・ローテーション実装パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトでmysqldumpの自動バックアップを設計する方法|世代管理・エラートラップ・ローテーション実装パターン
「シェルスクリプトでmysqldumpのバックアップを取っているが、失敗しても翌朝まで気づかない」
「バックアップファイルがどんどん増えてディスクを圧迫している」

このような状況は、エラーハンドリングと世代管理を組み込んでいないスクリプトでよく起きます。mysqldumpは単体では失敗を通知してくれません。接続エラーや権限不足のとき、終了コード0を返す設定になっていると、cronで夜間に回していると翌朝まで気づけないことがあります。

この記事では、シェルスクリプトでmysqldumpの自動バックアップを設計するパターンを解説します。接続情報の安全な管理・trapによるエラー処理・世代管理ローテーション・バックアップ検証まで、実際の運用で動くスクリプトを例示しながら説明します。RHEL 9.4 / Ubuntu 24.04 LTSで動作確認済みです。

この記事のポイント

・mysqldump失敗を検出するにはset -euo pipefailとtrap ERRの組み合わせが基本
・接続情報は.my.cnfに外部化するとスクリプト内にパスワードを書かずに済む
・findコマンドで指定日数より古いダンプファイルを自動削除して世代管理できる
・gzip -tで圧縮ファイルの整合性チェックをバックアップ直後に自動実行する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

cronにmysqldumpを直書きするだけでは足りない理由

次のようなcrontab設定でバックアップを回しているケースをよく見かけます。

# NG例:エラー検知も世代管理もない単純なcron設定 0 2 * * * mysqldump -u root -pPASSWORD mydb | gzip > /backup/mydb_$(date +%Y%m%d).sql.gz

この設定には3つの問題があります。

エラーを検知できない: mysqldumpが失敗してもcronは正常終了として記録し、空ファイルや不完全なダンプが残る
パスワードの平文埋め込み: crontabにパスワードを直書きするとps auxで他のユーザーに見える環境もある
ディスク枯渇: 世代管理がなく古いバックアップが削除されないためディスクが徐々に埋まっていく

これらを一気に解決するのが、シェルスクリプトを挟んだバックアップ設計です。

基本スクリプトの設計 — 接続情報管理・日付ファイル名・trap EXIT

1. 接続情報を .my.cnf で管理する

MySQL 5.6以降は~/.my.cnf(または任意パスの設定ファイル)に接続情報を書いておくと、コマンドライン引数にパスワードを渡さずに済みます。

# /etc/mysql/backup.cnf(rootが読める600権限で保存する) [client] user=backup_user password=s3cret_pass host=127.0.0.1

# 権限設定(他ユーザーに読まれないよう必ず実施) $ chmod 600 /etc/mysql/backup.cnf $ ls -la /etc/mysql/backup.cnf -rw------- 1 root root 61 Sep 21 09:00 /etc/mysql/backup.cnf

スクリプト側では--defaults-extra-fileオプションで読み込みます。--defaults-fileと違い、他のデフォルト設定ファイルも継続して読み込まれます。

2. ダンプファイル名に日付を付ける設計

#!/bin/bash set -euo pipefail MYSQL_CNF="/etc/mysql/backup.cnf" BACKUP_DIR="/backup/mysql" DB_NAME="mydb" DATE=$(date +%Y%m%d_%H%M%S) DUMP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz" TMPFILE="" # バックアップディレクトリがなければ作成 mkdir -p "$BACKUP_DIR"

date +%Y%m%d_%H%M%Sで秒まで含むファイル名にすると、同日に複数回実行しても上書きされません。set -euo pipefailは最初に必ず書き、意図しないエラーのスルー防止を全体に適用します。

3. trap EXIT で確実なクリーンアップ設計

#!/bin/bash set -euo pipefail MYSQL_CNF="/etc/mysql/backup.cnf" BACKUP_DIR="/backup/mysql" DB_NAME="mydb" DATE=$(date +%Y%m%d_%H%M%S) DUMP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz" TMPFILE="" # 後片付け関数(EXITで必ず呼ばれる) cleanup() { local exit_code=$? if [[ -n "$TMPFILE" && -f "$TMPFILE" ]]; then rm -f "$TMPFILE" fi if [[ $exit_code -ne 0 ]]; then echo "[ERROR] バックアップが失敗しました(exit code: ${exit_code})" >&2 # 不完全なダンプファイルを削除 [[ -f "$DUMP_FILE" ]] && rm -f "$DUMP_FILE" fi } trap cleanup EXIT mkdir -p "$BACKUP_DIR" # 一時ファイルに書き出してから最終的にリネーム(原子的置換) TMPFILE=$(mktemp "${BACKUP_DIR}/.${DB_NAME}_XXXX.sql.gz.tmp") mysqldump --defaults-extra-file="$MYSQL_CNF" \ --single-transaction \ --routines \ --events \ "$DB_NAME" | gzip -c > "$TMPFILE" # 問題がなければ最終ファイル名にリネーム mv "$TMPFILE" "$DUMP_FILE" TMPFILE="" # cleanup関数がTMPFILEを削除しないようクリア echo "[OK] バックアップ完了: ${DUMP_FILE}"

trap cleanup EXITを先に書くことで、スクリプトがどんな理由で終了しても(エラー・シグナル・正常終了)cleanup関数が呼ばれます。TMPFILE=""でクリアする行は、正常完了時に不要なファイル削除をしないための分岐です。

--single-transactionはInnoDB向けのオプションで、テーブルロックをかけずに整合性のあるスナップショットを取得します。本番環境への影響を最小化するために必ず付けます。

世代管理とローテーションの設計

実運用でバックアップを取り続けるとディスクが枯渇します。findコマンドで古いファイルを削除する設計を組み込みましょう。

シェルスクリプトの世代管理やログローテーション設計についてはシェルスクリプト実践講座(Linux Master Pro)でも体系的に解説しています。

4. find コマンドで古いバックアップを自動削除する

# バックアップ保持日数の設定 RETENTION_DAYS=14 # 削除前に残存ファイル数を確認 BEFORE_COUNT=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" | wc -l) # 14日より古いバックアップを削除 find "$BACKUP_DIR" \ -name "${DB_NAME}_*.sql.gz" \ -mtime +"$RETENTION_DAYS" \ -delete AFTER_COUNT=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" | wc -l) DELETED=$(( BEFORE_COUNT - AFTER_COUNT )) echo "[INFO] ローテーション完了: ${DELETED}本削除(残存: ${AFTER_COUNT}本)"

-mtime +14は「最終更新から15日以上」のファイルが対象になります(+NはN日超)。意図した保持期間と1日ずれないよう注意してください。

5. 週次フル・日次差分の組み合わせ設計

MySQLには差分バックアップ(バイナリログベース)と論理バックアップ(mysqldump)があります。小中規模のDBであれば毎日フルダンプするのが最もシンプルですが、DB容量が大きい場合は次の設計が有効です。

週次(日曜): mysqldumpでフルダンプ(全テーブル)
日次(月~土): バイナリログを退避して差分として保管

# 曜日で処理を切り分ける(0=日曜, 1=月曜 ... 6=土曜) DOW=$(date +%w) if [[ "$DOW" -eq 0 ]]; then # 日曜: フルバックアップ TYPE="full" mysqldump --defaults-extra-file="$MYSQL_CNF" \ --single-transaction --all-databases | gzip -c > \ "${BACKUP_DIR}/all_${TYPE}_${DATE}.sql.gz" else # 月~土: バイナリログのフラッシュ&コピー TYPE="binlog" mysql --defaults-extra-file="$MYSQL_CNF" -e "FLUSH LOGS;" # バイナリログの保管は /var/lib/mysql/mysql-bin.* を対象にする rsync -a /var/lib/mysql/mysql-bin.* \ "${BACKUP_DIR}/binlog_${DATE}/" fi echo "[OK] ${TYPE} バックアップ完了"

バックアップ検証とエラー通知の設計

バックアップが「取れた」だけでは不十分です。「復元できるか」を担保するために、最低限の整合性チェックを組み込みます。

6. gzip -t でバックアップ直後に破損検証する

# バックアップ後すぐに整合性チェック if gzip -t "$DUMP_FILE" 2>/dev/null; then echo "[OK] 整合性チェック: ${DUMP_FILE}" else echo "[ERROR] ダンプファイルが破損しています: ${DUMP_FILE}" >&2 rm -f "$DUMP_FILE" exit 1 fi # ファイルサイズが極端に小さい場合もエラーとして扱う(100KB未満) FILE_SIZE=$(stat -c%s "$DUMP_FILE") if [[ "$FILE_SIZE" -lt 102400 ]]; then echo "[WARN] ダンプファイルが異常に小さいです: ${FILE_SIZE}バイト" >&2 fi

gzip -tはgzipファイルの整合性を検証するオプションで、展開せずに圧縮データの構造を確認します。破損したダンプファイルを「あり」と記録してしまう前に即座に検出できます。

7. エラー発生時のメール通知設計

ALERT_MAIL="admin@example.com" HOSTNAME_STR=$(hostname -s) notify_error() { local message="$1" local subject="[BACKUP ERROR] ${HOSTNAME_STR}: ${DB_NAME}" # mailxがある環境向け(sendmailの場合はsendmailコマンドに変える) echo "$message" | mailx -s "$subject" "$ALERT_MAIL" 2>/dev/null || \ echo "[WARN] メール送信に失敗しました" >&2 } cleanup() { local exit_code=$? if [[ -n "$TMPFILE" && -f "$TMPFILE" ]]; then rm -f "$TMPFILE" fi if [[ $exit_code -ne 0 ]]; then local msg="バックアップ失敗(exit: ${exit_code})\nDB: ${DB_NAME}\nサーバー: ${HOSTNAME_STR}\n時刻: $(date '+%Y-%m-%d %H:%M:%S')" echo -e "$msg" >&2 notify_error "$msg" [[ -f "$DUMP_FILE" ]] && rm -f "$DUMP_FILE" fi } trap cleanup EXIT

trap cleanup EXIT内でメール通知を呼ぶことで、スクリプトのどこでエラーが起きても確実に通知が飛びます。mailxコマンドはRHEL/Rocky LinuxではMailxパッケージ、UbuntuではbsdmailxかHeirloom mailxをインストールする必要があります。

よくある失敗パターンとトラブルシュート

8. mysqldump が exit 0 を返すのにダンプが不完全な場合

mysqldumpの終了コードはMySQLサーバーへの接続失敗時は1を返しますが、テーブルのロック失敗などは警告メッセージを出力したまま0を返すことがあります。set -o pipefailだけに頼らず、ダンプ後のファイルサイズチェックとgzip -tを必ず追加してください。

# NG: mysqldump の exit codeだけを信じる mysqldump -u root mydb > /backup/mydb.sql echo $? # 0が返っても不完全な可能性がある # OK: gzip -t でダンプ後に整合性検証を必ず追加する mysqldump ... | gzip -c > "$TMPFILE" gzip -t "$TMPFILE" || { echo "破損検出" >&2; exit 1; }

9. 「Access denied」で取得できないテーブルがある

backup_userに必要な権限が付与されていないと、一部のテーブルだけ欠落します。バックアップ専用ユーザーには最低限次の権限を付与します。

# バックアップ専用ユーザーに必要な権限 mysql> GRANT SELECT, RELOAD, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, EVENT, TRIGGER ON *.* TO 'backup_user'@'127.0.0.1'; mysql> FLUSH PRIVILEGES;

RELOADFLUSH TABLES WITH READ LOCKに必要です。REPLICATION CLIENTはバイナリログ位置の記録(--master-dataオプション)を使う場合に必要です。

10. gzip パイプで「Broken pipe」が出る

set -o pipefailが有効の場合、パイプラインの途中でエラーが起きると最後のコマンドでなく最初のコマンドの終了コードが採用されます。mysqldump ... | gzipでgzipが途中で失敗するとset -eでスクリプトが止まります。これは意図どおりの動作です。止まらない場合はset -o pipefailが有効になっているか確認してください。

本記事のまとめ

シェルスクリプトでmysqldumpのバックアップを設計するには、エラーハンドリング・接続情報の外部化・世代管理・整合性検証の4つを組み合わせることが重要です。

やりたいこと 実装パターン
接続情報を安全に管理する mysqldump --defaults-extra-file=/etc/mysql/backup.cnf(600権限のファイルを指定)
ダンプ失敗を確実に検出する set -euo pipefailtrap cleanup EXIT
一時ファイルの確実な後片付け mktempで一時ファイル生成後、trap cleanup EXITで削除
InnoDB をロックせずにダンプする mysqldump --single-transactionを付ける
古いバックアップを自動削除する find "$BACKUP_DIR" -mtime +14 -delete
ダンプファイルの破損を検出する gzip -t "$DUMP_FILE"をバックアップ直後に実行する
エラー時に即座に通知する trap cleanup EXIT内でmailxを呼ぶ
DB容量が大きい場合のバックアップ 週次フルダンプ + 日次バイナリログ退避の組み合わせ
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。