Nginx をソースからビルド

# はじめに

Nginx のモジュールを書く機会があったので、とりあえず nginx 本体をソースからビルドする方法を書いておきます。今更ですね。本来ならモジュールを作るところまで書きたいのですが、そこまで筆が進まないので保留です。軽めの記事です。

能書きはいい、コマンドだけ教えろという方は[こちら](#コマンド一覧)をどうぞ。

# 方針

Qiita の他の nginx ビルド記事を見ると、nginx-build というビルドのお助けツールがあるみたいですが、本記事ではちまちまと手でダウンロードします。依存するライブラリも、ビルド済みパッケージを使わずソースからビルドします。

手元の Ubuntu 22.04 の apt install nginx で nginx をインストールすると、ビルド オプションは以下のようになっていました。これと同じ構成にして、稼働中のバイナリを置き換えることを目指します。使う予定のないモジュールもありますが、とりあえず入れます。ただし　nginx 及び各ライブラリのバージョンは稼働中のバージョンに関わらず最新のものを使います。

```
$ nginx -V
nginx version: nginx/1.18.0 (Ubuntu)
built with OpenSSL 3.0.2 15 Mar 2022
TLS SNI support enabled
configure arguments:
 --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-d8gVax/nginx-1.18.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2'
 --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -fPIC'
 --prefix=/usr/share/nginx
 --conf-path=/etc/nginx/nginx.conf
 --http-log-path=/var/log/nginx/access.log
 --error-log-path=/var/log/nginx/error.log
 --lock-path=/var/lock/nginx.lock
 --pid-path=/run/nginx.pid
 --modules-path=/usr/lib/nginx/modules
 --http-client-body-temp-path=/var/lib/nginx/body
 --http-fastcgi-temp-path=/var/lib/nginx/fastcgi
 --http-proxy-temp-path=/var/lib/nginx/proxy
 --http-scgi-temp-path=/var/lib/nginx/scgi
 --http-uwsgi-temp-path=/var/lib/nginx/uwsgi
 --with-compat
 --with-debug
 --with-pcre-jit
 --with-http_ssl_module
 --with-http_stub_status_module
 --with-http_realip_module
 --with-http_auth_request_module
 --with-http_v2_module
 --with-http_dav_module
 --with-http_slice_module
 --with-threads
 --add-dynamic-module=/build/nginx-d8gVax/nginx-1.18.0/debian/modules/http-geoip2
 --with-http_addition_module
 --with-http_gunzip_module
 --with-http_gzip_static_module
 --with-http_sub_module
```

# ダウンロードリスト

先に一覧を載せておきます。今は全部 GitHub にあるので便利ですね。私の環境ではソースは全て /data/src に置き、インストール先を /data/bin にしています。

| Module | Version (as of 2023-02-20) | URL | Depends on |
|-|-|-|-|
| Nginx | release-1.23.3 | https://github.com/nginx/nginx | - |
| ngx_http_geoip2_module | 3.4 | https://github.com/leev/ngx_http_geoip2_module | - |
| libmaxminddb | 1.7.1 | https://github.com/maxmind/libmaxminddb | ngx_http_geoip2_module |
| OpenSSL | openssl-3.0.8 | https://github.com/openssl/openssl/ | ngx_http_ssl_module |
| zlib | v1.2.13 | https://github.com/madler/zlib.git | ngx_http_gzip_module |
| PCRE2 | pcre2-10.42 | https://github.com/PCRE2Project/pcre2 | ngx_http_rewrite_module |

# ツール

真っ新の Ubuntu を使って進めるので、ビルドツールからインストールします。autoconf automake libtool は libmaxminddb をビルドするのに必要です。

```
$ sudo apt install build-essential autoconf automake libtool
```

# ダウンロード

前述のテーブルにしたがって、ひたすら git clone します。簡単ですね。libmaxminddb はサブモジュールを含むので --recursive が必要です。

```
$ cd /data/src
$ git clone --depth=1 --branch=release-1.23.3 https://github.com/nginx/nginx.git
$ git clone --depth=1 --branch=3.4 https://github.com/leev/ngx_http_geoip2_module.git
$ git clone --depth=1 --branch=1.7.1 --recursive https://github.com/maxmind/libmaxminddb
$ git clone --depth=1 --branch=openssl-3.0.8 https://github.com/openssl/openssl.git
$ git clone --depth=1 --branch=v1.2.13 https://github.com/madler/zlib.git
$ git clone --depth=1 --branch=pcre2-10.42 https://github.com/PCRE2Project/pcre2.git
```

pcre2 については、事前に autogen だけ実行する必要があります。忘れると、nginx をビルドするときに configure ファイルが見つからないという理由で怒られます。

```
$ cd /data/src/pcre2
$ ./autogen.sh
```

ソースをダウンロードして nginx の configure 時にパスを渡すだけで依存モジュールもまとめてビルドしてくれますが、nginx の孫モジュール、すなわち geoip2 モジュールに依存する libmaxminddb まではビルドしてくれないので、予め自分でビルドします。

```
$ cd /data/src/libmaxminddb
$ ./bootstrap
$ ./configure --prefix=/data/bin/libmaxminddb
$ make -j8
$ make install
```

# ビルド

基本的には nginx -V で出力されたオプションを auto/configure に渡しますが、ビルド環境に依存する以下の部分を変えます。

- コンパイルオプションから -ffile-prefix-map 削除
- コンパイルオプションに libmaxminddb のヘッダーパスを追加
- リンカオプションに libmaxminddb のライブラリパスを追加
- ngx_http_geoip2_module のソースコードパスを変更
- --with-pcre、--with-openssl、--with-zlib を追加

あとはいつもの configure-make-make-install ダンス、ではなく、configure-make に留めておきます。というのも --prefix が /usr/share/nginx　なので make install には sudo が必要になりますが、その際に意図しないファイルが勝手に置き換わってほしくないのです。

もし make install を綺麗に実行したい場合、--prefix、--conf-path、--modules-path を /data/bin など sudo を必要としないディレクトリに変えておくのが無難です。

```
$ cd /data/src/nginx
# auto/configure \
 --with-cc-opt='-g -O2 -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2 -I/data/bin/libmaxminddb/include' \
 --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -fPIC -L/data/bin/libmaxminddb/lib' \
 --prefix=/usr/share/nginx \
 --conf-path=/etc/nginx/nginx.conf \
 --http-log-path=/var/log/nginx/access.log \
 --error-log-path=/var/log/nginx/error.log \
 --lock-path=/var/lock/nginx.lock \
 --pid-path=/run/nginx.pid \
 --modules-path=/usr/lib/nginx/modules \
 --http-client-body-temp-path=/var/lib/nginx/body \
 --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
 --http-proxy-temp-path=/var/lib/nginx/proxy \
 --http-scgi-temp-path=/var/lib/nginx/scgi \
 --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
 --with-compat \
 --with-debug \
 --with-pcre-jit \
 --with-http_ssl_module \
 --with-http_stub_status_module \
 --with-http_realip_module \
 --with-http_auth_request_module \
 --with-http_v2_module \
 --with-http_dav_module \
 --with-http_slice_module \
 --with-threads \
 --add-dynamic-module=/data/src/ngx_http_geoip2_module \
 --with-http_addition_module \
 --with-http_gunzip_module \
 --with-http_gzip_static_module \
 --with-http_sub_module \
 --with-pcre=/data/src/pcre2 \
 --with-openssl=/data/src/openssl \
 --with-zlib=/data/src/zlib
$ make -j8

```

バイナリは objs/nginx と objs/ngx_http_geoip2_module.so に作成されるので、稼働中のバイナリと置き換えます。なお、apt install したパッケージには geoip2 以外にも動的モジュールが含まれており、インストール直後の状態で /etc/nginx/modules-enabled 配下の conf ファイルから全てロードされるようになっています。今回 nginx のバージョンを 1.18.0 から 1.23.3 に上げたため、ビルドした geoip2 は apt でインストールされた動的モジュールとは混在できません。もし使っている動的モジュールがあれば、全てビルドし直す必要があります。使っていないのであれば、/etc/nginx/modules-enabled の conf ファイルをどこかに退避させるなどして、わざわざロードさせなくてもいいと思います。

# 試す

テストを兼ねて、ビルドした ssl と geoip2 モジュールを動かします。クライアント IP の国情報を取ってきてアクセスログに出力させるものです。

/etc/nginx/nginx.conf の http セクションに以下の設定を追加します。設定は https://github.com/leev/ngx_http_geoip2_module に書かれている内容ほぼそのままです。mmdb ファイルは、http://dev.maxmind.com/geoip/geoip2/geolite2/ から取ってきました。

```
http {
  ...

  geoip2 /data/conf/GeoLite2-Country.mmdb {
    auto_reload 5m;
    $geoip2_data_country_code default=? country iso_code;
    $geoip2_data_country_name default=? country names en;
  }
  geoip2 /data/conf/GeoLite2-City.mmdb {
    auto_reload 5m;
    $geoip2_data_city_name default=? city names en;
  }
  log_format geolog
    '[$time_local] $remote_addr'
    ' ($geoip2_data_country_code $geoip2_data_country_name $geoip2_data_city_name)'
    ' "$request" $status $bytes_sent $request_time "$http_user_agent"';

  ...
}
```

さらに、以下の内容の virtual host 設定を /etc/nginx/nginx.conf 内か、単独の conf ファイルとして書きます。

```
server {
  listen 443 ssl;
  ssl_certificate /data/conf/cert.pem;
  ssl_certificate_key /data/conf/privkey.pem;
  access_log /var/log/nginx/ssl-access.log geolog;
  error_log /var/log/nginx/ssl-error.log;
  location / {
    proxy_pass http://localhost:8000;
  }
}
```

この SSL サーバーに対して、内部 IP と https://reqbin.com/ からアクセスしたときのログが以下の通りです。geoip2 動いてますね。

```
$ sudo tail -f /var/log/nginx/ssl-access.log
[19/Feb/2023:19:28:04 -0800] 10.0.0.99 (? ? ?) "GET / HTTP/1.1" 200 850 0.000 "curl/7.83.1"
[19/Feb/2023:19:29:34 -0800] 10.0.0.99 (? ? ?) "GET / HTTP/1.1" 200 850 0.000 "curl/7.83.1"
[19/Feb/2023:19:30:10 -0800] 206.189.205.251 (US United States North Bergen) "GET / HTTP/1.1" 200 645 0.000 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0 X-Middleton/1"
[19/Feb/2023:19:32:53 -0800] 206.189.205.251 (US United States North Bergen) "GET / HTTP/1.1" 200 645 0.000 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0 X-Middleton/1"
```

# コマンド一覧

```bash
mkdir /data/src
mkdir /data/bin

sudo apt update -y
sudo apt install build-essential autoconf automake libtool -y

cd /data/src
git clone --depth=1 --branch=release-1.23.3 https://github.com/nginx/nginx.git
git clone --depth=1 --branch=3.4 https://github.com/leev/ngx_http_geoip2_module.git
git clone --depth=1 --branch=1.7.1 --recursive https://github.com/maxmind/libmaxminddb
git clone --depth=1 --branch=openssl-3.0.8 https://github.com/openssl/openssl.git
git clone --depth=1 --branch=v1.2.13 https://github.com/madler/zlib.git
git clone --depth=1 --branch=pcre2-10.42 https://github.com/PCRE2Project/pcre2.git

cd /data/src/pcre2
./autogen.sh

cd /data/src/libmaxminddb
./bootstrap
./configure --prefix=/data/bin/libmaxminddb
make -j8
make install

cd /data/src/nginx
auto/configure \
 --with-cc-opt='-g -O2 -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2 -I/data/bin/libmaxminddb/include' \
 --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -fPIC -L/data/bin/libmaxminddb/lib' \
 --prefix=/usr/share/nginx \
 --conf-path=/etc/nginx/nginx.conf \
 --http-log-path=/var/log/nginx/access.log \
 --error-log-path=/var/log/nginx/error.log \
 --lock-path=/var/lock/nginx.lock \
 --pid-path=/run/nginx.pid \
 --modules-path=/usr/lib/nginx/modules \
 --http-client-body-temp-path=/var/lib/nginx/body \
 --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
 --http-proxy-temp-path=/var/lib/nginx/proxy \
 --http-scgi-temp-path=/var/lib/nginx/scgi \
 --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
 --with-compat \
 --with-debug \
 --with-pcre-jit \
 --with-http_ssl_module \
 --with-http_stub_status_module \
 --with-http_realip_module \
 --with-http_auth_request_module \
 --with-http_v2_module \
 --with-http_dav_module \
 --with-http_slice_module \
 --with-threads \
 --add-dynamic-module=/data/src/ngx_http_geoip2_module \
 --with-http_addition_module \
 --with-http_gunzip_module \
 --with-http_gzip_static_module \
 --with-http_sub_module \
 --with-pcre=/data/src/pcre2 \
 --with-openssl=/data/src/openssl \
 --with-zlib=/data/src/zlib
make -j8
```