FreeBSDのNginxをportsでビルド → 本番環境はダウンタイムなしで切り替える

Nginxのパフォーマンス改善にはportsからビルドするのがベストだと思ってやってみたときの覚書。
Gemini 2.5 Proと対話。

環境: FreeBSD 14.3-RELEASE-p2,  nginx 1.28.0


1. Portsリポジトリを取得

FreeBSD14からportsnapコマンドではなくgitコマンドを使う。
参考: Chapter 4. Installing Applications: Packages and Ports | FreeBSD Documentation Portal
まずは公式サイトにあるようにgit cloneする。
--depth 1 を付けることで、最新のコミット1つ分のみを取得。
# git clone --depth 1 https://git.FreeBSD.org/ports.git /usr/ports

移動して確認。
# cd /usr/ports/
# ls



2. Nginxをビルド

pkgでインストールしたNginxのバージョンとビルドオプションを確認。
# nginx -V 2>&1 | sed 's/ --/\n--/g'

nginx version: nginx/1.28.0
built with OpenSSL 3.0.16 11 Feb 2025
TLS SNI support enabled
configure arguments:
--prefix=/usr/local/etc/nginx
--with-cc-opt='-I /usr/local/include'
--conf-path=/usr/local/etc/nginx/nginx.conf
--sbin-path=/usr/local/sbin/nginx
--pid-path=/var/run/nginx.pid
--error-log-path=/var/log/nginx/error.log
--user=www
--group=www
--with-compat
--with-pcre
--modules-path=/usr/local/libexec/nginx
--with-file-aio
--http-client-body-temp-path=/var/tmp/nginx/client_body_temp
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp
--http-proxy-temp-path=/var/tmp/nginx/proxy_temp
--http-scgi-temp-path=/var/tmp/nginx/scgi_temp
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp
--http-log-path=/var/log/nginx/access.log
--with-http_v2_module
--with-http_v3_module
--with-http_addition_module
--with-http_auth_request_module
--with-http_dav_module
--with-http_flv_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_mp4_module
--with-http_random_index_module
--with-http_realip_module
--with-http_secure_link_module
--with-http_slice_module
--with-http_ssl_module
--with-http_stub_status_module
--with-http_sub_module
--without-mail_imap_module
--without-mail_pop3_module
--without-mail_smtp_module
--with-mail_ssl_module
--with-stream
--with-stream_realip_module
--with-stream_ssl_module
--with-stream_ssl_preread_module
--with-threads
--with-mail=dynamic
--with-stream=dynamic
--with-ld-opt='-L /usr/local/lib'

ディレクトリに移動してビルドオプション設定。
# cd www/nginx/
# make  config

Gemini先生による各オプションの説明。

基本オプション

  • DEBUGLOG: 開発者向けのデバッグログを有効にします。
  • DSO: Nginx本体を再ビルドせずに、モジュールを動的に追加・削除できるようにします(.soファイルを別途コピーし、load_moduleする必要あり)。オフにした場合、モジュールはすべて本体に一体化されるためload_moduleする必要なし。
  • FILE_AIO: ファイルの非同期I/Oを有効にし、ディスクI/Oのパフォーマンスを改善します。
  • IPV6: IPv6をサポートします。
  • NJS: 設定ファイル内でJavaScript(NJS)を実行できるようにします。
  • NJS_XML: NJSでXMLを扱う機能を追加します。
  • OTEL: OpenTelemetryによる分散トレーシングをサポートします。
  • THREADS: スレッドプールを有効にし、ブロッキングI/O(ディスク読み込み等)の性能を向上させます。
  • WWW: index.htmlや50x.htmlなどのサンプルファイルをインストールします。

HTTPモジュール関連

  • GOOGLE_PERFTOOLS: Googleのパフォーマンス解析ツールを有効にします。
  • HTTP: HTTPサーバー機能を有効にします。(通常は必須)
  • HTTP_ADDITION: レスポンスの前後に別のコンテンツを追記する機能です。
  • HTTP_AUTH_REQ: 外部サーバーに問い合わせて認証を行う機能です。
  • HTTP_CACHE: プロキシキャッシュ機能を有効にします。
  • HTTP_DAV: WebDAV(ファイルのアップロードや管理)機能を有効にします。
  • HTTP_DEGRADATION: メモリ不足時にレスポンス品質を落としてサービスを継続させる機能です。
  • HTTP_FLV: FLV形式の動画ストリーミング配信をサポートします。
  • HTTP_GUNZIP_FILTER: gzip圧縮されたレスポンスを動的に解凍する機能です。
  • HTTP_GZIP_STATIC: 事前に圧縮された.gzファイルを配信し、CPU負荷を軽減します。
  • HTTP_IMAGE_FILTER: 画像のリサイズや回転などの処理を行う機能です。
  • HTTP_MP4: MP4形式の動画ストリーミング配信をサポートします。
  • HTTP_PERL: 設定ファイル内でPerlスクリプトを実行できるようにします。
  • HTTP_RANDOM_INDEX: ディレクトリからランダムに選んだファイルをインデックスページとして表示します。
  • HTTP_REALIP: ロードバランサ経由のアクセス時に、クライアントの本当のIPアドレスを取得します。
  • HTTP_SECURE_LINK: 期限付き・IP制限付きのセキュアなダウンロードリンクを生成します。
  • HTTP_SLICE: 大きなファイルを分割して効率的にキャッシュ・配信します。
  • HTTP_SSL: HTTPS (SSL/TLS) 通信を有効にします。(通常は必須)
  • HTTP_STATUS: /nginx_statusで簡単な接続情報を表示するstub_statusモジュールです。
  • HTTP_SUB: レスポンス内の文字列を置換します。
  • HTTP_XSLT: XMLレスポンスをXSLTで変換します。
  • HTTPV2: HTTP/2プロトコルをサポートします。
  • HTTPV3: HTTP/3 (QUIC) プロトコルをサポートします。
  • HTTPV3_BORING: HTTP/3にGoogleのBoringSSLを使用します。
  • HTTPV3_LSSL: HTTP/3にLibreSSLを使用します。
  • HTTPV3_QTLS: HTTP/3にopenssl-quictlsを使用します。(今回の推奨)

MAILモジュール関連

  • MAIL: IMAP/POP3/SMTPのメールプロキシ機能を有効にします。
  • MAIL_IMAP / MAIL_POP3 / MAIL_SMTP: 各メールプロトコルのプロキシ機能です。
  • MAIL_SSL: メールプロキシでSSL/TLSを有効にします。

STREAMモジュール関連

  • STREAM: TCP/UDPの汎用プロキシ(L4ロードバランサ)機能を有効にします。
  • STREAM_REALIP: PROXYプロトコルからクライアントのIPアドレスを取得します。
  • STREAM_SSL: STREAMプロキシでSSL/TLSを有効にします。
  • STREAM_SSL_PREREAD: SSL/TLSハンドシェイク情報(SNIなど)を先に読み取って処理を分岐させます。

サードパーティモジュール (代表的なもの)

  • BROTLI: GoogleのBrotli圧縮アルゴリズムを有効にします。(Gzipより高圧縮)
  • HTTP_GEOIP2: IPアドレスから国や都市などの位置情報を判定します。
  • HTTP_UPSTREAM_CHECK: バックエンドサーバーのヘルスチェック(死活監視)を行います。
  • HEADERS_MORE: HTTPヘッダーの追加・変更・削除をより柔軟に行います。
  • LUA: 設定ファイル内で高機能なLuaスクリプトを実行できるようにします。
  • MODSECURITY3: WAF (Web Application Firewall) であるModSecurityを有効にします。
  • PASSENGER: Ruby/Python/Node.jsアプリケーションサーバーのPhusion Passengerを統合します。
  • VTS: バーチャルホストごとの詳細なアクセス統計情報(Virtual host Traffic Status)を表示します。
  • RTMP: ライブ動画ストリーミング(RTMPプロトコル)を配信するサーバー機能を追加します。
  • HTTP_FANCYINDEX: 標準より見栄えの良いファイル一覧ページを生成します。

GSSAPI実装

  • GSSAPI_HEIMDAL / GSSAPI_MIT: Kerberos認証などで使われるGSSAPIの実装ライブラリを選択します。


オンにしたビルドオプション一覧

  • FILE_AIO
  • IPV6
  • THREADS
  • HTTP
  • HTTP_CACHE
  • HTTP_GUNZIP_FILTER
  • HTTP_GZIP_STATIC
  • HTTP_REALIP
  • HTTP_SLICE
  • HTTP_SSL
  • HTTP_STATUS
  • HTTP_SUB
  • HTTPV2
  • HTTPV3
  • HTTPV3_QTLS

---

  • BROTLI
  • CACHE_PURGE
  • HTTP_GEOIP2
  • VTS


既存のNginxを停止し、設定をバックアップ。
# service nginx stop
# cp -Rp /usr/local/etc/nginx /usr/local/etc/nginx.bak

Nginxのビルドとインストールを実行。
make reinstallを使うと、既存版のアンインストールと新規インストールを自動で行ってくれる。
# make reinstall clean

openssl-quictls-3.0.15_1のビルドオプションが表示された。
デフォルトのままでOK。
TLS 1.3が項目にないのは常に有効な機能だから。

gmake-4.4.1のビルドオプションが表示された。
NLSを選択したままでOK。

数分でビルド完了した。

バージョンとビルドオプションを確認。
# nginx -V 2>&1 | sed 's/ --/\n--/g'

nginx version: nginx/1.28.0
built with OpenSSL 3.0.15+quic 3 Sep 2024
TLS SNI support enabled
configure arguments:
--prefix=/usr/local/etc/nginx
--with-cc-opt='-I /usr/local/include'
--conf-path=/usr/local/etc/nginx/nginx.conf
--sbin-path=/usr/local/sbin/nginx
--pid-path=/var/run/nginx.pid
--error-log-path=/var/log/nginx/error.log
--user=www
--group=www
--with-compat
--with-pcre
--modules-path=/usr/local/libexec/nginx
--with-file-aio
--http-client-body-temp-path=/var/tmp/nginx/client_body_temp
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp
--http-proxy-temp-path=/var/tmp/nginx/proxy_temp
--http-scgi-temp-path=/var/tmp/nginx/scgi_temp
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp
--http-log-path=/var/log/nginx/access.log
--with-http_v2_module
--with-http_v3_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_realip_module
--with-http_slice_module
--with-http_ssl_module
--with-http_stub_status_module
--with-http_sub_module
--without-mail_imap_module
--without-mail_pop3_module
--without-mail_smtp_module
--with-threads
--add-dynamic-module=/usr/ports/www/nginx/work/ngx_brotli-a71f931
--add-dynamic-module=/usr/ports/www/nginx/work/ngx_cache_purge-a84b0f3
--add-dynamic-module=/usr/ports/www/nginx/work/ngx_http_geoip2_module-3.4
--add-dynamic-module=/usr/ports/www/nginx/work/nginx-module-vts-bf64dbf
--with-ld-opt='-L /usr/local/lib'

設定ファイルを確認。
# cd /usr/local/etc/nginx/
# less nginx.conf

とりあえず今の設定で起動できるか確認。
# nginx -t
# service nginx start
# service nginx status

サイトを表示して確認。

pkg upgrade で上書きされないようにNginxパッケージをロック。
# pkg lock nginx


早くHTTP/3を確認したくなるけども、まずはこのportsからのビルドバージョンで本番環境のpkgバージョンと入れ替えを行う。


3. 本番環境はダウンタイムなしで切り替える

本番環境ではNginxをビルドのみ実行。
# git clone --depth 1 https://git.FreeBSD.org/ports.git /usr/ports
# cd /usr/ports/www/nginx
# make config
# make

下記エラー

===>  openssl-quictls-3.0.15_1 has known vulnerabilities:
openssl-quictls-3.0.15_1 is vulnerable:
  OpenSSL -- OOB memory access vulnerability
  CVE: CVE-2024-9143
  WWW: https://vuxml.FreeBSD.org/freebsd/c6f4177c-8e29-11ef-98e7-84a93843eb75.html

1 problem(s) in 1 package(s) found.
=> Please update your ports tree and try again.
=> Note: Vulnerable ports are marked as such even if there is no update available.
=> If you wish to ignore this vulnerability rebuild with 'make DISABLE_VULNERABILITIES=yes'
*** Error code 1

Stop.
make[3]: stopped in /usr/ports/security/openssl-quictls
*** Error code 1

Stop.
make[2]: stopped in /usr/ports/security/openssl-quictls
*** Error code 1

Stop.
make[1]: stopped in /usr/ports/www/nginx
*** Error code 1

Stop.

CVE-2024-9143という脆弱性が見つかってまだパッチが適用されていないらしい。
とりあえずダウンタイムなしで切り替えることを試したいからHTTPV3_QTLSは外す。
# make config
# make clean
# make

現在のバイナリの場所と新しいバイナリの場所を確認。
# whereis nginx

nginx: /usr/local/sbin/nginx /usr/local/share/man/man8/nginx.8.gz /usr/ports/www/nginx

# ls /usr/ports/www/nginx/work/stage/usr/local/sbin

バージョンとビルドオプションを確認。
# /usr/ports/www/nginx/work/stage/usr/local/sbin/nginx -V 2>&1 | sed 's/ --/\n--/g'

新しいバイナリでテスト実行。
# /usr/ports/www/nginx/work/stage/usr/local/sbin/nginx -t -c /usr/local/etc/nginx/nginx.conf

マスタープロセスとワーカープロセスの親子関係をツリー形式で視覚的に確認pstreeをインストールする。
# pkg install pstree

nginxのプロセスを表示。
# pstree -s nginx



ダウンタイムなしのオンラインアップグレード開始

別コンソールでnginxプロセスを監視(1秒ごとに再実行と再描画)。
# while true; do clear; pstree -s nginx; sleep 1; done

現在のマスタープロセスのPIDを確認。
# cat /var/run/nginx.pid

pstreeの表示と一致していることを確認。

現在のバイナリをバックアップして新しいバイナリを配置。
# mv /usr/local/sbin/nginx /usr/local/sbin/nginx.old
# cp /usr/ports/www/nginx/work/stage/usr/local/sbin/nginx /usr/local/sbin/nginx
# chmod +x /usr/local/sbin/nginx

USR2シグナルを送信して、新バイナリで新しいマスタープロセスを起動。
(USR2 = ユーザー定義のシグナル2 => nginxのソースコードにオンラインアップグレードはUSR2で行うと定義されている)
参考: Controlling nginx
# kill -USR2 $(cat /var/run/nginx.pid)

pstreeでマスタープロセスが増えているのを確認。

nginx.pid.oldbinがあるか確認して、古いプロセスを段階的に終了させる。
(WINCH = Window Change)
# ls /var/run/
# kill -WINCH $(cat /var/run/nginx.pid.oldbin)

古いワーカープロセスはすべて終了したのをpstreeで確認。
最後に古いマスタープロセス自体を終了させる。
# kill -QUIT $(cat /var/run/nginx.pid.oldbin)

/var/run/には新しいPIDのnginx.pidしか残っていないはず。
# ls /var/run/
# cat /var/run/nginx.pid

ブラウザで正常に表示されるか確認する。


もし元のバイナリに戻す場合の手順

新しいマスタープロセスをすぐに停止。
# kill -QUIT $(cat /var/run/nginx.pid)

この時点では、PIDファイルが /var/run/nginx.pid.oldbin のままなので、古いマスタープロセスに設定を再読み込みさせ、PIDファイルを元に戻すよう指示。
参考: Controlling nginx
# kill -HUP $(cat /var/run/nginx.pid.oldbin)

バイナリを元に戻す。
# mv /usr/local/sbin/nginx.old /usr/local/sbin/nginx

これで元に戻っているはず。

分からなくなったらnginxを再起動する。
(この場合はダウンタイムが発生する)
# service nginx restart



▼ 関連記事