<h2>一、为什么需要定时任务</h2>
<p>运维工作中大量工作带有"周期性":日志切割、数据备份、缓存清理、监控巡检、证书续期检查。靠人工每天重复执行既不现实也不可靠,定时任务就是把这类工作自动化托管给系统的核心手段。Linux 下主流方案有两个:<strong>Crontab</strong>(传统、普及、简单)和 <strong>Systemd Timer</strong>(现代、可控、适合服务化场景),本文从入门到生产实践逐一讲透。</p>
<h2>二、Crontab 基础:语法与常用写法</h2>
<pre># 编辑当前用户的定时任务
crontab -e
# 查看当前用户的定时任务
crontab -l
# 删除所有定时任务
crontab -r</pre>
<p>Crontab 每行由"时间表达式 + 命令"组成,时间表达式共 5 个字段:</p>
<pre>分 时 日 月 周 命令
* * * * * /usr/local/bin/backup.sh</pre>
<p>常用示例:</p>
<pre># 每天凌晨 2:30 执行备份
30 2 * * * /opt/scripts/backup.sh
# 每 10 分钟执行一次健康检查
*/10 * * * * /opt/scripts/healthcheck.sh
# 每天 8-18 点之间每隔 2 小时执行
0 8-18/2 * * * /opt/scripts/sync.sh
# 每周一至周五早上 9 点执行
0 9 * * 1-5 /opt/scripts/report.sh
# 每月 1 号和 15 号凌晨执行
0 0 1,15 * * /opt/scripts/cleanup.sh</pre>
<p>注意:<code>crontab -e</code> 编辑的是当前用户的表;系统级任务放在 <code>/etc/crontab</code>(多一个"用户"字段)或 <code>/etc/cron.d/</code> 目录下,root 权限即可管理全局任务。</p>
<h2>三、Crontab 进阶:环境变量、日志与常见坑</h2>
<p><strong>1. 环境变量问题(最高频的坑)</strong>:cron 执行时的 PATH 极其精简,很多命令找不到。脚本内务必使用绝对路径,或脚本开头显式声明环境:</p>
<pre>#!/bin/bash
# 脚本内推荐写法
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export LANG=en_US.UTF-8</pre>
<p><strong>2. 输出与日志</strong>:默认 cron 把输出用邮件发给用户(多数服务器没配邮件,输出就丢了),建议手动重定向:</p>
<pre>30 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1</pre>
<p><strong>3. 脚本要有锁,防止上次没跑完下次又启动</strong>:</p>
<pre>#!/bin/bash
LOCKFILE=/tmp/backup.lock
if [ -f "$LOCKFILE" ]; then
echo "已有备份任务在运行,退出"
exit 1
fi
touch "$LOCKFILE"
trap 'rm -f "$LOCKFILE"' EXIT
# 业务逻辑...
sleep 600</pre>
<p><strong>4. 注意系统时区</strong>:cron 按系统时区执行,服务器时区不一致会导致任务时间错乱,务必统一 <code>timedatectl set-timezone Asia/Shanghai</code>。</p>
<h2>四、Systemd Timer:更现代的替代方案</h2>
<p>Systemd Timer 由两个单元组成:<strong>service 单元</strong>(定义要执行的任务)和 <strong>timer 单元</strong>(定义触发时间)。优点:支持精确到秒、支持随机延迟避免风暴、支持依赖与资源限制、日志统一走 journald。</p>
<pre># /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh</pre>
<pre># /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=60
Persistent=true
[Install]
WantedBy=timers.target</pre>
<pre># 启用并查看
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers</pre>
<p>几个关键指令:<code>Persistent=true</code> 表示错过的任务在开机后补跑;<code>RandomizedDelaySec</code> 随机延迟防止多机同时执行造成负载尖峰;<code>OnCalendar</code> 支持 <code>Mon..Fri 09:00</code>、<code>*/15min</code> 等人类可读写法,比 cron 表达式直观得多。</p>
<h2>五、Crontab vs Systemd Timer 如何选择</h2>
<p><strong>选 Crontab</strong>:任务简单、追求兼容性(老系统/容器内无 systemd)、只想要一行表达式。</p>
<p><strong>选 Systemd Timer</strong>:需要精确控制(秒级、随机延迟、错过后补跑)、希望日志统一进 journald、任务较多需要独立管理,或者任务依赖某个服务启动状态。生产环境建议:系统级维护任务尽量迁移到 Systemd Timer,业务脚本按团队习惯保持 Crontab 亦可,但务必统一规范。</p>
<h2>六、生产环境最佳实践清单</h2>
<pre>1. 所有脚本路径、命令用绝对路径,避免环境变量坑
2. 输出必须落日志文件,并配置 logrotate 防止日志撑爆磁盘
3. 耗时任务加锁防重入
4. 关键任务(备份、同步)执行后做结果校验并告警,例如失败时发送通知
5. 定时任务变更走版本管理(脚本入库),禁止在服务器上直接裸改
6. 定期用 crontab -l 和 systemctl list-timers 审计服务器上的全部定时任务
7. 服务器时区统一,避免因时区导致任务时间漂移</pre>
<p>定时任务是运维自动化的基石,把"人肉执行"变成"系统托管",再配合监控告警,就能把重复劳动彻底释放掉。掌握 Crontab 与 Systemd Timer,是每个运维工程师的基本功。</p>
另外,我们自己做了个免费的[七亿批量远程管理工具](https://blog.7ehl.com/index.php/archives/3/),用来批量远程管理服务器(RDP 九宫格 / SSH 终端 / SFTP),有需要可以看看。
评论