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

学习如何利用 systemd 定时器单元的强大功能,在 Linux 上进行高效的任务调度。本指南提供了创建、配置和管理 `.timer` 和 `.service` 单元的全面分步教程,并提供了针对每日、每小时和特定时间事件的实用示例。了解如何使用 `systemctl` 和 `journalctl` 启用、启动、停止和监控您的计划任务,并理解其相对于传统 cron 作业的优势。非常适合寻求可靠自动化解决方案的系统管理员和开发人员。

42 浏览量

精通 Systemd 计时器单元:一份综合指南

Systemd 作为 Linux 中无处不在的系统和服务管理器,为任务调度提供了一个强大而灵活的传统 cron 作业替代方案。Systemd 计时器单元是直接内置于 systemd 生态系统中的一项功能,它提供了增强的控制力、与系统服务的更好集成以及更细粒度的日志记录能力。本指南将引导您完成创建、管理和监控 systemd 计时器单元的过程,使您能够自信而高效地自动化任务。

尽管几十年来 cron 一直是任务调度的首选工具,但 systemd 计时器提供了多项优势。它们可以直接绑定到服务单元,这意味着计时器只有在系统准备就绪时才能激活服务,或者如果在计时器到期前服务尚未完成,服务可以被停止。这种紧密的集成简化了复杂的依赖关系管理。此外,systemd 的日志记录基础结构 (journald) 为所有计时器活动提供了一个集中化且可搜索的日志,使得调试比筛选分散的 cron 日志要容易得多。

理解 Systemd 计时器单元结构

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

A 个典型的计时器单元文件具有 .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*-*-* 00:00:00 的简写。Systemd 支持范围广泛的日历事件规范,类似于 cron 但更灵活。其他示例包括:
      • 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 将在关联服务上次完成一小时后触发。
    • 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] 部分:此部分通常存在于应在启动时启动的服务中,尽管对于由计时器触发的服务,如果它只打算由计时器启动,这可能不是必需的。

创建和启用计时器单元

请遵循以下步骤来创建和管理您的 systemd 计时器单元:

  1. 创建服务单元文件:在 .service 文件中定义您的任务。将其放在 /etc/systemd/system/(或用户特定计时器放在 ~/.config/systemd/user/)。
    bash sudo nano /etc/systemd/system/mytask.service
    粘贴上面的示例服务内容并保存。

  2. 创建计时器单元文件:在相应的 .timer 文件中定义计划。将其放在与服务文件相同的目录中。
    bash sudo nano /etc/systemd/system/mytask.timer
    粘贴上面的示例计时器内容并保存。

  3. 重新加载 Systemd 守护进程:创建或修改单元文件后,您需要告诉 systemd 重新加载其配置。
    bash sudo systemctl daemon-reload

  4. 启用计时器:要使计时器在启动时自动启动,请启用它。
    bash sudo systemctl enable mytask.timer
    注意:如果服务文件仅旨在由计时器触发,则您应启用服务文件.

  5. 启动计时器:立即启动计时器。然后它将根据其计划运行。
    bash sudo systemctl start mytask.timer

管理和监控计时器单元

Systemd 提供了一些 systemctl 命令来管理和监控您的计时器:

  • 列出所有计时器:查看所有活动的和非活动的计时器。
    bash 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
    这显示了计时器计划何时运行,距离运行还有多长时间,上次运行时间以及它激活了哪个服务。

  • 列出特定单元的计时器:如果您想查看与特定服务相关的计时器。
    bash systemctl list-timers --all | grep mytask.service

  • 检查计时器状态:获取有关特定计时器的详细信息。
    bash systemctl status mytask.timer
    这将显示计时器是否处于活动状态,下次计划运行时间以及最近的日志条目。

  • 查看服务日志:要查看计时器执行的任务的输出和状态,请检查关联服务的日志。
    bash journalctl -u mytask.service
    您也可以实时跟踪日志:
    bash journalctl -f -u mytask.service

  • 停止计时器:如果您需要暂时禁用计时器。
    bash sudo systemctl stop mytask.timer

  • 禁用计时器:以防止计时器在启动时启动并停止它(如果它正在运行)。
    bash sudo systemctl disable 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

PersistentWakeUpOn

  • Persistent=true 确保如果由于系统关闭而错过了 OnCalendar 事件,它将在下次启动时运行一次。这对于不应跳过的任务至关重要。
  • WakeUpOn=(例如 WakeUpOn=batteryWakeUpOn=ac)可用于指定系统应根据哪些条件唤醒以处理计时器。这更高级,通常与 systemd-suspend.service 结合使用。

计时器与 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 计时器单元为 Linux 系统上的任务调度提供了一种强大而现代的方法。通过了解它们的结构、创建和管理,您可以有效地自动化日常操作,提高系统可靠性,并利用 systemd 生态系统的全部功能。请记住,更改后务必重新加载守护进程,启用计时器以实现持久性,并利用 journalctl 进行高效的监控和故障排除。