NginxでNJSを使用する環境をDockerで構築する

2024/08/22に公開

NginxでNJSを使用する環境をDockerで構築する

はじめに

本記事では、DockerとDocker Composeを使用して、NginxにNJSモジュールを追加した環境を構築する方法を紹介します。この環境では、NginxでJavaScriptの実行が可能になります。

プロジェクト構成

.
├── docker
│   └── nginx
│       ├── Dockerfile
│       ├── default.conf
│       ├── nginx.conf
│       └── njs-script.js
└── docker-compose.yml

環境構築

docker-compose.yml の作成

docker-compose.yml
services:
  nginx:
    build:
      context: docker/nginx
      args:
        ENABLED_MODULES: brotli njs
    image: nginx-with-brotli-njs:v1
    container_name: "brotli-njs-nginx"
    ports:
      - "80:80"
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./docker/nginx/njs-script.js:/etc/nginx/js/njs-script.js

注目すべき点:

  • ENABLED_MODULES: brotli njs NginxにBrotliとNJSモジュールを追加します。
  • ボリュームマウントにより、設定ファイルとNJSスクリプトをコンテナ内に配置します。

Dockerfile の作成
docker/nginx/Dockerfile

Nginx公式のDockerイメージにモジュールを追加するには、公式が提供するDockerfileを利用し、ENABLED_MODULESを指定する必要があります。
この方法は、Adding third-party modules to nginx official imageというドキュメントに詳しく説明されています。

まずは、公式のDockerfileをプロジェクトにコピーします:

$ curl -o docker/nginx/Dockerfile https://raw.githubusercontent.com/nginxinc/docker-nginx/master/modules/Dockerfile
Dockerfile
ARG NGINX_FROM_IMAGE=nginx:mainline
FROM ${NGINX_FROM_IMAGE} as builder

ARG ENABLED_MODULES

# (中略: モジュールのビルドプロセス)

FROM ${NGINX_FROM_IMAGE}
RUN --mount=type=bind,target=/tmp/packages/,source=/tmp/packages/,from=builder \
    apt-get update \
    && . /tmp/packages/modules.env \
    && for module in $BUILT_MODULES; do \
           apt-get install --no-install-suggests --no-install-recommends -y /tmp/packages/nginx-module-${module}_${NGINX_VERSION}*.deb; \
       done \
    && rm -rf /var/lib/apt/lists/

このDockerfileは、指定されたモジュール(BrotliとNJS)を含むカスタムNginxイメージをビルドします。

Nginx の設定ファイル作成

docker/nginx/nginx.conf

nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
load_module modules/ngx_http_js_module.so;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;

    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    resolver 8.8.8.8 ipv6=off valid=5s;

    js_fetch_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
    js_path "/etc/nginx/js/";
    js_import main from njs-script.js;

    include /etc/nginx/conf.d/*.conf;
}

注目すべき点:

  • NJSモジュールのロード
  • NJSの設定(証明書、スクリプトパス、インポート)

NJSスクリプトの作成
docker/nginx/njs-script.js

njs-script.js
async function fetch(r) {
    try {
        let reply = await ngx.fetch('https://inet-ip.info/ip', {
            verify: true
        });
        
        if (!reply.ok) {
            throw new Error(`HTTP error! status: ${reply.status}`);
        }
        
        let ip = await reply.text();
        ip = ip.trim();

        r.return(200, `Your IP Address is: ${ip}`);
    } catch (error) {
        r.error(`Error fetching IP address: ${error.message}`);
        r.return(500, "Internal Server Error");
    }
}

export default {fetch};

このスクリプトは、外部APIを使用してクライアントのIPアドレスを取得し、表示します。

Nginxのサーバーブロック設定

docker/nginx/default.conf

default.conf
server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /sample-njs {
        js_content main.fetch;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

/sample-njs エンドポイントでNJSのfetch関数を呼び出すように設定しています。

環境構築のポイント

  • ENABLED_MODULESにnjsを指定し、NJSモジュールを追加
  • nginx.confでNJSモジュールを読み込み、スクリプトのパスを設定
  • njs-script.jsで外部APIを使用してIPアドレスを取得する関数を実装
  • default.conf/sample-njsエンドポイントにNJS関数を割り当て

実行結果

環境を起動し、http://localhost/sample-njsにアクセスすることで、クライアントのIPアドレスが表示されます。

まとめ

このプロジェクトでは以下を実現しました:

  • DockerとDocker Composeを使用したNginx+NJS環境の構築
  • NJSを使用したサーバーサイドJavaScriptの実行
  • 外部APIとの連携によるダイナミックなコンテンツ生成

おわりに

NginxにNJSを組み合わせることで、柔軟性の高いWebサーバー環境を構築できます。NJSを使用することで、複雑なサーバーサイドロジックをJavaScriptで実装でき、APIとの連携や動的なコンテンツ生成が可能になります。

Discussion