Nginx 命令行 / 重载配置文件 / 热部署 / 日志切割

Nginx 命令行

  1. 格式: nginx -c config_file 与平时的Linux命令没有什么两样。

  2. 帮助命令: -h / -? 没啥好说的,帮助命令你没有用过吗?

  3. nginx -c config_file 使用指定的配置文件

  4. nginx -c config_file -p prefix/for/relative/paths -p后面指定的是运行目录

  5. nginx -t 测试配置文件,但是不会立即生效。专门用来在应用新配置前的测试命令。

  6. -s 信号 -s拿来发送信号的,常见的信号参数:stop quit reload reopen

  7. nginx -s reload 不宕机,平滑地加载配置文件;在升级/修改配置的时候,平滑过度到新配置。

  8. nginx -s stop 立即停止Nginx服务

  9. nginx -s quit 潇洒地停止Nginx服务

  10. nginx -s reopen 重新开始记录Nginx日志

  11. nginx -g 覆盖默认的一些参数

  12. nginx -v -V 前一个是打印版本信息,后一个是打印编译信息

1
2
3
4
5
6
[jane@centos7 sbin]$ ./nginx -v
nginx version: nginx/1.14.2
[jane@centos7 sbin]$ ./nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
configure arguments: --prefix=/home/jane/preview

重载配置文件

简单总结一句,就是不宕机(不停止对外服务),重新加载配置文件以应用新的配置。

参照之前的编译安装文章,找到你的安装目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
preview/
├── conf
│ ├── fastcgi.conf
│ ├── fastcgi.conf.default
│ ├── fastcgi_params
│ ├── fastcgi_params.default
│ ├── koi-utf
│ ├── koi-win
│ ├── mime.types
│ ├── mime.types.default
│ ├── nginx.conf <<< nginx的默认配置文件位置
│ ├── nginx.conf.default
│ ├── scgi_params
│ ├── scgi_params.default
│ ├── uwsgi_params
│ ├── uwsgi_params.default
│ └── win-utf
├── html
│ ├── 50x.html
│ └── index.html
├── logs
└── sbin
└── nginx <<< 二进制执行文件

我是没有把Nginx加入到环境变量的,所以执行命令的时候都是在文件的目录里面执行的。./nginx这样的命令模式,在本篇中很常见。

先启动Nginx。

1
2
3
自己进目录

sudo ./nginx ##不用sudo提权可能监听不了默认的80端口

启动后,可以用下面的命令看看进程ID

1
ps -ef | grep nginx

修改配置文件

在你初次启动的时候,应该会在旁边生成很多目录的。只要根据上面的目录找到配置文件即可。

这里的演示就是去掉这里的注释以打开gzip压缩,稍作修改。

1
2
3
4
5
#gzip  on;

↓↓↓↓↓

gzip on;

检查配置文件是否正确

现在我就当你已经修改好了。

修改完之后,当然要检查你写的新配置对不对啦。

1
sudo ./nginx -t

只要没有报error的错误,有successful,基本就OK。

有报错,自己Google+百度。

加载新的配置文件

1
sudo ./nginx -s reload

没报错,就是加载成功了。

热部署

如果你对Nginx的信号集不了解,可以参考另外一篇文章看来–Nginx-master信号处理流程以及其信号集(热更新/日志切割)

版本升级,仅更换二进制文件。

!!!这个时候千万别搞什么新的配置文件来一起更新啊。

大致流程:

这里全部使用kill来发送命令到指定的进程。

  1. 备份旧的二进制文件

  2. 采用新的二进制文件覆盖旧的二进制文件

  3. 发送USR2老的master进程

  4. 发送WINCH老的master进程

  5. 发送QUIT老的master进程

  6. 后面有版本回退的操作演示


在这里,我准备了两个不同的Nginx版本。

一个是已经使用make install安装好的Nginx,版本为1.12.2

另一个是仅仅是编译好的Nginx,在objs目录放着,版本为1.14.2


!!!!! 注意: 新编译的Nginx的prefix目录参数,一定要与原来的一致。不然在你发送USR2的时候,你会发现新的master进程起不来。。。心酸泪。。。

可以在编译新版本之前,使用nginx -V,看看之前的编译参数。确保新编译的prefix参数与原来的一致。

不会编译的可以看—-编译适合自己的Nginx


目标是从运行中的Nginx 1.12.2,升级到 1.14.2,其中不停止对外服务。

现在把1.12.2的跑起来~~~

1
2
3
4
5
6
7
8
9
10
11
[root@centos7 sbin]# ./nginx -V
nginx version: nginx/1.12.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
configure arguments: --prefix=/root/nginx/

[root@centos7 sbin]# ./nginx

[root@centos7 sbin]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 12295 12294 0 08:38 ? 00:00:00 nginx: worker process
root 12297 6498 0 08:38 pts/0 00:00:00 grep --color=auto nginx

访问下该服务的端口,我的是默认的80端口。地址后面输个错的,可以看到服务器的版本号。


现在备份旧的Nginx

1
mv nginx nginx.old #我这里直接重命名了。

如果你担心这个操作对于现在的Nginx有影响,没关系,看到文章最后,有解释。

不过你要是知道inode,就不会有这个疑问了。2333


移动新版本1.14.2的二进制可执行的Nginx文件到老版本的Nginx那个目录。

要是你对目录结构不清楚,看这个 – 介绍各目录以及部分文件

1
2
3
4
5
6
7
8
9
[root@centos7 objs]# ./nginx  -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
configure arguments: --prefix=/root/nginx/

[root@centos7 objs]# cp ./nginx /root/nginx/sbin/

[root@centos7 objs]# ls /root/nginx/sbin/
nginx nginx.old

给进程中的Nginx-master发送信号USR2,告诉其要进行版本升级,热更新,热部署。

master进程首先重命名PID文件,在文件名后添加.oldbin后缀,比如nginx.pid会被重命名为nginx.pid.oldbin。

然后会新起一个master进程以及worker进程,使用了最新的Nginx进程。

这里稍微解释下PID和PPID(免得一些萌新懵逼):

  • 每个进程都会拥有 PID 和 PPID。

  • PID 就是现在进程的一个ID,你给他发送信号的时候就是用这ID。

  • PPID 这个就是启动它的程序的PID。可以让你知道这个进程是那一个进程启动的。混乱?没关系,看下面,有例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Nginx-worker是由Nginx-master启动的,再看看仔细看看下面的PID和PPID,体会下。

[root@centos7 objs]# ps -ef | grep nginx
UID PID PPID #这一行我手动加的,实际上执行该命令,没这行
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 12295 12294 0 08:38 ? 00:00:00 nginx: worker process
root 14807 6498 0 08:50 pts/0 00:00:00 grep --color=auto nginx


[root@centos7 objs]# kill -USR2 12294 #发送信号给Nginx的master进程(老的)

从下面的输出,我们可以看出新的master是由老的master启动的。

然后新的master又启动了一个属于自己的worker。

[root@centos7 objs]# ps -ef | grep nginx
UID PID PPID #这一行我手动加的,实际上执行该命令,没这行
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 12295 12294 0 08:38 ? 00:00:00 nginx: worker process
root 14809 12294 0 08:51 ? 00:00:00 nginx: master process ./nginx
nobody 14810 14809 0 08:51 ? 00:00:00 nginx: worker process
root 14812 6498 0 08:51 pts/0 00:00:00 grep --color=auto nginx

老的master/worker进程已经不再监听原来的端口了。

新的请求,新的连接,只会进入到新的Nginx里面。


发送信号 WINCH 给老的master进程,潇洒地关闭老的worker进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 12295 12294 0 08:38 ? 00:00:00 nginx: worker process #将会被退出
root 14809 12294 0 08:51 ? 00:00:00 nginx: master process ./nginx
nobody 14810 14809 0 08:51 ? 00:00:00 nginx: worker process
root 14835 6498 0 09:17 pts/0 00:00:00 grep --color=auto nginx


[root@centos7 objs]# kill -WINCH 12294 #发送信号给Nginx的master进程(老的)


[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
root 14809 12294 0 08:51 ? 00:00:00 nginx: master process ./nginx
nobody 14810 14809 0 08:51 ? 00:00:00 nginx: worker process
root 14837 6498 0 09:18 pts/0 00:00:00 grep --color=auto nginx

老的master进程,不会自己退出,留作版本回退。就怕你翻车23333

如果你觉得新版本OK,没有问题(建议跑上几天看看效果再说)。那么就可以退出老的master进程了。

发送 QUIT 信号,让他潇洒地走人。

1
kill -QUIT [Nginx-master-old-PID]
1
2
3
4
5
6
7
8
9
10
⮀ ps -ef | grep nginx
0 55268 1 0 9:00PM ?? 0:00.00 nginx: master process ./nginx
0 58796 55268 0 9:07PM ?? 0:00.00 nginx: master process ./nginx
-2 58801 58796 0 9:07PM ?? 0:00.00 nginx: worker process

⮀ sudo kill -QUIT 55268

⮀ ps -ef | grep nginx
0 58796 1 0 9:07PM ?? 0:00.00 nginx: master process ./nginx
-2 58801 58796 0 9:07PM ?? 0:00.00 nginx: worker process

不要在意这里的PID和上面的演示不连贯,原理都一样的。我好多机子的2333


版本回退

突然发现这个编译的版本翻车了,赶紧回滚,那该怎么做呢?


方案一(推荐)

  1. 给老的master发送HUP信号

  2. 给新的master发送QUIT信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
root 14809 12294 0 08:51 ? 00:00:00 nginx: master process ./nginx
nobody 14810 14809 0 08:51 ? 00:00:00 nginx: worker process
root 14866 6498 0 09:35 pts/0 00:00:00 grep --color=auto nginx

[root@centos7 objs]# kill -HUP 12294 #重新读取配置文件,启动新的worker,并且优雅地退出老的worker(虽然这里没有什么worker可以让他退罢了)

HUP信号相当于reload

[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
root 14809 12294 0 08:51 ? 00:00:00 nginx: master process ./nginx
nobody 14810 14809 0 08:51 ? 00:00:00 nginx: worker process
nobody 14867 12294 0 09:36 ? 00:00:00 nginx: worker process
root 14869 6498 0 09:36 pts/0 00:00:00 grep --color=auto nginx

[root@centos7 objs]# kill -QUIT 14809 #优雅地关闭整个服务

[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 14867 12294 0 09:36 ? 00:00:00 nginx: worker process
root 14872 6498 0 09:37 pts/0 00:00:00 grep --color=auto nginx


方案二

  1. 给新的master发送TERM信号或者INT信号或者QUIT信号

  2. 老的master进程,会自己启动worker

1
2
3
4
5
6
7
8
9
10
[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
root 14876 12294 0 09:45 ? 00:00:00 nginx: master process ./nginx
nobody 14877 14876 0 09:45 ? 00:00:00 nginx: worker process
root 14882 6498 0 09:45 pts/0 00:00:00 grep --color=auto nginx
[root@centos7 objs]# kill -QUIT 14876
[root@centos7 objs]# ps -ef | grep nginx
root 12294 1 0 08:38 ? 00:00:00 nginx: master process ./nginx
nobody 14883 12294 0 09:46 ? 00:00:00 nginx: worker process
root 14885 6498 0 09:46 pts/0 00:00:00 grep --color=auto nginx

日志切割

如果你对Nginx的信号集不了解,可以参考另外一篇文章看来–Nginx-master信号处理流程以及其信号集(热更新/日志切割)

做日志切割很正常啦,特别是部署在生产环境。基本上一天一割。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@centos7 logs]# ll
total 24
-rw-r--r--. 1 root root 4691 Feb 20 10:03 access.log
-rw-r--r--. 1 root root 6114 Feb 20 10:03 error.log
-rw-r--r--. 1 root root 6 Feb 20 09:49 nginx.pid
-rw-r--r--. 1 root root 6 Feb 20 08:38 nginx.pid.oldbin

[root@centos7 logs]# tail -2 access.log
192.168.1.116 - - [20/Feb/2019:10:03:58 -0500] "GET / HTTP/1.1" 403 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:66.0) Gecko/20100101 Firefox/66.0"
192.168.1.116 - - [20/Feb/2019:10:03:58 -0500] "GET / HTTP/1.1" 403 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:66.0) Gecko/20100101 Firefox/66.0"

[root@centos7 logs]# tail -2 error.log
2019/02/20 10:03:58 [error] 14902#0: *3 "/root/nginx//html/index.html" is forbidden (13: Permission denied), client: 192.168.1.116, server: localhost, request: "GET / HTTP/1.1", host: "192.168.1.119"
2019/02/20 10:03:58 [error] 14902#0: *3 "/root/nginx//html/index.html" is forbidden (13: Permission denied), client: 192.168.1.116, server: localhost, request: "GET / HTTP/1.1", host: "192.168.1.119"

  1. 使用mv直接重命名日志(不会影响现在Nginx的日志写入的)

千万别用cp,不然丢日志的2333

1
2
3
4
5
6
7
[root@centos7 logs]# ls
access.log error.log nginx.pid nginx.pid.oldbin

[root@centos7 logs]# mv access.log access.log$(date +"%Y-%m-%dT%H:%M:%SZ")

[root@centos7 logs]# ls
access.log2019-02-20T10:10:12Z error.log nginx.pid nginx.pid.oldbin

  1. 发送USR1信号Nginx-master

kill -USR1 master.nginx.pid

或者

nginx -s reopen

1
2
3
4
[root@centos7 sbin]# ./nginx -s reopen
[root@centos7 sbin]# cd ../logs/
[root@centos7 logs]# ls
access.log access.log2019-02-20T10:10:12Z error.log nginx.pid nginx.pid.oldbin

  1. 如果你需要对mv出来的日志进一步操作,务必等一秒以上。

很多人认为,一发送指令到worker,worker就会立即执行。实际上并不是的。造成这种错觉,完全是因为CPU的运行太特么快了。实际上,master发送指令到worker的时候,只是做了一些标记,而worker实际上非常之繁忙,这可能就是为啥Nginx那么高效。当他完成一轮的事件调度之后,才会真正去执行master下达的指令。特别是业务量巨大的时候。一旦业务量大,这个时间就能扩大很多倍啦。

所以,你切割的时候,虽然你下达了reopen的命令,实际上worker可能压根还没有执行呢。23333

然后你设定的脚本就对日志进行下一步操作了,这可能就会造成日志有损坏或者遗漏。

1
2
3
4
$ mv access.log access.log.old
$ kill -USR1 master.nginx.pid 或者 nginx -s reopen
$ sleep 1 #务必
$ gzip access.log.old # do something with access.log.old

为什么改名字不会影响正在写入的文件?

实际上,在Linux/Unix里面,文件名只是inode的一个绰号/别称,实际上是靠inode来识别文件的。

inode是指在许多“类Unix文件系统”中的一种数据结构。每个inode保存了文件系统中的一个文件系统对象(包括文件、目录、设备文件、socket、管道, 等等)的元信息数据,但不包括数据内容或者文件名[1]。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

正在写入的那些文件,系统内核其实已经拿到他的inode了,你改个名字压根是没有影响的。

1
2
3
4
5
6
[root@centos7 ~]# touch test
[root@centos7 ~]# ls -i test
34179485 test
[root@centos7 ~]# mv test test2
[root@centos7 ~]# ls -i test2
34179485 test2

当Nginx使用reopen的时候,

  1. 寻找指定位置有没有默认的日志文件

  2. 没有,创一个

  3. 有,调用系统的内核,拿他的inode值

  4. 然后Nginx的日志记录就全用上面inode值。

自己想一想,感觉有点绕。

0%