🛠️

Nginxのサードパーティーモジュールをビルドする方法

に公開

サードパーティー製のNginx用モジュールを、Nginx本体をビルドせずに動的モジュールとしてビルドする方法をまとめます。

TL;DR

Nginx公式のpkg-ossに含まれるbuild_module.shを使うのが簡単です。
ただし、Alpine Linuxでは一部バグがあるため、筆者によるバグ修正版の利用を推奨します。

サードパーティーモジュール入りのDockerイメージを作る場合は、公式イメージリポジトリmodule用Dockerfileが使えます。
もしくは、筆者が作成した軽量化Dockerfileを使うと便利です。

背景

Nginxはモジュールを用いるアーキテクチャを採用しており、標準以外にも多くのサードパーティーモジュールが存在します。

サードパーティー製モジュールをNginxに組み込む方法として、多くのマニュアルや解説では、Nginx本体のソースをダウンロードし、configureでモジュールを指定してビルドする方法が説明されています。
しかしこの方法ではパッケージ管理の恩恵が受けられず、運用上の負担が増します。

Nginxでは2016-09-13のバージョン1.11.4から、動的モジュール(.soファイル)として個別にビルドし、Nginx設定で実行時にロードできます。
この動的モジュールは、Nginx本体をビルドすることなく、必要なモジュールだけをビルドできます。

モジュールのバイナリパッケージの生成

pkg-oss

https://github.com/nginx/pkg-oss

バイナリパッケージ作成用のNginx公式のツール群としてpkg-ossが提供されています。
公式サポートディストリビューション向けのパッケージ作成に対応し、サードパーティーモジュールのビルドにも利用できます。
特にF5 NGINX Plusで公式サポートされるモジュールのサポートバージョンは、用意されたMakefileを使ってビルドできます。(詳細手順は割愛)

任意のサードパーティーモジュールは、build_module.shスクリプトでビルドできます。
ただし、Alpine Linuxでモジュール名にハイフン(-)を含む場合に失敗するバグ(Pull Request提出中)が存在します。
このバグを修正したフォークを筆者が公開していますので、該当する環境では修正版build_module.shを使ってください。

build_module.shの使い方

最初に、build_module.sh本家または修正版)をダウンロードします。
build_module.shの内部でpkg-ossをcloneするので、pkg-oss全体のcloneは不要です。

次に、以下のコマンドを実行します。

/bin/sh build_module.sh -v $NGINX_VER $MODULE_SOURCE

$NGINX_VERは、対象とするNginxのバージョン番号です。
例えば2025-05-15時点でのmainline最新版であれば1.27.5を指定します。

$MODULE_SOURCEは、モジュールのソースへのURLまたはパスです。
例えば、ngx_cache_purgeのバージョン2.3を用いるのであれば、https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gzを指定します。
URLにはGitリポジトリURL(拡張子 .git)も指定可能です。

実行すると、必要に応じて何個か質問されるので回答します。

ビルドが成功すると、カレントディレクトリのbuild-module-artifactsにバイナリパッケージが生成されます。
出力先は-oオプションで変更可能です。

利用可能なオプションは、build_module.shを引数なしで実行すると確認できます。

Dockerイメージのビルド

Nginx公式提供の方法

https://github.com/nginx/docker-nginx/

サードパーティーモジュールを組み込んだDockerイメージは、Nginx公式イメージリポジトリのmoduleディレクトリにあるDockerfile(Debian用, Alpine用)で簡単に作成できます。
手順はmodule/README.mdに記載されています。

しかし、この公式版Dockerfileにはちょっとした欠点があります。

まず、URL指定でモジュールを組み込みたい場合、ビルドコンテキスト(docker buildで指定するパス/URL)の下に、モジュール名のディレクトリを作成し、その下にsourceというファイルを用意してURLを書く必要があり、少し手間です。

さらに、この指定方法に関連するのですが、Dockerfileの前方で

COPY ./ /modules/

と記述されているため、ビルドコンテキスト配下に(イメージビルドに関係なくても)何か変更があれば、ビルドキャッシュが使われずにゼロから再ビルドが行われてしまいます。

これらの欠点は、イメージをさらにカスタマイズする際に問題となる可能性があります。

軽量化Dockerfile

https://github.com/simayosi/docker-nginx-3rdparty-module

公式版Dockerfileの欠点を解消するために、新たにDockerfileを作成しました。

このDockerfileでは、以下のビルド引数を指定するだけで、Nginx公式イメージに指定したサードパーティーモジュールを組み込んだイメージを作成できます。

  • MODULE_NAME: 組み込むモジュール名
  • MODULE_SOURCE: モジュールソースのURL(例: https://example.com/module.tar.gz
  • NGINX_IMAGE_TAG: ベースにするNginx公式イメージのタグ(例: 1.27-alpine
  • BUILD_DEPS: モジュールのビルドに必要な追加パッケージ(スペース区切り)

例えば、公式イメージのタグstable-alpineをベースに、ビルド時にapkパッケージlibwebp-devとlibavif-devを必要とするモジュールをGitレポジトリから組み込みたい場合は以下のようにします。

docker build -f Dockerfile.alpine \
  --build-arg MODULE_NAME=your-module \
  --build-arg MODULE_SOURCE=https://url-to-your-repository.git \
  --build-arg NGINX_IMAGE_TAG=stable-alpine \
  --build-arg BUILD_DEPS="libwebp-dev libavif-dev" \
  -t nginx-with-custom-module:stable-alpine \
  https://github.com/simayosi/docker-nginx-3rdparty-module.git

Docker Composeを使う場合は以下のようにします。(ローカルの nginx.conf を利用)

services:
  nginx:
    build:
      context: https://github.com/simayosi/docker-nginx-3rdparty-module.git
      dockerfile: Dockerfile.alpine
      args:
        MODULE_NAME: your-module
        MODULE_SOURCE: https://url-to-your-repository.git
        NGINX_IMAGE_TAG: stable-alpine
        BUILD_DEPS: "libwebp-dev libavif-dev"
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

このDockerfileは単一モジュールの組み込みに特化していますが、マルチステージビルドを利用しているため、buildステージをモジュールごとに別名でコピーすれば、簡単に複数モジュールに対応できます。
その方法の場合、変更のないモジュールにはビルドキャッシュが有効で、ビルド時間が短縮されます。

このプロジェクトはMITライセンスで公開していますので、自由に改変して使ってください。

おわりに

Nginx本体をビルドせずにサードパーティー製の動的モジュールをビルドする公式の方法と、モジュールを組み込んだDockerイメージを作成する方法についてまとめました。

いずれの方法でも、ビルドした動的モジュールはNginxのバージョンに依存するため、Nginxのバージョンを変更する際は再ビルドが必要です。
また、動的モジュールに対応していないサードパーティーモジュールはこの方法ではビルドできず、Nginx本体も含めてビルドする必要があります。

Discussion