nginxの記事を書くのは久しぶりの滝澤です。
nginxにApache HTTP Serverの動的共有モジュール(Dynamic Shared Object)(DSO)のような機能が欲しいと思っていた人も多いでしょう。筆者もそうです。秘伝のタレのようなビルド用のスクリプト(実際はRPMパッケージのSPECファイル)を保守し続けるのは辛いなと思っていました。
そのような方々に朗報です。2016年2月9日にリリースされたnginx-1.9.11において動的モジュール(Dynamic Modules)がサポートされました。
しかし、Apache HTTP ServerのDSOと比べると、現時点ではまだ制約があります。 本記事ではnginx-1.9.11における動的モジュールの説明と制約について説明します。
注: 2017年2月22日にフォローアップ記事「nginxの動的モジュールの最新状況(2017年2月版)」を公開しましたのでこちらもご覧ください。
動的モジュール
nginxは多数のモジュールから構成されており、nginxをビルドするときにモジュールがnginxバイナリに静的に組み込まれます。 追加したいモジュールがあるときにも、同様にnginxのビルド時にそのモジュールを指定することによりnginxバイナリにそのモジュールが静的に組み込まれます。 このようにnginxのモジュールを追加したり変更したりするたびにnginxバイナリ自体をビルドし直す必要があり、運用が煩雑でした。
しかし、nginx-1.9.11で動的モジュールがサポートされ、動的モジュールを利用時(nginxの起動時やリロード時)に組み込むことができるようになりました。
次のように、設定ファイルにload_moduleディレクティブの値として動的モジュールの共有オブジェクトファイルのパスを指定して、nginxを起動あるいはリロードすると動的モジュールを利用できるようになります。
load_module "modules/foo_module.so";
nginxの標準モジュールすべてが動的モジュールとして利用できるわけではありません。現バージョンでは次のモジュールが動的モジュールとして利用することができます。
- XSLT (ngx_http_xslt_module)
- Image Filter (ngx_http_image_filter_module)
- GeoIP (ngx_http_geoip_module)
- Mail (ngx_mail_module)
- Stream (ngx_stream_module)
また、サードパーティモジュールも動的モジュールとして利用可能になりますが、configスクリプトの修正が必要となるため、動的モジュールへの対応をサポートしているモジュール以外はそのままでは利用できません。
設定ファイルへの指定
設定ファイルへの記述方法について説明します。
動的モジュールを読み込ませるためには、設定ファイルにload_moduleディレクティブの値に動的モジュールの共有オブジェクトファイルのパスを指定します。
load_module "modules/foo_module.so"; load_module "modules/ngx_stream_module.so";
load_moduleディレクティブの記述場所はmainコンテキストです。 さらに、次の例のように各ブロック(events, http, stream, mail)より前に記述する必要があります。
user nginx; worker_processes 1; load_module "modules/ngx_stream_module.so"; load_module "modules/ngx_http_geoip_module.so"; events { worker_connections 1024; } http { 以下略
動かない例も示します。次のようにeventsブロックより後にload_moduleディレクティブを記述したとします。
user nginx; worker_processes 1; events { worker_connections 1024; } load_module "modules/ngx_stream_module.so"; load_module "modules/ngx_http_geoip_module.so"; http { 以下略
この場合は、次のようなメッセージが出力され、動きません。
nginx: [emerg] "load_module" directive is specified too late in /usr/local/nginx/conf/nginx.conf:16
動的モジュールのビルド方法
現バージョンでは動的モジュール単体だけをビルドすることはできません。 動的モジュールをビルドするためには、nginxバイナリをビルドする手順とほぼ同じ手順を行う必要があります。
なお、将来のバージョンではモジュール単体をビルドできるようにする計画があるようです。
Introducing Dynamic Modules in NGINX 1.9.11 より
In future releases, we plan to add the ability to compile modules after the NGINX binary has been compiled.
configureスクリプトのオプション
まず、configureスクリプトの実行時にいくつかオプションを指定します。
configureスクリプトの動的モジュール関連のオプションとしては次のようなものがあります。
--modules-path=PATH set modules path --with-http_xslt_module=dynamic enable dynamic ngx_http_xslt_module --with-http_image_filter_module=dynamic enable dynamic ngx_http_image_filter_module --with-http_geoip_module=dynamic enable dynamic ngx_http_geoip_module --with-mail=dynamic enable dynamic POP3/IMAP4/SMTP proxy module --with-stream=dynamic enable dynamic TCP proxy module --add-dynamic-module=PATH enable dynamic external module
それぞれ見ていきましょう。
動的モジュールのインストール先の指定
--modules-pathの値に動的モジュールの共有オブジェクトファイルをインストールするパスを指定します。デフォルトでは--prefixで指定したディレクトリ下のmodulesディレクトリになります。
--modules-path=PATH
/usr/local/nginx/modulesにインストールする場合は次のように指定します。
--modules-path=/usr/local/nginx/modules
動的モジュールの指定(標準モジュールの場合)
nginxの標準モジュールの場合はオプションの値に"dynamic"を指定します。
--with-stream=dynamic
動的モジュールの指定(サードパーティモジュールの場合)
サードパーティモジュールの場合は--add-dynamic-moduleの値にそのソースコードのディレクトリを指定します。
--add-dynamic-module=/path/to/foo_module
configureスクリプトの実行
configureスクリプトのオプションの指定が完了したら、実行します。
$ ./configure \ --prefix=/usr/local/nginx \ --modules-path=/usr/local/nginx/modules \ --user=nginx --group=nginx \ 中略 --with-stream=dynamic \ --with-stream_ssl_module \ --add-dynamic-module=/path/to/foo_module checking for OS + Linux 3.10.0-327.4.5.el7.x86_64 x86_64 中略 adding module in /path/to/foo_module + foo_module was configured 中略 Configuration summary + using threads 以下略
configureスクリプトの実行が成功したら、makeを実行してnginxバイナリと一緒にビルドします。 makeが完了するとobjsディレクトリに動的モジュールが拡張子soの共有オブジェクトのファイルとして生成されます。
$ make 中略 $ ls -1 objs/*.so objs/foo_module.so objs/ngx_stream_module.so
さらに、make installを実行すると、nginxバイナリはsbinディレクトリに、動的モジュールはmodulesディレクトリにコピーされます。
$ sudo make install $ ls -1 /usr/local/nginx/{sbin,modules} /usr/local/nginx/modules: foo_module.so ngx_stream_module.so /usr/local/nginx/sbin: nginx
なお、すでに同じ環境でビルドされたnginxバイナリがインストール済みの場合は、objsディレクトリに生成された動的モジュールのファイルをmodulesディレクトリに手動で配置しても利用できます。
$ sudo cp objs/*.so /usr/local/nginx/modules/
ただし、ビルド環境のライブラリとAPIの有無およびビルドオプションが異なる場合は、後述するsignatureの問題でうまく動かないこともあります。
制約事項
上述した内容にも制約がありますが、以下に述べるような制約もあります。
nginxバイナリとモジュールのバージョンが異なると動作しない
nginxバイナリとモジュールのバージョンが異なると動作しません。 これは、nginxバイナリだけバージョンアップして、動的モジュールはそのままのバージョンの場合は、バージョンが異なるため動作しなくなるということになります。
このことについて説明します。
nginxのビルド時に、nginxバイナリおよび各モジュールのバイナリにバージョン番号から生成される整数値が埋め込まれます。 例えば、nginx-1.9.11の場合は"1009011"になります。
nginxはモジュールの追加時にモジュールのバージョン番号の整数値がnginxバイナリが持っているものと同じかを確認します。異なる場合は次のようなメッセージが出力されます。
nginx: [emerg] module "/usr/local/nginx/modules/ngx_stream_module.so" version 1009011 instead of 1009012 in /usr/local/nginx/conf/nginx.conf:8
なお、バージョン番号の整数値はソースコードsrc/core/nginx.h内のnginx_versionに定義されています。
nginxバイナリとモジュールのsignatureが異なると動作しない
nginxバイナリと異なる環境でビルドされた動的モジュールを組み合わせて動かすことはできません。 これにより困ることの例としては、公式サイトやディストリビューションからnginxのRPMパッケージをインストールした環境に、その環境でビルドした動的モジュールを利用することができません。
このことについて説明します。
nginxのビルド時に、ビルド環境のライブラリとAPIの有無およびビルドオプションからsignatureの文字列が生成され、nginxバイナリおよび各モジュールのバイナリに埋め込まれます。
例えば、nginx公式RPMパッケージ(CentOS 7, X86_64)のnginx-1.9.11のnginxバイナリのsignatureの文字列は次のようになっています。
8,4,8,001011111101001111111111110110111
nginxはモジュールの追加時にモジュールのsignatureがnginxバイナリが持っているものと同じかを確認します。signatureが異なる場合は次のようなメッセージが出力されます。
nginx: [emerg] module "/etc/nginx/modules/ngx_stream_module.so" is not binary compatible in /etc/nginx/nginx.conf:8
そのため、nginxバイナリと異なる環境でビルドされたモジュールを組み合わせて動かすことは基本的にはできません。 ただし、ビルド環境のライブラリとAPIの有無およびビルドオプションがすべて同じであればsignatureも同じになるため、この場合は動作します。
開発者向けのメーリングリストでもsignatureの問題は提起されているため、将来、何らかの改善が行われると思われます。
なお、signatureがどのような条件で構成されているかを確認してみたい方は、ソースコードsrc/core/ngx_module.h内のNGX_MODULE_SIGNATUREの定義を見てみてください。
注: 2017年2月22日にフォローアップ記事「nginxの動的モジュールの最新状況(2017年2月版)」を公開しましたのでこちらもご覧ください。
参考資料
- NGINX Dynamic Modules (nginx-devel mailing list)
- Introducing Dynamic Modules in NGINX 1.9.11 (NGINX, Inc.)
- Extending NGINX (NGINX, Inc.)
- Dynamic Modules Development: Ruslan Ermilov, NGINX, Inc. (YouTube)