本記事は「NGINX Advent Calendar 2018」に参加しています。
TL;DR
- njsとはNGINXの内部で使用可能な、JavaScriptのサブセット
- NGINX自体の動的な設定や拡張に利用可能なもので、Node.jsなどに置き換わるWebアプリ用ではない
- 動的なURLの書き換えやヘッダー・レスポンスの生成が可能。nginx.confの静的な設定以上のことをしたいがWebアプリを作るほどではない場合に有用
インストール
NGINX (OSS) の場合
nginx.orgのレポジトリで配布されているパッケージでnginx本体をインストールしている場合は、ダイナミックモジュールのパッケージを追加インストールします。
nginx.orgのレポジトリからnginx本体をインストールする方法はこちらにあります。
$ sudo yum (apt) install nginx-module-njs
Linuxディストリビューションで配布されているnginxを使用している場合などに、ソースからビルドする手順
# njsライブラリ
cd ~/src # 任意の作業ディレクトリ
hg clone http://hg.nginx.org/njs
cd njs
./configure
make
# njsコマンド
# readline エラーが出る場合は libedit-dev パッケージを追加してみる (Ubuntu)
make njs
sudo cp build/njs /usr/local/bin
# nginx モジュール
# すでに nginx-1.15.6 パッケージがインストールされている状態で、
# ダイナミックモジュールを生成する例
cd ~/src
curl http://nginx.org/download/nginx-1.15.6.tar.gz | tar xfoz -
cd nginx-1.15.6
# ./configure --add-dynamic-module=<njsディレクトリ>/nginx \
# <nginx -V の configure オプションを全てコピー&ペースト>
# libssl-dev など、必要パッケージを適宜追加インストールしながら configure コマンドを完了させる
./configure --add-dynamic-module=../njs/nginx --prefix=/etc/nginx
--sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules
--conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock ...
make modules
sudo cp objs/ngx_*_js_module.so /usr/lib/nginx/modules
NGINX Plusの場合
$ sudo yum (apt) install nginx-plus-module-njs
ドキュメンテーション
- 総合: https://nginx.org/en/docs/njs/
- リファレンス: https://nginx.org/en/docs/njs/reference.html
- ngx_http_js_module: https://nginx.org/en/docs/http/ngx_http_js_module.html
- ngx_stream_js_module: https://nginx.org/en/docs/stream/ngx_stream_js_module.html
基本的な使い方
- スクリプトはnginx.conf等の設定とは別のファイルに記述
- confファイルから呼び出す
- パスは /etc/nginx 相対
# mainコンテキスト
load_module modules/ngx_http_js_module.so;
http {
js_include js/hello_world.js;
server {
location /njs {
js_content hello;
}
}
}
function hello(r) {
r.headersOut['Content-Type'] = "text/plain";
r.return(200, "Hello world!\n");
}
$ curl localhost/njs
Hello world!
時刻でレスポンスを変える例
function hours(r) {
var h = new Date().getHours();
r.headersOut['Content-Type'] = "text/plain";
if (h >= 9 && h <= 17) {
r.return(200, "Business hours\n");
} else {
r.return(200, "Outside hours\n");
}
}
http {
js_include js/hours.js;
server {
location /hours {
js_content hours;
}
}
}
$ curl localhost/hours
Business hours
(9-17時の場合)
サブリクエストの例
あるURLにリクエストがあると、裏でサブリクエストとして天気情報APIにアクセスし、その結果を返すような例を考えます。ここではAPIとしてOpenWeatherMapのWeather APIを利用しました。
Weather API: https://openweathermap.org/api
サブリクエストはr.subquest()で呼び、第3引数にコールバック関数を登録し、そこで戻り値を操作するようになっています。
function weather(r) {
r.subrequest('/weather-api',
{ method: 'GET', args: 'q=Tokyo&APPID=xxxxxxxxxxxx' },
function(res) {
if (res.status != 200) {
r.return(res.status);
return;
}
var json = JSON.parse(res.responseBody);
r.headersOut['Content-Type'] = "text/plain";
r.return(200, JSON.stringify(json, undefined, 1));
}
);
}
サブリクエストはlocalhostにだけアクセスできるようです。今回は外部のAPIをUpstreamとしてReverse Proxyを設定しました。内部リクエストのみを許可しています。
http {
js_include js/hours.js;
server{
location /weather {
js_content weather;
}
location /weather-api {
internal;
proxy_pass http://api.openweathermap.org/data/2.5/weather;
}
}
}
$ curl localhost/weather
{
"coord": {
"lon": 139.76,
"lat": 35.68
},
"base": "stations",
"wind": {
"speed": 6.2,
"deg": 330,
"gust": 12.9
},
"name": "Tokyo",
"visibility": 10000,
"dt": 1544749200,
"clouds": {
"all": 20
},
"id": 1850147,
"sys": {
"type": 1,
"id": 8077,
"message": 0.0051,
"country": "JP",
"sunrise": 1544737340,
"sunset": 1544772509
},
"weather": [
{
"id": 801,
"main": "Clouds",
"description": "few clouds",
"icon": "02d"
}
],
"cod": 200,
"main": {
"temp": 281.84,
"pressure": 1020,
"humidity": 36,
"temp_min": 281.15,
"temp_max": 282.15
}
}
コマンドラインツール
njsというコマンドラインツールがあり、デバッグや動作確認に便利です。
$ njs
interactive njs 0.2.6
v.<Tab> -> the properties and prototype methods of v.
type console.help() for more information
>> var d=new Date().getHours()
undefined
>> d
10
気に入っている点
- nginxの設定を動的に変えられる
- JavaScriptなので取っ付きやすい
- 今の所仕様が小さいので覚えられる
注意点
- jsファイルの変更は、nginx -s reload しないと反映されない
- js_include は1行しか書けない
- r.subrequest() は localhost しかアクセスできない
- require() で外部ファイルを読み込めない
余談
- シンタックスハイライト可能な言語にnginxが入っていて驚いた。ちょっと色付けがヘンなところがあるが…