博客 | NGINX

从失败的 NGINX Plus 升级中恢复:‘模块“M”版本为 X,而不是 Y’

NGINX-F5-horiz-black-type-RGB 的一部分
Liam Crilly 缩略图
利亚姆·克里利
2017 年 3 月 14 日发布

如果您使用动态模块,您可能会在升级到新的 NGINX 或 NGINX Plus 版本时看到如下错误:

设置 nginx-plus (1.11.10-1~xenial) ... nginx:[emerg] 模块“/etc/nginx/modules/ngx_http_geoip_module.so”版本 1011005 而不是 /etc/nginx/nginx.conf:7 中的 1011006 nginx:配置文件 /etc/nginx/nginx.conf 测试失败,invoke-rc.d:initscript nginx,操作“升级”失败。

最可能的原因是,您没有升级指定的动态模块( .so文件):

  • 如果您正在运行 NGINX 开源,则必须根据您要升级到的版本进行编译动态模块。
  • 如果您正在运行 NGINX Plus,则必须根据与您要升级到的 NGINX Plus 版本相对应的 NGINX 开源版本来编译动态模块。

为了简洁起见,从现在开始我们将仅引用 NGINX Plus。

不要恐慌!

请放心,此错误消息并不表示您的 NGINX Plus 服务器已关闭。 NGINX Plus 升级是无缝的,因此当升级失败时,旧版本仍继续运行。 但是,升级不完整,并且您的系统处于不一致状态:

  • 内存中的 NGINX Plus 进程正在运行旧版本
  • 磁盘上与 NGINX Plus 相关的所有文件都与新版本相对应
  • 错误消息中提到的动态模块是针对旧版本编译的

不要手动重启nginx ,或者重启系统。 这样做会导致停机,因为当动态模块与新的 NGINX Plus 版本不同步时,新的 NGINX Plus 进程无法启动。

请注意,错误消息仅与升级过程中发现的第一个不兼容模块有关,因此在配置中找到所有load_module指令并确保每个模块都针对适当的 NGINX 版本进行编译非常重要。 每个 NGINX Plus 版本都提供了所有由 NGINX 编写和认证的第三方动态模块的正确版本,因此您只需重新编译未经认证的模块。本文介绍如何安全地完成升级过程。

哪里出了问题?

NGINX 1.11.5 和NGINX Plus R11引入了针对 NGINX Open Source 编译动态模块并将其加载到 NGINX Plus 中的功能。 这种二进制兼容性要求模块和 NGINX Plus 共享相同的基础开源版本。 由于版本不匹配,在未先安装针对相应 NGINX 开源版本构建的动态模块的情况下升级 NGINX Plus 会失败。

如果您的动态模块是由第三方供应商提供的,那么您需要联系供应商获取新版本,该新版本与您打算升级到的 NGINX Plus 版本的 NGINX 版本相匹配。 如果您的动态模块是从源代码编译的(并且您可以访问源代码)那么继续阅读。

复苏之路

步骤 0: 准备构建环境

我们强烈建议您在单独的系统上编译动态模块,我们在此将其称为“构建环境”。 这样做可以最大限度地降低运行带有动态模块的 NGINX Plus 的系统的风险和复杂性(我们将其称为“生产环境”)。 构建环境必须与生产环境具有相同的操作系统和版本;此外,还必须安装以下组件:

  • UnZip 实用程序
  • 编译器和 make 实用程序
  • Perl 兼容正则表达式库(开发文件)
  • Zlib 压缩库(开发文件)

为了确保您的构建环境已安装这些先决条件,请运行以下命令。

  • 对于 Ubuntu/Debian:

    buildenv$ sudo apt-get install unzip gcc make libpcre3-dev zlib1g-dev
  • 对于 CentOS/RHEL/Oracle Linux:

    buildenv$ sudo yum install unzip gcc make pcre-devel zlib-devel

步骤 1: 获取 NGINX 开源

  1. 在生产环境中,运行以下命令来识别与正在运行的 NGINX Plus 版本相对应的 NGINX 开源版本。 在此输出中它以橙色突出显示: NGINX 1.11.10,对应 NGINX Plus R12。

    生产$ nginx -v nginx 版本:nginx/ 1.11.10(nginx-plus-r12)
  2. 在构建环境中,下载适当 NGINX 开源版本的源代码。

    build-env$ wget -qO - http://nginx.org/download/nginx-1.11.10 .tar.gz | tar zxfv -

第 2 步: 获取动态模块源

将动态模块的源代码复制到您选择的构建目录。 这里我们从 GitHub 存储库复制一个示例 NGINX“hello world”模块。

buildenv$ git clone https://github.com/perusio/nginx-hello-world-module.git克隆到‘nginx-hello-world-module’...

步骤3: 编译动态模块

  1. 首先运行带有--with-compat参数的 NGINX配置脚本来编译动态模块,以使动态模块与 NGINX Plus 二进制兼容。 然后运行make modules来编译模块。

    buildenv$ cd nginx-1.11.10/ buildenv$ ./configure --with-compat --add-dynamic-module=../nginx-hello-world-module buildenv$ make modules
  2. 验证构建过程是否已在objs子目录中将动态模块创建为.so文件。

    buildenv$ ls objs/*.so objs/ngx_http_hello_world.so
  3. 创建模块文件的副本,文件名中包含 NGINX 开源版本。 这使得在生产环境中管理动态模块的多个版本变得更加简单。

    buildenv$ cp objs/ngx_http_hello_world.so ./ngx_http_hello_world_1.11.10.so

步骤4: 将动态模块复制到生产环境

因为我们创建了文件名中包含 NGINX 开源版本的动态模块,所以我们可以安全地将其复制到生产环境而不会影响操作。

buildenv$ scp ./ngx_http_hello_world_1.11.10.so 生产:/etc/nginx/modules

步骤5: 交换动态模块

在生产环境中,用新的.so文件替换当前文件。 此时这样做是安全的,因为只有在nginx重新启动或重新加载配置时,动态模块才会加载到内存中。

生产$ cd /etc/nginx/modules生产$ cp ngx_http_hello_world.so ngx_http_hello_world_ROLLBACK.so生产$ ln -fs ngx_http_hello_world_1.11.10.so ngx_http_hello_world.so生产$ ls -gG -rw-r--r-- 1 243576 Jan 31 16:18 ngx_http_hello_world_1.11.10.so -rw-r--r-- 1 243576 Oct 20 10:40 ngx_http_hello_world_ROLLBACK.so lrwxrwxrwx 1 24 Jan 31 16:26 ngx_http_hello_world.so -> ngx_http_hello_world_1.11.10.so

第 6 步: 完成 NGINX Plus 升级

  1. 运行以下命令来测试模块版本是否正确以及 NGINX Plus 是否准备好完成升级过程。

    production$ nginx -t nginx:配置文件/etc/nginx/nginx.conf语法正确 nginx:配置文件/etc/nginx/nginx.conf测试成功
  2. 完成升级过程,以便 NGINX Plus 使用新版本和动态模块。

    生产$服务 nginx 升级启动新的主 nginx:[确定] 正常关闭旧 nginx:[确定]

防止升级失败

下次升级 NGINX Plus 时,您可以在升级前按照这些说明进行操作,以节省时间并避免错误,以便在开始升级过程时更新的动态模块已经可用。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”