Nginx 编译第三方动态模块

最近尝试用 nginx 搭建某网站的反代, 但是 nginx 内置的功能无法替换 html 内的链接, 最后考虑用 nginx 的第三方模块 ngx_http_substitutions_filter_module 来实现链接替换的功能。

但是网上搜到的教程基本上都是下载模块源码和 nginx 源码, 然后重新编译一次 nginx, 十分的不方便。而且我服务器上为了避免炸依赖, 使用了 nginx 的官方镜像, 重新编译个 nginx 再挂载进 container 怎么想都太扭曲了, 查了一下文档发现 nginx 自从 1.9.11 以后就支持动态加载模块了, 不需要重新编译 nginx, 只需要将模块编译为 so 然后在 nginx 的配置文件中加载就行。

下载对应版本 nginx 源码

nginx 版本可用 nginx -v 查看

1
2
root@aliyun:/# nginx -v
nginx version: nginx/1.17.9

可以得知目前 docker 容器内的 nginx 版本为 1.17.9, 下载 nginx 源码以及模块源码并解压

1
2
wget https://github.com/nginx/nginx/archive/release-1.17.9.tar.gz
tar -xvf release-1.17.9.tar.gz

下载模块源码并配置模块

1
git clone https://github.com/yaoweibin/ngx_http_substitutions_filter_module.git

Nginx 模块源码中存在着 config 脚本文件, 静态模块和动态模块的配置文件时不同的。新格式的 config 脚本是同时兼容动态模块和静态模块的, 模块的 config脚本还是旧的格式, 则需要根据文档去转换。

有些模块是无法被配置为动态模块的。

新式配置脚本样例

1
2
3
4
5
6
7
8
9
10
11
12
ngx_addon_name=ngx_http_hello_world_module

if test -n "$ngx_module_link"; then
ngx_module_type=HTTP
ngx_module_name=ngx_http_hello_world_module
ngx_module_srcs="$ngx_addon_dir/ngx_http_hello_world_module.c"

. auto/module
else
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"
fi

旧式配置脚本样例

1
2
3
ngx_addon_name=ngx_http_response_module
HTTP_MODULES="$HTTP_MODULES ngx_http_response_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_response_module.c"

转换的方法见: https://www.nginx.com/resources/wiki/extending/converting/

本文提及的 ngx_http_substitutions_filter_module 使用的新式脚本, 因此可以直接被编译为动态模块无需配置。

编译模块

先安装编译时依赖的库(需要安装的库可能不止这些, 跟着报错找的)

1
sudo apt install make gcc zlib1g-dev libpcre3-dev libssl-dev

由于新版 nginx 目录结构的改版, 源码的目录结构和旧版本的不太一样:

nginx-tree.png

configure 脚本从根目录移到了 auto 目录下

cd 进 nginx 源码目录后使用 --with-compat--add-dynamic-module 参数来配置编译环境

关于参数 --with-compat 的文档很少, 只知道是一个兼容动态模块的参数

最后在邮件列表中发现了这个参数的解释:

http://mailman.nginx.org/pipermail/nginx-devel/2016-October/008920.html

总结一下就是, 加上这个参数后编译出的 nginx 可执行文件是兼容动态模块的, 至于编译动态模块为什么要加上这个参数就没找到相关解释了, 总之还是加上

1
2
./auto/configure --with-compat --add-dynamic-module=../ngx_http_substitutions_filter_module
make modules

编译出来的模块在 objs\ngx_http_subs_filter_module.so

根据官方文档来讲, 只需要添加两个参数就可以编译动态模块了。但是, 实际环境中 nginx 的编译参数可能是很复杂的, 如果发现编译出来的模块无法正常加载, 可以考虑用 nginx -V 查看编译 nginx 时使用的参数, 将参数加入 configure 命令后再次尝试编译加载模块

加载并使用模块

我使用的 nginx docker 镜像为 nginx 官方的镜像, 模块被存放在 /usr/lib/nginx/modules 下, 将模块挂载入 container 后, 修改 nginx 配置文件 /etc/nginx/nginx.conf, 加入

1
load_module modules/ngx_http_subs_filter_module.so;

重载容器中的 nginx 配置即可使用新模块

修改完配置文件后可以在 maps 中看到 so 已被加载

module-load.png

参考资料

  1. 动态模块: https://docs.nginx.com/nginx/admin-guide/dynamic-modules/dynamic-modules/
  2. 编译动态模: https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus/
  3. 转换静态模块为动态模块: https://www.nginx.com/resources/wiki/extending/converting/