Pull to refresh
88.29
Nixys
DevOps, DevSecOps, MLOps — IT Solutions Integrator

Cборка динамических модулей для Nginx

Reading time8 min
Views14K

image


Недавно мы собирали динамический модуль для Nginx, а когда всё было уже готово, то выяснилось, что наш модуль оказался не совместимым с Nginx, который уже был установлен на сервере. Готового решения возникшей проблемы нам найти не удалось и мы начали бороться с ней самостоятельно. Мы потратили много времени, но получили новый опыт и, самое главное, работающее решение. Которым и хотелось бы поделиться.


Начнём с описания процесса сборки динамического модуля на примере https://github.com/vozlt/nginx-module-vts. А затем покажем какая возникает ошибка и что с ней делать.


Вводные данные:
ОС Debian 9
Nginx v1.10.3 из стандартного репозитория Debian. Вывод nginx -V:


nginx version: nginx/1.10.3
built with OpenSSL 1.1.0k  28 May 2019 (running with OpenSSL 
1.1.1c  28 May 2019)
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 
-fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. 
-fstack-protector-strong -Wformat -Werror=format-security 
-Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro 
-Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/
nginx/nginx.conf --http-log-path=/var/log/nginx/access.log 
--error-log-path=/var/log/nginx/error.log 
--lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid 
--modules-path=/usr/lib/nginx/modules 
--http-client-body-temp-path=/var/lib/nginx/body 
--http-fastcgi-temp-path
=/var/lib/nginx/fastcgi 
--http-proxy-temp-path=/var/lib/nginx/proxy 
--http-scgi-temp-path=/var/lib/nginx/scgi 
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi 
--with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module -
-with-http_stub_status_module --with-http_realip_mod
ule --with-http_auth_request_module --with-http_v2_module
--with-http_dav_module --with-http_slice_module --with-threads 
--with-http_addition_module --with-http_geoip_module=dynamic 
--with-http_gunzip_module --with-http_gzip_static_module 
--with-http_image_filter_module=
dynamic --with-http_sub_module --with-http_xslt_module=dynamic 
--with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic 
--with-mail_ssl_module 
--add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-auth-pam 
--add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-dav-ext-module 
--add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-echo 
--add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-upstream-fair 
--add-dynamic-module=/build/nginx-DhOtPd/nginx
-1.10.3/debian/modules/ngx_http_substitutions_filter_module

В выводе этой команды есть информация, которая необходима для сборки динамических модулей, а именно, все что написано после configure arguments: и до первого --add-dynamic-module, то есть:


--with-cc-opt='-g -O2 
-fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. 
-fstack-protector-strong -Wformat -Werror=format-security 
-Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro 
-Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/
nginx/nginx.conf --http-log-path=/var/log/nginx/access.log 
--error-log-path=/var/log/nginx/error.log 
--lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid 
--modules-path=/usr/lib/nginx/modules 
--http-client-body-temp-path=/var/lib/nginx/body 
--http-fastcgi-temp-path
=/var/lib/nginx/fastcgi 
--http-proxy-temp-path=/var/lib/nginx/proxy 
--http-scgi-temp-path=/var/lib/nginx/scgi 
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug 
--with-pcre-jit --with-ipv6 --with-http_ssl_module 
--with-http_stub_status_module --with-http_realip_mod
ule --with-http_auth_request_module --with-http_v2_module 
--with-http_dav_module --with-http_slice_module --with-threads 
--with-http_addition_module --with-http_geoip_module=dynamic 
--with-http_gunzip_module --with-http_gzip_static_module 
--with-http_image_filter_module=
dynamic --with-http_sub_module --with-http_xslt_module=dynamic 
--with-stream=dynamic --with-stream_ssl_module 
--with-mail=dynamic --with-mail_ssl_module

Итак, приступаем к сборке модуля:


  • Для сборки удобно использовать docker контейнер, чтобы не захламлять основную систему, за основу возьмем образ debian:9, для удобства копирования готового модуля из контейнера можно указать volume. Команда для запуска контейнера в таком случае будет выглядеть так:

docker run --rm -it -v /tmp/nginx_module:/nginx_module debian:9 bash

  • После запуска контейнера устанавливаем необходимые пакеты. Прошу заметить, что пакеты подобраны по выводу команды nginx -V, поэтому, возможно, часть библиотек Вам не понадобится. В любом случае, configure скрипт предупредит Вас об отсутствии зависимостей:

apt update
apt install git make gcc autoconf wget libpcre3-dev libpcre++-dev zlib1g-dev libxml2-dev libxslt-dev libgd-dev libgeoip-dev

В данном списке отсутствует пакет libssl-dev, по той причине, что nginx установленный на сервере собран с версией OpenSSL 1.1.0k, а на момент написания статьи при установке пакета libssl-dev из репозитория, версия OpenSSL в нем была уже 1.1.0l. Поэтому, исходники OpenSSL нужной версии необходимо скачать отдельно.


  • Скачиваем с сайта http://nginx.org/download nginx нужной версии, в нашем случае это 1.10.3. Исходники OpenSSL скачиваем с официального сайта https://www.openssl.org/source/old и, собственно, исходный код самого модуля nginx-module-vts загружаем с github.

cd /usr/local/src/
wget http://nginx.org/download/nginx-1.10.3.tar.gz
wget https://www.openssl.org/source/old/1.1.0/openssl-1.1.0k.tar.gz
git clone git://github.com/vozlt/nginx-module-vts.git
tar xvfz nginx-1.10.3.tar.gz
tar xzvf openssl-1.1.0k.tar.gz
cd nginx-1.10.3

  • Теперь все готово к запуску скрипта configure. В качестве параметров скрипта указываем все параметры из вывода команды nginx -V, о которых я писал в начале статьи. Также, добавляем параметр --add-dynamic-module для сборки модуля nginx-module-vts и указываем путь к директории с исходными файлами OpenSSL через параметр --with-openssl. Итоговая команда будет выглядеть так:

./configure --with-cc-opt='-g -O2 
-fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. 
-fstack-protector-strong -Wformat -Werror=format-security 
-Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro 
-Wl,-z,now' --prefix=/usr/share/nginx 
--conf-path=/etc/nginx/nginx.conf 
--http-log-path=/var/log/nginx/access.log 
--error-log-path=/var/log/nginx/error.log 
--lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid 
--modules-path=/usr/lib/nginx/modules 
--http-client-body-temp-path=/var/lib/nginx/body 
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi 
--http-proxy-temp-path=/var/lib/nginx/proxy 
--http-scgi-temp-path=/var/lib/nginx/scgi 
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug 
--with-pcre-jit --with-ipv6 --with-http_ssl_module 
--with-http_stub_status_module --with-http_realip_module 
--with-http_auth_request_module --with-http_v2_module 
--with-http_dav_module --with-http_slice_module 
--with-threads --with-http_addition_module 
--with-http_geoip_module=dynamic --with-http_gunzip_module 
--with-http_gzip_static_module 
--with-http_image_filter_module=dynamic 
--with-http_sub_module --with-http_xslt_module=dynamic 
--with-stream=dynamic --with-stream_ssl_module 
--with-mail=dynamic --with-mail_ssl_module 
--with-openssl=../openssl-1.1.0k/ 
--add-dynamic-module=../nginx-module-vts/ 

  • После завершения работы скрипта configure, запускаем сборку модулей, весь nginx нам собирать незачем:

make modules

Заметьте, данная команда доступна только с nginx версии 1.9.13.


  • После завершения процесса сборки, копируем полученный so файл в volume, который был смонтирован при старте контейнера :

cp objs/ngx_http_vhost_traffic_status_module.so /nginx_module/

  • Далее размещаем файл на целевом сервере, в данном случае, каталог с модулями находится по пути /usr/share/nginx/modules. Для того, чтобы включить модуль в nginx создаем файл /etc/nginx/modules-enabled/mod-http-vhost-traffic-status.conf со следующим содержимым:

load_module modules/ngx_http_vhost_traffic_status_module.so;

И создаем символьную ссылку на этот файл в каталог /etc/nginx/modules-enabled.


После проделанных работ, вызываем команду nginx -t и… получаем ошибку:


nginx: [emerg] module "/usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so" is not binary compatible in /etc/nginx/modules-enabled/50-mod-http-vhost-traffic-status.conf:1

И вот здесь обычно наступает некоторое недоумение, ведь версия исходников и версия nginx установленного на сервере одинаковые, версии OpenSSL тоже, параметры для скрипта configure так же скопированы целиком. Так в чем же дело?


Чтобы разобраться в этой проблеме пришлось понять, как nginx проверяет подходит ли ему динамический модуль. За эту проверку отвечает код, который расположен в исходниках nginx в файле src/core/ngx_module.h (https://github.com/nginx/nginx/blob/master/src/core/ngx_module.h). В этом файле имеется некоторое количество проверок, в данном случае 34, при прохождении которых nginx задает для переменных вида NGX_MODULE_SIGNATURE_0 (1,2,3 и т.д.)
значения 1 или 0. Далее следует переменная NGX_MODULE_SIGNATURE
в которой собираются результаты всех проверок в одну строку. Соответственно, проблема заключается в том, что строка сигнатур нового модуля не соответветствует строке сигнатур имеющегося nginx. Чтобы проверить значения строк сигнатур можно воспользоваться следующими командами :


  • Строка сигнатур модуля, который был собран :

strings /usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so| fgrep '8,4,8'
8,4,8,000011111101011111111111110110111

  • Строка сигнатур бинарного файла nginx установленного на сервере:

strings /usr/sbin/nginx| fgrep '8,4,8'
8,4,8,000011111101011111111111110111111

При сравнении этих строк невооруженным взглядом видно, что в строке сигнатур модуля, четвертая с конца переменная получилась 0, при том что у nginx она 1. Для понимания того, чего именно не хватает в модуле, необходимо обратиться к файлу src/core/ngx_module.h и найти четвертую переменную с конца, выглядит это, примерно, так :


#if (NGX_HTTP_REALIP)
#define NGX_MODULE_SIGNATURE_29  "1"
#else
#define NGX_MODULE_SIGNATURE_29  "0"
#endif

#if (NGX_HTTP_HEADERS)
#define NGX_MODULE_SIGNATURE_30  "1"
#else
#define NGX_MODULE_SIGNATURE_30  "0"
#endif

#if (NGX_HTTP_DAV)
#define NGX_MODULE_SIGNATURE_31  "1"
#else
#define NGX_MODULE_SIGNATURE_31  "0"
#endif

#if (NGX_HTTP_CACHE)
#define NGX_MODULE_SIGNATURE_32  "1"
#else
#define NGX_MODULE_SIGNATURE_32  "0"
#endif

#if (NGX_HTTP_UPSTREAM_ZONE)
#define NGX_MODULE_SIGNATURE_33  "1"
#else
#define NGX_MODULE_SIGNATURE_33  "0"
#endif

#define NGX_MODULE_SIGNATURE                                                  

Нас интересует переменная NGX_HTTP_HEADERS, при сборке nginx она была 1, а при сборке модуля 0. Для того, чтобы модуль собирался с NGX_HTTP_HEADERS необходимо скорректировать файл config в директории с исходных кодом модуля, добавив строку have=NGX_HTTP_HEADERS . auto/have в начало файла config. Ниже представлено начало файла config после внесения изменений:


ngx_addon_name=ngx_http_vhost_traffic_status_module
have=NGX_STAT_STUB . auto/have
have=NGX_HTTP_HEADERS . auto/have
...

Далее делаем make clean, а затем заново запускаем configure и make modules. После сборки модуля проверяем его строку сигнатур и видим, что теперь она соответствует строке nginx:


$ strings /usr/sbin/nginx| fgrep '8,4,8'
8,4,8,000011111101011111111111110111111
$ strings /usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so | fgrep '8,4,8'
8,4,8,000011111101011111111111110111111

После этого, модуль подключается без каких-либо проблем и можно собирать расширенную статистику с nginx.


В вашем случае, возможно, строка сигнатур будет выглядеть иначе, как и переменные, которые отвечают за каждую позицию в этой строке. Однако, теперь вы сможете понять, что именно не так при сборке модуля и исправить проблему.


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


Также читайте другие статьи в нашем блоге:


Tags:
Hubs:
+11
Comments5

Articles

Information

Website
nixys.io
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Vlada Grishkina-Makareva