Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么不要用systemctl reload nginx重载nginx配置 #27

Open
san3Xian opened this issue Jul 10, 2020 · 5 comments
Open

为什么不要用systemctl reload nginx重载nginx配置 #27

san3Xian opened this issue Jul 10, 2020 · 5 comments

Comments

@san3Xian
Copy link
Owner

san3Xian commented Jul 10, 2020

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参数值吧

@san3Xian
Copy link
Owner Author

又是一篇大水文

@san3Xian
Copy link
Owner Author

(这就好像经常有人跟我说重启节点不要直接用reboot要用init 6)
(因为一个是"优雅地重启",一个粗暴啊)

@SkrShadow
Copy link

吓我一跳,今天刚用systemctl reload重载的nginx(

@Xingeqwd
Copy link

还有这事啊,怪不得之前reload没用,咳咳,应该是我的service文件里没有reload的指令,咳咳,用这个-s也不错

@CoelacanthusHex
Copy link

debian 最遲可以追溯到11年前就已經採用 nginx -s reload 了。
https://salsa.debian.org/nginx-team/nginx/-/commit/04d69c73f404049e7132592b0f412c888a4b683f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants