为 Python 脚本创建 systemd
服务¶
如果您像许多系统管理员一样,是使用 * * * * * /I/launch/my/script.sh
启动的 cron 脚本爱好者,那么本文应该会让您考虑使用 systemd
提供的所有强大功能和便利性来实现另一种方式。
我们将编写一个 Python 脚本,它将提供一个连续的循环来执行您定义的动作。
我们将了解如何将此脚本作为 systemd
服务运行,在 journalctl 中查看日志,以及当脚本崩溃时会发生什么。
先决条件¶
让我们开始安装脚本使用 journalctl 所需的一些 Python 依赖项
shell > sudo dnf install python36-devel systemd-devel
shell > sudo pip3 install systemd
编写脚本¶
让我们考虑以下脚本 my_service.py
"""
Sample script to run as script
"""
import time
import logging
import sys
from systemd.journal import JournaldLogHandler
# Get an instance of the logger
LOGGER = logging.getLogger(__name__)
# Instantiate the JournaldLogHandler to hook into systemd
JOURNALD_HANDLER = JournaldLogHandler()
JOURNALD_HANDLER.setFormatter(logging.Formatter(
'[%(levelname)s] %(message)s'
))
# Add the journald handler to the current logger
LOGGER.addHandler(JOURNALD_HANDLER)
LOGGER.setLevel(logging.INFO)
class Service(): # pylint: disable=too-few-public-methods
"""
Launch an infinite loop
"""
def __init__(self):
duration = 0
while True:
time.sleep(60)
duration += 60
LOGGER.info("Total duration: %s", str(duration))
# will failed after 4 minutes
if duration > 240:
sys.exit(1)
if __name__ == '__main__':
LOGGER.info("Starting the service")
Service()
我们首先实例化必要的变量,将日志发送到 journald。然后脚本启动一个无限循环,暂停 60 秒(这是 cron 执行的最小时间单位,因此我们可以低于此限制)。
注意
我个人在使用此脚本的更高级版本,它会不断查询数据库,并通过 rundeck API 检索信息来执行作业。
Systemd 集成¶
现在我们有了可以作为您想象力的基础的脚本,我们可以将其安装为 systemd 服务。
让我们创建此文件 my_service.service
并将其复制到 /etc/systemd/system/
。
[Unit]
Description=My Service
After=multi-user.target
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/python3 my_service.py
WorkingDirectory=/opt/my_service/
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=my_service
[Install]
WantedBy=multi-user.target
正如您所见,脚本是从 /opt/my_service/
启动的。请记住修改脚本的路径和 syslog 标识符。
启动并启用新服务
shell > sudo systemctl daemon-reload
shell > sudo systemctl enable my_service.service
shell > sudo systemctl start my_service.service
测试¶
现在我们可以通过 journalctl 查看日志
shell > journalctl -f -u my_service
oct. 14 11:07:48 rocky8 systemd[1]: Started My Service.
oct. 14 11:07:49 rocky8 __main__[270267]: [INFO] Starting the service
oct. 14 11:08:49 rocky8 __main__[270267]: [INFO] Total duration: 60
oct. 14 11:09:49 rocky8 __main__[270267]: [INFO] Total duration: 120
现在让我们看看当脚本崩溃时会发生什么
shell > ps -elf | grep my_service
4 S root 270267 1 0 80 0 - 82385 - 11:07 ? 00:00:00 /usr/bin/python3 my_service.py
shell > sudo kill -9 270267
shell > journalctl -f -u my_service
oct. 14 11:10:49 rocky8 __main__[270267]: [INFO] Total duration: 180
oct. 14 11:11:49 rocky8 __main__[270267]: [INFO] Total duration: 240
oct. 14 11:12:19 rocky8 systemd[1]: my_service.service: Main process exited, code=killed, status=9/KILL
oct. 14 11:12:19 rocky8 systemd[1]: my_service.service: Failed with result 'signal'.
oct. 14 11:12:19 rocky8 systemd[1]: my_service.service: Service RestartSec=100ms expired, scheduling restart.
oct. 14 11:12:19 rocky8 systemd[1]: my_service.service: Scheduled restart job, restart counter is at 1.
oct. 14 11:12:19 rocky8 systemd[1]: Stopped My Service.
oct. 14 11:12:19 rocky8 systemd[1]: Started My Service.
oct. 14 11:12:19 rocky8 __main__[270863]: [INFO] Starting the service
我们也可以等待 5 分钟让脚本自行崩溃:(在生产环境请删除此项)
oct. 14 11:16:02 rocky8 systemd[1]: Started My Service.
oct. 14 11:16:03 rocky8 __main__[271507]: [INFO] Starting the service
oct. 14 11:17:03 rocky8 __main__[271507]: [INFO] Total duration: 60
oct. 14 11:18:03 rocky8 __main__[271507]: [INFO] Total duration: 120
oct. 14 11:19:03 rocky8 __main__[271507]: [INFO] Total duration: 180
oct. 14 11:20:03 rocky8 __main__[271507]: [INFO] Total duration: 240
oct. 14 11:21:03 rocky8 __main__[271507]: [INFO] Total duration: 300
oct. 14 11:21:03 rocky8 systemd[1]: my_service.service: Main process exited, code=exited, status=1/FAILURE
oct. 14 11:21:03 rocky8 systemd[1]: my_service.service: Failed with result 'exit-code'.
oct. 14 11:21:03 rocky8 systemd[1]: my_service.service: Service RestartSec=100ms expired, scheduling restart.
oct. 14 11:21:03 rocky8 systemd[1]: my_service.service: Scheduled restart job, restart counter is at 1.
oct. 14 11:21:03 rocky8 systemd[1]: Stopped My Service.
oct. 14 11:21:03 rocky8 systemd[1]: Started My Service.
oct. 14 11:21:03 rocky8 __main__[271993]: [INFO] Starting the service
正如您所见,systemd 的重启功能非常有用。
结论¶
systemd
和 journald
为我们提供了工具,可以轻松地创建健壮而强大的脚本,从而取代我们旧的、可靠的 crontab 脚本。
希望这个解决方案对您有所帮助。
作者:Antoine Le Morvan
贡献者:Steven Spencer