PostgreSQL в systemd: почему кластер не поднимается
В PostgreSQL под systemd есть особенность, которая путает разбор: postgresql.service в Debian и Ubuntu — это пустая обёртка, а настоящая работа идёт в шаблонном unit вида postgresql@16-main.service. Смотреть журнал нужно именно у него.
О службе
Обёртка существует, чтобы одной командой поднимать все кластеры на машине. Она ничего не запускает сама: Type=oneshot с RemainAfterExit=yes. Поэтому её состояние почти всегда active, даже когда кластер не работает. Настоящее состояние смотрят у экземпляра: systemctl status postgresql@16-main.
Вторая особенность — строгие требования к каталогу данных. PostgreSQL отказывается стартовать, если права на каталог шире 0700 (или 0750 в новых версиях) или владелец не совпадает с пользователем службы. Это защита от случайного доступа, и обходить её не нужно — нужно исправить права.
Третья — время запуска. После аварийного завершения кластер восстанавливается по журналу транзакций, и это может занять минуты. Значение таймаута по умолчанию иногда оказывается меньше времени восстановления, и тогда systemd убивает процесс посередине — что только удлиняет следующую попытку.
Как устроена
| Настоящий unit | шаблонный postgresql@ВЕРСИЯ-ИМЯ.service, например postgresql@16-main.service |
|---|---|
| Обёртка | postgresql.service — oneshot без процессов, её состояние ничего не говорит о кластере |
| Каталог данных | /var/lib/postgresql/ВЕРСИЯ/ИМЯ, права строго 0700 или 0750 |
| Собственный журнал | /var/log/postgresql/postgresql-ВЕРСИЯ-ИМЯ.log — там причина, если в journalctl только код |
| Порт по умолчанию | 5432, задаётся в postgresql.conf и в файле кластера |
Частые ошибки
32 записи базы отмечены за этой службой.
Коды выхода
- status=1/FAILURE в systemdКод 1/FAILURE означает, что программа запустилась и сама завершилась с ошибкой. Как найти настоящую причину в журнале службы.
- status=217/USER в systemdКод 217/USER означает, что systemd не смог определить или сменить пользователя из User=. Разбор причин: пользователя нет, имя недопустимо, конфликт с DynamicUser.
- status=216/GROUP в systemdКод 216/GROUP: systemd не смог определить или сменить группу из Group= или SupplementaryGroups=. Причины и как проверить.
- status=238/STATE_DIRECTORY в systemdКод 238/STATE_DIRECTORY: не удалось подготовить постоянный каталог службы в /var/lib из StateDirectory=.
- status=242/NUMA_POLICY в systemdКод 242/NUMA_POLICY: не удалось применить политику размещения памяти NUMA из NUMAPolicy= и NUMAMask=.
Сообщения журнала
- Permission denied в журнале службыОтказ в доступе у службы systemd: права на файл, каталоги по пути, пользователь службы, параметры изоляции, SELinux и AppArmor.
- Address already in use при запуске службыПорт или адрес уже занят: bind() failed (98: Address already in use). Как найти владельца порта и что делать с остатками прежнего процесса.
- Connection refused в журнале службыСоединение отклонено: служба не может подключиться к базе, кешу или другому сервису. Порядок запуска, адрес, брандмауэр.
- Cannot assign requested address при привязкеОшибка 99 при привязке: адрес не принадлежит машине или ещё не поднят. Разбор с network-online.target и ip_nonlocal_bind.
- Cannot set LC_ALL: языковая среда не созданаСлужбы и команды сыпят предупреждениями о языковой среде: нужная среда не создана в системе.
- I/O error, dev sda, sector N: ошибка носителяЯдро сообщает об ошибке чтения или записи на устройстве. Что делать со службой и данными.
- No such file or directory в журнале службыСлужба не находит файл или каталог. Разбор: путь, момент запуска, изоляция unit-файла, символические ссылки, приватный /tmp.
- Приложение не стартует: блокировка миграций базыСлужба зависает или падает на применении миграций: осталась блокировка после прерванного запуска.
Ресурсы и ограничения
- No space left on device в журнале службыНет места на устройстве: разбор по свободным блокам, по inode, по журналу systemd и по удалённым, но открытым файлам.
Состояния результата
- Failed with result 'timeout'Состояние timeout: служба не уложилась в отведённое время при запуске, остановке или перезагрузке настроек.
- Failed with result 'oom-kill'Состояние oom-kill: процесс службы убит из-за нехватки памяти. Как понять, упёрлись в предел службы или в память машины.
- Failed with result 'exit-code'Состояние exit-code: служба завершилась с ненулевым кодом. Что это сообщение значит и где искать настоящую причину.
- Job for … failed because a timeout was exceededЗадание на запуск прервано по таймауту. Как отличить медленный старт от заблокированного и правильно настроить TimeoutStartSec.
Конфигурация unit
- Служба active, но не работает: обёртки и oneshotПочему состояние active не означает работающую программу: oneshot с RemainAfterExit, обёртки, потерянный главный процесс.
- Шаблонный unit: экземпляр не запускаетсяСлужба вида имя@экземпляр не запускается: нет файла настроек экземпляра, неверное экранирование, не включён шаблон.
Зависимости и порядок
- Служба не запускается вместе с зависимостьюAfter= без Wants= не запускает зависимость. Разбор частой путаницы между порядком и необходимостью.
Сигналы
- signal=BUS (status=7/BUS) в systemdПроцесс службы завершён сигналом BUS: ошибка доступа к памяти, часто из-за усечённого файла в отображении или заполненного диска.
- signal=KILL (status=9/KILL) в systemdПроцесс службы убит сигналом KILL. Кто мог его послать: OOM-killer, таймаут остановки systemd, администратор.
Ошибки служб
- PostgreSQL: could not bind IPv4 addressКластер PostgreSQL не занимает порт: адрес занят другим экземпляром или остался файл сокета. Разбор.
- PostgreSQL: data directory has invalid permissionsКластер PostgreSQL не запускается из-за прав на каталог данных. Требование 0700 или 0750 и как вернуть владельца.
- PostgreSQL: too many clients alreadyБаза отказывает в подключениях: исчерпан max_connections. Как считать значение и зачем пул соединений.
- PostgreSQL: долгий запуск и таймаут восстановленияКластер восстанавливается после аварийного завершения дольше таймаута systemd. Как правильно увеличить TimeoutStartSec.
- PostgreSQL: локаль кластера не совпадает с системойКластер не запускается или сортировка ломается после обновления системы: несовпадение версии библиотеки локалей.
- PostgreSQL: нет места под журнал предзаписиБаза останавливается из-за заполненного каталога журнала предзаписи: неработающая архивация, слот репликации, недостаточный раздел.
- postgresql.service активен, а кластер не работаетОбёртка postgresql.service — пустой oneshot. Почему её состояние ничего не говорит о кластере и что смотреть.
- База временных рядов не запускается после обновленияОбновление сменило пользователя или расположение данных: служба не может открыть свой каталог.
- Приложение ломается при работе через пул соединенийЗапросы отказывают после перехода на пул: режим пула не поддерживает подготовленные запросы или временные таблицы.
Коды выхода этой службы
Числа в status=N ниже 200 назначает сама программа, 200 и выше — systemd, когда не смог подготовить запуск.
| status | Что означает у этой службы | Куда смотреть |
|---|---|---|
1/FAILURE |
самый частый: ошибка в конфигурации, права на каталог данных, занятый порт. Причина — в собственном журнале кластера. | разбор |
2 |
проблема с каталогом данных: не найден или не принадлежит нужному пользователю. | разбор |
убит сигналом KILL |
нехватка памяти при больших запросах или тесный MemoryMax. | разбор |
Диагностика
Состояние настоящего unit кластера, а не обёртки.
systemctl status postgresql@16-main --no-pager -lСобственный журнал кластера: именно там причина отказа запуска.
sudo tail -50 /var/log/postgresql/postgresql-16-main.logСостояние кластера с точки зрения самого PostgreSQL, минуя systemd.
sudo -u postgres /usr/lib/postgresql/16/bin/pg_ctl -D /var/lib/postgresql/16/main statusПрава и владелец каталога данных: при расхождении кластер не стартует.
sudo ls -ld /var/lib/postgresql/16/mainСписок кластеров с версиями, портами и состоянием (в Debian и Ubuntu).
pg_lsclustersПараметры unit, которые тут важны
- TimeoutStartSec=восстановление после сбоя занимает минуты — значение по умолчанию бывает мало
- User=кластер работает от postgres; каталог данных должен принадлежать ему
- MemoryMax=ограничение памяти для базы задавать осторожно: при упоре процесс убивают
- OOMScoreAdjust=базе обычно снижают привлекательность для OOM-killer
- Type=notify в современных пакетах: postmaster сообщает о готовности
Частые вопросы
systemctl status postgresql показывает active, но база не отвечает. Почему?
Потому что это обёртка. Она отработала и осталась в состоянии active (exited). Настоящий кластер — в unit postgresql@ВЕРСИЯ-ИМЯ.service, его состояние и надо смотреть.
Кластер не успевает подняться за таймаут. Что делать?
Увеличить TimeoutStartSec= в переопределении unit экземпляра. Убивать восстановление на середине вредно: следующий запуск начнётся заново и займёт ещё больше времени.
Источники
- Документация PostgreSQL: запуск сервера
- systemd.service(5)
- Воспроизведено на тестовой машине, systemd 255 (Ubuntu 24.04)