rsync同步脚本

rsync同步脚本

𝓓𝓸𝓷 Lv6

rsync同步文件到远程服务器脚本

一、创建ssh密钥认证

SSH 密钥认证(最推荐,安全且标准) ,如果使用nfs挂载目录方式,则跳过此步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这是 Linux 远程管理最标准、最安全的做法。

(1). 生成密钥对(在本地/源服务器执行,如果已有可跳过):
ssh-keygen -t rsa -b 4096
# 一路回车即可

(2). 将公钥复制到远程服务器:
ssh-copy-id user@remote_host
# 或者手动复制本地 ~/.ssh/id_rsa.pub 的内容到远程服务器的 ~/.ssh/authorized_keys 文件中

(3). 权限设置
# 本地修复
chmod 600 ~/.ssh/id_ed25519

# 远程修复
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

(4). 验证:
再次执行 rsync 命令,无需输入密码即可直接同步。

如果远程服务器修改了用户登录密码,‌SSH 密钥认证完全不受影响‌,无需重新生成密钥,也无需重新部署公钥。

只有在以下场景下,才需要处理 SSH 密钥:

场景 操作建议
‌私钥泄露或丢失‌ ‌必须重新生成‌。立即生成新的密钥对,将新公钥部署到服务器,并从服务器的 authorized_keys 中删除旧公钥。
‌更换客户端设备‌ ‌无需重新生成‌(可选)。你可以将原有的私钥安全复制到新设备,或者为新设备生成一套新的密钥对并部署公钥。
‌服务器重装系统‌ ‌需要重新部署‌。因为 ~/.ssh/authorized_keys 文件被清空,你需要将本地公钥再次复制到新系统中。
‌误删了 authorized_keys‌ ‌需要重新部署‌。只需将本地公钥再次追加到服务器的 ~/.ssh/authorized_keys 文件中即可,无需生成新密钥。

二、创建同步脚本

1.sync同步至nfs目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env bash

# ==============================================================================
# 严格模式设置
# ==============================================================================
set -euo pipefail # e: 出错立即退出; u: 未定义变量报错; o pipefail: 管道中任一命令失败则整体失败

# ==============================================================================
# 配置变量
# ==============================================================================
SRC_DIR="/backup"
DST_DIR="/backup-rsync"
LOG_FILE="/var/log/rsync_backup/sync.log"
LOCK_FILE="/tmp/rsync_backup.lock"
RSYNC_TIMEOUT=600 # rsync IO 超时时间
HEALTH_CHECK_TIMEOUT=10 # 健康检查超时时间

# ==============================================================================
# 函数定义
# ==============================================================================

# 日志函数:同时输出到屏幕和日志文件
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$msg" | tee -a "${LOG_FILE}"
}

# 清理函数:确保脚本退出时释放锁
cleanup() {
# 如果文件描述符 200 已打开,则解锁并关闭
if [ -n "${LOCK_FD:-}" ]; then
flock -u 200 2>/dev/null || true
exec 200>&- 2>/dev/null || true
fi
}
trap cleanup EXIT

# 健康检查函数:检查目录是否可读写且非空(可选)
check_dir_health() {
local dir="$1"
local label="$2" # 用于日志标识 "Source" 或 "Destination"

# 1. 检查目录是否存在且为目录
if [ ! -d "$dir" ]; then
log "ERROR: ${label} directory '$dir' does not exist or is not a directory."
return 1
fi

# 2. 检查是否可写 (对于源目录,至少需要可读;对于目标,需要可写)
# 这里统一检查可写性,因为 rsync 需要在目标端创建临时文件
# 如果是纯源端检查,可以改为 [ -r "$dir" ] && [ -x "$dir" ]
local test_f="$dir/.health_check_tmp_$$"
if ! timeout "${HEALTH_CHECK_TIMEOUT}" bash -c "echo 'test' > '$test_f' && rm -f '$test_f'" 2>/dev/null; then
log "ERROR: ${label} directory '$dir' is not writable or IO is stuck (NFS stale?)."
return 1
fi

# 3. 额外安全检查:防止源目录为空导致 --delete 误删目标
if [ "$label" == "Source" ]; then
# 尝试列出少量文件,确认源目录非空且可读
if [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
log "WARNING: Source directory '$dir' appears empty. Aborting to prevent accidental deletion on destination."
return 1
fi
fi

return 0
}

# ==============================================================================
# 主逻辑
# ==============================================================================

# 1. 获取排他锁 (Non-blocking: 如果已被锁定,则直接退出,避免排队)
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
log "ERROR: Another instance is running (lock file: $LOCK_FILE). Exiting."
exit 1
fi
LOCK_FD=200 # 标记锁已获取,供 cleanup 使用

log "=========================================="
log "Backup sync started."
log "Source: $SRC_DIR"
log "Target: $DST_DIR"
log "=========================================="

# 2. 前置健康检查
# 检查源目录:必须存在、可读、非空(防止误删)
if ! check_dir_health "$SRC_DIR" "Source"; then
log "ABORTING: Source health check failed."
exit 1
fi

# 检查目标目录:必须存在、可写
if ! check_dir_health "$DST_DIR" "Destination"; then
log "ABORTING: Destination health check failed."
exit 1
fi

log "Health checks passed. Starting rsync..."

# 3. 执行 rsync
# 关键参数说明:
# --delete: 删除目标端多余文件,保持镜像一致
# --timeout: 防止 NFS/网络卡死导致进程永久挂起
# --partial: 保留部分传输的文件,支持断点续传
# --stats: 输出统计信息,方便日志分析
rsync -avz --delete \
--timeout="${RSYNC_TIMEOUT}" \
--partial \
--stats \
"$SRC_DIR/" "$DST_DIR/" >> "$LOG_FILE" 2>&1

# 4. 检查退出码
# 由于设置了 set -e,如果 rsync 失败,脚本会自动终止并跳到 trap
# 如果执行到这里,说明成功
log "Sync completed successfully."

log "=========================================="
log "Backup sync finished."
log "=========================================="

exit 0

‌含义:

-n 等同于 --dry-run (模拟运行)‌

它会让 rsync 执行完整的同步检查流程,‌列出所有将会被传输、更新或删除的文件,但不会实际执行任何文件复制或修改操作‌。

主要用途:

  • 安全预览‌:在执行可能具有破坏性的操作(如使用 --delete 参数)之前,先确认哪些文件会被影响,防止误删重要数据。

  • 调试命令‌:验证排除规则(--exclude)或包含规则是否按预期工作。

  • 估算传输量‌:查看即将同步的文件列表和大小,预估所需时间和带宽。

常用组合示例:

通常与 -v(详细模式)一起使用,以便清晰地看到输出结果:

--delete 用于实现‌镜像同步(Mirror Sync)‌。它的作用是删除目标目录($DST_DIR)中存在、但源目录($SRC_DIR)中不存在的文件和目录。

‌核心逻辑:‌

  • ‌以源为准‌:同步结束后,目标目录的内容将与源目录完全一致。
  • ‌清理多余文件‌如果源端删除了某个文件,执行同步后,目标端对应的文件也会被删除。
  • ‌应用场景‌:常用于静态网站部署、代码发布或需要保持两端数据严格一致的备份场景。

‌⚠️ 高危风险警告:‌
这是 rsync 中最危险的参数之一。如果源目录因为挂载失败、权限不足或路径错误导致内容为空或不完整,--delete 会忠实地将目标目录中的大量数据清空。

  • ‌最佳实践建议‌:在执行包含 --delete 的命令前,务必先使用 -n (即 --dry-run) 参数进行模拟运行,检查将要删除的文件列表是否符合预期。

    1
    rsync -avzn --delete "$SRC_DIR/" "$DST_DIR/"
2.rsync通过ssh同步到远程目录

需要在第一步中创建ssh密钥认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/bin/bash

# ==============================================================================
# 严格模式设置
# ==============================================================================
set -euo pipefail # e: 出错立即退出; u: 未定义变量报错; o pipefail: 管道中任一命令失败则整体失败

# ==============================================================================
# 配置变量
# ==============================================================================
DATE_D=$(date +%Y%m%d)
SYNC_DAYS=2
BACKUP_PATH="/backup"
REMOTE_USER="tidb"
REMOTE_HOST="172.28.6.120"
REMOTE_PATH="/backup/10.195.6.31"
LOGFILE="/home/tidb/script/tidb_rsync_backup_${DATE_D}.log"
LOCKFILE="/tmp/rsync_backup.lock"

# ==============================================================================
# 函数定义
# ==============================================================================

# 日志函数:同时输出到屏幕和日志文件
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$msg" | tee -a "${LOGFILE}"
}

# 清理函数:确保脚本退出时释放锁
cleanup() {
# 如果文件描述符 200 已打开,则解锁并关闭
if [ -n "${LOCK_FD:-}" ]; then
flock -u 200 2>/dev/null || true
exec 200>&- 2>/dev/null || true
fi
}
trap cleanup EXIT

# ==============================================================================
# 主逻辑
# ==============================================================================

# 1. 获取排他锁 (Non-blocking: 如果已被锁定,则直接退出,避免排队)
exec 200>"$LOCKFILE"
if ! flock -n 200; then
log "ERROR: Another instance is running (lock file: $LOCKFILE). Exiting."
exit 1
fi
LOCK_FD=200 # 标记锁已获取,供 cleanup 使用

log "=========================================="
log "Backup started. Date: ${DATE_D}"
log "Source: ${BACKUP_PATH}"
log "Target: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}"
log "=========================================="

# 2. 前置检查:确保备份目录存在
if [ ! -d "$BACKUP_PATH" ]; then
log "ERROR: Backup path '$BACKUP_PATH' does not exist."
exit 1
fi

# 3. 执行同步
# 注意:使用 subshell () 包裹 cd,防止改变当前脚本的工作目录
# 使用 --files-from=- 配合 find 的相对路径
(
cd "$BACKUP_PATH"

# 查找最近 SYNC_DAYS 天内修改的文件
# 注意:find 输出相对路径,如 ./data/file.txt
find . -type f -mtime -"${SYNC_DAYS}" -print0 | \
rsync -avz \
--partial \
--append-verify \
--timeout=600 \
--files-from=- \
--from0 \
-e "ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3" \
./ "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}"
) >> "$LOGFILE" 2>&1

# 4. 检查退出码
# 由于设置了 set -e 和 pipefail,如果上面命令失败,脚本会自动终止并跳到 trap
# 如果执行到这里,说明成功
log "Sync completed successfully."

# 5. 可选:清理旧备份逻辑(示例)
# find /backup -type f -mtime +30 -delete
# log "Old backups cleaned."

log "=========================================="
log "Backup finished."
log "=========================================="

exit 0


改进:
# 优化点:
# 1. 移除 --checksum:避免全量哈希计算,极大提升启动速度。
# 2. 保留 --append-verify:确保断点续传的安全性。
# 3. 添加 --inplace (可选):如果文件很大且经常微调,--inplace 比默认的先传临时文件再改名更快,但会失去原子性。对于备份,默认行为(临时文件)更安全,建议先不加 inplace。
# 4. 添加 -h (--human-readable):方便看日志
  • Title: rsync同步脚本
  • Author: 𝓓𝓸𝓷
  • Created at : 2026-11-05 12:30:56
  • Updated at : 2026-11-05 14:08:08
  • Link: https://www.zhangdong.me/linux-rsync-script.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
评论