Запуск Gitea в NetBSD с помощью daemontools
Оглавление
Введение
Моё знакомство с daemontools
началось с одной стороны случайно, а с другой стороны - с необходимости.
Случайные обстоятельства были следующими. При настройке ikiwiki
я рассматривал различные приложения для обработки CGI-запросов и на странице руководства одного из них - spawn-fcgi - упоминались программы svc и supervise со ссылкой на страницу http://cr.yp.to/daemontools.html.
А вот необходимость возникла после того, как я установил Gitea в виртуальной машине с NetBSD. У Gitea есть одна интересная особенность: в ней не предусмотрен запуск в режиме демона, Gitea может работать только в интерактивном режиме. Это не проблема, если в системе есть systemd, но в NetBSD systemd нет. В скрипте инициализации, поставляющемся в составе pkgsrc, запуск Gitea в фоновом режиме реализован при помощи символа амерсанда, добавленного к списку аргументов:
#!/bin/sh
#
# $NetBSD: gitea.sh,v 1.2 2019/08/04 12:26:59 nia Exp $
#
# REQUIRE: DAEMON
# PROVIDE: gitea
. /etc/rc.subr
name="gitea"
rcvar=${name}
required_files="/usr/pkg/etc/gitea/conf/app.ini"
command="/usr/pkg/sbin/gitea"
command_args="--config /usr/pkg/etc/gitea/conf/app.ini 2>/dev/null >/dev/null &"
gitea_env="GITEA_WORK_DIR=/usr/pkg/share/gitea"
gitea_env="${gitea_env} GITEA_CUSTOM=/usr/pkg/etc/gitea"
gitea_env="${gitea_env} HOME=/var/db/gitea"
gitea_env="${gitea_env} USER=git"
gitea_user="git"
gitea_group="git"
load_rc_config $name
run_rc_command "$1"
Кроме того, т.к. Gitea выводит соообщения в процессе работы прямо на стандартный вывод и на стандартный поток диагностических сообщений, к опциям добавлены конструкции, перенаправляющие текст в устройство /dev/null
. Мало того, что все эти конструкции для оболочки в списке аргументов кажутся чужеродными, по каким-то неведомым причинам Gitea не запускается при загрузке системы. После входа по SSH и ручного запуска Gitea запускается и работает нормально до следующего перезапуска системы.
Т.к. все эти конструкции на меня производят удручающее впечатление, то и разбираться в причинах проблемы мне не хотелось. В NetBSD нет systemd, при помощи которого можно было бы решить эту проблему, но зато в системе pkgsrc есть daemontools от Дэниела Бернштайна. Программы Дэниела Бернштайна большинству пользователей кажутся довольно неуклюжими, поэтому ими мало кто пользуется. Однако идеи, реализованные в этих программах, оказываются передовыми и часто перенимаются последователями. А по поводу кажущейся неуклюжести можно возразить, что при ближайшем рассмотрении эти программы оказываются написанными в соответствии с принципами Unix в их предельном выражении: каждая программа должна решать только одну задачу, но решать её хорошо.
Установка daemontools
Воспользуемся готовым pkgsrc и установим daemontools:
# cd /usr/pkgsrc/sysutils/daemontools
# make install
Создадим каталог /service/
, в котором будут располагаться настройки сервисов, управляемых daemontools:
# mkdir /service/
Пропишем запуск главного процесса daemontools в файл /etc/rc.local, чтобы он запускался при загрузке системы:
# $NetBSD: rc.local,v 1.32 2008/06/11 17:14:52 perry Exp $
# originally from: @(#)rc.local 8.3 (Berkeley) 4/28/94
#
# This file is (nearly) the last thing invoked by /etc/rc during a
# normal boot, via /etc/rc.d/local.
#
# It is intended to be edited locally to add site-specific boot-time
# actions, such as starting locally installed daemons.
#
# An alternative option is to create site-specific /etc/rc.d scripts.
#
echo -n 'Starting local daemons:'
# Add your local daemons here, eg:
#
#if [ -x /path/to/daemon ]; then
# /path/to/daemon args
#fi
if [ -x /usr/pkg/bin/svscanboot ]; then
csh -cf '/usr/pkg/bin/svscanboot &'
fi
echo '.'
И запустим svcscanboot
вручную прямо сейчас:
# /usr/pkg/bin/svscanboot &
Создадим группу и пользователя mulitlog, которые будем использовать в качетсве группы доступа и владельца журналов, формируемых утилитой multilog из пакета daemontools:
# groupadd multilog
# useradd -g multilog -d /var/log/ multilog
Настройка запуска Gitea
Выставим в секции [log]
файла конфигурации /usr/pkg/etc/gitea/conf/app.ini
режим журналирования:
MODE = console
Создадим каталог /service/.gitea
:
# mkdir /service/.gitea
Создадим внутри него файл run
со следующим содержимым:
#!/bin/sh
exec 2>&1
if [ -f /usr/pkg/etc/gitea/conf/app.ini ]
then
exec \
envdir ./env \
setuidgid git \
/usr/pkg/sbin/gitea --config /usr/pkg/etc/gitea/conf/app.ini web
else
echo "Missing /usr/pkg/etc/gitea/conf/app.ini"
exit 1
fi
И сделаем его исполняемым:
# chmod +x /service/.gitea/run
Теперь создадим каталог /service/.gitea/env
, а в нём по файлу для каждой из переменных окружения, которые нужно передать сервису:
# mkdir /service/.gitea/env/
# cd /service/.gitea/env/
# echo -n "/usr/pkg/etc/gitea" > GITEA_CUSTOM
# echo -n "/usr/pkg/share/gitea" > GITEA_WORK_DIR
# echo -n "/var/db/gitea" > HOME
# echo -n "git" > USER
В принципе, этого уже достаточно для того, чтобы daemontools
смог запустить Gitea, но при помощи этого пакета можно собирать в журнал сообщения, выводимые Gitea на консоль.
Создадим каталог /service/.gitea/log
:
# mkdir /service/.gitea/log
Создадим скрипт /service/.gitea/log/run
со следующим содержимым:
#!/bin/sh
exec \
setuidgid multilog \
multilog t /var/log/gitealog/
Сделаем файл /service/.gitea/log/run
исполнимым:
# chmod +x /service/.gitea/log/run
Создадим каталог /var/log/gitealog/
, принадлежащий пользователю multilog
и группе multilog
:
# mkdir -p /var/log/gitealog/
# chown multilog:multilog /var/log/gitealog/
Существующий каталог /var/log/gitea/
можно удалить:
# rm -R /var/log/gitea/
Однако стоит учитывать, что этот каталог будет вновь создаваться при каждой переустановке или обновлении Gitea, а его владельцем будет пользователь git
и группа git
. Именно по этой причине я указал утилите multilog
для ведения журналов другой каталог - /var/log/gitealog/
.
Перед тем, как запустить сервис при помощи daemontools
, остановим демон Gitea, запущенный при помощи стандартного скрипта инициализации /etc/rc.d/gitea
:
# /etc/rc.d/gitea stop
Для запуска сервиса переименуем каталог /service/.gitea
в /service/gitea
:
# mv /service/.gitea /service/gitea
Теперь нужно отключить запуск Gitea скриптом инициализации /etc/rc.d/gitea
при загрузки системы. Для этого нужно удалить из файла /etc/rc.conf
строчку gitea=YES
. Также можно удалить и сам сценарий инициализации /etc/rc.d/gitea
, т.к. он не входит в состав базовой системы NetBSD и был помещён туда вручную при настройке Gitea.
Теперь при перезагрузке системы Gitea у меня запускается автоматически и без проблем.
Управление сервисом
Чтобы проверить состояние сервиса, можно воспользоваться следующей командой:
# svstat /service/gitea/
/service/gitea/: up (pid 14327) 315 seconds
Чтобы остановить сервис, можно воспользоваться такой командой:
# svc -d /service/gitea/
Если проверить состояние сервиса сейчас, то можно будет увидеть следующее:
# svstat /service/gitea/
/service/gitea/: down 1 seconds, normally up
Чтобы снова запустить сервис, можно воспользоваться такой командой:
# svc -u /service/gitea/
Для перезапуска сервиса путём его остановки и повторного запуска, можно воспользоваться такой командой:
# svc -du /service/gitea/
Некоторые сервисы умеют обрабатывать сигнал HUP, по которому они перечитывают и применяют изменения в файле конфигурации. Кроме того, подобные сервисы обычно также закрывают и повторно открывают файлы журналов, что не так важно, если журналами управляет multilog
.
Просмотр журналов
Один из плюсов программы multilog
заключается в том, что она умеет выполнять автоматическую ротацию журналов: при достижении журналом размера более 99999 байт создаётся новый журнал, всего поддерживается не более 10 журналов. При необходимости эти настройки можно поменять, передав multilog соответствующие опции, о которых можно почитать на его странице руководства.
Для ротации журналов не требуется каким-либо образом извещать сервис о необходимости переоткрыть файлы журала, т.к. данные со стандартного вывода и стандартного потока диагностических сообщений попадают в канал, откуда их подхватывает multilog
. Сам multilog
следит за размером файла журнала, при необходимости создаёт новый файл, удаляет устаревшие журналы и продолжает запись в только что созданный журнальный файл.
В файле /var/log/gitea/current можно увидеть текст, который выводит Gitea в процессе работы:
# tail -f /var/log/gitea/current
@40000000612f62c30620e2c4 2021/09/01 16:23:37 routers/init.go:134:GlobalInit() [T] Custom path: /usr/pkg/etc/gitea
@40000000612f62c307bc7224 2021/09/01 16:23:37 routers/init.go:135:GlobalInit() [T] Log path: /var/log/gitea
@40000000612f62c307bcc044 2021/09/01 16:23:37 routers/init.go:56:checkRunMode() [I] Run Mode: Production
@40000000612f646a0c75720c 2021/09/01 16:30:40 cmd/web.go:108:runWeb() [I] Starting Gitea on PID: 14056
@40000000612f646a0c8e707c 2021/09/01 16:30:40 ...dules/setting/git.go:91:newGit() [I] Git Version: 2.32.0, Wire Protocol Version 2 Enabled
@40000000612f646a0d50bac4 2021/09/01 16:30:40 routers/init.go:132:GlobalInit() [T] AppPath: /usr/pkg/sbin/gitea
@40000000612f646a0d515ed4 2021/09/01 16:30:40 routers/init.go:133:GlobalInit() [T] AppWorkPath: /usr/pkg/share/gitea
@40000000612f646a0d527044 2021/09/01 16:30:40 routers/init.go:134:GlobalInit() [T] Custom path: /usr/pkg/etc/gitea
@40000000612f646a100bb444 2021/09/01 16:30:40 routers/init.go:135:GlobalInit() [T] Log path: /var/log/gitea
@40000000612f646a100c1dbc 2021/09/01 16:30:40 routers/init.go:56:checkRunMode() [I] Run Mode: Production
В начале каждой строки multilog
добавляет закодированную отметку времени, о чём мы его попросили при помощи опции t
. Посмотреть эти метки в раскодированном виде можно с помощюь утилиты tai64nlocal
, например, следующим образом:
# tail -f /var/log/gitea/current | tai64nlocal
2021-09-01 16:23:37.102818500 2021/09/01 16:23:37 routers/init.go:134:GlobalInit() [T] Custom path: /usr/pkg/etc/gitea
2021-09-01 16:23:37.129790500 2021/09/01 16:23:37 routers/init.go:135:GlobalInit() [T] Log path: /var/log/gitea
2021-09-01 16:23:37.129810500 2021/09/01 16:23:37 routers/init.go:56:checkRunMode() [I] Run Mode: Production
2021-09-01 16:30:40.209023500 2021/09/01 16:30:40 cmd/web.go:108:runWeb() [I] Starting Gitea on PID: 14056
2021-09-01 16:30:40.210661500 2021/09/01 16:30:40 ...dules/setting/git.go:91:newGit() [I] Git Version: 2.32.0, Wire Protocol Version 2 Enabled
2021-09-01 16:30:40.223394500 2021/09/01 16:30:40 routers/init.go:132:GlobalInit() [T] AppPath: /usr/pkg/sbin/gitea
2021-09-01 16:30:40.223436500 2021/09/01 16:30:40 routers/init.go:133:GlobalInit() [T] AppWorkPath: /usr/pkg/share/gitea
2021-09-01 16:30:40.223506500 2021/09/01 16:30:40 routers/init.go:134:GlobalInit() [T] Custom path: /usr/pkg/etc/gitea
2021-09-01 16:30:40.269202500 2021/09/01 16:30:40 routers/init.go:135:GlobalInit() [T] Log path: /var/log/gitea
2021-09-01 16:30:40.269229500 2021/09/01 16:30:40 routers/init.go:56:checkRunMode() [I] Run Mode: Production
Первыми отображаются отметки времени добавленные multilog
, а далее следуют отметки времени от самой Gitea.
Среди прочих опций multilog
можно указать программу-фильтр. Если в качестве программы-фильтра указать утилиту tai64nlocal
, то в журналах на диске будут фигурировать не закодированные отметки времени, а пригодные для чтения человеком.
И кроме этого, можно настроить multilog
так, чтобы он выделял из потока сообщений отдельные сообщения в соответствии с указанными критериями и помещал их в отдельные журналы. Кроме того, любой из журналов можно превратить в файл статуса, в котором будет храниться только последняя строчка, соответствующая указанным критериям фильтрации. Примеры подобной настройки multilog
можно найти в статьях Запуск socklog в NetBSD с помощью daemontools и Мониторинг dnscache из djbdns в NetBSD через Zabbix-агента.
Просмотр дерева процессов
Для просомтра дерева процессов можно воспользоваться стандартной системной командой ps
, запустив её следующим образом:
# ps -Ado pid,command
PID COMMAND
0 [system]
1 - init
277 |-- /bin/sh /usr/pkg/bin/svscanboot
326 | |-- readproctitle service errors: .................................................................................................................
332 | `-- svscan /service
271 | |-- supervise zabbix_agentd
439 | | `-- /usr/pkg/sbin/zabbix_agentd -fc /usr/pkg/etc/zabbix_agentd.conf
367 | | |-- zabbix_agentd: listener #1 [waiting for connection]
430 | | |-- zabbix_agentd: collector [idle 1 sec]
432 | | `-- zabbix_agentd: active checks #1 [idle 1 sec]
295 | |-- supervise powerd
383 | | `-- /usr/sbin/powerd -d
308 | |-- supervise sshd
389 | | `-- /usr/pkg/bin/tcpserver -c10 -HRv -l0 0 22 /usr/sbin/sshd -Die -u0 -f /etc/ssh/sshd_config
9157 | | `-- sshd: stupin [priv]
8740 | | `-- sshd: stupin@pts/0 (sshd)
9062 | | `-- -sh
14584 | | `-- su -
9067 | | `-- -sh
9183 | | `-- ps -Ado pid,command
314 | |-- supervise log
96 | | `-- multilog t /var/log/cron/
330 | |-- supervise log
427 | | `-- multilog t /var/log/zabbix_agentd/
334 | |-- supervise openntpd
12204 | | `-- /usr/pkg/sbin/ntpd -d -s -f /usr/pkg/etc/ntpd.conf
4760 | | `-- ntpd: ntp engine
12187 | | `-- ntpd: dns engine
336 | |-- supervise log
547 | | `-- multilog t /var/log/sshd/
337 | |-- supervise gitea
380 | | `-- /usr/pkg/sbin/gitea --config /usr/pkg/etc/gitea/conf/app.ini web
338 | |-- supervise log
516 | | `-- multilog t /var/log/openntpd/
339 | |-- supervise log
472 | | `-- multilog t /var/log/gitealog/
345 | |-- supervise log
484 | | `-- multilog t /var/log/powerd/
349 | |-- supervise log
98 | | `-- multilog t /var/log/socklog-unix/
350 | |-- supervise cron
41 | | `-- /usr/sbin/cron -n
357 | `-- supervise socklog-unix
97 | `-- /usr/pkg/sbin/socklog nix /var/run/log
422 |-- /usr/libexec/getty Pc constty
327 |-- /usr/libexec/getty Pc ttyE1
104 |-- /usr/libexec/getty Pc ttyE2
73 `-- /usr/libexec/getty Pc ttyE3
Если установить в систему утилиту proctree
из pkgsrc pstree
, то можно увидеть следующую иерархию процессов:
# proctree -g3
─┬= 00001 root init
├─┬= 00277 root /bin/sh /usr/pkg/bin/svscanboot
│ ├─── 00326 root readproctitle service errors: ................................................................................................
│ └─┬─ 00332 root svscan /service
│ ├─┬─ 00271 root supervise zabbix_agentd
│ │ └─┬─ 00439 zabbix /usr/pkg/sbin/zabbix_agentd -fc /usr/pkg/etc/zabbix_agentd.conf
│ │ ├─── 00367 zabbix zabbix_agentd: listener #1 [waiting for connection]
│ │ ├─── 00430 zabbix zabbix_agentd: collector [idle 1 sec]
│ │ └─── 00432 zabbix zabbix_agentd: active checks #1 [idle 1 sec]
│ ├─┬─ 00295 root supervise powerd
│ │ └─── 00383 root /usr/sbin/powerd -d
│ ├─┬─ 00308 root supervise sshd
│ │ └─┬─ 00389 root /usr/pkg/bin/tcpserver -c10 -HRv -l0 0 22 /usr/sbin/sshd -Die -u0 -f /etc/ssh/sshd_config
│ │ └─┬─ 09157 root sshd: stupin [priv]
│ │ └─┬─ 08740 stupin sshd: stupin@pts/0 (sshd)
│ │ └─┬= 09062 stupin -sh
│ │ └─┬= 14584 root su -
│ │ └─┬= 09067 root -sh
│ │ └─┬= 15672 root proctree -g3
│ │ └─── 08948 root ps -axwwo user,pid,ppid,pgid,command
│ ├─┬─ 00314 root supervise log
│ │ └─── 00096 multilog multilog t /var/log/cron/
│ ├─┬─ 00330 root supervise log
│ │ └─── 00427 multilog multilog t /var/log/zabbix_agentd/
│ ├─┬─ 00334 root supervise openntpd
│ │ └─┬─ 12204 root /usr/pkg/sbin/ntpd -d -s -f /usr/pkg/etc/ntpd.conf
│ │ └─┬─ 04760 _ntp ntpd: ntp engine
│ │ └─── 12187 _ntp ntpd: dns engine
│ ├─┬─ 00336 root supervise log
│ │ └─── 00547 multilog multilog t /var/log/sshd/
│ ├─┬─ 00337 root supervise gitea
│ │ └─── 00380 git /usr/pkg/sbin/gitea --config /usr/pkg/etc/gitea/conf/app.ini web
│ ├─┬─ 00338 root supervise log
│ │ └─── 00516 multilog multilog t /var/log/openntpd/
│ ├─┬─ 00339 root supervise log
│ │ └─── 00472 multilog multilog t /var/log/gitealog/
│ ├─┬─ 00345 root supervise log
│ │ └─── 00484 multilog multilog t /var/log/powerd/
│ ├─┬─ 00349 root supervise log
│ │ └─── 00098 multilog multilog t /var/log/socklog-unix/
│ ├─┬─ 00350 root supervise cron
│ │ └─── 00041 root /usr/sbin/cron -n
│ └─┬─ 00357 root supervise socklog-unix
│ └─── 00097 root /usr/pkg/sbin/socklog nix /var/run/log
├──= 00422 root /usr/libexec/getty Pc constty
├──= 00327 root /usr/libexec/getty Pc ttyE1
├──= 00104 root /usr/libexec/getty Pc ttyE2
└──= 00073 root /usr/libexec/getty Pc ttyE3
В Linux для просмотра дерева процессов с помощью ps
можно воспользоваться следующей командой:
# ps -eHo pid,command
Чтобы убрать из вывода команды системные процессы, которых в Linux может быть довольно много, можно воспользоваться такой командой:
# ps -eHo pid,command | grep -vE '^ *[0-9]+ +\['
Совместимость с rc
Для совместимости с системой инициализации /etc/rc
создадим скрипт /etc/rc.d/gitea
со следующим содержимым:
#!/bin/sh
#
# REQUIRE: DAEMON
# PROVIDE: gitea
if [ -f /etc/rc.subr ]; then
. /etc/rc.subr
fi
name=gitea
rcvar=$name
load_rc_config $name
if checkyesno $rcvar ; then
rm -f /service/$name/down
else
touch /service/$name/down
fi
status_cmd="/usr/pkg/bin/svstat /service/$name/ | sed -e 's,^/service/\(.*\)/: up (\(pid .*\)).*$,\1 is running as \2.,g; s,^/service/\(.*\)/: down .*,\1 is not running.,g'"
start_cmd="/usr/pkg/bin/svc -u /service/$name/ ; echo 'Starting $name.'"
stop_cmd="/usr/pkg/bin/svc -d /service/$name/ ; echo 'Stopping $name.'"
restart_cmd="/usr/pkg/bin/svc -du /service/$name/ ; echo 'Restarting $name.'"
extra_commands="status"
run_rc_command "$1"
После создания файла нужно добавить права на его выполнение:
# chmod +x /etc/rc.d/gitea
Теперь можно будет включать и выключать сервис Gitea привычным образом через переменную gitea
в файле /etc/rc.conf
, а также запускать, останавливать, перезапускать и проверять состояние сервиса с помощью скрипта /etc/rc.d/gitea
. Для непосвящённого наблюдателя всё будет выглядеть привычным образом:
git# /etc/rc.d/gitea rcvar
# gitea
$gitea=YES
git# /etc/rc.d/gitea status
gitea is running as pid 312.
git# /etc/rc.d/gitea restart
Restarting gitea.
git# /etc/rc.d/gitea status
gitea is running as pid 504.
В процессе загрузки системы первыми запускаются скрипты в каталоге /etc/rc.d/
и только потом обрабатывается файл /etc/rc.local
. Поскольку к моменту обработки файла /etc/rc.local
в каталогах /services/.../
уже будут созданы или удалены файлы down
в соответствии с настройками из файла /etc/rc.conf
, то daemontools
запустит только сервисы, активированные через файл /etc/rc.conf
.
Альтернативы
Существуют аналогичные системы, развивающие идеи daemontools: