Устранение сбоев служб Systemd: Пошаговое руководство
Systemd стал стандартом де-факто для управления системами и службами в большинстве современных дистрибутивов Linux, играя критически важную роль в управлении службами, демонами и процессами. Несмотря на мощность и эффективность, службы, управляемые systemd, иногда могут не запускаться, что приводит к простоям приложений или нестабильности системы. Диагностика этих сбоев требует систематического подхода с использованием надежных возможностей systemd для ведения журналов и интроспекции.
В этом руководстве представлен всесторонний пошаговый метод устранения распространенных сбоев при запуске служб systemd. Мы охватим все: от первоначальных проверок состояния и глубокого анализа журналов до проверки файлов юнитов и решения сложных проблем с зависимостями. К концу этой статьи вы будете обладать практическими знаниями и инструментами для эффективной диагностики и устранения большинства сбоев служб systemd, гарантируя бесперебойную работу ваших приложений и служб.
Первая линия обороны: systemctl status
Когда служба не запускается, первой командой, которую следует выполнить, является systemctl status <имя_службы>. Эта команда предоставляет моментальный снимок текущего состояния службы, включая то, активна ли она, загружена ли, и, что особенно важно, фрагмент ее недавних журналов. Это часто дает достаточно информации для быстрого выявления проблемы.
Предположим, ваша служба веб-приложения mywebapp.service не запускается:
systemctl status mywebapp.service
Интерпретация примера вывода:
● mywebapp.service - My Web Application
Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Mon 2023-10-26 10:30:05 UTC; 10s ago
Process: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
CPU: 10ms
Oct 26 10:30:05 hostname systemd[1]: Started My Web Application.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Error: Port 8080 already in use
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Failed with result 'exit-code'.
Из этого вывода мы можем немедленно увидеть:
* Служба mywebapp.service находится в состоянии failed.
* Она завершилась с Result: exit-code, что означает, что команда ExecStart завершилась с ненулевым статусом.
* Строка Process показывает, что команда mywebapp-start.sh завершилась с status=1/FAILURE.
* Важно, что строки журнала указывают: Error: Port 8080 already in use (Ошибка: Порт 8080 уже используется). Это явный индикатор проблемы.
Эта команда — ваш первый диагностический инструмент, который часто напрямую указывает на причину или сужает область поиска.
Глубокое погружение с journalctl
В то время как systemctl status предоставляет краткое резюме, journalctl — это ваша основная команда для детального ведения журналов. Она запрашивает журнал systemd, который собирает журналы из всех частей системы, включая службы.
Базовый обзор журналов
Чтобы просмотреть все журналы для определенной службы, включая исторические записи:
journalctl -u mywebapp.service
Это покажет все записи журнала, связанные с mywebapp.service. Если служба неоднократно сбоит, вы увидите записи каждой неудачной попытки.
Фильтрация и запросы по времени
Чтобы сузить результаты, особенно после недавнего сбоя, вы можете использовать флаги, такие как --since и --priority:
- Показать журналы с определенного времени:
bash journalctl -u mywebapp.service --since "10 minutes ago" journalctl -u mywebapp.service --since "2023-10-26 10:00:00" - Показать только сообщения уровня ошибки или выше:
bash journalctl -u mywebapp.service -p err - Комбинировать с
-xeдля расширенного объяснения и подробного вывода:
bash journalctl -u mywebapp.service -xe --since "5 minutes ago"
Это невероятно полезно, так какjournalctl -xeпредоставляет дополнительный контекст, включая объяснения некоторых сообщений журнала и трассировки стека, если они доступны.
Понимание сообщений журнала
Ищите ключевые слова, такие как Error (Ошибка), Failed (Сбой), Warning (Предупреждение), или сообщения, специфичные для приложения, которые указывают на то, что пошло не так. Обращайте внимание на временные метки, чтобы понять последовательность событий, приведших к сбою.
Совет: Если скрипт ExecStart вашей службы выводит данные в стандартный вывод или стандартный поток ошибок, эти сообщения обычно захватываются journalctl. Убедитесь, что ваши скрипты регистрируют описательные сообщения об ошибках.
Проверка файла юнита: Чертеж вашей службы
Каждая служба systemd определяется файлом юнита (например, mywebapp.service). Неправильные настройки в этом файле являются распространенной причиной сбоев при запуске. Вам нужно понять, что служба пытается сделать.
Получение файла юнита
Чтобы просмотреть активный файл юнита для вашей службы:
systemctl cat mywebapp.service
Эта команда показывает точный файл юнита, который использует systemd, включая любые переопределения.
Основные директивы для проверки
Сосредоточьтесь на разделе [Service] для проблем, связанных с выполнением, и на разделе [Unit] для зависимостей.
ExecStart: Это команда, которую systemd выполняет для запуска вашей службы. Убедитесь, что путь правильный, и сама команда исполняема и успешно выполняется при ручном вызове (например, от имени указанногоUser).
ini ExecStart=/usr/local/bin/mywebapp-start.shType: Определяет тип запуска процесса. Распространенные типы включают:simple(по умолчанию):ExecStart— это основной процесс.forking:ExecStartсоздает дочерний процесс, а родительский завершается. Systemd ждет завершения родительского процесса.oneshot:ExecStartвыполняется и завершается; systemd считает службу активной до тех пор, пока команда выполняется.notify: Служба отправляет уведомление systemd, когда готова.- Неправильный
Typeможет привести к тому, что systemd посчитает службу сбойной, хотя она на самом деле запустилась, или наоборот.
User/Group: Пользователь и группа, от имени которых будет запущена служба. Проблемы с разрешениями часто возникают из-за того, что служба пытается получить доступ к файлам или ресурсам, на которые у нее нет прав от имени этого пользователя.
ini User=mywebappuser Group=mywebappgroupWorkingDirectory: Каталог, из которого будет выполняться служба. Относительные пути вExecStartили других командах зависят от этого.Restart: Определяет, когда служба должна быть перезапущена. Если установлено значениеon-failureилиalways, сбойная служба может постоянно перезапускаться, что затрудняет выявление первоначального сбоя.TimeoutStartSec/TimeoutStopSec: Сколько времени systemd ждет запуска или остановки службы. Если инициализация службы занимает больше времени, чемTimeoutStartSec, systemd ее завершит и сообщит о сбое.
Распространенные проблемы с файлами юнитов
- Неправильные пути: Опечатка в
ExecStartили других путях к файлам. - Отсутствие переменных
Environment: Службам часто требуются специфичные переменные среды (например,PATH), которых может не быть в чистом окружении systemd (см. ниже). - Разрешения: Указанный
Userне имеет прав на выполнение скрипта или прав на чтение/запись необходимых файлов данных. - Синтаксические ошибки: Простые опечатки в самом файле юнита.
Для ручного тестирования ExecStart:
Переключитесь на пользователя службы и попробуйте выполнить команду напрямую:
sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh
Это часто воспроизводит ошибку, увиденную в journalctl, непосредственно в вашем терминале, что облегчает отладку.
Управление зависимостями: Когда службы не могут запуститься самостоятельно
Службы часто зависят от других служб или компонентов системы, которые должны быть активны, прежде чем они смогут запуститься сами. Systemd использует директивы Wants, Requires, After и Before для управления этими зависимостями.
Определение зависимостей
Используйте systemctl list-dependencies <имя_службы>, чтобы увидеть, что служба явно требует или желает для своего запуска.
systemctl list-dependencies mywebapp.service
Распространенные директивы в разделе [Unit]:
After=: Указывает, что эта служба должна начать работу после перечисленных юнитов. Если перечисленный юнит дает сбой, эта служба все равно попытается запуститься (еслиRequires=также не используется).Requires=: Указывает, что эта служба требует перечисленных юнитов. Если какой-либо из требуемых юнитов не смог запуститься, эта служба не запустится.Wants=: Более слабая формаRequires=. Если желаемый юнит дает сбой, эта служба все равно попытается запуститься.
Пример:
[Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service
Здесь mywebapp.service запустится только после запуска network.target и mysql.service, и она требует, чтобы mysql.service был успешным. Если mysql.service даст сбой, mywebapp.service не запустится.
Устранение конфликтов зависимостей
Если служба дает сбой из-за проблемы с зависимостью, journalctl обычно указывает, какая зависимость не была удовлетворена. Например, он может сообщить Dependency failed for My Web Application (Зависимость не удовлетворена для My Web Application), за чем последуют сведения о сбое mysql.service.
Шаги по устранению:
1. Проверьте зависимую службу: Выполните systemctl status <имя_зависимой_службы> (например, systemctl status mysql.service) и journalctl -u <имя_зависимой_службы>, чтобы сначала устранить ее сбой.
2. Проверьте директивы After= и Requires=: Убедитесь, что они правильно отражают желаемый порядок запуска и строгость. Иногда службе нужно ждать открытия определенного порта, а не просто активности службы. Для сложных случаев могут быть полезны systemd-socket-activate или пользовательские скрипты ExecStartPre.
Переменные среды и пути: Скрытые ловушки
Службы Systemd выполняются в очень чистом и минимальном окружении. Это часто приводит к проблемам, когда команды, прекрасно работающие в оболочке пользователя, дают сбой при выполнении systemd, поскольку отсутствуют критически важные переменные среды (например, PATH).
Чистое окружение Systemd
Когда systemd запускает службу, он не наследует полное окружение пользователя, который инициировал systemctl start. Например, переменная PATH часто урезана, что означает, что команды, такие как python или node, могут не быть найдены, если они не находятся в стандартных расположениях, таких как /usr/bin или /bin.
Симптом: ExecStart=/usr/local/bin/myscript.sh завершается ошибкой "