Relayium

用 cron 任务定时做异地服务器副本

最近更新: 2026-09-01

需要你记得手动做的副本,往往就不会发生。cron 会记得,而 Relayium CLI 正是为此而生:一条非交互式命令,把目录复制(或镜像)到另一台机器,并校验它传输的每个文件。

先把定时任务能给你什么说清楚。push 和 sync 都是把文件此刻的样子放到另一台机器上,两者都不保留旧版本。尤其是定时 sync,会在下一次运行时把源端的删除或原地损坏一起带过去,而 --delete 更是把删除这一半明确打开。如果你需要上周那一版,要么像下面那样让 push 写进按日期命名的目录,要么在目标端保留快照。

本文介绍如何用 cron 定时运行 relayium push 和增量的 relayium sync、为什么反复运行的 push 需要一个全新的目标、两者共用的两种传输方式,以及可以直接抄走的 crontab 行。

push 与 sync:按日期的完整副本,还是一份持续更新的镜像

push 和 sync 都能把一个目录送到另一台机器,但只有其中一个是为往同一个位置反复运行而设计的。

push 每次都通过 SSH 或 daemon 直连发送一份完整拷贝;在远端装有 relayium 时,它会拒绝已存在的目标,而不是覆盖或续传。这让它完全不适合每晚指向同一个固定目录——第一晚成功,之后每晚都被拒——却恰好适合每次写进一个全新的、按日期命名的目录,而那也是这里唯一能给你留下旧副本的做法。push 还是唯一能对付没装 relayium 的裸服务器的命令,靠的是一条逐文件什么都不校验的 tar 兜底路径。

sync 则把一个目标目录维护成源目录的增量单向镜像:大小与修改时间都没变的文件会被跳过,只发送变化的部分,上一次中断留下的半截文件会在下次运行时接着传。sync 始终需要两端都用 relayium 的原生协议——它没有 tar 兜底方案。既然是镜像,它反映的就是当下而不是历史:源端删掉或损坏一个文件,下一次运行就会把这件事同步过去。

  • 希望每次运行各自独立、旧副本还能留着,就让 push 写进按日期命名的目标。
  • 目录很大或者经常变动,只想保留一份持续更新的副本、每晚重发全部内容太浪费,就用 sync。
  • 走原生协议时,两者都会对自己传输的文件做逐文件 SHA-256 校验。push 和 pull 都不续传;三者之中只有 sync 会接着传半截文件,而 tar 兜底路径既不校验也不续传。

开始之前

下面用到的都是 relayium CLI,所以没装的话先装上。在 macOS 或 Linux 上,一条命令就能把预编译二进制放进你的 PATH:

curl -fsSL https://relayium.com/install.sh | sh
  • 想自己挑文件,或在 Windows 上?从发布页下载二进制——relayium.com/cli 列出了所有安装方式(装了 Go 也可 go build ./cmd/relayium)。
  • relayium --version 可确认是否装好。不装这一步,下面的命令只会报 “command not found”。

两种传输方式:SSH 或daemon 直连

两个命令都可以指向一个 SSH 目标(scp 风格,走你的 ~/.ssh/config);如果对方机器正跑着 relayium serve,也可以用daemon 直连(daemon-direct)协议直接连过去——不需要 SSH。

# SSH 目标——使用你现有的 SSH 密钥和配置
relayium push ./data user@backup-server:/srv/backups/

# daemon 直连——目标机器运行着 "relayium serve",无需 SSH
relayium push ./data relayium://backup-server:9031
  • daemon 直连走的是带证书证书固定的 TLS 1.3:首次连接时信任(trust-on-first-use),之后每次运行都校验同一个指纹。
  • sync 接受和 push 完全相同的两种目标写法。

用 cron 定时运行

开始之前你需要什么

  • 本机装好 CLI;如果你打算用 sync,目标机器上也要装。sync 没有 tar 兜底。
  • 一把没有口令的 SSH 密钥,或者一台在跑 relayium serve 的目标机。cron 既没有 agent 也没有终端,回答不了口令提示。
  • 一个在 cron 触发的那一刻确实存在的源目录——不能是那种只有你登录时才挂上的网络挂载点。
  • 一个写日志的地方。输出无处可去的 cron 任务,等于一份等你真正需要时才会发现问题的备份。

push 和 sync 都是单条非交互式命令,可以直接放进 crontab。给它指定一个没有口令的密钥(或者用 agent),并把输出记下来,好让失败能被看见。注意 push 那一行的目标里带了日期:因为 push 会拒绝已存在的目标。在 crontab 里 % 必须写成 \%,裸的 % 表示命令到此为止:

# 每晚 2 点做一次按日期归档的完整复制——每次都是全新的目标,
# 所以冲突检查永远不会拒绝它——添加到你的 crontab(crontab -e)
0 2 * * * relayium push -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/$(date +\%F)/ >> ~/relayium-backup.log 2>&1

# 改为每 15 分钟做一次增量镜像
*/15 * * * * relayium sync -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/ >> ~/relayium-sync.log 2>&1
  1. 先查清 relayium 到底在哪。cron 不用你 shell 的 PATH,而 install.sh 在 /usr/local/bin 不可写时会退到 ~/.local/bin——那正好是 cron 看不见的地方。

    command -v relayium
  2. 确认这把密钥在没人守着键盘时也能用。BatchMode=yes 会直接失败而不是弹提示,这正是 cron 的处境。

    ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server true
  3. 先手动完整跑一次,写法要和 cron 将要执行的一模一样,包括绝对路径。

    /usr/local/bin/relayium push -i ~/.ssh/backup_key ~/documents user@backup-server:/srv/backups/
  4. 确认之后再加计划任务。绝对路径和重定向都要保留。

    crontab -e
  5. 第一次定时执行之后,去读日志,不要想当然。这一步是最容易被跳过的,也正是本可以提前告诉你问题的那一步。

    tail -n 20 ~/relayium-backup.log

一个配置正确的备份长什么样

relayium 解析出一个可以直接粘进 crontab 的绝对路径,并且那条 BatchMode 的 ssh 检查什么都不打印、什么都不问、退出码为 0。只在你的交互式 shell 里能跑通的备份,还不算配好了。

$ command -v relayium
/usr/local/bin/relayium
$ ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server true
$ echo $?
0
  • 只要有文件没通过完整性校验,命令就会以非零状态退出,cron 的失败邮件通知就能发现问题。
  • 被中断的 sync 会在下一次计划运行时补上:已匹配的跳过,半截的接着传。被中断的 push 不续传——但因为每晚都写进各自按日期命名的目录,第二晚是一次干净的完整复制,而不是一次拒绝。
  • 按日期命名的目录会越堆越多。请在目标端按你负担得起的节奏清理,否则磁盘会替你回答这个问题。

镜像删除与实时同步

默认情况下,sync 只会在目标端新增或更新文件。加上 --delete 才会变成真正的镜像,把源目录里已经不存在的文件也从目标端删掉——而谁需要同意这件事,取决于你用的是哪种目标。走 relayium:// 时,接收端是别人启动的独立进程,所以它必须以 serve --allow-delete 启动;否则这些删除会被跳过,并回报给你为 denied。走 SSH 时根本没有独立的监听端可以同意:sync 是通过你自己的 SSH 会话、以你的身份把接收端拉起来的——没有 --allow-delete 可设,传了 --delete 就是真删。本文里的 SSH 目标属于后一种情况。

两件事限定了破坏范围:删除只会发生在这一次运行真正发送的顶层目录内,目标端的兄弟目录永远不会被碰;而且如果源端解析不出任何文件,sync 会直接拒绝 --delete,所以源路径写错、或者该挂的没挂上,都清空不了目标目录。

不想等 cron 的下一个执行点,就用 --watch:它会让 relayium sync 常驻运行,源目录下一有文件变动,片刻之后就自动重新同步——比按计划轮询更轻量。

  • relayium sync ./data user@backup-server:/srv/backups/ --delete 会把删除也镜像过去,而走 SSH 时除了你自己没人需要同意。如果源端误删比目标端留个旧文件更糟,就别加 --delete。
  • relayium sync ./data relayium://backup-server:9031 --delete 只有在那个监听端以 serve --allow-delete 启动时才会真删;否则会回报为 denied。
  • relayium sync ./data user@backup-server:/srv/backups/ --watch 会常驻运行,一有变化就同步,而不是靠 cron 单次触发。

出问题时怎么办

这里每一种在你去看日志之前都是不可见的,所以那条日志重定向写在 crontab 行里,而不是可选项。第五种比不可见更糟:它看起来像成功。

现象、检查、修复

日志里写 relayium: command not found,但同一条命令在你的 shell 里能跑。
tail -n 5 ~/relayium-backup.log
# /bin/sh: relayium: command not found

cron 使用一个最小化的 PATH,通常只有 /usr/bin:/bin。如果 install.sh 当初写不进 /usr/local/bin,它会把二进制放在 ~/.local/bin,而 cron 永远找不到那里。把 command -v 给出的绝对路径写进 crontab 行,或者在 crontab 顶部单独加一行 PATH=。

日志显示 ssh 连接被拒绝,或者第一次运行之后就什么都没有了。
ssh -i ~/.ssh/backup_key -o BatchMode=yes user@backup-server true
# Permission denied (publickey).

cron 既没有 ssh-agent 也没有终端,所以带口令的密钥只能卡住或失败。用 -i 指向一把专供备份、没有口令的密钥,并用 BatchMode=yes 确认——它宁可拒绝也不会去等一个根本不在场的人。

sync 跑得很干净,但源端已删除的文件在目标端还在。
grep -i deni ~/relayium-sync.log

走 relayium:// 目标时,删除是接收端的显式选项:对端没有 serve --allow-delete,这些删除就会被跳过并回报为 denied,所以答案在日志里而不在退出码里,给那个监听器加上 --allow-delete 重启即可。走 SSH 目标时根本没有监听端可问,所以被拒不是原因——去确认 crontab 那行上真的写了 --delete,因为不加它 sync 只会新增和更新。

sync 直接拒绝执行 --delete。
relayium sync ~/documents user@backup-server:/srv/backups/ --delete
# refusing --delete with an empty source: this would delete everything on the destination. Check the path(s).

源端解析下来一个文件都没有,这时镜像会把目标端清空。这个拒绝是刻意的。检查路径是不是敲错了,也检查那里该挂载的东西在 cron 触发的时刻是否真的挂着,而不是只在你登录时才挂。

备份跑了,退出码 0,但它并不是你以为的那个东西。
ssh user@backup-server command -v relayium

远端没有 relayium 时,push 会退回到走 SSH 的普通 tar 流。文件确实到了,所以没有任何东西报警——但这条路径既没有逐文件 SHA-256 校验,也没有发送前的冲突预检,而这两点恰恰是你不用 scp 而设这个计划任务的理由,何况 tar -x -k 还可能让一批文件只装了一半。在目标机上装好 CLI 就能把它们拿回来。sync 不存在这种失败方式,因为它根本没有兜底:它会直接大声失败。

常见问题

备份服务器需要装 relayium 吗?

要看用哪条命令。push 不管远端装没装都能用:装了就走原生协议(发送前的冲突预检,外加对它传输的每个文件做 SHA-256 校验);没装的话,push 会退回到通过 SSH 传输 tar 流,一台裸服务器也照样能收,只是逐文件什么都不校验。sync 则始终需要远端有 relayium 的原生协议——它没有 tar 兜底方案,请先在远端装好。

这份副本会加密并校验吗?

传输层是加密的;逐文件校验也有,但有一个值得知道的边界。通过 SSH 或 daemon 直连推送时,字节已经受该连接自身的加密保护,不需要额外配置什么。走原生协议时,这一次运行传输的每个文件都会做端到端的 SHA-256 校验——但 tar 兜底路径不做任何哈希,而 sync 是按大小和修改时间决定要不要发送的,被它跳过的文件根本不会被读取,也就不会被哈希。目录总大小对得上只是一个粗略的自检,不能证明被跳过的文件内容仍然一致。

如果 cron 任务执行到一半被中断会怎样?

看你排的是哪条命令。sync 会接着来:下一次运行跳过已匹配的文件,并把半截的文件接着传,--no-resume 可以关掉这一点。push 在两条协议下都不续传——它会拒绝已存在的目标,这正是上面那行 push 写进按日期命名目录的原因,好让第二晚是一次干净的完整复制而不是一次拒绝。--no-resume 在 push 上能被接受,但什么也不做。

--delete 会不会不小心清空我的目标目录?

如果源目录里一个文件都没有,sync 会直接拒绝执行 --delete;删除也只会发生在这一次运行真正发送的顶层目录内。至于谁需要同意,取决于目标:走 relayium:// 时接收端必须以 serve --allow-delete 启动,删除才会生效,否则会被跳过并报告给你;走 SSH 时没有独立的监听端可以拒绝,传了 --delete 就是真删。

需要账号吗,这个要收费吗?

都不需要。CLI 的 push、pull、sync 都不需要账号,也不收费——传输走的是你自己的 SSH 连接,或者一条daemon 直连,不经过 Relayium 的服务器。

这算备份吗?

它是备份里“异地副本”那一半。定时 sync 保持一份目录的最新状态,这也意味着它会在下一次运行时把源端的删除或原地损坏一起带过去;定时 push 写进按日期命名的目录确实会留下旧副本,但也只在你不清理这些目录的期间有效,而且这里没有任何东西会替你清理或校验它们。请把它当作一份放在你自己硬件上的副本,如果需要恢复到上周的样子,再配一层快照或会做版本管理的工具。

把一份异地副本交给一个你不用记着的日程——传输加密、逐文件校验,而且免费。

获取 CLI

继续阅读