Что нового

Воркеры nginx падают с SIGSEGV: диагностика через core dump и пересборка динамического модуля

Сегодня утром на сервере начали массово падать воркеры nginx с SIGSEGV. За несколько минут мастер-процесс успел перезапустить их больше двадцати раз — и каждый новый воркер падал снова. В этой статье я разберу полный процесс диагностики: от первых строчек в логах до получения backtrace из core dump и финального решения через пересборку динамического модуля.

Редактировать статью  SysAdmin.guru - Твой проводник по миру IT - Google Chrome.jpg

Симптомы​


В error.log появились вот такие записи:

Bash:
2026/06/09 08:59:25 [alert] 794004#794004: worker process 794042 exited on signal 11 (core dumped)
2026/06/09 08:59:25 [alert] 794004#794004: worker process 794044 exited on signal 11 (core dumped)
2026/06/09 08:59:27 [alert] 794004#794004: worker process 794046 exited on signal 11 (core dumped)

Signal 11 — это SIGSEGV, классический сегфолт. Три воркера за две секунды означают, что проблема воспроизводится стабильно и, скорее всего, связана с конкретным запросом или инициализацией. Мастер-процесс запускает новый воркер, тот падает снова — и так по кругу.

Первичная диагностика​


Первым делом я решил посмотреть, что предшествовало крашам. Команда grep -B 20 даёт контекст до нужных строк:

Bash:
grep -B 20 "exited on signal 11" /var/log/nginx/error.log | tail -40

Самое интересное в выводе — вот эта строка, появившаяся за девять секунд до первого краша:

Bash:
2026/06/09 08:58:33 [alert] ignore long locked inactive cache entry 1519f056a62c381cc8ffb16f1d0f1e59, count:4

Это сообщение из модуля файлового кэша nginx: запись в proxy-кэше намертво залочена. Первая гипотеза — повреждённый кэш-файл, с которым воркеры не могут нормально работать. Параллельно смотрю в access.log на промежуток времени между этим алертом и первым крашем — подозрительных запросов нет.

Параллельно проверяю сборку:

Bash:
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'version|module|with-'

В выводе замечаю несколько важных вещей: флаг --with-cc-opt='-g' означает, что в бинаре встроены отладочные символы. Среди модулей есть --with-http_perl_module=dynamic — Perl-модуль, известный нестабильностью при работе с несколькими воркерами. И есть путь к динамическим модулям: --modules-path=/usr/lib/nginx/modules.

Ложный след: кэш и Perl-модуль​


Очищаю кэш и перезапускаю nginx:

Bash:
systemctl stop nginx
find /var/cache/nginx -type f -delete
systemctl start nginx

Крашей стало меньше, но они не прекратились. Значит, кэш — не причина, а совпадение по времени.

Проверяю, реально ли загружается Perl-модуль:

Bash:
grep -r "load_module" /etc/nginx/nginx.conf /etc/nginx/modules-enabled/ 2>/dev/null
grep -r "perl" /etc/nginx/ 2>/dev/null

Вывод разочаровывает: load_module нигде не фигурирует, Perl-модуль не загружается — только MIME-тип в mime.types. Ещё один ложный след.

Получение core dump​


В выводе error.log есть пометка core dumped. Смотрю, куда ядро пишет дампы:

Bash:
cat /proc/sys/kernel/core_pattern

Вывод:
Bash:
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -F%F -- %E

Дампы перехватывает apport — Ubuntu-инструмент для сбора отчётов о крашах. Они хранятся в /var/crash/:

Bash:
ls -lht /var/crash/ | head -10

Вижу файл _usr_sbin_nginx.*.crash. Распаковываю:

Bash:
cd /tmp
apport-unpack /var/crash/_usr_sbin_nginx.*.crash nginx_crash/
ls -lh nginx_crash/

Файл CoreDump на 339 МБ — это полный дамп памяти воркера в момент краша.

Backtrace через gdb​


Устанавливаю gdb. С nginx-dbgsym — пакетом отладочных символов — возникает конфликт версий: в ddebs-репозитории лежит символьный пакет для версии 1.24.0-2ubuntu7.9, а установлен nginx 1.24.0-2ubuntu7.10. Но это не критично: флаг -g в сборке означает, что символы уже встроены в сам бинарь.

Bash:
apt install gdb -y

gdb /usr/sbin/nginx /tmp/nginx_crash/CoreDump \
  -batch \
  -ex "set pagination off" \
  -ex "info threads" \
  -ex "thread apply all bt" \
  2>&1 | head -60

Вывод однозначный:

Bash:
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005ad26149d623 in ngx_strncasecmp ()
#1  0x00007f1fc39ac77d in ?? () from /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so
#2  0x00007f1fc39acced in ngx_http_headers_more_exec_cmd () from /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so
#3  0x00007f1fc39acdb2 in ?? () from /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so

Причина найдена. Модуль ngx_http_headers_more_filter_module передаёт битый указатель в ngx_strncasecmp(). Функция пытается читать по невалидному адресу — SIGSEGV.

Анализ причины​


Смотрю на дату файла модуля:

Bash:
ls -la /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so
-rw-r--r-- 1 root root 28648 Apr 17  2024 /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so

Апрель 2024 года. Проверяю политику пакетов:

Bash:
apt-cache policy nginx libnginx-mod-http-headers-more-filter

Картина проясняется полностью:
— nginx установлен версии 1.24.0-2ubuntu7.10 (обновился вместе с security-патчем)​
libnginx-mod-http-headers-more-filter — версия 1:0.37-2build1 из noble/universe, последний раз обновлялся в апреле 2024​
— в Ubuntu репозиториях нет обновлённой версии модуля, совместимой с nginx .10

При обновлении nginx пакет с модулем не подтянулся автоматически. Nginx обновился, ABI изменился, модуль остался старый — и при первом же обращении к заголовкам ответа падает с сегфолтом.

Проверяю где именно модуль используется:

Bash:
grep -r "more_set_headers\|more_clear_headers" /etc/nginx/ 2>/dev/null

Одна строка в nginx.conf:

Bash:
more_set_headers 'Server: https://sysadmin.guru';

Просто подмена заголовка Server. Нативный add_header здесь не поможет — он не перезаписывает встроенные заголовки nginx.

Решение: пересборка модуля из исходников​


Поскольку в репозиториях нет совместимой версии, единственный правильный вариант — собрать модуль самостоятельно под точную версию установленного nginx.

Шаг 1. Зависимости для сборки

Bash:
apt install dpkg-dev build-essential libpcre3-dev zlib1g-dev libssl-dev git -y

Шаг 2. Исходники nginx точно под установленную версию

Добавляю deb-src репозитории (без них apt source не работает):

Bash:
echo "deb-src http://ru.archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb-src http://ru.archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse" \
| tee /etc/apt/sources.list.d/sources-src.list

apt update
cd /tmp
apt source nginx

Проверяю что версия совпадает:

Bash:
cat /tmp/nginx-1.24.0/debian/changelog | head -1
# nginx (1.24.0-2ubuntu7.10) noble-security

Точное совпадение — важно.

Шаг 3. Исходники headers-more-nginx-module

Bash:
git clone https://github.com/openresty/headers-more-nginx-module /tmp/headers-more-src

Шаг 4. Configure и сборка только динамического модуля

Ключевой флаг — --with-compat: он включает режим совместимости для динамических модулей, выравнивая ABI под установленный nginx:

Bash:
cd /tmp/nginx-1.24.0
./configure --with-compat --add-dynamic-module=/tmp/headers-more-src
make modules

Полная пересборка nginx не нужна — make modules собирает только .so-файл.

Шаг 5. Замена модуля

Bash:
# Бэкап старого на всякий случай
cp /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so \
   /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so.bak

# Устанавливаю новый
cp /tmp/nginx-1.24.0/objs/ngx_http_headers_more_filter_module.so \
   /usr/share/nginx/modules/

ls -la /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so

Шаг 6. Проверка и перезапуск

Bash:
nginx -t && systemctl restart nginx

Bash:
sleep 60 && grep "signal 11" /var/log/nginx/error.log | tail -5

Пустой вывод. Крашей больше нет.

Проверяю что заголовок подменяется корректно:

Bash:
curl -sI https://sysadmin.guru | grep -i server
# Server: https://sysadmin.guru

Всё работает как должно.

Что делать чтобы не повторилось​


1. Обновлять все nginx-пакеты одновременно

Когда выходит security-обновление для nginx, пакеты с модулями (libnginx-mod-*) нужно обновлять в той же транзакции. Быстрая проверка перед применением обновлений:

Bash:
apt list --upgradable 2>/dev/null | grep nginx

Если в списке есть nginx, но нет libnginx-mod-* — это тревожный сигнал.

2. Для модулей из universe держать скрипт пересборки

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

Bash:
#!/bin/bash
# rebuild-nginx-modules.sh
set -e

NGINX_VER=$(nginx -v 2>&1 | grep -o '[0-9.]*')
SRC_DIR="/tmp/nginx-rebuild"

mkdir -p "$SRC_DIR"
cd "$SRC_DIR"

# Исходники nginx
apt source nginx

# Пересборка headers-more
cd nginx-${NGINX_VER}
./configure --with-compat --add-dynamic-module=/opt/headers-more-nginx-module
make modules

cp objs/ngx_http_headers_more_filter_module.so \
   /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so

nginx -t && systemctl reload nginx
echo "Done. Module rebuilt for nginx $NGINX_VER"

Исходники headers-more-nginx-module стоит хранить локально в /opt, а не клонировать каждый раз:

Bash:
git clone https://github.com/openresty/headers-more-nginx-module /opt/headers-more-nginx-module

3. Настроить мониторинг воркеров

Если nginx падает, мониторинг должен сигналить раньше чем вы сами это заметите. Например, через Netdata или простую проверку через systemd:

Bash:
grep "signal 11" /var/log/nginx/error.log

Наличие таких строк — немедленный алерт.

4. Перезагрузить сервер

В процессе диагностики обнаружилось, что сервер работал на ядре 6.8.0-110-generic при ожидаемом 6.8.0-124-generic — 14 версий без перезагрузки. Это само по себе не вызвало крашей nginx, но несоответствие версии ядра и обновлённых библиотек userspace — потенциальный источник проблем. Регулярная перезагрузка в плановое окно — хорошая практика.

Итог​


Весь путь от первого алерта до работающего nginx занял около часа. Ключевые точки:
error.log и journalctl дали контекст, но не причину​
— core dump через apport + gdb дал точный стектрейс с именем модуля​
— причина оказалась банальной: ABI-несовместимость после обновления nginx без обновления динамического модуля​
— решение — пересборка модуля из исходников с флагом --with-compat

Если nginx падает с SIGSEGV и у вас есть динамические модули — первым делом проверяйте их даты и версии. Backtrace из gdb экономит часы гадания.
Об авторе
Guru
Василий, cистемный админ /gnu/linux/windows/macos/mikrotik/troubleshooter, создатель сайта
Интересуюсь всем что делает инфраструктуру быстрой и надёжной
Открыт к общению и проектам, написать мне можно через форму или в личном сообщении

❗ Если есть пожелания по обзору какого-либо вопроса не представленного на сайте - пиши в комментариях

Комментарии

Нет комментариев для отображения.

Информация о статье

Автор
Guru
Время чтения статьи
6 мин чтения
Просмотры
20
Посл. обновление

Ещё в Размышления системного администратора

Ещё от Guru

Поделиться этой статьёй

Назад
Верх