NGINX JavaScript ModuleでリクエストごとにJavaScriptのパワーと利便性を活用

NGINX | 2021年4月23日

編集者 – 「NGINX JavaScriptモジュールの概要」というタイトルのブログ記事から、このページにリダイレクトされます。この記事は、2021年4月時点でサポートされているNGINX JavaScript Moduleのディレクティブと機能を反映して更新されています。

NGINX JavaScriptモジュール(njs)は、NGINX Open Source 1.11.10およびNGINX Plus R12で安定版モジュールとして一般提供が開始されました。[このモジュールは当初nginScriptと呼ばれていたため、一部の古い投稿ではその名称が使用されています。]NGINX JavaScriptは2015年9月のリリース以来、継続的に開発が進められ、安定版モジュールに含まれる機能と言語サポートが追加されてきました。

NGINX JavaScriptは、サーバーサイドのユースケースとリクエスト単位の処理に特化して設計された、NGINXおよびNGINX Plus向けの独自のJavaScript実装です。NGINXの設定構文をJavaScriptコードで拡張し、高度な設定ソリューションを実現します。

NGINX JavaScriptモジュールはHTTPとTCP/UDPの両方のプロトコルで利用できるため、幅広いユースケースに対応します。NGINX JavaScriptのユースケース例は次のとおりです。

NGINX JavaScriptについて詳しく説明する前に、まずよくある2つの誤解を解消しておきましょう。

NGINX JavaScriptはLuaではない

NGINXコミュニティは長年にわたり、プログラムによる拡張機能を複数開発してきました。執筆時点では、その中でLuaが最も広く利用されており、NGINX用モジュールおよびNGINX Plus向けのサポート対象のビルド済みサードパーティ製ダイナミックモジュールとして提供されています。Luaモジュールとアドオンライブラリは、NGINXコアとの緊密な統合に加え、Redis用ドライバーを含む豊富な機能を提供します。

Luaは強力なスクリプト言語です。しかし、採用は比較的限定的であり、フロントエンド開発者やDevOpsエンジニアが一般的に備えているスキルセットには含まれていません。

NGINX JavaScriptはLuaに取って代わることを目指すものではなく、同等の機能レベルに達するまでにはしばらく時間がかかります。NGINX JavaScriptの目標は、広く普及しているプログラミング言語を使用することで、プログラムによる構成ソリューションを可能な限り幅広いコミュニティに提供することです。

NGINX JavaScriptはNode.jsではない

NGINX JavaScriptは、NGINXまたはNGINX Plusをアプリケーションサーバーに変えることを目的としたものではありません。簡単に言えば、JavaScriptコードはクライアントとコンテンツの間で実行されるため、NGINX JavaScriptのユースケースはミドルウェアに似ています。技術的には、Node.jsと、NGINX JavaScriptをNGINXまたはNGINX Plusと組み合わせた構成には、イベント駆動型アーキテクチャとJavaScriptプログラミング言語という2つの共通点がありますが、類似点はそれだけです。

Node.jsはGoogle V8 JavaScriptエンジンを使用します。一方、NGINX JavaScriptは、NGINXおよびNGINX Plus専用に設計されたECMAScript標準の独自実装です。Node.jsは永続的なJavaScript仮想マシン(VM)をメモリ内に保持し、メモリ管理のために定期的なガベージコレクションを実行します。一方、NGINX JavaScriptはリクエストごとに新しいJavaScript VMと必要なメモリを初期化し、リクエストが完了するとそのメモリを解放します。

サーバーサイド言語としてのJavaScript

前述のとおり、NGINX JavaScriptはJavaScript言語の独自実装です。既存の他のJavaScriptランタイムエンジンはすべて、Webブラウザ内で実行されるように設計されています。クライアント側とサーバー側では、システムリソースの可用性から同時実行可能なランタイム数に至るまで、コード実行の性質がさまざまな点で異なります。

サーバーサイドでのコード実行要件を満たし、NGINXのリクエスト処理アーキテクチャに無理なく適合させるため、独自のJavaScriptランタイムを実装することにしました。NGINX JavaScriptの設計原則は次のとおりです。

  • ランタイム環境のライフサイクルはリクエストと連動NGINX JavaScriptモジュールは、迅速に初期化および破棄できるよう設計された、シングルスレッドのバイトコード実行方式を採用しています。ランタイム環境はリクエストごとに初期化されます。初期化が必要な複雑な状態やヘルパーがないため、起動は非常に高速です。実行中のメモリはプールに蓄積され、完了時にプールを解放することで一括して解放されます。このメモリ管理方式により、個々のオブジェクトを追跡して解放したり、ガベージコレクターを使用したりする必要がありません。
  • ノンブロッキングでのコード実行NGINXとNGINX Plusのイベント駆動モデルは、個々のNGINX JavaScriptランタイム環境の実行をスケジューリングします。NGINX JavaScriptルールがブロッキング操作(ネットワークデータの読み取りや外部サブリクエストの発行など)を実行すると、NGINXとNGINX Plusは、関連するNGINX JavaScript VMの実行を透過的に一時停止し、イベントの完了時に再スケジューリングします。これにより、ルールをシンプルかつ直線的に記述でき、NGINXとNGINX Plusが内部でブロッキングすることなく実行をスケジューリングします。
  • 必要な言語機能のみを実装JavaScriptの仕様は、ECMAScript標準で定義されています。NGINX JavaScriptは、ECMAScript 5.1に準拠し、数学関数には一部のECMAScript 6を採用しています。独自のJavaScriptランタイムを実装することで、サーバーサイドのユースケースに必要な言語機能を優先し、不要な機能を除外できます。現在サポートされている言語要素の一覧を公開しています。
  • リクエスト処理フェーズとの緊密な統合NGINXとNGINX Plusは、リクエストを個別のフェーズに分けて処理します。通常、設定ディレクティブは特定のフェーズで動作し、ネイティブNGINXモジュールでは、特定のフェーズでリクエストを検査または変更できる機能がよく活用されます。NGINX JavaScriptでは、一部の処理フェーズを設定ディレクティブを通じて公開し、JavaScriptコードを実行するタイミングを制御できるようにしています。この設定構文との統合により、ネイティブNGINXモジュールの強力さと柔軟性を、JavaScriptコードの簡潔さとともに活用できます。以下の表は、執筆時点でNGINX JavaScriptからアクセスできる処理フェーズと、そのアクセスを可能にする設定ディレクティブを示しています。
処理フェーズHTTPモジュールStreamモジュール
Access – 認証とアクセス制御auth_requestおよびjs_contentjs_access
Pre-read – ペイロードの読み取り/書き込み該当なしjs_preread
Filter – プロキシ処理中のレスポンスの読み取り/書き込みjs_body_filter
js_header_filter
js_filter
Content – クライアントへのレスポンス送信js_content該当なし
Log / Variables – オンデマンドで評価js_setjs_set

NGINX JavaScript入門 – 実践的な例

NGINX JavaScriptは、NGINX Open Sourceバイナリにコンパイルして組み込むことも、NGINXまたはNGINX Plusに動的にロードすることもできるモジュールとして実装されています。NGINX JavaScriptを有効にする手順(NGINXおよびNGINX Plus向け)は、この記事の末尾に記載されています。

この例では、NGINXまたはNGINX Plusをシンプルなリバースプロキシとして使用し、NGINX JavaScriptを使用して、次のような特殊な形式のアクセスログエントリを作成します。

  • クライアントから送信されたリクエストヘッダーを含む
  • バックエンドから返されたレスポンスヘッダーを含む
  • キーと値のペアを使用することで、ELK Stack(現在の名称はElastic Stack)、Graylog、Splunkなどのログ処理ツールへの効率的な取り込みと検索が可能です

この例のNGINX設定は非常にシンプルです。

コード例を読み込み中…

ご覧のとおり、NGINX JavaScriptコードは設定構文内に直接記述しません。代わりに、js_importディレクティブを使用して、すべてのJavaScriptコードを含むファイルを指定します。js_setディレクティブでは、新しいNGINX変数$access_log_headersと、その値を設定するJavaScript関数を定義します。log_formatディレクティブでは、kvpairsという新しい形式を定義します。この形式では、各ログ行に$access_log_headersの値が書き込まれます。

The serverブロックでは、すべてのリクエストをhttps://www.example.comに転送するシンプルなHTTPリバースプロキシを定義します。access_logディレクティブでは、すべてのリクエストをkvpairs形式でログに記録するよう指定します。

次に、ログエントリを作成するJavaScriptコードを見てみましょう。

コード例を読み込み中…

The return value from the kvAccess関数の戻り値であるログエントリは、js_set設定ディレクティブ(rawheader_logging.conf内)に渡されます。NGINX変数は必要に応じて評価されるため、js_setで定義されたJavaScript関数は、その変数の値が必要になった時点で実行されます。この例では、$access_log_headersがlog_formatディレクティブで使用されているため、kvAccess()はログ記録時に実行されます。mapまたはrewriteディレクティブ(この例には記載されていません)で使用される変数は、対応するJavaScriptをより早い処理フェーズで実行させます。

リクエストをリバースプロキシ経由で送信し、生成されたログファイルのエントリを確認すると、このNGINX JavaScript拡張ロギングソリューションの動作を確認できます。このエントリには、接頭辞「in.」が付いたリクエストヘッダーと、接頭辞「out.」が付いたレスポンスヘッダーが含まれます。

$ curl http://127.0.0.1/$ tail --lines=1 /var/log/nginx/access_headers.log2021-04-23T10:08:15+00:00 client=172.17.0.1 method=GET uri=/index.html status=200 in.Host=localhost:55081 in.User-Agent=curl/7.64.1 in.Accept=*/* out.Content-Type=text/html out.Content-Length=612 out.ETag=x22606339ef-264x22 out.Accept-Ranges=bytes

NGINX JavaScriptの有用性の多くは、NGINXの内部機能にアクセスできることによるものです。この例では、リクエスト(r)オブジェクトのプロパティをいくつか使用しています。Stream NGINX JavaScriptモジュール(TCPおよびUDPアプリケーション向け)では、独自のプロパティセットを持つセッションオブジェクト(s)を使用します。HTTPおよびTCP/UDP向けのNGINX JavaScriptソリューションのその他の例については、NGINX JavaScriptモジュールのユースケースを参照してください。

NGINX JavaScriptのユースケースを思いついたら、ぜひ下のコメント欄でお聞かせください。

NGINX JavaScriptモジュールのユースケース

NGINX JavaScriptモジュールのその他のHTTPおよびTCP/UDPユースケースについては、以下のブログ記事をご覧ください。

[ngx_snippet name=’njs-enable-instructions’]


共有
タグ: F5 NGINX, 2021, Tech

著者について

Liam Crilly
Liam Crillyプロダクトマネジメント担当シニアディレクター

Liam Crillyのその他のブログ記事

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります