自作スクリプトでMastodonのバックアップ
バックアップ全体の方針
私はVPSを2台借りており、3つのWordpressサイト、1つのマストドンサイト、1つのPukiwikiサイトを運用しています。また重要なファイルのバックアップは AWS S3 に行っています。せっかくVPSが2台あるので以下のような構成を考えました。2つのサーバとAWSのうち、2箇所で障害がおきてもなんとかなる方式です。
また、バックアップの頻度・回数ですが、Mastodon はデータベースのバックアップを月4回(1ヶ月保存)、ファイルのバックアップを月1回(2ヶ月保存)としました。
マストドンのバックアップ取得
日次と月次で別のスクリプトを作り、rootの cron に登録します。日次は毎月3,12,20,28日に、月次は毎月2日にバックアップを行います。時間はUTCなので、登録は18時ですが、実際に走るのは夜の1時台(当方ベトナム在住のためUTC+7)です。なぜ曜日指定で走らせないかというと、曜日指定だと「1ヶ月前のファイルを削除する」処理が面倒になるからです(ファイルのタイムスタンプではなくファイル名に埋め込んだ日付で処理したいし)。
10 18 3,12,20,28 * * daily_mastodon_backup.sh
15 18 2 * * monthly_mastodon_backup.sh
日次バックアップのスクリプトは以下のようになります。アーカイブ名に20250223のようにyyyymmddで日付が入り、yyyymm(-1ヶ月)ddのファイルがあれば削除、という世代管理です。
#daily_mastodon_backup.sh
#変数定義
DATE=`/bin/date '+%Y%m%d'`
TODAY=`/bin/date '+%d'`
DELETE_MONTH=`/bin/date -d '1 month ago' +%Y%m`
ARCHIVE_DIR`/backups/mastodon`
WORK_DIR=`/backups/mastodon/tmp`
#Redis のバックアップ
cp /var/lib/redis/dump.rdb ${WORK_DIR}/dump.rdb_redis_mastodon
#nginx の設定バックアップ
cp /etc/nginx/conf.d/mastodon.conf ${WORK_DIR}/nginx_conf.d_mastodon.conf
#ポスグレのデータベースダンプ
sudo -u postgres pg_dumpall | gzip -c > ${WORK_DIR}/pg_dumpall_mastodon.gz
#dot.env.productionのバックアップ
cp /var/www/mastodon/.env.production ${WORK_DIR}/dot.env.production_mastodon
#圧縮して作業ユーザのアクセスを許可、テンポラリを削除
/usr/bin/tar zcfv " ${ARCHIVE_DIR}/mastodon_bkup_${DATE}.tgz" ${WORK_DIR}/*
chown -c username:www-data "${ARCHIVE_DIR}/mastodon_bkup_${DATE}.tgz"
rm -r ${WORK_DIR}/*
#一ヶ月前のファイルを除去
if [ -f "${ARCHIVE_DIR}/mastodon_bkup_${DELETE_MONTH}${TODAY}.tgz" ]; then
rm "${ARCHIVE_DIR}/mastodon_bkup_${DELETE_MONTH}${TODAY}.tgz"
fi
月次バックアップのスクリプトは以下のようになります。こちらはデータベースのダンプはとらず、ディレクトリまるごとバックアップ、ただし public/system/ 以下にあるメディアファイルは除外します(10GBとかになってしまうので)。アーカイブ名に202502のようにyyyymmで日付が入り、yyyymm(-2ヶ月)のファイルがあれば削除、という世代管理です。
#monthly_mastodon_backup.sh
#変数定義
DATE=`/bin/date '+%Y%m'`
DELETE_DATE=`/bin/date -d '2 month ago' +%Y%m`
ARCHIVE_DIR`/backups/mastodon`
WORK_DIR=`/backups/mastodon/tmp`
#バックアップ収集
/usr/bin/tar zcfv "${ARCHIVE_DIR}/mastodon_bkup_monthly_${DATE}.tgz" --exclude "public/system/*" -C /var/www/ mastodon
#作業ユーザのアクセスを許可
chown -c user1:www-data "${ARCHIVE_DIR}/mastodon_bkup_monthly_${DATE}.tgz"
# 過去のバックアップを削除
if [ -f "${ARCHIVE_DIR}/mastodon_bkup_monthly_${DELETE_DATE}.tgz" ]; then
rm "${ARCHIVE_DIR}/mastodon_bkup_monthly_${DELETE_DATE}.tgz"
fi
rsyncでもう1台のVPSに転送
とくになにも工夫していません。以下を一般作業ユーザの crontab に登録するだけ。
30 19 * * * rsync -avz --delete -e "ssh -p ポート番号 -i ~/.ssh/鍵ファイル" (アーカイブを置いたディレクトリ)* user1@VPS2:/(転送先ディレクトリ)/
転送先のVPSでチェックのうえAWS S3にSync
以下を一般作業ユーザの crontab に登録
15 21 * * * mtdn.transfer_s3.sh
スクリプトは以下です。2つのチェックを行っています
- DoBackup というファイルが存在しない場合はエラーを吐いて停止。転送元ディレクトリに異常があった場合、S3上の既存のファイルを消滅させないためです。
- 転送元ディレクトリにゼロバイトのファイルが1つでもあった場合はエラーを吐いて停止。転送元ファイルに異常があった場合、S3上の既存のファイルを消滅させないためです。
#mtdn.transfer_s3.sh
WORK_DIR=`/storage/backup_mtdn`
#ゼロバイトのファイルがあればメッセージを残し終了
cd ${WORK_DIR} #←なぜかこれがないと動かなかった。なぜだ。for in の変数の書き方が間違ってる?
for file in $(ls ${WORK_DIR}/); do
if [ ! -s ${file} ]; then
echo "Find zero byte file. exit."
exit
fi
done
#DoBackupファイルがあれば転送して、その後に転送先のtgzファイルの一覧を取得。DoBackupなければメッセージを残しエラー終了
if [ -e "${WORK_DIR}/DoBackup" ]; then
echo "MTDN: Sync to aws starting....."
aws s3 sync ${WORK_DIR}/ s3://(バケット名)/(保存ディレクトリ名)/ --delete --storage-class STANDARD_IA > /dev/null
aws s3 ls s3://(バケット名)/(保存ディレクトリ名)/ | grep tgz
else
echo "MTDN: Something wrong? transfer to aws not start"
exit
fi
あとは crondaemon からのレポートメールをみて、エラー終了していないか、アーカイブリストが想定通りかを日々確認するだけです。
これと同じようにして WordPress サイトのバックアップも行っています。そちらについては後日別エントリにて。