Hướng dẫn dùng rsync để đồng bộ file trên Linux hiệu quả

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Vấn đề thực tế khi sao lưu và đồng bộ file

Mình từng dùng cp -r để backup thư mục web mỗi đêm. Nghe có vẻ ổn, nhưng sau vài tuần thì nhận ra vấn đề: cứ mỗi lần backup là copy toàn bộ vài GB dữ liệu, dù chỉ có vài chục file thay đổi. Kết quả là cron job chạy 30-40 phút, I/O disk tăng vọt, server ì ạch vào đúng giờ cao điểm.

Vấn đề nằm ở chỗ cp không phân biệt được file nào đã thay đổi — nó copy tất cả mà không cần biết. rsync ra đời đúng để giải quyết chuyện này.

rsync (Remote Sync) hoạt động theo nguyên tắc delta transfer — chỉ truyền phần dữ liệu thực sự thay đổi giữa nguồn và đích. Trên server production Ubuntu 22.04 với 4GB RAM mình đang quản lý, sau khi chuyển sang rsync, thời gian backup giảm từ 35 phút xuống còn 2-3 phút cho cùng lượng dữ liệu.

rsync đã có sẵn chưa? Kiểm tra trước khi cài

Thật ra bạn có thể không cần cài gì cả. Hầu hết distro Linux hiện đại đều đã có rsync sẵn — kiểm tra nhanh:

rsync --version

Ra output dạng rsync version 3.2.x thì dùng luôn, không cần cài thêm. Chưa có thì:

# Ubuntu / Debian
sudo apt install rsync

# RHEL / AlmaLinux / Rocky
sudo dnf install rsync

# Arch Linux
sudo pacman -S rsync

Với đồng bộ remote qua SSH, máy đích cũng cần có rsync. Kiểm tra luôn:

ssh user@remote-server "rsync --version"

Cú pháp cơ bản và các flag quan trọng

Cú pháp nhìn đơn giản:

rsync [options] source destination

Nhưng phần quan trọng là biết chọn flag nào. Đây là bộ mình hay dùng nhất, kèm giải thích thực tế:

  • -a — archive mode: giữ nguyên permission, owner, timestamp, symlink, recursive
  • -v — verbose: hiển thị file đang được sync
  • -z — nén dữ liệu khi truyền qua network (hữu ích với kết nối chậm)
  • -P — hiển thị progress bar + resume nếu bị gián đoạn
  • --delete — xóa file ở đích nếu ở nguồn không còn nữa
  • --dry-run hoặc -n — chạy thử, không thực sự thay đổi gì
  • --exclude — bỏ qua file/thư mục theo pattern

Các trường hợp dùng thực tế

1. Đồng bộ thư mục local sang local

Trường hợp đơn giản nhất — sao lưu từ ổ này sang ổ khác trên cùng máy:

# Sync thư mục /var/www/html sang /mnt/backup/www
rsync -av /var/www/html/ /mnt/backup/www/

# Lưu ý dấu / ở cuối source:
# /var/www/html/  → copy NỘI DUNG bên trong thư mục
# /var/www/html   → copy CẢ thư mục html vào đích

Dấu / ở cuối đường dẫn nguồn là chỗ hay nhầm nhất khi mới dùng rsync. Cứ chạy --dry-run lần đầu cho chắc:

rsync -av --dry-run /var/www/html/ /mnt/backup/www/

2. Đồng bộ lên remote server qua SSH

Use case mình dùng nhiều nhất — đẩy backup lên server khác:

# Push từ local lên remote
rsync -avz -P /var/www/html/ [email protected]:/backup/www/

# Pull từ remote về local
rsync -avz -P [email protected]:/var/www/html/ /local/backup/www/

# Chỉ định SSH key và port khác
rsync -avz -e "ssh -i ~/.ssh/id_rsa -p 2222" \
  /var/www/html/ user@remote:/backup/www/

3. Đồng bộ có xóa file cũ (mirror)

Khi muốn đích giống hệt nguồn — file nào xóa ở nguồn thì cũng xóa ở đích:

rsync -av --delete /var/www/html/ /mnt/backup/www/

Cẩn thận với --delete: nếu chạy nhầm chiều (source và destination bị đổi chỗ), sẽ xóa dữ liệu gốc. Luôn test với --dry-run trước.

4. Loại trừ file/thư mục không cần sync

# Bỏ qua thư mục cache, log, temp
rsync -av \
  --exclude='cache/' \
  --exclude='*.log' \
  --exclude='.git/' \
  --exclude='node_modules/' \
  /var/www/myapp/ user@remote:/backup/myapp/

# Dùng file exclude list (tiện hơn khi có nhiều pattern)
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. Backup tự động với cron

Để rsync chạy tự động, SSH không được hỏi password. Dùng SSH key là cách đơn giản nhất:

# Tạo SSH key không passphrase cho backup user
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N ""

# Copy public key lên remote
ssh-copy-id -i ~/.ssh/backup_key.pub user@remote-server

Sau đó tạo script backup:

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] Starting backup..." >> "$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] Backup completed successfully" >> "$LOG_FILE"
else
  echo "[$DATE] Backup FAILED with exit code $EXIT_CODE" >> "$LOG_FILE"
fi
EOF

chmod +x /usr/local/bin/backup-web.sh

Thêm vào crontab để chạy hàng đêm lúc 2 giờ sáng:

crontab -e
# Thêm dòng sau:
0 2 * * * /usr/local/bin/backup-web.sh

Làm sao biết backup có chạy đúng không?

Exit code — đừng bỏ qua

Sau mỗi lần chạy, rsync trả về exit code. Con số 0 là ổn, khác 0 là có vấn đề:

  • 0 — thành công
  • 1 — lỗi cú pháp
  • 11 — lỗi I/O đọc/ghi file
  • 23 — transfer một phần bị lỗi (thường do permission)
  • 30 — timeout kết nối

Script backup ở trên đã bắt exit code rồi. Kiểm tra nhanh khi chạy tay:

rsync -av /source/ /destination/
echo "Exit code: $?"

So sánh source và destination

Muốn chắc chắn hai thư mục đã giống nhau chưa? Chạy lại rsync với --dry-run — không có output nghĩa là đồng bộ hoàn toàn:

rsync -av --dry-run /source/ /destination/
# Không có dòng nào được liệt kê → hai thư mục giống nhau

Theo dõi log

# Xem log backup realtime
tail -f /var/log/rsync-backup.log

# Xem 50 dòng cuối
tail -n 50 /var/log/rsync-backup.log

# Lọc xem chỉ các lần backup thất bại
grep "FAILED" /var/log/rsync-backup.log

Đo tốc độ và hiệu quả

Thêm --stats vào là có ngay bảng thống kê sau khi chạy — số liệu thực tế thường bất ngờ lắm:

rsync -av --stats /var/www/html/ /mnt/backup/www/

# Output mẫu:
# 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  ← chỉ 4MB thay vì 2.45GB!
# Speedup is 593.93

Dòng "Speedup" nói lên tất cả. Con số 593 nghĩa là rsync chỉ truyền 1/593 lượng dữ liệu so với copy toàn bộ — tức khoảng 0.17% dữ liệu thực sự đi qua mạng.

Lỗi hay gặp và cách xử lý

Permission denied: File ở đích có permission khác với user đang chạy rsync. Chạy với sudo hoặc điều chỉnh ownership thư mục đích.

"skipping non-regular file": rsync gặp socket file hoặc device file không copy được. Thêm --exclude="*.sock" là xong.

Tốc độ chậm trên network yếu: Bật -z để nén. Nhưng với file đã nén sẵn như jpg, mp4, zip — nén lại không giúp được gì, bỏ -z đi còn nhanh hơn.

Dùng rsync ổn rồi thì bước tiếp theo đáng khám phá: --backup để giữ lại bản cũ trước khi ghi đè, hoặc kết hợp hardlink để làm incremental backup — nhiều snapshot lịch sử mà không tốn thêm disk space đáng kể.

Share: