用 systemd 定时器替代 cron,做一次“失败会报警”的备份

举报
yd_246130537 发表于 2026/10/26 09:48:09 2026/10/26
【摘要】 cron 备份失败时太安静,本文用 systemd service 加 timer 组合:写好 oneshot 备份服务、用 OnCalendar 定时并开启 Persistent 补跑、用 OnFailure 做失败报警,再用 systemctl 与 journalctl 排查,让备份从“希望在跑”变成“能证明在跑”。

很多服务器的备份都写在 cron 里:凌晨两点跑 rsync,看起来一直在工作,直到有一天需要恢复,才发现备份早就停了。cron 最大的问题不是不准时,而是失败时太安静:它没有统一的状态,没有现成的日志入口,机器关机错过时间也不会补跑。

用 systemd 的 timer 加 service 组合,就能把备份变成一个可查状态、可看日志、失败能感知到的任务。下面以每天凌晨备份网站目录为例。

先写 service:只做一件事,并算好退出码

新建 /etc/systemd/system/backup.service:

[Unit]
Description=每日网站备份
After=network-online.target
Wants=network-online.target
OnFailure=backup-alert.service

[Service]
Type=oneshot
User=root
WorkingDirectory=/srv/www
ExecStart=/usr/bin/rsync -a --delete /srv/www/ /backup/www/
ExecStartPost=/usr/bin/find /backup/www -maxdepth 1 -type d -name '20*' | sort | head -n -7 | xargs -r rm -rf

要点有三点:第一,用绝对路径,不要依赖 cron 里不确定的 PATH;第二,Type=oneshot 适合一次跑完的任务,退出码非零就代表失败,systemd 才能据此触发报警;第三,备份脚本里不要把“删除旧备份”和“备份”混成一个不可分步骤,中间失败要能看出来是哪一步出问题。写完先手动跑一次:

systemctl daemon-reload
systemctl start backup.service
systemctl status backup.service

再写 timer:定时间、能补跑、别扎堆

新建 /etc/systemd/system/backup.timer:

[Unit]
Description=每天凌晨执行网站备份

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10min
Unit=backup.service

[Install]
WantedBy=timers.target

OnCalendar 是日历表达式,比 cron 的五个字段更直观。Persistent=true 是关键:机器在 02:30 关机,第二天开机后会把错过的这一次补跑回来,cron 则直接跳过。RandomizedDelaySec 可以把多台机器的备份时间打散,避免同时打满磁盘和带宽。启用并检查下一次执行时间:

systemctl enable --now backup.timer
systemctl list-timers backup.timer

如果 list-timers 里能看到 NEXT 和 LEFT,说明定时器已经真正挂上去了,而不是只写了个文件。

失败会报警:关键在 OnFailure

service 里的 OnFailure=backup-alert.service,就是“失败会报警”的开关。再写一个只负责报警的 service:

[Unit]
Description=备份失败报警

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-alert.sh

报警脚本里不要只发一句“失败了”,要带上可定位的信息,例如:

#!/bin/bash
journalctl -u backup.service --since '1 hour ago' --no-pager | tail -n 100 > /tmp/backup-fail.log
# 通过你已有的企业微信、钉钉或邮件通道发送 /tmp/backup-fail.log

更稳妥的做法是先故意让它失败一次:把源目录临时改名,跑一次 service,确认报警真的能收到,再把目录改回来。没有演练过的报警,等于没有报警。

日常排查:先看状态,再看日志

备份出问题时,按这个顺序查,基本不会走弯路:

systemctl status backup.timer backup.service
systemctl is-failed backup.service
journalctl -u backup.service --since today
journalctl -u backup.service -p err --since '7 days ago'

status 能看到最近一次是成功还是失败,journalctl 能看到 rsync 报错的原文。不要只看“昨天有没有生成新文件”,文件时间戳可能是旧备份残留的,状态和日志才是准的。

几个容易踩的坑

一是环境变量:service 默认环境很干净,脚本里用到的命令、配置文件路径都要写绝对路径,必要时用 EnvironmentFile 显式传入。二是权限和工作目录:备份目录、目标目录的属主和 WorkingDirectory 要提前确认,别等到凌晨才发现写不进去。三是重叠执行:备份还没跑完,下一次又启动,容易互相覆盖;可以在 service 里用 flock 或者把任务拆成更短的批次,避免同一任务长时间重叠。四是只备不恢复:每月至少做一次恢复演练,从备份目录里随机抽一个文件恢复到临时目录,确认内容能打开,备份才算真正有效。

小结

cron 能跑定时任务,但它不管结果;systemd timer 的价值在于把“跑没跑、跑成没成、失败谁知道”都变成了可查询、可验证的状态。一个 service 管执行,一个 timer 管时间,一个 OnFailure 管报警,再加上 Persistent 补跑和 journalctl 排查,备份就从“希望它在跑”变成了“能证明它在跑”。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。