Зачем пересобирать Nginx и чем это грозит
Обновление самосборного Nginx до актуальной версии - прямая необходимость, а не прихоть. Свежие релизы закрывают уязвимости, вроде CVE-2024-7341, добавляют поддержку HTTP/3 и улучшают производительность за счёт оптимизации event-цикла. Пропускать их - значит оставлять сервер под угрозой и лишать проект новых возможностей.
Стандартный пакетный менеджер здесь не помощник. apt upgrade или yum update либо не найдут вашу сборку, либо заменят её стандартным бинарником, удалив все кастомные модули. PageSpeed, GeoIP2, Brotli, ModSecurity - всё, ради чего затевалась компиляция из исходников, исчезнет за секунду.
Риски пересборки сводятся к трём точкам. Первая: несовместимость API модулей. Nginx меняет внутренние структуры, и модуль, работавший на версии 1.24, может отказаться компилироваться с версией 1.26. Вторая: ошибки линковки из-за отсутствия новых зависимостей. Третья: downtime при неудачной замене бинарника. Это руководство построено так, чтобы вы прошли по цепочке «аудит - проверка совместимости - сборка - тест - замена» и исключили каждый из этих рисков.
Перед тем как начать, убедитесь, что вы знакомы с базовым процессом компиляции. Если вы собираете Nginx впервые, начните с пошагового руководства по сборке Nginx с дополнительными модулями, где разобраны зависимости и флаги configure.
Подготовка: аудит текущей сборки и модулей
Первый шаг - зафиксировать точное состояние текущей инсталляции. Вам нужны три вещи: версия Nginx, полный список флагов компиляции и пути к исходникам сторонних модулей. Без этого повторить сборку не выйдет.
Как получить полный список флагов компиляции
Команда nginx -V выводит все опции, с которыми был собран бинарник. Выполните её и сохраните результат:
nginx -V 2>&1 | tee nginx_build_flags.txt
Вывод будет похож на этот:
--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules
--conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp
--http-proxy-temp-path=/var/cache/nginx/proxy_temp
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp
--http-scgi-temp-path=/var/cache/nginx/scgi_temp
--user=nginx --group=nginx --with-compat --with-file-aio --with-threads
--with-http_addition_module --with-http_auth_request_module --with-http_dav_module
--with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module
--with-http_mp4_module --with-http_random_index_module --with-http_realip_module
--with-http_secure_link_module --with-http_slice_module --with-http_ssl_module
--with-http_stub_status_module --with-http_sub_module --with-http_v2_module
--with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module
--with-stream_ssl_module --with-stream_ssl_preread_module
--add-module=/usr/src/ngx_brotli --add-module=/usr/src/incubator-pagespeed-ngx-latest-stable
--add-module=/usr/src/ngx_http_geoip2_module
Ключевые флаги, на которые нужно обратить внимание: все --add-module и --add-dynamic-module. Именно они подключают сторонние плагины. Флаги вида --with-http_* включают стандартные модули, их тоже нужно сохранить, чтобы не потерять функциональность.
Инвентаризация сторонних модулей: где лежат исходники
Самосборные модули часто разбросаны по файловой системе. Типичные места: /usr/src/, /opt/, /usr/local/src/ или домашняя директория администратора, который ставил сервер. Найдите каждый каталог, указанный в --add-module.
Зафиксируйте версию каждого модуля. Если каталог - git-репозиторий, выполните внутри него:
git log -1 --format="%H %ai"
Эта команда выдаст хеш коммита и дату. Запишите их. Если исходники скачаны архивом, найдите файл VERSION, CHANGES или посмотрите заголовки в ngx_http_*_module.c, где часто указана строка вида #define NGX_MODULE_VERSION.
Создайте резервную копию текущего бинарника и конфигурации:
cp /usr/sbin/nginx /usr/sbin/nginx.backup.$(date +%Y%m%d)
cp -r /etc/nginx /etc/nginx.backup.$(date +%Y%m%d)
Без этой копии откат превратится в переустановку с нуля.
Проверка совместимости модулей с новой версией Nginx
Слепая сборка с флагами от старой версии - прямой путь к ошибкам компиляции. Каждый сторонний модуль нужно проверить на совместимость с целевой версией Nginx. Потратьте на это 20 минут сейчас, чтобы не терять часы на отладку потом.
Источники информации: README.md в репозитории модуля, раздел Releases на GitHub, открытые и закрытые Issues с меткой «compatibility». Ищите упоминания конкретной версии Nginx, на которую вы переходите. Например, для перехода на Nginx 1.26 ищите issues с заголовками «build failed with nginx 1.26» или «compatibility with 1.26».
Типичные точки отказа: на что обратить внимание
Модуль PageSpeed (ngx_pagespeed) - самый проблемный. Он жёстко привязан к версии Nginx и часто отстаёт на 2-3 минорных релиза. Перед обновлением проверьте страницу релизов: в описании каждого указана максимальная поддерживаемая версия Nginx. Если ваша целевая версия не указана, сборка с высокой вероятностью провалится.
Brotli (ngx_brotli) более стабилен, но требует актуальной библиотеки libbrotli-dev. Ошибка unknown type name 'BrotliEncoderPreparedDictionary' при сборке означает, что системная библиотека устарела. Обновите её до версии 1.1.0 или новее.
GeoIP2 (ngx_http_geoip2_module) зависит от libmaxminddb. Если модуль собирался с версией 1.6, а в системе стоит 1.7 - проблем не будет. Но переход с 1.5 на 1.7 может сломать API. Проверьте pkg-config --modversion libmaxminddb.
Универсальный способ проверки - тестовая компиляция в Docker-контейнере. Создайте Dockerfile с целевой версией ОС и Nginx, скопируйте туда исходники модулей и запустите ./configure с вашими флагами. Если сборка проходит, можно переносить процесс на рабочий сервер.
Пошаговая пересборка Nginx с теми же флагами
Загрузите исходный код целевой версии Nginx:
cd /usr/src
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -xzf nginx-1.26.2.tar.gz
cd nginx-1.26.2
Теперь скопируйте флаги из сохранённого вывода nginx -V. Важный момент: пути в --add-module должны указывать на актуальные директории с исходниками модулей. Если вы обновили модуль PageSpeed и он лежит в новом каталоге, укажите новый путь. Команда configure будет выглядеть так:
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--user=nginx \
--group=nginx \
--with-compat \
--with-file-aio \
--with-threads \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_v3_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--add-module=/usr/src/ngx_brotli \
--add-module=/usr/src/incubator-pagespeed-ngx-latest-stable \
--add-module=/usr/src/ngx_http_geoip2_module
После успешного конфигурирования запустите сборку:
make -j$(nproc)
Флаг -j$(nproc) задействует все ядра процессора. Сборка на 8-ядерном сервере занимает около 30-40 секунд. Не выполняйте make install - это затрёт текущий бинарник. Новый файл лежит в objs/nginx.
Сборка с динамическими модулями: когда это возможно
Динамические модули загружаются директивой load_module в nginx.conf и не требуют пересборки всего бинарника при обновлении. Если модуль поддерживает динамическую загрузку, соберите его с флагом --add-dynamic-module вместо --add-module.
Пример для Brotli:
./configure --with-compat --add-dynamic-module=/usr/src/ngx_brotli
make modules
Собранный .so-файл появится в objs/. Скопируйте его в /usr/lib/nginx/modules/ и добавьте в конфиг:
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
Не все модули можно собрать динамически. PageSpeed, например, требует статической линковки из-за глубокой интеграции с ядром Nginx. Проверьте документацию конкретного модуля перед выбором подхода. Детальный разбор архитектуры модулей и их типов есть в полном гиде по модулям Nginx.
Тестирование новой сборки перед развёртыванием
Новый бинарник находится в objs/nginx. Проверьте его синтаксисом конфигурации:
objs/nginx -t -c /etc/nginx/nginx.conf
Если проверка прошла, запустите его на альтернативном порту с тестовым конфигом. Создайте временный конфиг /tmp/nginx_test.conf:
pid /tmp/nginx_test.pid;
error_log /tmp/nginx_test_error.log;
worker_processes 1;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
server {
listen 8080;
server_name localhost;
location / {
root /var/www/html;
}
}
}
Запустите тестовый экземпляр:
objs/nginx -c /tmp/nginx_test.conf
Теперь проверьте, что сервер отвечает на порту 8080:
curl -I http://localhost:8080/
Быстрый тест модулей: чек-лист
- PageSpeed:
curl -I http://localhost:8080/ | grep X-Page-Speed. В ответе должен быть заголовокX-Page-Speed: 1.13.35.2-0или аналогичный с версией модуля. - Brotli:
curl -H "Accept-Encoding: br" -I http://localhost:8080/ | grep Content-Encoding. Ответ:Content-Encoding: br. - GeoIP2: настройте в тестовом конфиге переменную
$geoip2_data_country_codeи проверьте её значение черезadd_header X-Country $geoip2_data_country_code;. - SSL/HTTPS: если собирали с
--with-http_ssl_module, настройте тестовый server на порт 8443 с самоподписанным сертификатом и проверьтеcurl -k https://localhost:8443/.
Сравните производительность старого и нового бинарника утилитой ab:
ab -n 10000 -c 100 http://localhost:8080/
Запустите тест на старом бинарнике, затем на новом. Requests per second не должны отличаться более чем на 5-10%. Падение на 20% и более - признак проблемы с компиляцией или новым кодом модуля.
Остановите тестовый экземпляр:
objs/nginx -c /tmp/nginx_test.conf -s quit
Скрипт автоматизации пересборки
Ручной ввод флагов configure при каждом обновлении - источник ошибок. Скрипт ниже автоматизирует загрузку исходников, конфигурирование и сборку. Сохраните его как nginx_rebuild.sh и адаптируйте под свои модули.
#!/bin/bash
set -euo pipefail
# === НАСТРОЙКИ ===
NGINX_VERSION="1.26.2"
NGINX_SRC_DIR="/usr/src/nginx-${NGINX_VERSION}"
MODULES_DIR="/usr/src"
BACKUP_DIR="/root/nginx_backups"
# Список модулей: "имя_каталога" или "имя_каталога|url_git"
MODULES=(
"ngx_brotli|https://github.com/google/ngx_brotli.git"
"incubator-pagespeed-ngx-latest-stable|https://github.com/apache/incubator-pagespeed-ngx.git"
"ngx_http_geoip2_module|https://github.com/leev/ngx_http_geoip2_module.git"
)
# Флаги configure (без --add-module, они добавятся автоматически)
CONFIGURE_FLAGS=(
--prefix=/etc/nginx
--sbin-path=/usr/sbin/nginx
--modules-path=/usr/lib/nginx/modules
--conf-path=/etc/nginx/nginx.conf
--error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log
--pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock
--user=nginx
--group=nginx
--with-compat
--with-file-aio
--with-threads
--with-http_ssl_module
--with-http_v2_module
--with-http_v3_module
--with-http_realip_module
--with-http_stub_status_module
--with-http_gzip_static_module
--with-http_sub_module
--with-stream
--with-stream_ssl_module
--with-stream_ssl_preread_module
)
# === РЕЗЕРВНОЕ КОПИРОВАНИЕ ===
mkdir -p "$BACKUP_DIR"
cp /usr/sbin/nginx "$BACKUP_DIR/nginx.$(date +%Y%m%d_%H%M%S)"
cp -r /etc/nginx "$BACKUP_DIR/nginx_conf.$(date +%Y%m%d_%H%M%S)"
# === ЗАГРУЗКА ИСХОДНИКОВ NGINX ===
cd /usr/src
wget -q "https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" -O "nginx-${NGINX_VERSION}.tar.gz"
tar -xzf "nginx-${NGINX_VERSION}.tar.gz"
# === ОБНОВЛЕНИЕ МОДУЛЕЙ ===
for module_entry in "${MODULES[@]}"; do
IFS='|' read -r dir_name git_url <<< "$module_entry"
if [ -d "$MODULES_DIR/$dir_name/.git" ]; then
echo "Обновляю $dir_name..."
(cd "$MODULES_DIR/$dir_name" && git pull)
else
echo "Клонирую $dir_name..."
git clone "$git_url" "$MODULES_DIR/$dir_name"
fi
done
# === КОНФИГУРИРОВАНИЕ ===
cd "$NGINX_SRC_DIR"
ADD_MODULES=()
for module_entry in "${MODULES[@]}"; do
IFS='|' read -r dir_name git_url <<< "$module_entry"
ADD_MODULES+=(--add-module="$MODULES_DIR/$dir_name")
done
./configure "${CONFIGURE_FLAGS[@]}" "${ADD_MODULES[@]}"
# === СБОРКА ===
make -j$(nproc)
# === ТЕСТ ===
objs/nginx -t -c /etc/nginx/nginx.conf
echo "Сборка завершена. Новый бинарник: $(pwd)/objs/nginx"
echo "Для установки выполните: cp objs/nginx /usr/sbin/nginx && systemctl reload nginx"
Как использовать и адаптировать скрипт
Измените массив MODULES, добавив свои модули в формате "каталог|url_репозитория". Если модуль уже скачан и не требует обновления из git, уберите часть с |url. Поправьте CONFIGURE_FLAGS под вывод вашего nginx -V.
Запустите скрипт:
chmod +x nginx_rebuild.sh
./nginx_rebuild.sh
После выполнения вы получите новый бинарник в /usr/src/nginx-1.26.2/objs/nginx и резервную копию старого в /root/nginx_backups/.
Замена бинарного файла с минимальным простоем
Nginx поддерживает замену исполняемого файла на лету без разрыва установленных соединений. Этот механизм называется graceful upgrade и использует сигналы USR2 и WINCH.
Пошаговый алгоритм:
- Скопируйте новый бинарник на место старого:
cp objs/nginx /usr/sbin/nginx - Отправьте сигнал USR2 текущему мастер-процессу:
Старый мастер-процесс переименовывает свой PID-файл вkill -USR2 $(cat /var/run/nginx.pid)nginx.pid.oldbinи запускает новый мастер-процесс с новым бинарником. Оба процесса работают параллельно, обслуживая запросы. - Отправьте сигнал WINCH старому мастеру, чтобы он завершил свои worker-процессы:
Новые соединения больше не принимаются старым процессом, но уже установленные продолжают обрабатываться.kill -WINCH $(cat /var/run/nginx.pid.oldbin) - Проверьте, что новый процесс обслуживает запросы:
curl -I http://localhost/ - Когда старые worker'ы завершат активные соединения, отправьте QUIT старому мастеру:
kill -QUIT $(cat /var/run/nginx.pid.oldbin)
Весь процесс занимает от 5 до 30 секунд в зависимости от длительности keepalive-сессий. Ни одного оборванного запроса.
План отката: что делать, если что-то пошло не так
Если новый бинарник запустился, но работает некорректно - возвращаем старый. У вас есть резервная копия, сделанная в начале или скриптом автоматизации.
Алгоритм отката:
# Останавливаем новый процесс
kill -QUIT $(cat /var/run/nginx.pid)
# Возвращаем старый бинарник
cp /root/nginx_backups/nginx.20260807_120000 /usr/sbin/nginx
# Запускаем nginx заново
systemctl start nginx
# Проверяем логи на ошибки
tail -50 /var/log/nginx/error.log
Если nginx не запускается из-за ошибки конфигурации, восстановите конфиги из резервной копии:
cp -r /root/nginx_backups/nginx_conf.20260807_120000/* /etc/nginx/
nginx -t && systemctl start nginx
Критическое правило: всегда держите открытой вторую SSH-сессию при манипуляциях с бинарником. Если nginx упадёт и заблокирует сетевой доступ, вы сможете восстановить его через резервную консоль. Подробнее о безопасном управлении конфигурацией и reload - в руководстве по безопасному обновлению конфигурации Nginx.
Заключение: поддержание самосборного Nginx в актуальном состоянии
Цикл обновления самосборного Nginx сводится к пяти шагам: аудит текущей версии и модулей, проверка совместимости, сборка с сохранением флагов, тестирование на отдельном порту, замена бинарника через USR2. Повторяйте его при выходе каждого значимого релиза Nginx - в среднем раз в 2-3 месяца.
Автоматизируйте процесс. Скрипт из этого руководства можно поставить в cron с еженедельной проверкой новых версий или встроить в CI/CD пайплайн. Привяжитесь к RSS-ленте nginx.org/en/CHANGES и запускайте пересборку при появлении стабильного релиза.
Подпишитесь на обновления репозиториев ваших модулей через GitHub Watch - Releases only. Так вы узнаете о критических патчах для PageSpeed или Brotli до того, как они понадобятся в production.
Регулярное обновление - это страховка от уязвимостей и доступ к новым возможностям. С отлаженным процессом оно занимает 15 минут и не вызывает простоя. А если вы хотите глубже разобраться в тюнинге производительности после обновления, изучите гид по оптимизации Nginx для высоких нагрузок.