cron,这是 Linux 系统中用于定时执行任务的核心服务,也是实现自动化运维的基石。

cron - Linux 任务计划调度器

一、cron 概念

  • 是什么cron 是一个在后台持续运行的守护进程 (daemon),名为 crond。它会根据一个名为 crontab (cron table) 的配置文件,在指定的时间、日期或间隔自动执行预设的命令或脚本。
  • 用途
    • 系统维护:定时清理日志文件、更新软件包、检查系统状态。
    • 数据处理:定时备份数据库、同步文件、生成报表。
    • 应用任务:定时执行爬虫、发送邮件通知、运行批处理作业。
    • 监控脚本:定时运行我们之前讨论过的性能监控脚本(如检查内存、磁盘空间)并发送告警。

二、crontab 配置文件

cron 的所有调度规则都定义在 crontab 文件中。每个用户都可以拥有自己的 crontab 文件,此外还有一个系统级的 crontab 文件。

  1. 用户 crontab:

    • 这是最常用的方式。每个用户定义的任务都以该用户的身份运行。
    • 文件通常存储在 /var/spool/cron/ (CentOS/RHEL) 或 /var/spool/cron/crontabs/ (Debian/Ubuntu) 目录下,文件名与用户名相同。
    • 绝不应该直接编辑这些文件,而应使用 crontab -e 命令。
  2. 系统 crontab:

    • 文件路径是 /etc/crontab
    • 这个文件通常由系统管理员维护,用于执行系统级的维护任务。
    • 与用户 crontab 不同,它的语法需要在命令前指定运行该命令的用户名
  3. 预设的 cron 目录:

    • 很多系统还提供了更简单的定时任务配置方式,只需将可执行脚本放入以下目录即可:
      • /etc/cron.hourly/: 每小时执行一次。
      • /etc/cron.daily/: 每天执行一次。
      • /etc/cron.weekly/: 每周执行一次。
      • /etc/cron.monthly/: 每月执行一次。

三、crontab 命令

这是管理用户 crontab 文件的标准工具。

  • crontab -e: 编辑当前用户的 crontab 文件。第一次运行时,可能会让你选择一个默认的文本编辑器(如 nano, vim)。
  • crontab -l: 列出当前用户的所有 cron 任务。
  • crontab -r: 移除当前用户的所有 cron 任务。这个命令非常危险,会不经确认直接删除所有任务,请谨慎使用!
  • crontab -u <username> -e: (需要 root 权限) 编辑指定用户的 crontab 文件。

四、crontab 语法

crontab 的每一行都代表一个定时任务,其格式如下:

1
2
3
4
5
6
7
8
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 一个月中的第几天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 一周中的星期几 (0 - 7) (0 和 7 都代表星期日)
# │ │ │ │ │
# │ │ │ │ │
* * * * * <要执行的命令或脚本的绝对路径>

特殊字符的含义:

  • * (星号): 代表该字段的每一个可能值。例如,分钟字段的 * 表示“每分钟”。
  • , (逗号): 用于分隔一个列表。例如,1,15,30 在分钟字段表示第 1、15、30 分钟。
  • - (连字符): 用于表示一个范围。例如,9-17 在小时字段表示从早上 9 点到下午 5 点的每个小时。
  • / (斜杠): 用于指定步长(间隔)。例如,*/5 在分钟字段表示“每 5 分钟”。它等同于 0,5,10,15,...

五、实战示例

  1. 每天凌晨 2:15 清理临时文件:

    1
    15 2 * * *   /usr/bin/rm -rf /tmp/*
  2. 每周日凌晨 4:30 执行数据库备份脚本:

    1
    30 4 * * 0   /home/mugster/scripts/backup_mysql.sh

    (这里的 0 代表星期日)

  3. 工作日的每天下午 6 点整发送报告:

    1
    0 18 * * 1-5   /home/mugster/scripts/send_report.py

    (这里的 1-5 代表星期一到星期五)

  4. 每隔 10 分钟检查一次网站健康状态:

    1
    */10 * * * *   /usr/bin/curl -s --head http://yourwebsite.com || /home/mugster/scripts/notify_admin.sh

    (如果 curl 命令失败,则执行通知脚本)

  5. 每月 1 号的凌晨 0 点 0 分执行月度归档任务:

    1
    0 0 1 * *   /home/mugster/scripts/monthly_archive.sh

六、运维中的重要注意事项

  1. 使用绝对路径cron 执行任务时的环境变量非常有限,可能找不到你登录时可以正常使用的命令。因此,在 crontab 中,所有命令和脚本都应使用绝对路径。可以用 which <command> 来查找命令的绝对路径。

  2. 重定向输出:默认情况下,cron 任务的任何输出(标准输出和标准错误输出)都会以邮件形式发送给任务的所有者。为了避免产生大量不必要的邮件,通常需要重定向输出:

    • 不关心输出:
      1
      * * * * * /path/to/command > /dev/null 2>&1
      这会将标准输出 (>) 和标准错误输出 (2>&1) 都重定向到“黑洞”设备 /dev/null
    • 记录到日志文件:
      1
      * * * * * /path/to/command >> /var/log/my_cron_job.log 2>&1
      >> 表示追加到日志文件,而不是覆盖。
  3. 权限问题cron 任务是以其所有者的身份运行的。确保该用户对要执行的脚本有执行权限 (chmod +x /path/to/script.sh),并且对脚本中涉及的所有文件和目录有正确的读写权限。

  4. % 符号的转义:在 crontab 的命令部分,% 符号有特殊含义,会被转换成换行符。如果你需要使用 %(例如在 date 命令的格式化字符串中),你需要用反斜杠 \ 进行转义。

    1
    2
    3
    4
    # 错误
    0 * * * * cp /var/log/app.log /backups/app-$(date +%Y-%m-%d).log
    # 正确
    0 * * * * cp /var/log/app.log /backups/app-$(date +\%Y-\%m-\%d).log

cron 是 Linux 系统自动化的核心,理解并熟练使用它,是每一位系统管理员和运维工程师的必备技能。

systemd timer这是 systemd 体系中用于替代传统 cron 的现代化任务调度机制。它功能更强大、日志更完善、依赖关系管理更灵活。

systemd timer - 新一代的 Linux 定时任务管理器

一、systemd timer 概念与优势

  • 是什么systemd timer 是一种 systemd 的单元 (unit) 类型,文件名以 .timer 结尾。它不直接执行任务,而是像一个闹钟,在指定的时间被激活 (trigger),然后去启动另一个与之配对的 systemd 单元(通常是一个 .service 单元)。
  • 核心理念将“何时执行” (timer) 与“执行什么” (service) 解耦。
  • 相比 cron 的优势
    1. 日志记录超强:所有 timer 触发的服务的输出都会自动被 journald 捕获,可以通过 journalctl 轻松查看任务是否成功、输出是什么、错误信息是什么,无需手动重定向。
    2. 依赖与顺序管理:可以精确定义任务在系统启动后多久、在某个网络服务可用后、或在某个挂载点就绪后才开始执行。这是 cron 难以做到的。
    3. 灵活性更高:支持更复杂的定时规则,如“开机后 5 分钟”、“上次运行后 1 小时”等,并且可以为不同的定时器设置不同的精确度。
    4. 资源控制:由于任务是通过 .service 文件执行的,因此可以利用 systemd 强大的资源控制能力(Cgroups),限制任务的 CPU、内存使用量。
    5. 易于管理和调试:可以使用标准的 systemctl 命令来控制(启动、停止、查看状态)定时任务,就像管理普通服务一样。

二、systemd timer 的组成部分

一个完整的 systemd timer 任务通常由两个文件组成,它们的文件名(除后缀外)必须相同。

  1. .timer 文件 (定义何时执行)

    • 作用:描述定时规则。
    • 存放路径
      • 系统级:/etc/systemd/system/
      • 用户级:~/.config/systemd/user/
    • 关键配置段
      • [Unit]: Description 描述信息。
      • [Timer]: 核心部分,定义时间。
        • OnCalendar=: 基于日历的绝对时间,语法类似 cron 但更灵活。
        • OnBootSec=: 系统启动后多长时间执行。
        • OnStartupSec=: systemd 进程启动后多长时间执行。
        • OnUnitActiveSec=: 该 timer 单元上次被激活后多长时间执行。
        • Persistent=true: 如果系统在 timer 应该触发时处于关机状态,那么在下次开机后立即补偿执行一次错过的任务。
        • RandomizedDelaySec=: 随机延迟,避免大量任务在同一精确时刻同时执行,造成系统负载尖峰(Thundering Herd Problem)。
      • [Install]: WantedBy=timers.target 表示这个 timer 应该被 timers.target 管理。
  2. .service 文件 (定义做什么)

    • 作用:描述要执行的具体命令或脚本。
    • 存放路径:与 .timer 文件相同。
    • 关键配置段
      • [Unit]: Description 描述信息,可以定义依赖关系(如 After=network.target)。
      • [Service]: 核心部分,定义执行的动作。
        • Type=oneshot: 表示这是一个一次性执行的任务,而不是长期运行的服务。
        • ExecStart=: 指定要执行的命令或脚本的绝对路径

三、OnCalendar 语法详解

OnCalendar 的格式非常灵活:DayOfWeek Year-Month-Day Hour:Minute:Second

  • 可以省略前面的部分,例如 HH:MM:SS 表示每天的这个时间。
  • *: 通配符,表示每个值。
  • ,: 列表分隔符,例如 Mon,Wed,Fri
  • -: 范围,例如 9-17
  • /: 步长,例如 *:0/15 表示每 15 分钟。
  • 预设别名minutely, hourly, daily, weekly, monthly, yearly

对比 cron 的示例

描述cron 语法systemd OnCalendar 语法
每分钟* * * * *minutely*-*-* *:*:00
每 5 分钟*/5 * * * **:0/5
每天凌晨 2:3030 2 * * *daily(不精确)或 02:30:00
周一早上 9 点0 9 * * 1Mon *-*-* 09:00:00

四、实战:创建一个 systemd timer 任务

目标:创建一个系统级的定时任务,每 5 分钟清理一次 /tmp 目录,并在网络可用后才执行。

第一步:创建 .service 文件

使用文本编辑器(如 nanovim)创建文件:如何知道systemd的配置文件在哪

1
sudo nano /etc/systemd/system/cleanup-tmp.service

写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=Clean up the /tmp directory
# 这是一个好的实践,描述任务是什么

[Service]
Type=oneshot
# 这是一个一次性任务
ExecStart=/usr/bin/rm -rf /tmp/my-app-*
# 必须使用绝对路径,这里只删除特定模式的文件,更安全
User=nobody
# 以一个低权限用户运行,增加安全性
Group=nogroup

第二步:创建 .timer 文件

文件名必须与 .service 文件匹配:

1
sudo nano /etc/systemd/system/cleanup-tmp.timer

写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Run cleanup-tmp.service every 5 minutes

[Timer]
# OnCalendar=*:0/5 # 这是基于日历的每5分钟
OnUnitActiveSec=5min
# 这种方式更灵活:从上次激活后5分钟执行
OnBootSec=1min
# 开机1分钟后第一次执行
RandomizedDelaySec=30s
# 增加最多30秒的随机延迟

[Install]
WantedBy=timers.target
# 确保 timer 会随系统启动

第三步:管理和启用 timer

  1. 重新加载 systemd 配置

    1
    sudo systemctl daemon-reload
  2. **启动并启用 timer**(注意,我们操作的是 .timer 文件,不是 .service 文件!):

    1
    2
    3
    4
    5
    # 立即启动 timer,让它开始计时
    sudo systemctl start cleanup-tmp.timer

    # 设置 timer 开机自启
    sudo systemctl enable cleanup-tmp.timer

第四步:验证和调试

  1. **列出所有活动的 timer**:

    1
    systemctl list-timers

    你应该能看到 cleanup-tmp.timer,以及它下一次触发的时间 (NEXT) 和距离现在的时间 (LEFT)。

  2. 查看 timer 的状态

    1
    systemctl status cleanup-tmp.timer
  3. 查看 service 的执行日志
    timer 触发过 service 后,你可以用 journalctl 查看日志。

    1
    2
    3
    4
    5
    # 查看关联服务的日志
    journalctl -u cleanup-tmp.service

    # 持续关注日志
    journalctl -u cleanup-tmp.service -f
  4. 手动触发一次任务进行测试
    如果你不想等待定时器触发,可以手动运行 .service 文件来测试命令是否正确。

    1
    sudo systemctl start cleanup-tmp.service

    然后立即检查日志。