ファイルのバックアップと同期における実際の問題
以前はcp -rを使って毎晩Webディレクトリをバックアップしていました。一見問題なさそうに見えましたが、数週間後に問題に気づきました。数十ファイルしか変更されていないのに、毎回数GBのデータを丸ごとコピーしていたのです。その結果、cronジョブの実行に30〜40分かかり、ディスクI/Oが急増し、ピーク時にサーバーが重くなっていました。
問題の本質は、cpがどのファイルが変更されたかを判断できないことにあります。変更の有無に関わらず、すべてをコピーしてしまうのです。rsyncはまさにこの問題を解決するために生まれました。
rsync(Remote Sync)はデルタ転送の原理で動作します。つまり、ソースと転送先の間で実際に変更されたデータ部分だけを転送します。私が管理しているUbuntu 22.04のproductionサーバー(RAM 4GB)では、rsyncに切り替えた後、同じデータ量のバックアップ時間が35分から2〜3分に短縮されました。
rsyncはすでにインストールされている?インストール前に確認
実は何もインストールしなくていい場合がほとんどです。最近のLinuxディストリビューションにはrsyncが標準で搭載されています。手軽に確認してみましょう:
rsync --version
rsync version 3.2.xのような出力が表示されれば、そのまま使えます。インストールされていない場合は:
# Ubuntu / Debian
sudo apt install rsync
# RHEL / AlmaLinux / Rocky
sudo dnf install rsync
# Arch Linux
sudo pacman -S rsync
SSH経由でのリモート同期の場合、転送先のマシンにもrsyncが必要です。確認してみましょう:
ssh user@remote-server "rsync --version"
基本的な構文と重要なフラグ
構文はシンプルに見えます:
rsync [options] source destination
ただし、重要なのはどのフラグを選ぶかを知ることです。よく使うフラグとその実践的な説明をまとめました:
-a— アーカイブモード:パーミッション、オーナー、タイムスタンプ、シンボリックリンクを保持し、再帰的にコピー-v— 冗長出力:同期中のファイルを表示-z— ネットワーク転送時にデータを圧縮(低速回線で有効)-P— プログレスバーを表示+中断した場合に再開--delete— ソースに存在しないファイルを転送先から削除--dry-runまたは-n— テスト実行。実際には何も変更しない--exclude— パターンに一致するファイル/ディレクトリをスキップ
実践的な使用例
1. ローカルディレクトリ間の同期
最もシンプルなケース:同じマシン上で別のドライブにバックアップする場合です:
# /var/www/html を /mnt/backup/www に同期
rsync -av /var/www/html/ /mnt/backup/www/
# ソースの末尾のスラッシュに注意:
# /var/www/html/ → ディレクトリの中身をコピー
# /var/www/html → htmlディレクトリごとコピー
ソースパスの末尾の/は、rsync初心者が最もよく間違えるポイントです。最初は--dry-runで確認しましょう:
rsync -av --dry-run /var/www/html/ /mnt/backup/www/
2. SSH経由でリモートサーバーに同期
最もよく使うユースケース:別のサーバーにバックアップをプッシュする場合です:
# ローカルからリモートへプッシュ
rsync -avz -P /var/www/html/ [email protected]:/backup/www/
# リモートからローカルへプル
rsync -avz -P [email protected]:/var/www/html/ /local/backup/www/
# SSHキーと別のポートを指定
rsync -avz -e "ssh -i ~/.ssh/id_rsa -p 2222" \
/var/www/html/ user@remote:/backup/www/
3. 古いファイルを削除しながら同期(ミラーリング)
転送先をソースと完全に一致させたい場合:ソースで削除されたファイルは転送先でも削除されます:
rsync -av --delete /var/www/html/ /mnt/backup/www/
--deleteの取り扱いに注意:ソースと転送先を逆にして実行すると、元のデータが消えてしまいます。必ず先に--dry-runでテストしてください。
4. 同期不要なファイル/ディレクトリを除外
# cache、log、tempディレクトリを除外
rsync -av \
--exclude='cache/' \
--exclude='*.log' \
--exclude='.git/' \
--exclude='node_modules/' \
/var/www/myapp/ user@remote:/backup/myapp/
# 除外リストファイルを使用(パターンが多い場合に便利)
cat > /etc/rsync-exclude.txt << 'EOF'
cache/
*.log
*.tmp
.git/
node_modules/
EOF
rsync -av --exclude-from='/etc/rsync-exclude.txt' \
/var/www/myapp/ user@remote:/backup/myapp/
5. cronを使った自動バックアップ
rsyncを自動実行するには、SSHでパスワード入力を求められないようにする必要があります。SSHキーを使うのが最もシンプルな方法です:
# バックアップ用にパスフレーズなしのSSHキーを生成
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N ""
# 公開鍵をリモートサーバーにコピー
ssh-copy-id -i ~/.ssh/backup_key.pub user@remote-server
次にバックアップスクリプトを作成します:
cat > /usr/local/bin/backup-web.sh << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/rsync-backup.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] バックアップを開始します..." >> "$LOG_FILE"
rsync -az --delete \
-e "ssh -i /root/.ssh/backup_key -o StrictHostKeyChecking=no" \
--exclude-from='/etc/rsync-exclude.txt' \
/var/www/html/ \
[email protected]:/backup/www/ \
>> "$LOG_FILE" 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo "[$DATE] バックアップが正常に完了しました" >> "$LOG_FILE"
else
echo "[$DATE] バックアップが失敗しました(終了コード: $EXIT_CODE)" >> "$LOG_FILE"
fi
EOF
chmod +x /usr/local/bin/backup-web.sh
毎晩午前2時に実行するようにcrontabに追加します:
crontab -e
# 以下の行を追加:
0 2 * * * /usr/local/bin/backup-web.sh
バックアップが正常に動作しているか確認する方法
終了コード(exit code)を見逃さない
実行後、rsyncは終了コードを返します。0は正常、それ以外は問題があることを示します:
0— 成功1— 構文エラー11— ファイルの読み書きI/Oエラー23— 部分的な転送エラー(多くはパーミッションの問題)30— 接続タイムアウト
上のバックアップスクリプトはすでに終了コードをキャプチャしています。手動実行時の簡単な確認方法:
rsync -av /source/ /destination/
echo "終了コード: $?"
ソースと転送先を比較
2つのディレクトリが完全に一致しているか確認したい場合は、--dry-runでrsyncを再実行します。出力がなければ同期が完了しています:
rsync -av --dry-run /source/ /destination/
# 何も出力されない → 2つのディレクトリは完全に一致している
ログの監視
# バックアップログをリアルタイムで確認
tail -f /var/log/rsync-backup.log
# 末尾50行を表示
tail -n 50 /var/log/rsync-backup.log
# 失敗したバックアップだけを抽出
grep "失敗" /var/log/rsync-backup.log
速度と効率の計測
--statsを追加するだけで、実行後に統計情報が表示されます。実際の数値を見ると驚くことが多いです:
rsync -av --stats /var/www/html/ /mnt/backup/www/
# 出力例:
# Number of files: 1,234
# Number of created files: 5
# Number of deleted files: 2
# Number of regular files transferred: 12
# Total file size: 2.45G bytes
# Total transferred file size: 4.23M bytes ← 2.45GBではなくたった4MB!
# Speedup is 593.93
「Speedup」の行がすべてを物語っています。593という数値は、rsyncが全コピーと比べて1/593のデータしか転送しなかったことを意味します。つまり、実際にネットワークを通過したのはわずか約0.17%のデータです。
よくあるエラーと対処法
Permission denied:転送先のファイルのパーミッションが、rsyncを実行しているユーザーと異なります。sudoで実行するか、転送先ディレクトリのオーナーシップを変更してください。
「skipping non-regular file」:rsyncがソケットファイルやデバイスファイルに遭遇し、コピーできません。--exclude="*.sock"を追加するだけで解決します。
低速ネットワークでの遅延:-zで圧縮を有効にしましょう。ただし、jpg、mp4、zipなどすでに圧縮されているファイルに対しては、再圧縮しても効果がありません。むしろ-zを外した方が速くなります。
rsyncを使いこなせたら、次のステップとして探求する価値があるのが:上書き前に古いバージョンを保持する--backupオプション、またはハードリンクと組み合わせた増分バックアップです。ディスクスペースをほとんど増やさずに多くの履歴スナップショットを保持できます。

