为Nginx构建动态模块

图片


最近,我们为Nginx构建了一个动态模块,当一切准备就绪时,事实证明我们的模块与服务器上已安装的Nginx不兼容。 我们找不到解决该问题的现成解决方案,因此我们开始自己进行斗争。 我们花了很多时间,但是有了新的经验,最重要的是,找到了可行的解决方案。 我想分享。


让我们使用示例https://github.com/vozlt/nginx-module-vts来描述动态模块组装过程。 然后,我们将显示发生什么错误以及如何处理。


输入数据:
Debian 9作业系统
来自标准Debian存储库的Nginx v1.10.3。 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:之后和first- --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映像为基础,为了方便从容器中复制完成的模块,可以指定体积。 在这种情况下,启动容器的命令将如下所示:

 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源代码。



 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 

  • 现在您可以运行配置脚本了。 作为脚本参数,我们指出了nginx -V命令输出中的所有参数,这是我在本文开头所写的。 我们还添加了--add-dynamic-module参数来构建nginx-module-vts模块,并通过--with-openssl参数指定带有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如何检查动态模块是否适合它。 src/core/ngx_module.hhttps://github.com/nginx/nginx/blob/master/src/core/ngx_module.h )中位于nginx源代码中的代码负责此检查。 该文件有很多检查,在这种情况下为34,在此期间nginx为NGX_MODULE_SIGNATURE_0格式的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进行NGX_HTTP_HEADERS您需要通过添加行have=NGX_HTTP_HEADERS . auto/have来调整模块源代码目录中的配置文件have=NGX_HTTP_HEADERS . auto/have have=NGX_HTTP_HEADERS . auto/haveconfig.文件的开头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 and 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与正确的模块一起组装来解决此问题。


另请阅读我们博客上的其他文章:


Source: https://habr.com/ru/post/zh-CN473578/


All Articles