最近经常有人问我Linux上怎么做定时任务,我自己摸索了挺久,踩过不少坑,把目前实际在用的方法整理了一下。
现在主要有两种方案:Cron 和 Systemd Timer。我自己现在新项目基本都用后者,主要是因为 Systemd Timer 比较新,但 Cron 也没完全淘汰,还是能满足日常需求。
定时需求场景
定时任务说白了就是让系统自己定时干活,常见的有:
- 每周日定时备份我的博客数据库
- 定期清理Docker镜像和日志
- 自动检查某个服务是不是还活着,挂了就重启或者告警
- 定时更新部署的开源服务,保持最新版本
当然大厂不会这样直接使用命令行来维护定时任务,我是个人服务器上使用,所以直接使用命令比较简单,也能满足我的需求。
下面介绍这两种定时任务使用方法:
Cron守护进程
作为开发,你可能多多少少了解过 Cron 表达式,所以 Cron 配置的时间也是下面的格式:
分钟 小时 日 月 星期 命令
用法很简单,只需在 Cron 表达式后面加一下 命令或bash文件 就可以了,我们输入 crontab -e 进行编辑模式:
# 每天凌晨2点备份0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1 # 每5分钟检查一次服务*/5 * * * * /usr/local/bin/check.sh # 工作日早上9点发报告0 9 * * 1-5 /usr/local/bin/report.sh
管理起来也很简单,crontab -e 编辑,crontab -l 查看。我一般会把输出都重定向到日志文件,方便查看定时任务执行日志,定位问题。
注意文件路径必须要是绝对路径,不然跑不起来!
还有定时任务执行是读取不到顶层环境变量,建议在定时任务运行的脚本文件头部添加你的环境变量,例如在文件 /usr/local/bin/check.sh 添加环境变量:
#!/bin/bashexport PATH=/usr/local/bin:/usr/bin:/binexport MY_VAR=my_valueexport LANG=zh_CN.UTF-8
Cron 使用就这么简单,接下来介绍下 Systemd Timer。
Systemd Timer
现在新机器我基本都切到 Systemd Timer 了。它要写两个文件,稍微麻烦一点,但使用起来感觉更舒服点。
举个我实际在用的例子,每天凌晨3点自动更新Docker Compose 项目。
先写服务文件 /etc/systemd/system/docker-update.service:
[Unit]Description=Daily Docker Compose UpdateAfter=docker.serviceRequires=docker.service [Service]Type=oneshotWorkingDirectory=/opt/myappExecStart=/bin/bash -c 'docker compose pull && docker compose up -d'
这里我踩过坑。一开始直接写 ExecStart=docker compose pull && docker compose up -d,结果根本不执行。systemd 默认不走 shell,&& 会被当成参数传进去。后来改成用 /bin/bash -c 包一层才正常。一般复杂一点的逻辑我现在都写成脚本再调用。
再写定时器文件 /etc/systemd/system/docker-update.timer:
[Unit]Description=Run Docker Update Daily at 3 AM [Timer]OnCalendar=*-*-* 03:00:00Persistent=trueRandomizedDelaySec=300 [Install]WantedBy=timers.target
Persistent=true 这个我配置很喜欢,机器如果刚好在定时执行时间点死机重启了,下次开机它会补跑一次。
RandomizedDelaySec 是为了避免一堆机器同时去拉镜像把仓库打挂。
启用定时任务:
sudo systemctl daemon-reloadsudo systemctl enable --now docker-update.timer
之后用 systemctl list-timers 看下次什么时候跑,用 journalctl -u docker-update.service 看日志,我觉得比 Cron 清爽多了。
时间格式我也记了几个常用的,更复杂的可以让 AI 帮你生成。
- 每天凌晨:
*-*-* 02:00:00或者直接写daily - 每周一早上:
Mon *-*-* 09:00:00 - 开机后5分钟:
OnBootSec=5min
如何选择定时任务
简单说一下我现在的判断标准:
| 场景 | 我更倾向用 |
|---|---|
| 一行就能搞定的小脚本 | Cron |
| 需要看日志、依赖其他服务、机器可能关机 | Systemd Timer |
| 要限制资源、做权限隔离 | Systemd Timer |
| 需要在非systemd的环境跑 | Cron |
生产环境我现在几乎都用Timer了。日志能直接用journalctl查,失败了也能看到原因,补跑机制也省心。Cron我留着处理一些临时的小任务,或者老机器上的遗留脚本。
其他几个我也偶尔用到的
at:只想跑一次的任务,比如“今天下午三点执行这个命令”。- anacron:笔记本或者经常关机的机器,它能处理错过的任务。
- 用户级Timer:不需要root权限的时候,把文件放到
~/.config/systemd/user/下面,用systemctl --user管理。
出问题了怎么查
- 任务没跑:先看
systemctl list-timers或者crontab -l,确认有没有启用。 - 跑了但失败:Timer的话直接执行
journalctl -u 服务名看日志,Cron 看重定向的日志。 - 执行的时间不对:用
timedatectl看一下系统时区,两者默认都用本地时间。
写在最后
我现在的习惯是:新任务优先用 Systemd Timer,老的小脚本继续用 Cron。两者都能干活,但Timer在日志和可靠性上确实更省心。尤其是你也在做自动更新、备份这类事的时候,花点时间把Timer配好,后面会少很多排查时间。
评论