用 systemd 定时器替代 cron,做一次“失败会报警”的备份
很多服务器的备份都写在 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 排查,备份就从“希望它在跑”变成了“能证明它在跑”。
- 点赞
- 收藏
- 关注作者
评论(0)