- How to make a Python script run like a service or daemon in Linux
- 16 Answers 16
- Запуск Python скрипта в виде службы через systemctl/systemd
- Как запустить python скрипт на Linux в виде demon
- Переносим локальный проект на сервер
- Создаем БД в PostgreSQL
- В коде должно быть подключение к PostreSQL
- Создаем демон для запуска нашего скрипта на python
- Читайте также
How to make a Python script run like a service or daemon in Linux
I have written a Python script that checks a certain e-mail address and passes new e-mails to an external program. How can I get this script to execute 24/7, such as turning it into daemon or service in Linux. Would I also need a loop that never ends in the program, or can it be done by just having the code re executed multiple times?
«checks a certain e-mail address and passes new e-mails to an external program» Isn’t that what sendmail does? You can define mail alias to route a mailbox to a script. Why aren’t you using mail aliases to do this?
On a modern linux which has systemd you can create a systemd service in daemon mode as described here. See also: freedesktop.org/software/systemd/man/systemd.service.html
16 Answers 16
You have two options here.
- Make a proper cron job that calls your script. Cron is a common name for a GNU/Linux daemon that periodically launches scripts according to a schedule you set. You add your script into a crontab or place a symlink to it into a special directory and the daemon handles the job of launching it in the background. You can read more at Wikipedia. There is a variety of different cron daemons, but your GNU/Linux system should have it already installed.
- Use some kind of python approach (a library, for example) for your script to be able to daemonize itself. Yes, it will require a simple event loop (where your events are timer triggering, possibly, provided by sleep function).
I wouldn’t recommend you to choose 2., because you would be, in fact, repeating cron functionality. The Linux system paradigm is to let multiple simple tools interact and solve your problems. Unless there are additional reasons why you should make a daemon (in addition to trigger periodically), choose the other approach.
Also, if you use daemonize with a loop and a crash happens, no one will check the mail after that (as pointed out by Ivan Nevostruev in comments to this answer). While if the script is added as a cron job, it will just trigger again.
+1 to the cronjob. I don’t think the question specifies that it is checking a local mail account, so mail filters do not apply
What happen does use a loop without termination in a Python program and then register it into crontab list will be? If I set up such .py for hourly, will it create many processes that will never be terminated? If so, I think this would quite like daemon.
I can see that cron is an obvious solution if you check check for emails once a minute (which is the lowest time resolution for Cron). But what if I want to check for emails every 10 seconds? Should I write the Python script to run query 60 times, which means it ends after 50 seconds, and then let cron start the script again 10 seconds later?
I have not worked with daemons/services, but I was under the impression that it (OS/init/init.d/upstart or what it is called) takes care of restarting a daemon when/if it ends/crashes.
I’m writing a python script that needs check about 20 sensors about 10 times per minute each 24/7. Is cron still a good use? I suspect a daemon would be better.
Here’s a nice class that is taken from here:
#!/usr/bin/env python import sys, os, time, atexit from signal import SIGTERM class Daemon: """ A generic daemon class. Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile def daemonize(self): """ do the UNIX double-fork magic, see Stevens' "Advanced Programming in the UNIX Environment" for details (ISBN 0201563177) http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 """ try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError, e: sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent sys.exit(0) except OSError, e: sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = file(self.stdin, 'r') so = file(self.stdout, 'a+') se = file(self.stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile atexit.register(self.delpid) pid = str(os.getpid()) file(self.pidfile,'w+').write("%s\n" % pid) def delpid(self): os.remove(self.pidfile) def start(self): """ Start the daemon """ # Check for a pidfile to see if the daemon already runs try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid: message = "pidfile %s already exist. Daemon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run() def stop(self): """ Stop the daemon """ # Get the pid from the pidfile try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if not pid: message = "pidfile %s does not exist. Daemon not running?\n" sys.stderr.write(message % self.pidfile) return # not an error in a restart # Try killing the daemon process try: while 1: os.kill(pid, SIGTERM) time.sleep(0.1) except OSError, err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print str(err) sys.exit(1) def restart(self): """ Restart the daemon """ self.stop() self.start() def run(self): """ You should override this method when you subclass Daemon. It will be called after the process has been daemonized by start() or restart(). """
Запуск Python скрипта в виде службы через systemctl/systemd
Есть несколько способов запуска вашей программы в качестве фоновой службы в Linux, таких как crontab, .bashrc и т. д., но сегодня будет разговор о systemd. Изначально я искал способ запустить свой скрипт на Python в качестве фоновой службы, поэтому даже если сервер по какой-то причине перезагрузится, мой скрипт все равно должен работать в фоновом режиме, после небольшого ресерча и я обнаружил, что systemd позволяет мне это сделать. Давайте начнем.
Настройки далее будут производиться на машине с Ubuntu 20.04.
Почти все версии Linux поставляются с systemd из коробки, но если у вас его нет, вы можете просто запустить следующую команду:
sudo apt install -y systemd
Примечание. Флаг -y означает быструю установку пакетов и зависимостей.
Чтобы проверить, какая версия systemd у вас установлена, просто выполните команду:
Создайте файл python с любым именем. Я назову свой скрипт именем test.py.
import time from datetime import datetime while True: with open("timestamp.txt", "a") as f: f.write("Текущая временная метка: " + str(datetime.now())) f.close() time.sleep(10)
Приведенный выше скрипт будет записывать текущую метку времени в файл каждые 10 секунд. Теперь напишем сервис.
sudo nano /etc/systemd/system/test.service
(имя службы, которая тестируется в этом случае)
[Unit] Description=My test service After=multi-user.target [Service] User=deepak Group=admin Type=simple Restart=always ExecStart=/usr/bin/python3 /home//test.py [Install] WantedBy=multi-user.target
Замените имя пользователя в вашей ОС, где написано . Флаг ExecStart принимает команду, которую вы хотите запустить. Таким образом, в основном первый аргумент — это путь к python (в моем случае это python3), а второй аргумент — это путь к скрипту, который необходимо выполнить. Флаг перезапуска всегда установлен, потому что я хочу перезапустить свою службу, если сервер будет перезапущен.
Здесь мы определили User=deepak и Group=admin, чтобы убедиться, что скрипт будет выполняться только от имени пользователя deepak, входящего в группу admin.
Теперь нам нужно перезагрузить демон.
sudo systemctl daemon-reload
Давайте включим наш сервис, чтобы он не отключался при перезагрузке сервера.
sudo systemctl enable test.service
А теперь давайте запустим наш сервис.
sudo systemctl start test.service
Теперь наш сервис работает.
Примечание. Файл будет записан в корневой каталог (/), потому что программа запишет путь с точки зрения systemd. Чтобы изменить это, просто отредактируйте путь к файлу. Например:
import time from datetime import datetime path_to_file = "введите желаемый путь к файлу" while True: with open(path_to_file, "a") as f: f.write("Текущая временная метка: " + str(datetime.now())) f.close() time.sleep(10)
Есть несколько команд, которые вы можете выполнить для запуска, остановки, перезапуска и проверки состояния.
sudo systemctl stop name_of_your_service
sudo systemctl restart name_of_your_service
sudo systemctl status name_of_your_service
Это было очень поверхностное знакомство с systemd, предназначенное для новичков, которые хотят начать писать свои собственные systemd службы для python.
ПРИМЕЧАНИЕ. Это относится не только к сценариям Python. Вы можете запустить любую программу с ним, независимо от языка программирования, на котором написана ваша программа.
Как запустить python скрипт на Linux в виде demon
Admin 16.10.2022 Linux, Python
Описание процесса от создания БД и до запуска скрипта.
Соединяемся со своим сервером и дальше работаем через консоль.
Переносим локальный проект на сервер
Создаем директорию для нашего нового приложения:
Клонируем свой git-проект на наш сервер:
Создаем виртуальное окружение python:
Создаем БД в PostgreSQL
Подключаемся к PostgreSQL:
CREATE DATABASE new-application TEMPLATE=template0 ENCODING ‘UTF-8’ LC_COLLATE ‘ru_RU.UTF-8’ LC_CTYPE ‘ru_RU.UTF-8’;
Назначаем привелегии новой таблице:
Выходим из postgres окружения:
В коде должно быть подключение к PostreSQL
Выдержка из примера подключения к PostreSQL вместе с логированием.
def create_connection (
db_name = ‘new-application’ ,
db_user = ‘ploshadka’ ,
db_password = ‘12345’ ,
db_host = ‘localhost’ ,
db_port = 5432
) :
connection = None
try :
connection = psycopg2. connect (
database = db_name ,
user = db_user ,
password = db_password ,
host = db_host ,
port = db_port ,
)
connection. autocommit = True
except psycopg2. OperationalError as error:
logger. info ( f ‘Ошибка: ‘ )
return connection
Создаем демон для запуска нашего скрипта на python
Внутри этого файла вставляем:
Description=New Application
After=network.target
[Service]
User=ploshadka
Group=www-data
WorkingDirectory=/home/ploshadka/new-application
Environment=»PATH=/home/ploshadka/new-application/venv/bin»
ExecStart=/home/ploshadka/new-application/venv/bin/python bot.py —start
ExecStop=/home/ploshadka/new-application/venv/bin/python bot.py —stop
ExecReload=/home/ploshadka/new-application/venv/bin/python bot.py —restart
TimeoutSec=30
Restart=always
Команды для запуска, остановки, перезагрузки службы
sudo systemctl start new-application
sudo systemctl stop new-application
sudo systemctl start new-application
sudo systemctl reload new-application
Читайте также
У сайта нет цели самоокупаться, поэтому на сайте нет рекламы. Но если вам пригодилась информация, можете лайкнуть страницу, оставить комментарий или отправить мне подарок на чашечку кофе.