Soft2Soft Ops Практическая база знаний
Linux

Systemd-сервис не запускается: как читать status и journalctl и найти причину

15 просмотров
linux systemd journalctl devops

Порядок диагностики failed unit

Если сервис перешёл в состояние failed, не меняйте права «наугад» и не отключайте защитные механизмы. Сначала определите этап сбоя: systemd не смог подготовить окружение, команда из ExecStart не выполнилась, приложение само завершилось с ошибкой либо не запустилась зависимость.

  1. Посмотрите состояние через systemctl status.
  2. Прочитайте полный журнал командой journalctl -u.
  3. Проверьте загруженный unit-файл, пути, пользователя и окружение.
  4. Повторите команду от имени сервисного пользователя.
  5. После исправления выполните проверку, daemon-reload и перезапуск.

Шаг 1. Читаем systemctl status

sudo systemctl status myapp.service --no-pager -l

В выводе важны строки Loaded, Active, Process и последние сообщения журнала. Loaded показывает путь к прочитанному unit-файлу. В Active видны состояние и результат, например failed (Result: exit-code). Строка процесса содержит команду, PID и поля code=/status=. Вывод status ограничен, поэтому используйте его как краткую сводку, а не как замену журналу. :contentReference[oaicite:0]{index=0}

После устранения ошибки при необходимости сбросьте лимит неудачных запусков:

sudo systemctl reset-failed myapp.service

Шаг 2. Ищем причину в journalctl

sudo journalctl -u myapp.service -b -n 100 --no-pager
sudo journalctl -u myapp.service --since "10 minutes ago" --no-pager

Ключ -u фильтрует записи по юниту, -b оставляет текущую загрузку системы, -n ограничивает число строк, а --since отделяет свежую попытку от старых ошибок. Ищите первую содержательную строку: «файл не найден», «отказано в доступе», «не существует пользователь», «не удалось перейти в каталог», ошибку конфигурации или сообщение самого приложения. :contentReference[oaicite:1]{index=1}

Строка Failed to start описывает итог; причина обычно находится выше.

Как понимать exit codes

code=exited, status=1 обычно означает, что программа была запущена, но сама вернула ненулевой код. Статусы systemd помогают распознать сбой до нормального запуска процесса:

СимптомВероятная причинаЧто проверить
203/EXECНе выполнена командаПуть, наличие файла, право исполнения, формат и shebang
200/CHDIRНедоступен рабочий каталогWorkingDirectory и права на путь
217/USERНе применён пользовательСуществует ли User
216/GROUPНе применена группаСуществует ли Group
status=1 или иной код приложенияОшибка внутри программыАргументы, конфигурацию, окружение и логи приложения
start-limit-hitСлишком много неудачных запусковПервую ошибку, затем reset-failed

Эти коды относятся к этапам подготовки процесса systemd. :contentReference[oaicite:2]{index=2}

Шаг 3. Проверяем загруженную конфигурацию

sudo systemctl cat myapp.service
sudo systemctl show myapp.service -p FragmentPath -p DropInPaths
sudo systemd-analyze verify /etc/systemd/system/myapp.service

systemctl cat показывает основной файл и drop-in-настройки, а systemd-analyze verify выявляет синтаксические ошибки и неизвестные директивы.

ExecStart и пути

Для предсказуемого запуска указывайте полные пути к интерпретатору, бинарному файлу и файлам приложения. systemd может разрешать простое имя программы через собственный путь поиска, но не использует интерактивное окружение вашей SSH-оболочки. Поэтому команда, работающая вручную, может не найтись в сервисе. :contentReference[oaicite:3]{index=3}

ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py

Конвейеры, перенаправления и && не обрабатываются оболочкой автоматически. Предпочтительно запускать программу напрямую. Когда shell действительно нужен, вызывайте его явно:

ExecStart=/bin/sh -c '/usr/bin/example --check && /usr/bin/example --run'

User, Group и WorkingDirectory

Проверьте существование учётной записи и доступ пользователя к исполняемому файлу, конфигурации, рабочему каталогу и каталогу данных:

getent passwd myapp
getent group myapp
namei -l /opt/myapp/venv/bin/python
sudo -u myapp test -x /opt/myapp/venv/bin/python
sudo -u myapp test -r /etc/myapp/myapp.env
sudo -u myapp test -w /var/lib/myapp

Нужно право прохода через родительские каталоги. WorkingDirectory должен существовать до запуска и быть доступен пользователю.

Не применяйте chmod 777 и не отключайте SELinux, AppArmor или изоляцию unit-файла. Определите конкретный недоступный ресурс и выдайте минимальные права владельцу или группе. Для системы контроля доступа исправьте метку, профиль либо разрешающее правило.

EnvironmentFile и переменные

Проверьте путь, права чтения и формат файла. EnvironmentFile содержит присваивания переменных, а не полноценный интерактивный shell-скрипт. Не рассчитывайте на загрузку .bashrc, алиасы или подстановку команд.

EnvironmentFile=/etc/myapp/myapp.env
Environment="APP_MODE=production"
APP_PORT=8080
APP_DATA=/var/lib/myapp

Запись EnvironmentFile=-/etc/myapp/myapp.env делает файл необязательным; применяйте её только осознанно.

Пример unit-файла

[Unit]
Description=MyApp application service
After=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target

Пользователь, каталоги, виртуальное окружение и файл переменных должны существовать заранее.

Шаг 4. Повторяем запуск от имени сервиса

Проверяйте команду не от root, а с теми же правами и рабочим каталогом:

sudo -u myapp -H sh -c '
  cd /opt/myapp || exit 1
  set -a
  . /etc/myapp/myapp.env
  set +a
  exec /opt/myapp/venv/bin/python /opt/myapp/app.py
'

Тест выявляет проблемы прав, каталога, переменных и конфигурации, хотя не эмулирует все ограничения systemd.

daemon-reload и зависимости

После изменения unit-файла перечитайте конфигурацию, перезапустите сервис и сразу проверьте свежие записи:

sudo systemctl daemon-reload
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager -l
sudo journalctl -u myapp.service -b -n 50 --no-pager

daemon-reload не перезапускает приложение, а restart без reload может использовать ранее загруженную конфигурацию.

При ошибке зависимости проверьте её отдельно:

sudo systemctl list-dependencies myapp.service
sudo systemctl show myapp.service -p Requires -p Wants -p After
sudo systemctl status required.service --no-pager -l
sudo journalctl -u required.service -b --no-pager

After задаёт порядок, но само по себе не притягивает другой юнит. Wants создаёт мягкую зависимость, а Requires — более сильную. Не маскируйте проблему задержкой запуска: найдите ресурс, который должен быть готов, и отвечающий за него юнит.

Чек-лист перед повторным запуском

  • Loaded указывает на ожидаемый unit-файл.
  • В журнале найдена первичная ошибка, а не только итог Failed to start.
  • ExecStart содержит правильную команду, аргументы и пути.
  • Исполняемый файл существует, имеет корректный shebang и доступен сервисному пользователю.
  • User и Group существуют.
  • WorkingDirectory существует и доступен для прохода.
  • EnvironmentFile читается и указан без опечаток.
  • Права выданы минимально необходимым образом, без chmod 777.
  • Зависимые юниты активны, а порядок запуска описан осмысленно.
  • После правки выполнены systemd-analyze verify, daemon-reload и restart.
  • После запуска проверены status и свежий журнал.