systemd
用于 Python 脚本的服务¶
如果您像许多系统管理员一样,是使用 * * * * * /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