実際の現場から:NAS使用率が92%に達し、バックアップに3時間以上を要した課題
約半年前、自社で運用しているCentOS Stream 9のサービス環境において、バックアップ処理の負荷が限界に近づいていました。このクラスターはNode.js/PHPで構成された2つのWebアプリケーションと、約40GBのトランザクションデータを保持する1つのMySQLクラスターで構成されています。
毎晩、cronジョブによってMySQLのダンプスクリプトを実行し、ソースコードディレクトリをtarでまとめてからrsyncでNASに転送していました。しかし、運用開始から4ヶ月が経過した頃、システムに以下の3つの重大なボトルネックが浮き彫りになりました。
- 保持ポリシー(Retention Policy)を直近14日分に限定していたにもかかわらず、NASの2TBストレージの使用率が92%に達し警告状態に陥った。
- バックアップ所要時間が当初の45分から3時間20分にまで肥大化。ディスクI/Oが急増し、アプリケーションのクエリ処理に遅延が発生した(I/O waitが35%超に急上昇)。
- ローカルネットワーク経由で転送されるデータが完全なプレーンテキストであり、エンドツーエンドの暗号化(End-to-End Encryption)が行われていなかった。
なぜ従来のrsyncとtarでは限界を迎えたのか?
従来の手法は一見シンプルに見えますが、内部的には技術面で3つの大きな壁が存在していました。
1. ブロックレベル(チャンク単位)の重複排除(Deduplication)の欠如
40GBのデータベースファイルにおいて、日次で変更されるレコードは全体の2〜3%程度に過ぎません。それにもかかわらず、mysqldumpやtarは異なるチェックサムを持つ完全に新しいファイルを生成します。rsyncが最適化できるのは転送時のネットワーク帯域のみであり、NASサーバー側で日次ロールバック履歴を保持するには、14世代分の独立したフルバックアップを保存し続ける必要があります。試算すると、データベースだけで実に560GBもの容量を消費していました。
2. ハードリンク機能がデータベースダンプや圧縮ファイルに対して機能しない
ストレージ容量を節約するために、rsyncの--link-destオプションを活用するケースは珍しくありません。この手法は、変更頻度の低い静的画像やメディアファイルに対しては非常に有効です。しかし、.tar.gzなどの圧縮ファイルやSQLダンプに対しては完全に無力です。ファイル内部のバイト位置がわずかでもずれてinodeが変化すると、rsyncはファイル全体を新規ファイルとして保存せざるを得なくなります。
3. gzipによる単一CPUコアへの高負荷と見合わない圧縮効率
従来のgzipプロセスは、単一のCPUコアを何時間も100%専有し続けます。これはサーバーリソースを無駄に浪費するだけでなく、後続の処理パイプライン全体を遅延させる原因となっていました。
導入前に比較検討した3つの選択肢
既存のスクリプトを破棄する前に、ステージング環境を構築して3つのソリューションを実地検証しました。
- 案1:rsync + ZFS/Btrfsスナップショット(NAS側)。 スナップショット作成速度は高速です。しかし、NASサーバーのディスク再フォーマットが必要である点、ZFSプールの稼働に伴い大量のメモリ(RAM)を消費する点、そしてCentOS Stream 9クライアント側での暗号化処理に対応できない点がデメリットでした。
- 案2:Restic。 Go言語製ツールで、クラウドバックエンドへの対応に非常に優れています。ただし、テスト環境でリポジトリが1.5TBを超えた際、
restic checkやpruneなどのメンテナンスコマンドが3.2GB以上のRAMを消費し、リソースの限られた小規模VPSでは枯渇を招きやすい点が課題でした。 - 案3:BorgBackup(Borg)。 データをローリングハッシュによって動的なチャンクへと細分化します。リポジトリにはこれまでに存在しない新規チャンクのみが保存されます。さらに、Borgは高速なZstandard(zstd)圧縮、クライアント側でのAES-256暗号化に対応し、FUSEを利用してバックアップポイントをディレクトリとして直接マウントすることも可能です。
CentOS Stream 9におけるBorgBackupの導入・標準化手順
BorgBackupは「容量節約」「高速性」「省メモリ」という3つの要件を完璧に満たしていました。以下に、本番環境へ適用した実際の手順を解説します。
ステップ1:EPELからBorgBackupとFUSEをインストール
パッケージはEPEL(Extra Packages for Enterprise Linux)リポジトリから入手可能です。将来的なファイルマウントを見据え、BorgとFUSEモジュールを同時にインストールします。
# EPELを有効化し、BorgとFUSEをインストール
sudo dnf install -y epel-release
sudo dnf install -y borgbackup fuse
インストール完了後、バージョンを確認します。
borg --version
# 出力例: borg 1.2.8
ステップ2:リポジトリの初期化とRepokeyの安全なバックアップ
本構成例では、バックアップ先ディレクトリを/mnt/backup-storage/borg-repo(NASからNFSマウント済み)とします。推奨される暗号化モードはrepokey-blake2です。暗号化キーはリポジトリのヘッダー内に格納され、パスフレーズによって保護されます。
# リポジトリ格納用ディレクトリを作成
sudo mkdir -p /mnt/backup-storage/borg-repo
# リポジトリを初期化
sudo borg init --encryption=repokey-blake2 /mnt/backup-storage/borg-repo
実行時にパスフレーズの入力が求められます。複雑なパスワードを設定し、パスワードマネージャーへ厳重に保管してください。パスフレーズを紛失すると、すべてのデータを恒久的に失うことになります。続けて、予備のキーを安全なファイルへエクスポートしておきます。
# 予備キーをテキストファイルとしてエクスポートし、隔離された安全な場所に退避
sudo borg key export /mnt/backup-storage/borg-repo /root/borg-backup-repo.key
ステップ3:ZSTD圧縮アルゴリズムによる初回バックアップの実行
バックアップ対象はWebのソースコードである/var/wwwと、SQLダンプディレクトリ/var/backups/dbです。圧縮アルゴリズムにzstd,3を指定することで、gzipレベル6と同等の圧縮率を維持しながら、約3倍高速に処理できます。
# 現在のセッション用の一時的なパスフレーズを宣言
export BORG_PASSPHRASE='YourSecurePassword123@'
# 初回アーカイブを作成
sudo -E borg create --stats --progress --compression zstd,3 \
/mnt/backup-storage/borg-repo::'{now:%Y-%m-%d_%H:%M}' \
/var/www \
/var/backups/db \
--exclude '*.log' \
--exclude '/var/www/*/cache'
コマンドの実行が完了すると、ストレージ容量の統計サマリーが表示されます。2日目以降の実行時には、Deduplicated size(重複排除後サイズ)の列が劇的に減少していることが確認できます。実際にディスクへ書き込まれるデータ量は、従来の数十ギガバイトから数百メガバイト程度へと激減します。
ステップ4:シェルスクリプトとSystemd Timerによる自動化
単純なcronジョブに頼るのではなく、アーカイブ作成、古い世代の整理(prune)、不要領域の回収(compact)の3工程を統合したスクリプトを作成することを推奨します。/usr/local/bin/borg-backup.shを作成します。
#!/usr/bin/env bash
set -euo pipefail
export BORG_REPO="/mnt/backup-storage/borg-repo"
export BORG_PASSPHRASE="YourSecurePassword123@"
ARCHIVE_NAME="$(hostname)-$(date +%Y-%m-%d_%H%M%S)"
echo "[$(date)] Borgバックアップ処理を開始します..."
# 1. 新規アーカイブの作成
borg create --stats --compression zstd,3 \
::"$ARCHIVE_NAME" \
/var/www \
/etc \
/var/backups/db \
--exclude '*/cache' \
--exclude '*/tmp'
echo "[$(date)] アーカイブ作成完了。古いバックアップの整理を開始します..."
# 2. 古いアーカイブの削除:直近7日、4週間、6ヶ月分を保持
borg prune -v --list \
--keep-daily=7 \
--keep-weekly=4 \
--keep-monthly=6
# 3. メタデータ削除後の空きディスク領域を回収
borg compact
echo "[$(date)] すべてのバックアップ処理が正常に完了しました!"
rootユーザーのみがスクリプトの読み取りおよび実行を行えるよう、パーミッションを厳格に設定します。
sudo chmod 700 /usr/local/bin/borg-backup.sh
CentOS Stream 9において、Systemd TimerはI/O優先度(I/O priority)の制御やjournalctlによる一元的なログ収集が可能なため、従来のcronを代替する極めて有効な手段です。
サービス定義ファイル/etc/systemd/system/borg-backup.serviceを作成します。
[Unit]
Description=BorgBackup Daily Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/borg-backup.sh
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7
午前2時30分にジョブを起動するタイマー定義ファイル/etc/systemd/system/borg-backup.timerを作成します。
[Unit]
Description=Trigger BorgBackup daily at 02:30 AM
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
設定をリロードしてタイマーを有効化します。
sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer
# 次回実行予定時刻を確認
sudo systemctl list-timers borg-backup.timer
ステップ5:データの復元と検証
復元テストを行っていないバックアップは、バックアップが存在しないのと同じです。Borgでは状況に応じて柔軟にファイルを復元できる2つの方法が用意されています。
方法1:アーカイブ全体を直接展開・抽出する
export BORG_PASSPHRASE="YourSecurePassword123@"
# リポジトリ内のアーカイブ一覧を表示
sudo -E borg list /mnt/backup-storage/borg-repo
# 特定のバックアップアーカイブの一時リストア先ディレクトリへの展開
mkdir -p /tmp/restore && cd /tmp/restore
sudo -E borg extract /mnt/backup-storage/borg-repo::centos9-prod-2026-10-20_023000
方法2:リポジトリをディレクトリとしてマウントし、個別ファイルを抽出する
開発者が特定の設定ファイル1つや過去の単一SQLテーブルのみを必要としている場合、巨大なアーカイブ全体を展開する必要はありません。FUSEを介してリポジトリをマウントするだけで、通常のファイル操作コマンドから直接参照・コピーできます。
sudo mkdir -p /mnt/borg-mount
# リポジトリを読み取り専用(read-only)でマウント
sudo -E borg mount /mnt/backup-storage/borg-repo /mnt/borg-mount
# 通常のディスクパーティションと同様にファイルをブラウズ
ls -la /mnt/borg-mount
cp /mnt/borg-mount/centos9-prod-2026-10-20_023000/var/www/html/app/config.php /tmp/
# 確認完了後、ディレクトリをアンマウント
sudo umount /mnt/borg-mount
本番運用6ヶ月後の成果
CentOS Stream 9サーバー環境における実測メトリクスは、極めて明確な効果を証明しました。
- ストレージ容量: 以前は14日分のバックアップでNAS容量を約1.8TB消費していました。現在、Borgリポジトリはこれらすべてのデータをわずか310GBに集約しており、約83%のディスク容量削減を達成しました。
- バックアップ所要時間: 毎晩の総所要時間が3時間20分から6〜8分へと短縮されました。既存のチャンクの大部分は再読み込みやネットワーク転送が不要になったためです。
- システム負荷:
Nice=19および低優先度I/Oクラスの設定により、午前2時30分のバックアップ実行時でもエンドユーザーからのリクエストクエリに一切の遅延やもたつきは見られなくなりました。

