如何创建和管理 Systemd 定时器单元

通过实际的`.timer`、`.service`、`systemctl`和`journalctl`示例,创建、启用、监控和排查systemd定时器单元问题。

如何创建和管理Systemd定时器单元

Systemd定时器单元可以在Linux上安排任务,无需依赖cron。如果你需要服务器在预定时间运行备份、清理任务、健康检查或报告,systemd定时器集调度、服务隔离、依赖处理和日志记录于一体。

核心思想很简单:.timer文件定义何时运行,.service文件定义运行什么。这种分离使得定时器易于通过systemctl检查,也易于通过journalctl调试。

理解Systemd定时器单元结构

一个systemd定时器单元总是与一个对应的服务单元(或其他单元类型)配对,该服务单元是定时器要激活的。定时器单元本身定义了何时激活关联单元,而服务单元定义了执行什么操作。两个单元通常位于同一目录中,自定义单元通常放在/etc/systemd/system/

典型的定时器单元文件扩展名为.timer,其关联的服务单元文件扩展名为.service。例如,如果要调度mytask.service中定义的任务,需要创建一个mytask.timer文件。

示例:mytask.timer

[Unit]
Description=每天运行我的自定义任务

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

我们来分解关键部分:

  • [Unit]部分

    • Description:定时器的人类可读描述。有助于在状态输出中识别。
  • [Timer]部分:这是定时器单元的核心,定义了调度。

    • OnCalendar=daily:此指令指定定时器何时激活。daily是每天午夜的简写。其他示例包括:
      • hourly:每小时。
      • weekly:每周(相当于Mon *-*-* 00:00:00)。
      • Sun *-*-* 10:00:每周日上午10点。
      • *-*-15 14:30:每月15日下午2:30。
      • Mon..Fri *-*-* 09:00:工作日上午9点。
    • Persistent=true:如果设置为true,当系统关闭期间错过了事件,定时器将尽快激活。对于OnCalendar定时器,这意味着如果系统在预定时间关闭,定时器将在系统启动并激活后立即触发。
    • OnBootSec=:在系统启动后指定时间激活定时器。例如,OnBootSec=15min将在启动后15分钟触发。
    • OnUnitActiveSec=:在它激活的单元上次激活后指定时间激活定时器。例如,OnUnitActiveSec=1h在关联服务上次激活后1小时安排另一次运行。
    • OnUnitInactiveSec=:在它激活的单元上次变为非活动状态后指定时间激活定时器。
    • AccuracySec=:指定定时器的精度。Systemd仅在事件处于此时间窗口内时尝试唤醒系统执行定时器,有助于节省电量。默认为1min
    • RandomizedDelaySec=:为定时器触发添加随机延迟,最多到指定时长。用于分散负载。
  • [Install]部分:此部分定义如何启用定时器单元。

    • WantedBy=timers.target:此指令确保启用定时器时,它成为timers.target的一部分,这是一个包含所有活动定时器的标准目标。这意味着定时器启用后将在启动时自动启动。

示例:mytask.service

[Unit]
Description=我的自定义任务服务

[Service]
Type=oneshot
ExecStart=/usr/local/bin/my_custom_script.sh
User=myuser
Group=mygroup

[Install]
WantedBy=multi-user.target
  • [Unit]部分

    • Description:服务的描述。
  • [Service]部分:定义服务本身。

    • Type=oneshot:适用于运行一次然后退出的任务。其他类型用于长时间运行的守护进程。
    • ExecStart:要执行的命令。确保脚本具有执行权限。
    • User/Group:指定命令运行的用户和组。除非绝对必要,否则不要以root身份运行任务。
  • [Install]部分:对于仅由定时器启动的oneshot服务,通常不需要此部分。启用定时器,而不是服务。

创建和启用定时器单元

按照以下步骤创建和管理systemd定时器单元:

  1. 创建服务单元文件:在.service文件中定义任务。将其放在/etc/systemd/system/(或用户特定定时器的~/.config/systemd/user/)。

    sudo nano /etc/systemd/system/mytask.service
    

    粘贴上面的示例服务内容并保存。

  2. 创建定时器单元文件:在对应的.timer文件中定义调度。将其放在与服务文件相同的目录中。

    sudo nano /etc/systemd/system/mytask.timer
    

    粘贴上面的示例定时器内容并保存。

  3. 重新加载Systemd守护进程:创建或修改单元文件后,需要通知systemd重新加载配置。

    sudo systemctl daemon-reload
    
  4. 启用定时器:要使定时器在启动时自动启动,请启用它。

    sudo systemctl enable mytask.timer
    

    注意:如果服务文件仅由定时器触发,则不要启用它。

  5. 启动定时器:立即启动定时器。它将根据其调度运行。

    sudo systemctl start mytask.timer
    

管理和监控定时器单元

Systemd提供了几个systemctl命令来管理和监控定时器:

  • 列出所有定时器:查看所有活动和非活动定时器。

    systemctl list-timers
    

    此命令输出如下:

    NEXT                        LEFT        LAST        PASSED      UNIT          ACTIVATES
    Tue 2023-10-27 08:00:00 UTC 10h left    Wed 2023-10-26 08:00:00 UTC 14h ago       mytask.timer  mytask.service
    

    显示定时器下次计划运行的时间、距离运行还有多久、上次运行时间以及激活的服务。

  • 列出特定单元的定时器:如果要查看与特定服务相关的定时器。

    systemctl list-timers --all | grep mytask.service
    
  • 检查定时器状态:获取特定定时器的详细信息。

    systemctl status mytask.timer
    

    将显示定时器是否活动、下次计划运行时间以及最近的日志条目。

  • 查看服务日志:要查看定时器执行任务的输出和状态,请检查关联服务的日志。

    journalctl -u mytask.service
    

    也可以实时跟踪日志:

    journalctl -f -u mytask.service
    
  • 停止定时器:如果需要临时禁用定时器。

    sudo systemctl stop mytask.timer
    
  • 禁用定时器:防止定时器在启动时启动,如果正在运行则停止它。

    sudo systemctl disable --now mytask.timer
    

高级定时器配置

设置特定间隔

除了dailyhourly,可以定义更精确的间隔:

  • 每N分钟OnUnitActiveSec=15min(服务上次激活后15分钟运行)。
  • 特定时间OnCalendar=*-*-* 02:30:00(每天凌晨2:30运行)。
  • 组合条件OnCalendar=Mon..Fri *-*-* 08:00:00(工作日上午8点运行)。

使用AccuracySec节省电量

如果任务不需要在精确时刻运行,考虑使用AccuracySec。例如,AccuracySec=5min告诉systemd在预定时间的5分钟内唤醒系统即可。这允许systemd合并定时器事件,可能使系统更长时间处于低功耗状态。

[Timer]
OnCalendar=hourly
AccuracySec=5min

PersistentWakeSystem

  • Persistent=true确保如果由于系统关闭而错过了OnCalendar事件,它将在下次启动时运行一次。这对于不能跳过的任务至关重要。
  • WakeSystem=true要求systemd在系统和硬件支持的情况下,为定时器从挂起状态唤醒系统。这与机器是否使用交流电源或电池无关。

定时器与Cron对比

特性 Systemd定时器 Cron
集成度 与systemd服务、目标深度集成 独立工具
调度 灵活(日历、相对、基于启动) 主要基于时间的表达式
日志记录 通过journalctl集中管理 分散(/var/log/syslog/var/log/cron.log
错误处理 可以将操作绑定到服务失败 基本邮件通知
依赖关系 可以依赖其他服务处于活动状态 有限
执行 可以以特定用户、组运行 可以通过crontab以特定用户运行
电源管理 可优化省电(AccuracySec 直接控制较少

何时选择Systemd定时器:

  • 需要与其他systemd服务更紧密集成时。
  • 集中日志记录和更易调试是优先事项时。
  • 需要更高级的调度选项(例如,自上次运行以来的时间)。
  • 与系统状态或电源管理相关的任务。

何时可能仍首选Cron:

  • 对于非常简单的独立任务,且系统未完全采用systemd。
  • 为了跨不同Linux发行版和旧系统的最大兼容性。

常见问题排查

  • 任务未运行
    • 检查定时器状态:systemctl status mytask.timer。查找Active: activeTriggered...消息。
    • 检查服务日志:journalctl -u mytask.service。确保脚本可执行且无错误。
    • 验证OnCalendar语法:使用systemd-analyze calendar 'your-calendar-string'测试。
    • 确保定时器已启用并启动:systemctl list-timers --all
  • 任务运行过早/过晚
    • 检查AccuracySecRandomizedDelaySec
    • 确保系统时钟准确(timedatectl status)。
  • 权限错误
    • 确认.service文件中指定的UserGroup对脚本及其访问的文件具有必要权限。
    • 如果未指定用户,默认为root。谨慎使用root权限。

总结

当任务属于机器的服务层时使用systemd定时器:备份、清理任务、监控检查、证书续订钩子和其他运维工作。先创建服务,再创建定时器,运行systemctl daemon-reload,启用并启动定时器,然后使用systemctl list-timers --alljournalctl -u your.service验证。