
Recentemente, construímos um módulo dinâmico para o Nginx e, quando tudo estava pronto, verificou-se que nosso módulo não era compatível com o Nginx, que já estava instalado no servidor. Não conseguimos encontrar uma solução pronta para o problema e começamos a combatê-lo por conta própria. Passamos muito tempo, mas obtivemos uma nova experiência e, mais importante, uma solução funcional. O que eu gostaria de compartilhar.
Vamos começar com uma descrição do processo de montagem do módulo dinâmico usando o exemplo https://github.com/vozlt/nginx-module-vts . E então mostraremos qual erro ocorre e o que fazer com ele.
Dados de entrada:
SO Debian 9
Nginx v1.10.3 do repositório Debian padrão. Saída 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
A saída deste comando contém informações necessárias para a montagem de módulos dinâmicos, ou seja, tudo o que é escrito após configure arguments:
e antes do primeiro - --add-dynamic-module
, ou seja:
--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
Então, prosseguimos para a montagem do módulo:
- Para montagem, é conveniente usar um contêiner de encaixe para não bagunçar o sistema principal, tendo como base a imagem debian: 9, para a conveniência de copiar o módulo acabado do contêiner, você pode especificar o volume. O comando para iniciar o contêiner nesse caso terá a seguinte aparência:
docker run --rm -it -v /tmp/nginx_module:/nginx_module debian:9 bash
- Após iniciar o contêiner, instale os pacotes necessários. Observe que os pacotes são selecionados de acordo com a saída do comando nginx -V, portanto, você pode não precisar de algumas bibliotecas. De qualquer forma, o script configure o alertará sobre a ausência de dependências:
apt update apt install git make gcc autoconf wget libpcre3-dev libpcre++-dev zlib1g-dev libxml2-dev libxslt-dev libgd-dev libgeoip-dev
O pacote libssl-dev está ausente nesta lista, porque o nginx instalado no servidor foi criado com a versão 1.1.0k do OpenSSL e, no momento da redação, ao instalar o pacote libssl-dev a partir do repositório, a versão do OpenSSL já era 1.1.0l. Portanto, o código fonte do OpenSSL para a versão correta deve ser baixado separadamente.
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
- Agora você está pronto para executar o script de configuração. Nós especificamos todos os parâmetros da saída do comando nginx -V como os parâmetros de script, sobre os quais escrevi no início do artigo. Também adicionamos o parâmetro --add-dynamic-module para criar o módulo nginx-module-vts e especificamos o caminho para o diretório com os arquivos de origem OpenSSL por meio do parâmetro --with-openssl. O comando final ficará assim:
./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/
- Depois que o script
configure
termina de funcionar, iniciamos a montagem dos módulos; não precisamos coletar todos os nginx
:
make modules
Observe que este comando está disponível apenas com o nginx
versão 1.9.13.
- Após a conclusão do processo de montagem, copie o arquivo so resultante para o
volume
, que foi montado quando o contêiner foi iniciado:
cp objs/ngx_http_vhost_traffic_status_module.so /nginx_module/
- Em seguida, colocamos o arquivo no servidor de destino; nesse caso, o diretório com os módulos está localizado no caminho
/usr/share/nginx/modules
. Para habilitar o módulo no nginx
, criamos o arquivo /etc/nginx/modules-enabled/mod-http-vhost-traffic-status.conf
com o seguinte conteúdo:
load_module modules/ngx_http_vhost_traffic_status_module.so;
E crie um link simbólico para esse arquivo no diretório /etc/nginx/modules-enabled
.
Após o trabalho, chamamos o comando nginx -t
e ... obtemos o erro:
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
E aqui geralmente surge uma confusão, porque a versão de origem e a versão do nginx
instalada no servidor são as mesmas, versões do OpenSSL também, os parâmetros para o script de configure
também são copiados na íntegra. Então, qual é o problema?
Para entender esse problema, eu tive que entender como o nginx verifica se o módulo dinâmico é adequado para ele. O código localizado nas fontes nginx
no src/core/ngx_module.h
( https://github.com/nginx/nginx/blob/master/src/core/ngx_module.h ) é responsável por essa verificação. Há várias verificações nesse arquivo, neste caso 34, durante as quais o nginx define variáveis do formato NGX_MODULE_SIGNATURE_0
(1,2,3, etc.)
valores 1 ou 0. A seguir, é a variável NGX_MODULE_SIGNATURE
que coleta os resultados de todas as verificações em uma linha. Consequentemente, o problema é que a sequência de assinaturas do novo módulo não corresponde à sequência de assinaturas do nginx existente. Para verificar os valores das cadeias de assinatura, você pode usar os seguintes comandos:
- A linha de assinatura do módulo que foi construído:
strings /usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so| fgrep '8,4,8' 8,4,8,000011111101011111111111110110111
- Linha de assinatura do arquivo binário
nginx
instalado no servidor:
strings /usr/sbin/nginx| fgrep '8,4,8' 8,4,8,000011111101011111111111110111111
Ao comparar essas linhas a olho nu, fica claro que na linha de assinatura do módulo, a quarta variável do final acabou sendo 0, enquanto o nginx
possui 1. Para entender o que exatamente está faltando no módulo, é necessário consultar o src/core/ngx_module.h
encontre a quarta variável no final, ela se parece com isso:
#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
Estamos interessados na variável NGX_HTTP_HEADERS
, ao construir o nginx
era 1 e ao construir o módulo 0. Para que o módulo seja NGX_HTTP_HEADERS
com NGX_HTTP_HEADERS
é necessário ajustar o arquivo de configuração no diretório com o código fonte do módulo, adicionando a linha have=NGX_HTTP_HEADERS . auto/have
have=NGX_HTTP_HEADERS . auto/have
no início do arquivo de config.
A seguir, é apresentado o início do arquivo de configuração após as alterações:
ngx_addon_name=ngx_http_vhost_traffic_status_module have=NGX_STAT_STUB . auto/have have=NGX_HTTP_HEADERS . auto/have ...
Em seguida, make clean
e reinicie o configure
e make modules
. Depois de montar o módulo, verificamos sua string de assinatura e vemos que agora ela corresponde à string 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
Depois disso, o módulo se conecta sem problemas e você pode coletar estatísticas avançadas com o nginx
.
No seu caso, talvez a linha de assinatura pareça diferente, bem como as variáveis responsáveis por cada posição nesta linha. No entanto, agora você pode entender o que exatamente está errado com a montagem do módulo e corrigir o problema.
Espero que este artigo salve alguém por algumas horas, já que pessoalmente enfrentei várias vezes o problema de o módulo não se encaixar no nginx
, como resultado, como regra, resolvi isso através da montagem completa do nginx
junto com o módulo correto.
Leia também outros artigos em nosso blog: