Содержание
Понадобилось засунуть php
скрипт (с вечным циклом) в автозапуск через systemd
: скрипт должен стартовать при старте системы и в случае своего падения должен возобновлять работу. При этом вывод скрипта очень хочется просмотреть в произвольный момент времени. Также нужен деплой с плавным перезапуском.
Для начала создадим файл my-daemon.service
в /etc/systemd/system/
и разместим там следующий конфиг:
ini[Unit] Description=my-daemon After=syslog.target network.target [Service] Type=simple WorkingDirectory=/home/byurrer/my-daemon/ User=root ExecStart=/usr/bin/php -f /home/byurrer/my-daemon/my-daemon.php & TimeoutSec=30 Restart=always RestartSec=10s [Install] WantedBy=multi-user.target
Запускаем:
After
)Type=simple
), но в самой команде запуска (ExecStart
) используется &
для перевода выполнения команды в фоновый режим. Здесь различия значений Type
(forking vs simple)root
TimeoutSec
), если скрипт не завершится за это время то процесс будет закрыт принудительноRestart
) через 10 секунд после падения (RestartSec
)bashsystemctl enable my-daemon
Для отключения сервиса нужно:
bashsystemctl disable my-daemon
Если в конфиг сервиса были внесены изменения (/etc/systemd/system/my-daemon.service
) то необходимо перезагрузить конфиги демонов:
bashsystemctl daemon-reload
Просмотр текущего статуса:
bashsystemctl status my-daemon
Вместо status
можно использовать другие команды systemctl
:
start
- стартstop
- остановкаrestart
- перезапуск, например при изменении кода скриптаДля того чтобы направить вывод демона в терминал, можно использовать reptyr:
bashreptyr PID
PID
можно узнать через запрос статуса демона (в данном выводу pid
=5965):
bashsystemctl status my-daemon ● my-daemon - my-daemon Loaded: loaded (/etc/systemd/system/my-daemon.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2021-07-19 20:47:57 MSK; 145ms ago Main PID: 5965 (php) Tasks: 1 (limit: 9280) Memory: 5.7M CGroup: /system.slice/my-daemon.service └─5965 /usr/bin/php -f /home/byurrer/my-daemon/my-daemon.php &
Если есть доступ к серверу по http
, то для деплоя можно использовать веб-хуки при push
в удаленный репозиторий.
Но доступа по http
нет. Поэтому напишем скрипт деплоя и повесим его на крон :)
Сначала нам нужно узнать, на что указывает HEAD
в удаленном репозитории:
bash> git ls-remote -h origin 5da44ad4a189dc6a70d4c82b847abd9edfd461cc refs/heads/master
Теперь нам нужно сравнить с HEAD
из локального репозитория (уже рассматривали в этой статье):
bash> git rev-parse HEAD edd43d00722a835a6ec24df14c38e103894d4128
Теперь мы имеем указатели на HEAD
в удаленном репозитории и в локальном, если они одинаковые, значит наш локальный репозиторий не нуждается в обновлении, иначе обновляем:
bashgit pull
Но есть проблема, первая и третья команды не могут успешно выполниться если удаленный репозиторий приватный. Для этого придется явно указывать в URL авторизационные данные.
При наличии self-hosted gitlab можно использовать токены доступа, а если есть аккаунт на gitlab.com можно использовать данные от своего аккаунта или создать дополнительный аккаунт для деплоя.
В итоге скрипт для деплоя будет выглядеть следующим образом (разместить надо в корне локального репозитория):
php$sDir = dirname(__FILE__); $sResultLsRemote = shell_exec("cd $sDir; git ls-remote -h https://login:password@gitlab.com/user/repo.git"); if(!preg_match("/^(.*?)\s/", $sResultLsRemote, $aMatch)) exit("unresolved response\n"); $sLastCommitRemote = trim($aMatch[1]); $sLastCommitLocal = trim(shell_exec("cd $sDir; git rev-parse HEAD")); if($sLastCommitRemote == $sLastCommitLocal) exit("no change\n"); $sResultPull = shell_exec("cd $sDir; git pull https://login:password@gitlab.com/user/repo.git");
Для перезапуска демона добавим конец такое (скрипт должен запускаться от имени root
):
phpshell_exec("systemctl restart my-daemon");
Совсем недавно мы рассматривали установку certbot в cron.
Делаем все аналогично, от имени root
сначала создаем файл /erc/cron.d/my-daemon-deploy.php
(*/10
- каждые 10 минут):
bash*/10 * * * * root php7.3 -f /home/byurrer/my-daemon/.deploy.php
Затем рестарт демона cron
:
bashsystemctl restart cron
Представим ситуацию что php
скрипт (который демонизируется) выполняется в бесконечном цикле и каждая итерация цикла работает с данными, которые нельзя потерять, например так:
php$redis->sPop($key, 100); //...
Тогда нам нужно плавное завершение, которое позволит завершить итерацию бесконечного цикла и прервать работу скрипта без потери данных. Для этого в php
можно использовать установку обработчика сигнала SIGTERM
при помощи функции pcntl_signal.
В моем случае все свелось к минимуму:
phpdeclare(ticks = 1); // нужно ли завершать исполнение $g_needTerminate = false; // обработчик сигналов function SigHandler($signo) { global $g_needTerminate; switch ($signo) { case SIGTERM: $g_needTerminate = true; break; default: break; } } // установка обработчика сигнала SIGTERM pcntl_signal(SIGTERM, "SigHandler"); // бесконечный цикл, каждая итерация которого зависит от $g_needTerminate while(!$g_needTerminate) { //... }
php
демон в systemd
, который: