nginxの動的モジュールの最新状況(2017年2月版)

こんにちは、滝澤です。 1年ぶりのnginxのエントリーです。 1年前と同じくnginxの動的モジュールのお話をします。

nginx-1.9.11以降では動的モジュール(Dynamic Modules)がサポートされています。このことについては昨年の記事「nginx-1.9.11で動的モジュールをサポート」で紹介しました。

nginxの動的モジュールの機能が実装されてから約1年経ちますので、2017年2月時点での最新状況について確認してみました。 状況が進展していたので最新状況について紹介します。

動的モジュールについての概要は昨年の記事「nginx-1.9.11で動的モジュールをサポート」に書きましたので合わせて読んでいただければと思います。

なお、執筆時点(2017年2月22日)での最新バージョンはnginx-1.11.10です。

動的モジュールとして利用できるモジュール

nginx-1.11.10において利用できる動的モジュールには次のものがあります。

  • XSLT (ngx_http_xslt_module)
  • Image Filter (ngx_http_image_filter_module)
  • GeoIP (ngx_http_geoip_module)
  • Perl (ngx_http_perl_module) (1.9.13以降)
  • Mail (ngx_mail_module)
  • Stream (ngx_stream_module)
  • Stream GeoIP (ngx_stream_geoip_module) (1.11.3以降)

PerlモジュールとStream GeoIPモジュールが動的モジュールとして利用できるようになりました。

また、標準のnginxには同梱されていませんが、nginScriptも動的モジュールに対応しています。

  • nginScript (ngx_http_js_module)
  • Stream nginScript (ngx_stream_js_module)

公式バイナリパッケージ

nginxの公式サイトでは、動的モジュールに対応したLinuxディストリビューション用のバイナリパッケージが提供されています。

nginx-1.11.10においては、次の動的モジュールのパッケージが提供されています。

パッケージ名モジュール名
nginx-module-xsltngx_http_xslt_filter_module
nginx-module-image-filterngx_http_image_filter_module
nginx-module-geoipngx_http_geoip_module, ngx_stream_geoip_module
nginx-module-perlngx_http_perl_module
nginx-module-njsngx_http_js_module, ngx_stream_js_module

StreamモジュールとMailモジュールについては、nginxバイナリに静的にビルドされて組み込まれています。

なお、nginxバイナリと動的モジュールの両方とも、後述するconfigure時のオプション「--with-compat」付きでビルドされています。

nginxバイナリとモジュールのsignatureが異なると動作しない

動的モジュールが実装された当初から「nginxバイナリとモジュールのsignatureが異なると動作しない」という制約があります。

このことについては昨年の記事「nginx-1.9.11で動的モジュールをサポート」において次のように説明しました。

nginxバイナリと異なる環境でビルドされた動的モジュールを組み合わせて動かすことはできません。 これにより困ることの例としては、公式サイトやディストリビューションからnginxのRPMパッケージをインストールした環境に、その環境でビルドした動的モジュールを利用することができません。

nginxのビルド時に、ビルド環境のライブラリとAPIの有無およびビルドオプションからsignatureの文字列が生成され、nginxバイナリおよび各モジュールのバイナリに埋め込まれます。

nginxはモジュールの追加時にモジュールのsignatureがnginxバイナリが持っているものと同じかを確認します。

そのため、nginxバイナリと異なる環境でビルドされたモジュールを組み合わせて動かすことは基本的にはできません。 ただし、ビルド環境のライブラリとAPIの有無およびビルドオプションがすべて同じであればsignatureも同じになるため、この場合は動作します。

この制約そのものは本記事の執筆時点でのnginx-1.11.10においても変更はありません。

しかし、同じOSの環境であれば同じsignatureになるようなconfigure時のオプション「--with-compat」がnginx-1.11.5から追加されました。

  --with-compat                      dynamic modules compatibility

これにより、ビルドした環境が同じOSであれば、別のサーバでビルドされたモジュールを持ってきても、そのまま使えるようになりました。

ただし、本記事の最後のセクションで述べますが、同じOSでもビルド環境のOSのAPIの有無が異なる場合や、configure時にデフォルトで有効なオプションを無効化している場合は、signatureが異なってしまう可能性があり、そのまま使えない恐れはあります。

「--with-compat」オプションを有効にしたビルドの例

CentOS 7の環境で公式パッケージをインストールしているときに、サードパーティの動的モジュールを「--with-compat」オプションを有効にしてビルドして追加する例を紹介します。

まず、パッケージでインストールしたnginxのバージョンと同じバージョンのnginxのソースコードを入手し、展開します。

$ rpm -q nginx
nginx-1.11.10-1.el7.ngx.x86_64
$ curl http://nginx.org/download/nginx-1.11.10.tar.gz -o nginx-1.11.10.tar.gz 
$ tar xvzf nginx-1.11.10.tar.gz

利用するサードパーティの動的モジュールを入手して展開します。

$ curl http://example.org/download/foo-module.tar.gz -o foo-module.tar.gz
$ tar xvzf foo-module.tar.gz

nginxのソースコードを展開したディレクトリに移動します。

$ cd nginx-1.11.10

configureを「--with-compat」オプションと「--add-dynamic-module」にモジュールのパスを指定したものを付けて実行します。

$ ./configure --with-compat --add-dynamic-module=/path/to/foo-module
中略
configuring additional dynamic modules
adding module in /path/to/foo-module
 + foo_module was configured
中略

モジュールをコンパイルします。

$ make modules

ビルドした動的モジュールをモジュール用ディレクトリにコピーします。

$ sudo cp objs/foo-module.so /etc/nginx/modules/

モジュールをロードする設定をnginx.confのevents{}の設定より前に追加します。

load_module modules/foo_module.so;

nginxを構文チェックを行った後に、リロードします。

$ sudo nginx -t
$ sudo systemctl reload nginx

以上で動的モジュールの追加が完了となります。

参考までに、nginxバイナリと追加インストールした動的モジュールのsignatureの文字列が同じであるかを比べてみましょう。

signatureの文字列はnginxバイナリおよびモジュールに含まれています。そのファイルに対してstringsコマンドを実行し、その出力に対する正規表現「^.,.,.,」で見つけることができます。

$ strings /usr/sbin/nginx | grep '^.,.,.,'
8,4,8,0011111111010111001111111111111111

$ strings /etc/nginx/modules/foo-module.so | grep '^.,.,.,'
8,4,8,0011111111010111001111111111111111

このように、筆者のテスト環境では同じになることを確認しました。

signatureの詳細

最後にsignatureの詳細について説明します。

signatureはソースコードファイル src/core/ngx_module.h に次のように定義されています。

#define NGX_MODULE_SIGNATURE_0                                                \
    ngx_value(NGX_PTR_SIZE) ","                                               \
    ngx_value(NGX_SIG_ATOMIC_T_SIZE) ","                                      \
    ngx_value(NGX_TIME_T_SIZE) ","

#if (NGX_HAVE_KQUEUE)
#define NGX_MODULE_SIGNATURE_1   "1"
#else
#define NGX_MODULE_SIGNATURE_1   "0"
#endif

中略

#if (NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_34  "1"
#else
#define NGX_MODULE_SIGNATURE_34  "0"
#endif

#define NGX_MODULE_SIGNATURE                                                  \
    NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2      \
中略
    NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34

これを表にまとめると次のようになります。

定義名値あるいは条件デフォルトNGX_COMPAT
有効時に有効化
公式パッケージ
(CentOS7)
NGX_MODULE_SIGNATURE_0ngx_value(NGX_PTR_SIZE) -8
,,,
ngx_value(NGX_SIG_ATOMIC_T_SIZE) -4
,,,
ngx_value(NGX_TIME_T_SIZE) -8
,,,
NGX_MODULE_SIGNATURE_1NGX_HAVE_KQUEUE -0
NGX_MODULE_SIGNATURE_2NGX_HAVE_IOCP -0
NGX_MODULE_SIGNATURE_3NGX_HAVE_FILE_AIO || NGX_COMPAT -1
NGX_MODULE_SIGNATURE_4NGX_HAVE_AIO_SENDFILE || NGX_COMPAT -1
NGX_MODULE_SIGNATURE_5NGX_HAVE_EVENTFD -1
NGX_MODULE_SIGNATURE_6NGX_HAVE_EPOLL -1
NGX_MODULE_SIGNATURE_7NGX_HAVE_KEEPALIVE_TUNABLE -1
NGX_MODULE_SIGNATURE_8NGX_HAVE_INET6 -1
NGX_MODULE_SIGNATURE_9111
NGX_MODULE_SIGNATURE_10111
NGX_MODULE_SIGNATURE_11NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER -0
NGX_MODULE_SIGNATURE_12111
NGX_MODULE_SIGNATURE_13NGX_HAVE_SETFIB -0
NGX_MODULE_SIGNATURE_14NGX_HAVE_TCP_FASTOPEN -1
NGX_MODULE_SIGNATURE_15NGX_HAVE_UNIX_DOMAIN -1
NGX_MODULE_SIGNATURE_16NGX_HAVE_VARIADIC_MACROS -1
NGX_MODULE_SIGNATURE_17000
NGX_MODULE_SIGNATURE_18000
NGX_MODULE_SIGNATURE_19NGX_HAVE_OPENAT -1
NGX_MODULE_SIGNATURE_20NGX_HAVE_ATOMIC_OPS -1
NGX_MODULE_SIGNATURE_21NGX_HAVE_POSIX_SEM -1
NGX_MODULE_SIGNATURE_22NGX_THREADS || NGX_COMPAT01
NGX_MODULE_SIGNATURE_23NGX_PCRE(1)*11
NGX_MODULE_SIGNATURE_24NGX_HTTP_SSL || NGX_COMPAT01
NGX_MODULE_SIGNATURE_25111
NGX_MODULE_SIGNATURE_26NGX_HTTP_GZIP11
NGX_MODULE_SIGNATURE_27111
NGX_MODULE_SIGNATURE_28NGX_HTTP_X_FORWARDED_FOR(1)*21
NGX_MODULE_SIGNATURE_29NGX_HTTP_REALIP01
NGX_MODULE_SIGNATURE_30NGX_HTTP_HEADERS11
NGX_MODULE_SIGNATURE_31NGX_HTTP_DAV01
NGX_MODULE_SIGNATURE_32NGX_HTTP_CACHE11
NGX_MODULE_SIGNATURE_33NGX_HTTP_UPSTREAM_ZONE11
NGX_MODULE_SIGNATURE_34NGX_COMPAT01
  • *1) "NGX_PCRE"はconfigure時に"--with-pcre"オプションで有効にしたときか、http_rewrite_moduleが有効であるときに有効になります。http_rewrite_moduleはデフォルトで有効であるため、configure時に明示的に無効にしない限り有効になります。
  • *2) "NGX_HTTP_X_FORWARDED_FOR"はhttp_realip_module, http_geoip_module, http_geo_module, http_proxy_moduleのいずれかが有効であるときには有効になります。http_geo_moduleとhttp_proxy_moduleはデフォルトで有効であるため、configure時に明示的に無効にしない限り有効になります。

0番から21番の定義値はOSの環境(OSのAPIの有無)により決定します。そのため、同じOSの環境であれば同じ値になるはずです。

22番以降の定義値はconfigure時のオプションにより決定します。 configure時のオプション「--with-compat」を有効にすると、内部的には"NGX_COMPAT"が有効になります。このとき、他のモジュールが有効化されたり、定義値の値が1になったりします。 その結果として、22番以降の値はすべて1になります。

以上より、configure時のオプション「--with-compat」を有効にすると、同じOSの環境であればsignatureの値は同じ値になることがわかります。

ただし、同じOSでもビルド環境のOSのAPIの有無が異なる場合や、configure時にデフォルトで有効なオプションを無効化している場合は、signatureが異なってしまう可能性があります。

参考文献

株式会社ハートビーツのインフラエンジニアから、ちょっとした情報をお届けします。