如何在 CentOS 7 中使用 Systemd Timer 来代替 Crontab 执行定时 Python 任务?
在 CentOS 7 中,使用 Systemd Timer 代替 Crontab 是更现代、可控性更高的方案。相较于 Cron,Systemd Timer 具备以下核心优势:独立且统一的日志收集:所有输出和报错(stdout/stderr)自动由 journalctl 收集,无需手动做 >> log.txt 日志重定向。精准的环境控制:可以直接在 Service 中定义工作目录(WorkingDirectory)、运行用户(User)和特定环境变量。错过补执行机制(Persistent):如果服务器在原定触发时间处于关机状态,开机后可以自动补执行错过的任务。Systemd 采用双单元机制:需要建立一个 .service 文件定义“做什么”,以及一个 .timer 文件定义“何时触发”。搭建与配置流程1.1. 创建 Service 服务单元 (.service):定义要执行的 Python 脚本。在 /etc/systemd/system/ 目录下创建一个名为 my-python-task.service 的文件:Ini, TOML# /etc/systemd/system/my-python-task.service
[Unit]
Description=My Scheduled Python Script Service
After=network.target
[Service]
Type=oneshot
User=root
WorkingDirectory=/home/user/project
ExecStart=/usr/bin/python3 /home/user/project/myscript.py
如果使用了 Python 虚拟环境,直接将 ExecStart 指向虚拟环境内的解释器:
ExecStart=/home/user/venv/bin/python /home/user/project/myscript.py
[Install]
WantedBy=multi-user.target
2.2. 创建 Timer 定时单元 (.timer):定义触发的时间规则。在同一目录下创建前缀同名的 my-python-task.timer 文件:Ini, TOML# /etc/systemd/system/my-python-task.timer
[Unit]
Description=Run My Python Task Periodically
[Timer]
1. 设定触发时间规则(例如:每天凌晨 02:00 运行)
OnCalendar=--* 02:00:00
2. 若关机错过了执行时间,开机后是否立即补执行一次
Persistent=true
显式绑定要触发的 service(如果文件名同名,此项可省略)
Unit=my-python-task.service
[Install]
WantedBy=timers.target
3.3. 加载并激活 Timer:重新加载 Systemd 配置并启动定时器。文件建立完毕后,需要刷新 Systemd 守护进程并启用该 Timer:Bash# 重新加载 Systemd 配置文件
systemctl daemon-reload
启动 Timer 并设置开机自启(注意是启动 .timer 而非 .service)
systemctl enable --now my-python-task.timer
4.4. 检查运行状态与手动测试:验证定时器与服务是否生效。可以通过以下命令验证 Timer 状态,并手动测试脚本运行是否正常:Bash# 1. 查看当前系统中所有活着的 Timer 列表及其下次触发时间
systemctl list-timers
2. 查看 Timer 自身的激活状态
systemctl status my-python-task.timer
3. 手动触发一次 Service(用于测试脚本本身能否正常正常跑通)
systemctl start my-python-task.service
常用 OnCalendar 时间语法Systemd 提供了非常人性化的时间表达式:需求OnCalendar 写法示例每 5 分钟OnCalendar=:0/5每小时整点OnCalendar=hourly 或 -- :00:00每天凌晨 2 点OnCalendar=daily 或 -- 02:00:00每周一上午 8:30OnCalendar=Mon -- 08:30:00每月 1 号 00:00OnCalendar=monthly 或 -*-01 00:00:00日志查看与故障排查Systemd 自动通过 journald 收集该任务的所有日志(包括 Python 脚本里的 print() 输出与异常抛出):Bash# 查看该 Python 服务的完整历史运行日志
journalctl -u my-python-task.service
实时追踪该服务的最新日志(类似 tail -f)
journalctl -u my-python-task.service -f -n 50
查看最近一次失败日志
journalctl -u my-python-task.service -p err