LabHub
学习 学习路径 课程

SSH 与文件传输

scp、rsync、sftp,什么时候用哪个

在 LabHub 中继续学习

一句话总结

这三种工具都运行在 SSH 通道之上。区别在于:是一次发送全部内容(scp)、只发送差异(rsync),还是像对话一样交互传输(sftp)

概念图: 端口选项是大写的 -P。 · SFTP 协议 · 无法续传 · 有些内容不包含在 -a 中。

为什么需要三种工具

传一个文件却有三种工具,是有原因的。第一次发送一个 100 MB 文件、每天同步一个 10 GB 目录,以及在服务器上查看文件列表后挑选下载,三种场景所需要的特性并不相同。

它们是如何工作的

scp——简单复制

scp -P 2222 -i ~/.ssh/id_ed25519 ./app.tar.gz root@127.0.0.1:/tmp/
scp -r ./dist root@host:/srv/app/
scp -p ./config.yaml root@host:/etc/app/       # mtime/권한 보존
scp root@host:/var/log/app.log ./              # 내려받기

有两点需要注意。

scp 无法续传。传到 90% 时中断,就只能从头开始,所以它不适合大型传输。

rsync——只发送差异

rsync -aHAX --numeric-ids --delete --dry-run /srv/data/ backup@host:/backup/data/
rsync -avz -e 'ssh -p 2222 -i ~/.ssh/id_labhub' ./dist/ root@127.0.0.1:/srv/app/

必须准确理解各选项的含义。

还要注意末尾斜杠规则。这是 rsync 最容易出错的地方。

rsync -a /srv/data  /backup/    # /backup/data/... 가 만들어진다
rsync -a /srv/data/ /backup/    # /backup/... 에 내용이 바로 들어간다

源路径末尾有斜杠,表示复制“该目录的内容”;没有斜杠,则表示复制“该目录本身”。

--delete 具有破坏性。 如果在源目录为空或挂载已断开的状态下运行,它会删除目标端的全部数据。这是实际工作中经常发生的事故,所以脚本中必须加入防护条件。

set -euo pipefail
[ "$(ls -A "$SRC" | wc -l)" -gt 0 ] || { echo "source empty, abort" >&2; exit 1; }
rsync -aHAX --numeric-ids --delete "$SRC/" "$DST"

可以利用硬链接低成本地制作多代备份。

rsync -aHAX --delete --link-dest=/backup/daily.1 /srv/data/ /backup/daily.0/

--link-dest 不会复制未变更的文件,而是为它们创建指向指定目录中文件的硬链接。每一代看起来都像完整快照,磁盘空间却只用于保存变更部分。

sftp——交互式或批处理

sftp -P 2222 -i ~/.ssh/id_labhub root@127.0.0.1
sftp -b /root/batch.txt -P 2222 root@127.0.0.1

在批处理文件中写入 cdputgetlsmkdir,就能以非交互方式运行。它至今仍广泛用于与合作伙伴自动交换文件。还可以组合 ChrootDirectoryForceCommand internal-sftp,创建没有 shell、只允许传输的专用账户,这也是 sftp 的优势。

实际工作中会遇到的情况

使用不理解硬链接的工具复制多代备份,导致容量膨胀到三倍。 --link-dest 节省下来的空间,会在一次复制中全部消失。-H 不包含在 -a 中,因此必须单独指定。

备份占满带宽,干扰线上服务。 可以组合 --bwlimit=50Mionice -c 3 nice -n 19 来降低优先级。备份不再干扰生产后,反而可以把执行周期缩短。

下一个实验要做什么

连续完成两个实验。首先用 scp 和 sftp 往返传输文件,并通过校验和确认完整性;然后使用 rsync 亲手验证末尾斜杠的差异、制作 --delete 防护,并通过 --link-dest 构建多代备份。