Nginx为什么不要直接用systemctl reload而是用nginx -s reload

来自三线的随记

subtitle

systemctl reload nginx 和 nginx 的差别

前言

经常有人跟我说不要用 systemctl reload nginx去重载nginx的配置,要用nginx -s reload

问他为什么他又说不出

(这就好像经常有人跟我说重启节点不要直接用reboot要用init 6)

不巧,今天在摸鱼的时候知道大概原因了

我又干了什么

今天在给一个nginx站点更新站点配置的时候对一个站点的conf文件进行了端口分离(原本这个文件里面有两段server配置,80和443)

然后我

cp default_80.conf default_443.conf

接着分别vim了两个配置文件删除了相应的不应该存在的server段

接着顺其自然地

systemctl reload nginx

shell 控制台没报错

继续对配置文件修修改改,猛然看到配置文件里面有这样一段指令

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

直觉这里必有妖,因为按照nginx性格,log_format 是不允许被重复定义的,否则会报致命错误

这里立刻去检查nginx error.log

[root@zsxs sites-enabled]# cat /var/log/nginx/error.log
2020/07/11 01:18:51 [emerg] 7467#0: duplicate "log_format" name "cms_upstream" in /etc/nginx/sites-enabled/default_443.conf:3

果然有emergency 错误,也就是换句话说,我的nginx配置是没有重载成功的,嗯?systemctl骗我??

谜底揭晓

众所周知,systemctl reload *** 是执行了对应***的service文件里面的指令

这里把有妖的nginx节点service文件cat出来看看,注意这里是Centos 7.7 + nginx 1.16.1

[root@zsxs nginx]# cat /etc/redhat-release 
CentOS Linux release 7.7.1908 (Core)

[root@zsxs nginx]# nginx -v
nginx version: nginx/1.16.1

[root@zsxs sites-enabled]# systemctl status nginx | grep Loaded
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)

[root@zsxs sites-enabled]# cat /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

可以看到systemctl reload nginx是执行了/bin/kill -s HUP $MAINPID命令

也就是向某个PID进程发送了SIGHUP信号

ps: 虽然SIGHUP信号很多文章都会说是终止进程信号,但是更为接近的说法应该是

(转)系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。

而根据其他博主文章记载,nginx里面定义了当进程收到SIGHUP信号以后,其并不会退出,而是执行 ngx_reconfigure 操作,也就是配置文件重载

而对于nginx配置是否成功重载,kill -s HUP的返回值都是0 (命令执行成功)

回到文章中,按道理 systemctl reload nginx 是能够报错的(我之前一直这样重载配置文件的..),不过我之前一直用的是ubuntu os,这里来看看ubuntu 16.04(nginx 1.16.1)是怎么写的

root@frpServer:~# cat /etc/issue
Ubuntu 16.04.1 LTS \n \l

root@frpServer:~# nginx -v
nginx version: nginx/1.16.1

root@frpServer:~# cat /lib/systemd/system/nginx.service
# Stop dance for nginx
# =======================
#
# ExecStop sends SIGSTOP (graceful stop) to the nginx process.
# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

划重点ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload

答案就在这了,ubuntu默认的nginx service文件和centos 里面的nginx service文件reload触发的命令是不一样的

ubuntu 16.04默认的nginx service中reload触发的操作是nginx -g 'daemon on; master_process on;' -s reload

也就是指定了daemon on; master_process on;指令的情况下去发送reload(SIGHUP)信号到nginx master进程

不同于用kill直接发送信号,nginx -s reload命令在nginx重载配置失败的时候,他的返回值是非0的

所以在某些节点上面使用systemctl reload nginx能够重载配置并且知道是否重载成功

本文到这也就差不多了,至于为什么centos和ubuntu里面的service文件有差异,有缘再说吧

然后按照个人习惯决定是否修改centos nginx service里面的ExecReload参数值吧