Help us understand the problem. What is going on with this article?

nginxをJavaScriptで動的に設定 - njs - NGINX JavaScript

More than 1 year has passed since last update.

本記事は「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

ドキュメンテーション

基本的な使い方

  • スクリプトはnginx.conf等の設定とは別のファイルに記述
  • confファイルから呼び出す
  • パスは /etc/nginx 相対
nginx.conf
# mainコンテキスト
load_module modules/ngx_http_js_module.so;

http {
  js_include js/hello_world.js;
  server {
    location /njs {
        js_content hello;
    }
  }
}
hello_world.js
function hello(r) {
    r.headersOut['Content-Type'] = "text/plain";
    r.return(200, "Hello world!\n");
}
実行結果
$ curl localhost/njs
Hello world!

時刻でレスポンスを変える例

hours.js
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");
    }
}
nginx.conf
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引数にコールバック関数を登録し、そこで戻り値を操作するようになっています。

weather.js
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を設定しました。内部リクエストのみを許可しています。

nginx.conf
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が入っていて驚いた。ちょっと色付けがヘンなところがあるが…
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした