【新・実践シェルスクリプト講座 第3回】ローカル保存じゃ終わらない!「DBバックアップ」と「AWS S3」への自動転送スクリプト

【新・実践シェルスクリプト講座 第3回】サーバーが爆発しても大丈夫?「3-2-1ルール」を守るクラウド連携術

こんにちは!「リナックス先生」です。
前回は、サーバーが落ちた時に自動復旧させる「監視スクリプト」を作りました。
コウ君、あれからサーバーは安定してる?

コウ君

先生!前回のスクリプトのおかげで、深夜のダウンも勝手に直って通知だけ来るようになりました。
バックアップも、シーズン1で習った通り、サーバーの中に毎日保存してるので完璧です!
もう何も怖くないですね!

リナックス先生

甘いわね…。
もし、そのVPSが動いているデータセンターが火事になったらどうするの?
あるいは、間違ってサーバーを「解約(削除)」してしまったら?
「サーバーの中にあるバックアップ」は、サーバーと運命を共にするのよ。外に逃がさなきゃ意味がないわ!

新シリーズ「新・実践シェルスクリプト講座」の第3回は、**「クラウドストレージへの自動転送」**です。
データベース(MariaDB/MySQL)をダンプし、業界標準のクラウドストレージである **Amazon S3** へ自動アップロードする、プロ仕様のBCP(事業継続計画)対策スクリプトを作成します。

本講座のカリキュラム(全4回)

今回は、データを「外」へ逃がす技術を学びます。

  1. 黒い画面でポチポチするな!「Webサーバー構築(LAMP環境)」をボタン一発で全自動化する【完了】
  2. Webサイトが死んだら自動で蘇生せよ!「サービス死活監視」と「自動再起動」【完了】
  3. 【今回】ローカル保存じゃ終わらない!「DBバックアップ」と「AWS S3/クラウドへの自動転送」
  4. 黒い画面をアプリ化!?「対話型メニュー」で作る自分だけの管理ツール

1. バックアップの鉄則「3-2-1ルール」

IT業界には、データを守るための有名なルールがあります。

  • 3つのデータコピーを持つ(本番データ+バックアップ2つ)
  • 2種類の異なる媒体に保存する(サーバーのディスク+クラウドなど)
  • 1つは遠隔地(オフサイト)に保存する

今回は、サーバー内のデータベースを圧縮し、Amazon S3(物理的に離れた場所にある安全なストレージ)に送ることで、このルールを達成します。

2. 事前準備:AWS CLIのセットアップ

シェルスクリプトからS3を操作するには、aws-cli というコマンドツールを使います。
(※AWSのアカウントと、S3バケットの作成、IAMユーザーのアクセスキー取得は済んでいるものとします)

AWS CLIのインストール(AlmaLinux系)

# zip解凍用ツールを入れる
dnf install -y unzip

# AWS CLI v2のインストーラーをダウンロード&実行
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
./aws/install

# バージョン確認
aws --version

初期設定

取得しておいたアクセスキーを設定します。

[root@server ~]# aws configure
AWS Access Key ID [None]: AKIAXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: ap-northeast-1  (東京リージョン)
Default output format [None]: json

これで、aws s3 ls と打ってバケット一覧が見えれば準備完了です。

3. S3転送バックアップスクリプト「backup_db_s3.sh」

では、スクリプトを作成します。
仕様は以下の通りです。

  1. DBダンプ: mysqldump で全データをエクスポートし、gzip で圧縮する。
  2. S3転送: 作成したファイルを aws s3 cp でクラウドへ送る。
  3. 古い削除: サーバー内の古いバックアップは7日で消す(ディスク圧迫防止)。
  4. 通知: 失敗したらDiscordに通知する(前回作成した関数を流用)。

vim backup_db_s3.sh を作成します。

#!/bin/bash

# ==========================================
# 設定エリア
# ==========================================
# DB接続情報
DB_USER="root"
DB_PASS="YourPassword123" # 実際は .my.cnf で管理するのが推奨
DB_NAME="my_database"     # 全DBなら --all-databases オプションを使う

# バックアップ保存先(ローカル)
LOCAL_BACKUP_DIR="/var/backups/db"
# S3バケット名(最後にスラッシュは入れない)
S3_BUCKET="s3://my-server-backup-bucket"
# ローカル保存期間(日)
RETENTION_DAYS=7

# ファイル名定義 (例: db_backup_20260107_1200.sql.gz)
DATE_STR=$(date +%Y%m%d_%H%M)
BACKUP_FILE="db_backup_${DATE_STR}.sql.gz"
FULL_PATH="${LOCAL_BACKUP_DIR}/${BACKUP_FILE}"

# Discord Webhook URL
DISCORD_URL="https://discord.com/api/webhooks/xxxxxxxx/xxxxxxxxx"
# ログファイル
LOG_FILE="/var/log/backup_db.log"

# --- 安全設定 ---
set -u
set -o pipefail # パイプエラーも検知

# ==========================================
# 関数定義
# ==========================================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

send_discord() {
    local message="$1"
    local payload="{\"content\": \"📦 **[Backup Job]**\n${message}\"}"
    curl -s -H "Content-Type: application/json" -X POST -d "$payload" "$DISCORD_URL"
}

# ==========================================
# メイン処理
# ==========================================
log "--- バックアップ処理開始 ---"

# 1. ディレクトリ作成
if [ ! -d "$LOCAL_BACKUP_DIR" ]; then
    mkdir -p "$LOCAL_BACKUP_DIR"
fi

# 2. DBダンプ & 圧縮
log "データベースをダンプ中..."

# mysqldump実行
# --single-transaction: Web稼働中でもロックせずにバックアップ
if mysqldump -u"$DB_USER" -p"$DB_PASS" --single-transaction "$DB_NAME" | gzip > "$FULL_PATH"; then
    log "ダンプ成功: $FULL_PATH"
else
    log "【失敗】mysqldumpに失敗しました。"
    send_discord "⛔ DBバックアップ(dump)に失敗しました!ログを確認してください。"
    exit 1
fi

# 3. AWS S3へ転送
log "S3へアップロード中..."

# aws s3 cp [元] [先]
if aws s3 cp "$FULL_PATH" "${S3_BUCKET}/db/${BACKUP_FILE}"; then
    log "S3転送成功: ${S3_BUCKET}/db/${BACKUP_FILE}"
    send_discord "✅ DBバックアップをS3へ転送しました。\nFile: $BACKUP_FILE"
else
    log "【失敗】S3への転送に失敗しました。"
    send_discord "⚠️ DBダンプは成功しましたが、S3転送に失敗しました。"
    # 転送失敗してもローカルにはあるので、exit 1 せずに処理を続ける(掃除へ)
fi

# 4. 古いローカルファイルの削除
log "古いバックアップを削除中(${RETENTION_DAYS}日経過)..."
find "$LOCAL_BACKUP_DIR" -name "db_backup_*.sql.gz" -mtime +$RETENTION_DAYS -delete

log "--- バックアップ処理完了 ---"

💡 プロのテクニック:S3側の掃除はどうする?

このスクリプトは「ローカルの掃除」しかしません。
S3上の古いファイルを消すために aws s3 rm ... を書くのはスマートではありません。
AWS S3には「ライフサイクルルール」という機能があり、「作成から30日経過したら自動削除する」といった設定がWebコンソールから簡単にできます。
「消す処理」はクラウド側の機能に任せるのが、バグを生まないコツです。

4. 定期実行の設定

Cronに登録します。サーバー負荷を考慮して、アクセスの少ない深夜(例えばAM 4:00)に実行します。

# 実行権限付与
chmod +x backup_db_s3.sh

# crontab -e
# 毎日AM 4:00に実行
0 4 * * * /root/backup_db_s3.sh >> /var/log/cron_backup.log 2>&1

5. 「リストア(復旧)」の手順も確認!

バックアップは「戻せて」初めて意味があります。
VPSが全損したと仮定して、新しいサーバーでデータを戻す手順は以下のようになります。

# 1. AWS CLIを入れて設定する (aws configure)

# 2. S3からバックアップファイルを取得
aws s3 cp s3://my-server-backup-bucket/db/db_backup_20260107_1200.sql.gz .

# 3. 解凍してDBに流し込む
gunzip < db_backup_20260107_1200.sql.gz | mysql -u root -p my_database

この手順書をNotionや社内Wikiに残しておくところまでが、エンジニアの仕事です。

まとめ:枕を高くして寝るために

今回は「クラウドへのバックアップ転送」を自動化しました。

工程 使用コマンド ポイント
ダンプ mysqldump --single-transactionで停止させずに取得
圧縮 gzip 転送量と保管コストの削減
転送 aws s3 cp CLIツールで安全にクラウドへコピー

これで、サーバーが物理的に爆発しても、あなたのデータはAmazonの堅牢なデータセンターに残ります。
「データさえあれば、サーバーはいつでも作り直せる」。この安心感こそが、自動化の最大のメリットです。

次回予告:黒い画面をアプリに変える魔法

ここまでで、構築、監視、バックアップと、裏方の仕事はほぼ自動化できました。
しかし、日々のちょっとした作業(ログを見る、サービスの再起動、ステータス確認)のために、毎回長いコマンドを打つのは面倒ですよね?

次回は最終回。
read コマンドや case 文を駆使して、「番号を選ぶだけで管理作業ができる対話型メニューツール」を作成します。
あなたの黒い画面(ターミナル)が、便利なアプリのような見た目に進化しますよ!

リナックス先生

今回はAWS S3を使ったけど、Google Cloud StorageやDropboxなどを使いたい場合は rclone というツールがおすすめよ。
コマンドの書き方は違うけど、「作って、送る」という流れは一緒だから応用してみてね!

▼AWS CLIの練習もできる!root権限が使えるVPS

【2026年最新】Linuxサーバー構築におすすめのVPS比較3選!現役エンジニアが速度とコスパで厳選
Linuxの勉強、まだ「自分のPC」でやって消耗していませんか?「Linuxを覚えたいけど、環境構築でエラーが出て先に進めない…」「VirtualBoxを入れたらパソコンが重くなった…」これは、Linux学習を始める9割の人がぶつかる壁です...

コメント