Вопрос или проблема
У нас есть служба, которая управляется systemd
. Я ищу способ программно перезапустить эту службу по запросу – например, отправив сигнал, записав в файл или отправив сообщение через сокет.
Контекст: служба, управляемая systemd
, управляет пулом безголовых экземпляров Chrome. Но она делает это довольно плохо, и со временем количество экземпляров и использование памяти увеличиваются. systemctl restart
решает эту проблему.
Я могу установить RuntimeMaxSec
и Restart=always
, чтобы периодически перезапускать прокси, но в момент перезапуска он может выполнять какие-то задачи.
Другой службой на этой машине является единственный потребитель прокси Chrome, который знает, когда не использует его. Таким образом, периодически, когда он не использует прокси Chrome (чтобы не потерять ни один запрос), он может перезапустить его.
Как я могу это сделать?
Одно из решений – заставить другую службу фактически выполнить systemctl restart some_service
. Это требует sudo
, но можно использовать файл sudoers
(visudo
), чтобы дать пользователю разрешение выполнить одну конкретную команду: some-user ALL=NOPASSWD: /some/command some args
Если вы хотите избежать использования sudo, вы можете установить флаг длительности для вашего пользователя и создать пользовательскую службу вместо системной службы. Это может не работать на CentOS 7 и ниже.
Еще одно решение – спросить systemctl, какой PID
systemctl --user status your_scraper
затем отправить SIGHUP этому идентификатору процесса
os.kill(main_pid_of_your_scraper, signals.SIGHUP)
что вам нужно поймать внутри вашего приложения и сбросить ваш браузер.
signal.signal(signal.SIGHUP, restart_myself)
Это также хорошо взаимодействует с systemctl, поскольку systemctl reload
также отправляет сигнал SIGHUP этому процессу.
Разрешено ли потребительской службе перезапускаться? Если да, то вы можете перезапустить потребителя и определить proxy.service для автоматического перезапуска, если потребитель перезапускается.
proxy.service должен включать
[Unit]
BindsTo = consumer.service
Requires = consumer.service
After = consumer.service
Затем, когда потребитель простаивает, завершите его программно с конкретным кодом выхода, например, 42. И определите consumer.service для перезапуска при коде выхода 42.
Ответ или решение
Для того чтобы другой процесс мог сигнализировать systemd о необходимости перезапуска службы, существует множество различных методов. В данном ответе мы рассмотрим функции systemd и различные способы их использования в контексте вашей задачи — перезапуска службы, которая управляет пулом экземпляров headless Chrome.
Основные методы перезапуска службы
-
Использование команд systemctl с привилегиями sudo
Одним из самых простых решений является использование командыsystemctl restart
. Однако для выполнения этой команды требуется привилегия суперпользователя. Это можно обойти, настроив файл sudoers с помощьюvisudo
, чтобы разрешить конкретному пользователю выполнять команду без запроса пароля. Например:some-user ALL=NOPASSWD: /bin/systemctl restart your_service
-
Создание пользовательской службы с поддержкой linger
Если минимизация привилегий важна, можно рассмотреть создание службы на уровне пользователя (user service) вместо системной. Для этого требуется, чтобы пользователь имел установленный флаг linger (это позволяет службам работать вне сеанса пользователя). Создание пользовательской службы обычно работает без дополнительных привилегий, но может не поддерживаться в более старых дистрибутивах, таких как CentOS 7 или ниже. -
Посылка сигналов процессам
Еще одним подходом является отправка сигналов напрямую процессу. Сначала вы можете получить идентификатор процесса (PID) вашей службы с помощью команды:systemctl status your_service
Затем можно использовать библиотеку Python для отправки сигнала SIGHUP этому PID. Например:
import os import signal os.kill(main_pid_of_your_service, signal.SIGHUP)
Ваша служба должна обрабатывать этот сигнал, чтобы выполнить перезапуск экземпляров. Рекомендуется использовать обработчик сигналов для корректного реагирования на SIGHUP.
import signal def restart_myself(signum, frame): # Логика перезапуска вашего браузера или экземпляра pass signal.signal(signal.SIGHUP, restart_myself)
Данный метод совместим с
systemctl reload
, который также отправляет SIGHUP. -
Использование зависимости между службами
Если ваша служба-потребитель может перезапускаться, вы можете установить зависимость между службами в конфигурационном файле вашего proxy-сервиса. Это можно сделать, добавив в файл сервиса следующие строки:[Unit] BindsTo=consumer.service Requires=consumer.service After=consumer.service
Затем, когда потребитель будет в неактивном состоянии, вы можете завершить его работу с конкретным кодом завершения (например, 42). Конфигурация вашего consumer.service может быть настроена так, чтобы перезапускаться только при выходе с кодом 42.
Заключение
Таким образом, существует несколько способов, как другой процесс может сигнализировать systemd для перезапуска службы, каждый из которых имеет свои плюсы и минусы. Выбор конкретного метода зависит от ваших требований к безопасности, удобству и архитектуре вашей системы. Рассмотрите эти подходы и выберите тот, который лучше всего соответствует вашим нуждам.