CentOS Stream 9でNginxをソースからビルドしてdnf版からダウンタイムなしで切り替える
本番環境でNginxをソースからビルドしたときの覚書。
Gemini 2.5 Proと対話。
環境: CentOS Stream 9
1. 事前準備
dnf版Nginxのビルドオプションを確認。
改行して見やすくする。
# nginx -V 2>&1 | sed 's/ --/\n--/g'
nginx version: nginx/1.20.1built by gcc 11.5.0 20240719 (Red Hat 11.5.0-7) (GCC)built with OpenSSL 3.5.0 8 Apr 2025 (running with OpenSSL 3.5.1 1 Jul 2025)TLS SNI support enabledconfigure arguments:--prefix=/usr/share/nginx--sbin-path=/usr/sbin/nginx--modules-path=/usr/lib64/nginx/modules--conf-path=/etc/nginx/nginx.conf--error-log-path=/var/log/nginx/error.log--http-log-path=/var/log/nginx/access.log--http-client-body-temp-path=/var/lib/nginx/tmp/client_body--http-proxy-temp-path=/var/lib/nginx/tmp/proxy--http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi--http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi--http-scgi-temp-path=/var/lib/nginx/tmp/scgi--pid-path=/run/nginx.pid--lock-path=/run/lock/subsys/nginx--user=nginx--group=nginx--with-compat--with-debug--with-file-aio--with-http_addition_module--with-http_auth_request_module--with-http_dav_module--with-http_degradation_module--with-http_flv_module--with-http_gunzip_module--with-http_gzip_static_module--with-http_image_filter_module=dynamic--with-http_mp4_module--with-http_perl_module=dynamic--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--with-http_v2_module--with-http_xslt_module=dynamic--with-mail=dynamic--with-mail_ssl_module--with-pcre--with-pcre-jit--with-stream=dynamic--with-stream_ssl_module--with-stream_ssl_preread_module--with-threads--with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection'--with-ld-opt='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,-E'
必要なライブラリをインストール。
参考: nginx/nginx: The official NGINX Open Source repository.
# dnf install gcc make zlib-devel pcre2-devel openssl-devel libmaxminddb-devel redhat-rpm-config
redhat-rpm-configは--with-cc-optと--with-ld-optで指定されているspecファイルをインストールするため。
2. Nginxをソースからビルド
前に開発環境で行った記事を参考に。
参考: CentOS Stream 10でNginxをソースからビルド
dnfでインストールしたときのビルドオプションにある「--with-cc-opt」と「--with-ld-opt」は積極的に使うべきとGemini先生が言うので、同じ値を設定してビルドした。
./configure \--prefix=/etc/nginx \--conf-path=/etc/nginx/nginx.conf \--sbin-path=/usr/sbin/nginx \--pid-path=/run/nginx.pid \--lock-path=/run/lock/subsys/nginx \--error-log-path=/var/log/nginx/error.log \--http-log-path=/var/log/nginx/access.log \--user=nginx \--group=www \--with-compat \--with-pcre \--with-file-aio \--http-client-body-temp-path=/var/lib/nginx/tmp/client_body_temp \--http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi_temp \--http-proxy-temp-path=/var/lib/nginx/tmp/proxy_temp \--http-scgi-temp-path=/var/lib/nginx/tmp/scgi_temp \--http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi_temp \--with-http_v2_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-module=./ngx_http_geoip2_module \--add-module=./nginx-module-vts \--add-module=./ngx_cache_purge \--with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' \--with-ld-opt='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,-E'
ビルドして確認する。
# make
# objs/nginx -V
nginx version: nginx/1.28.0built by gcc 11.5.0 20240719 (Red Hat 11.5.0-11) (GCC)built with OpenSSL 3.5.1 1 Jul 2025TLS SNI support enabledconfigure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/sbin/nginx --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --user=nginx --group=www --with-compat --with-pcre --with-file-aio --http-client-body-temp-path=/var/lib/nginx/tmp/client_body_temp --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi_temp --http-proxy-temp-path=/var/lib/nginx/tmp/proxy_temp --http-scgi-temp-path=/var/lib/nginx/tmp/scgi_temp --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi_temp --with-http_v2_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-module=./ngx_http_geoip2_module --add-module=./nginx-module-vts --add-module=./ngx_cache_purge --with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' --with-ld-opt='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,-E'
ちなみにFreeBSDの場合は、Ports Collectionが自動設定してくれるので./configureを実行する必要がない。
Ports Collectionがどのようなオプションを設定しているかを確認するコマンド。
# cd /usr/ports/www/nginx
# make -V CFLAGS
-O2 -pipe -I/usr/local/include -fstack-protector-strong -fno-strict-aliasing
# make -V LDFLAGS
-Wl,-rpath,/usr/local/lib
FreeBSDではOS全体として統合・設計されているので、サードパーティ製ソフトはPortsで管理するのが流儀。
(逆に言うとPortsで管理できないものはインストールすべきではない)
3. dnfでインストールしたNginxとダウンタイムなしで切り替える
別コンソールを開いてhtopを起動して「nginx」でフィルタ表示する。
# htop
現在稼働中のPIDと一致するか確認。
# cat /run/nginx.pid
設定ファイルとNginxバイナリのバックアップ。
aオプション(archiveモード)は権限とディレクトリ構成をできる限り維持する。
# cp -a /etc/nginx /etc/nginx.20250907
# mv /usr/sbin/nginx /usr/sbin/nginx.old
新しいNginxバイナリの配置。
# make install
設定ファイルの構文チェック。
# nginx -t
新しいNginxマスタープロセスを起動 (USR2シグナル)。
参考: Controlling nginx
# kill -USR2 $(cat /run/nginx.pid)
htopでマスタープロセスが増えているのを確認。
nginx.pid.oldbinがあるか確認して、古いプロセスを段階的に終了させる。
(WINCH = Window Change)
# ls /run/
# kill -WINCH $(cat /run/nginx.pid.oldbin)
古いワーカープロセスはすべて終了したのをhtopで確認。
最後に古いマスタープロセス自体を終了させる。
# kill -QUIT $(cat /run/nginx.pid.oldbin)
/run/には新しいPIDのnginx.pidしか残っていないはず。
# ls /run/
# cat /run/nginx.pid
ブラウザでサイトが表示できるか確認する。
wp-login.phpにアクセスすると不安定になっていて再起動したら改善されたので、結局アクセスがあまりない時間帯に実行するのが良さそう。
# reboot
4. dnfでインストールNginxをアンインストール
DNFパッケージの削除する前にサービスファイルの内容をコピーしておく。
# systemctl status nginx
# less /usr/lib/systemd/system/nginx.service
dnf版Nginxをアンインストール。# dnf remove nginx
/usr/sbin/nginxが削除された
(Gemini先生は削除されないと言ったのに…)。
慌てて再度make installを実行してNginx起動。
# cd /usr/local/src/
# make install
# cd /etc/nginx
# cp ../nginx.20250907/nginx.conf ./
# nginx -t
# vi /etc/systemd/system/nginx.service
# systemctl daemon-reload
# systemctl enable nginx
# systemctl start nginx
# systemctl status nginx
次回dnfから切り替えるときには気を付ける。
後片付け。
# rm /usr/sbin/nginx.old
# rm -rf /etc/nginx.20250907/
.rpmsaveはNginxパッケージをアンインストールした際に生成されるバックアップファイルなので削除する。
# ls /etc/nginx/*.rpmsave
# rm -f /etc/nginx/*.rpmsave
.rpmnewもdnf経由でインストールしたときに生成されるファイルなので削除する。
一応差分を表示して確認する。
# diff /etc/nginx/nginx.conf.default /etc/nginx/nginx.conf.rpmnew
# rm /etc/nginx/nginx.conf.rpmnew
5. ログローテーション設定
dnfアンインストールする前にログローテーション設定をメモるのを忘れていた。
Gemini先生に設定ファイルを生成してもらう。
# vi /etc/logrotate.d/nginx
# Nginxのログファイル(/var/log/nginx/ 内の .log で終わるファイル)を対象とする/var/log/nginx/*.log {# -----------------------------# ▼ ローテーションの頻度と世代管理# -----------------------------# 週に一度、ログファイルをローテーションするweekly# 104世代分の古いログファイルを保持する (週次 x 104世代 ≒ 2年分)rotate 104# -----------------------------# ▼ ファイルの圧縮と作成# -----------------------------# 古いログファイルをgzipで圧縮するcompress# 直近の1世代だけは圧縮せず、次回のローテーション時に圧縮する# (例: access.log.1 は圧縮せず、access.log.2.gz から圧縮される)delaycompress# ローテーション後、新しい空のログファイルを作成する# パーミッション: 640 (-rw-r-----)# 所有ユーザー: nginx# 所有グループ: wwwcreate 0640 nginx www# -----------------------------# ▼ 実行条件と後処理# -----------------------------# ログファイルが空の場合は、ローテーションしないnotifempty# ログファイルが見つからなくてもエラーにしないmissingok# postrotateスクリプトを、対象ファイル(access.log, error.logなど)ごとに毎回実行せず、一度だけ実行するsharedscripts# ローテーション処理が完了した後に、以下のスクリプトを実行するpostrotate# NginxのプロセスIDファイルが存在する場合のみ、中のコマンドを実行if [ -f /run/nginx.pid ]; then# 稼働中のNginxプロセスにUSR1シグナルを送り、ログファイルを再オープンさせる# これをしないと、Nginxは名前が変わった古いファイルにログを書き込み続けてしまう/bin/kill -USR1 `cat /run/nginx.pid`fiendscript}
動作テスト。
# logrotate --debug /etc/logrotate.d/nginx