Привет, команда,
Сообщаю о сбое в официальной сборке Docker/runit, который может незаметно завершить работу Sidekiq (и, следовательно, AI/фоновые задачи) без пересборки или обновления.
Окружение
- Официальная установка Discourse через Docker (стандартный контейнер + сервисы runit).
- Перед началом проблемы пересборка или обновление не проводились.
- Плагин Discourse AI включен, но AI перестал отвечать.
Симптомы
- В админ-интерфейсе AI выглядит включенным, но ответов от AI нет.
- Фоновые задачи (AI/векторизация/автоответ) зависают.
- Команда
sv status sidekiqпоказывает, что Sidekiq постоянно падает сразу после запуска:
down: sidekiq: 1s, normally up, want up
- Ручной запуск Sidekiq работает нормально, значит, само приложение в порядке:
bundle exec sidekiq -C config/sidekiq.yml
# работает стабильно, подключается к Redis, обрабатывает задачи
Что мы обнаружили
По умолчанию скрипт runit выглядел так:
exec chpst -u discourse:www-data \
bash -lc 'cd /var/www/discourse && ... bundle exec sidekiq -e production -L log/sidekiq.log'
Две уязвимые точки:
- Основная группа www-data: В моём контейнере типичные записываемые пути принадлежат пользователю
discourse:discourse. Любое расхождение во владенииtmp/pidsили общих путях может привести к выходу Sidekiq при запуске под пользователемwww-data, хотя ручной запуск от имениdiscourseработает. - Принудительная запись логов через
-L log/sidekiq.log: Путь к логу является символьной ссылкой на/shared/log/rails/sidekiq.log. Если этот файл или каталог будет пересоздан с другими правами доступа или владельцем, Sidekiq может завершиться сразу, не успев записать полезные логи.
Связанный триггер: ежедневный сбой logrotate
Отдельно стоит отметить, что logrotate каждый день завершался ошибкой:
error: skipping "...log" because parent directory has insecure permissions
Set "su" directive in config file ...
Причина — стандартные права в Debian/Ubuntu:
/var/logпринадлежитroot:admс правами0775(доступна для записи группе).logrotateотказывается выполнять ротацию, если не задана глобальная директиваsu. Это ожидаемое поведение с точки зрения разработчиков.
В момент, когда ежедневная задача logrotate завершалась с ошибкой, она также пересоздавала файлы в /shared/log/rails/ (включая sidekiq.log), что, вероятно, взаимодействовало с принудительным логированием через -L и способствовало циклу падений Sidekiq («1s crash»).
Исправление (пересборка не требуется)
- Исправить logrotate, чтобы он перестал трогать общие логи в случае сбоя: Добавить глобальную директиву
su:
# /etc/logrotate.conf (в начале файла)
su root adm
После этого logrotate -v завершается с кодом 0 и больше не сообщает об небезопасных правах родительского каталога.
- Заменить скрипт runit для Sidekiq на более надёжный вариант по умолчанию
Переход на пользователяdiscourse:discourseи стандартныйsidekiq.yml, а также отказ от принудительной записи через-L log/sidekiq.log, делает Sidekiq стабильным:
#!/bin/bash
exec 2>&1
cd /var/www/discourse
mkdir -p tmp/pids
chown discourse:discourse tmp/pids || true
exec chpst -u discourse:discourse \
bash -lc 'cd /var/www/discourse && rm -f tmp/pids/sidekiq*.pid; exec bundle exec sidekiq -C config/sidekiq.yml'
После этого:
sv status sidekiqпоказываетrun.- AI и фоновые задачи возобновляют работу.
Запрос / предложение
Можно ли рассмотреть возможность повышения надёжности официального сервиса Sidekiq в сборке Docker/runit по умолчанию?
Например:
- Запускать Sidekiq под пользователем
discourse:discourse(что соответствует типичному владению внутри контейнера). - Использовать
bundle exec sidekiq -C config/sidekiq.yml. - Избегать принудительной записи в общий файл логов через
-L log/sidekiq.logили сделать это устойчивым к изменениям прав доступа при ротации логов или работе с общими томами.
Даже примечание в документации («если Sidekiq показывает down: 1s, но ручной запуск работает, проверьте /etc/service/sidekiq/run и избегайте принудительного логирования в общие файлы») очень помогло бы тем, кто разворачивает систему самостоятельно.
Готов предоставить дополнительные логи при необходимости. Спасибо!
