Back
Please upgrade your browser or check your network connection.

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

重载配置文件

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

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

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

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

先启动Nginx。

1自己进目录
2
3sudo ./nginx  ##不用sudo提权可能监听不了默认的80端口

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

1ps -ef | grep nginx

修改配置文件

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

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

1#gzip  on;
2
3↓↓↓↓↓
4
5gzip on;

检查配置文件是否正确

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

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

1sudo ./nginx -t

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

有报错,自己Google+百度。

加载新的配置文件

1sudo ./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[root@centos7 sbin]# ./nginx -V
 2nginx version: nginx/1.12.2
 3built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
 4configure arguments: --prefix=/root/nginx/
 5
 6[root@centos7 sbin]# ./nginx
 7
 8[root@centos7 sbin]# ps -ef | grep nginx
 9root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
10nobody   12295 12294  0 08:38 ?        00:00:00 nginx: worker process
11root     12297  6498  0 08:38 pts/0    00:00:00 grep --color=auto nginx

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


现在备份旧的Nginx

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

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

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


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

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

1[root@centos7 objs]# ./nginx  -V
2nginx version: nginx/1.14.2
3built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
4configure arguments: --prefix=/root/nginx/
5
6[root@centos7 objs]# cp ./nginx /root/nginx/sbin/
7
8[root@centos7 objs]# ls /root/nginx/sbin/
9nginx  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。可以让你知道这个进程是那一个进程启动的。混乱?没关系,看下面,有例子。

 1Nginx-worker是由Nginx-master启动的,再看看仔细看看下面的PID和PPID,体会下。
 2
 3[root@centos7 objs]# ps -ef | grep nginx
 4UID      PID   PPID #这一行我手动加的,实际上执行该命令,没这行
 5root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
 6nobody   12295 12294  0 08:38 ?        00:00:00 nginx: worker process
 7root     14807  6498  0 08:50 pts/0    00:00:00 grep --color=auto nginx
 8
 9
10[root@centos7 objs]# kill -USR2 12294 #发送信号给Nginx的master进程(老的)
11
12从下面的输出,我们可以看出新的master是由老的master启动的。
13
14然后新的master又启动了一个属于自己的worker。
15
16[root@centos7 objs]# ps -ef | grep nginx
17UID      PID   PPID #这一行我手动加的,实际上执行该命令,没这行
18root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
19nobody   12295 12294  0 08:38 ?        00:00:00 nginx: worker process
20root     14809 12294  0 08:51 ?        00:00:00 nginx: master process ./nginx
21nobody   14810 14809  0 08:51 ?        00:00:00 nginx: worker process
22root     14812  6498  0 08:51 pts/0    00:00:00 grep --color=auto nginx

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

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


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

 1[root@centos7 objs]# ps -ef | grep nginx
 2root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
 3nobody   12295 12294  0 08:38 ?        00:00:00 nginx: worker process #将会被退出
 4root     14809 12294  0 08:51 ?        00:00:00 nginx: master process ./nginx
 5nobody   14810 14809  0 08:51 ?        00:00:00 nginx: worker process
 6root     14835  6498  0 09:17 pts/0    00:00:00 grep --color=auto nginx
 7
 8
 9[root@centos7 objs]# kill -WINCH 12294 #发送信号给Nginx的master进程(老的)
10
11
12[root@centos7 objs]# ps -ef | grep nginx
13root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
14root     14809 12294  0 08:51 ?        00:00:00 nginx: master process ./nginx
15nobody   14810 14809  0 08:51 ?        00:00:00 nginx: worker process
16root     14837  6498  0 09:18 pts/0    00:00:00 grep --color=auto nginx

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

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

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

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

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


版本回退

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


方案一(推荐)

  1. 给老的master发送HUP信号

  2. 给新的master发送QUIT信号

 1[root@centos7 objs]# ps -ef | grep nginx
 2root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
 3root     14809 12294  0 08:51 ?        00:00:00 nginx: master process ./nginx
 4nobody   14810 14809  0 08:51 ?        00:00:00 nginx: worker process
 5root     14866  6498  0 09:35 pts/0    00:00:00 grep --color=auto nginx
 6
 7[root@centos7 objs]# kill -HUP 12294 #重新读取配置文件,启动新的worker,并且优雅地退出老的worker(虽然这里没有什么worker可以让他退罢了)
 8
 9HUP信号相当于reload
10
11[root@centos7 objs]# ps -ef | grep nginx
12root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
13root     14809 12294  0 08:51 ?        00:00:00 nginx: master process ./nginx
14nobody   14810 14809  0 08:51 ?        00:00:00 nginx: worker process
15nobody   14867 12294  0 09:36 ?        00:00:00 nginx: worker process
16root     14869  6498  0 09:36 pts/0    00:00:00 grep --color=auto nginx
17
18[root@centos7 objs]# kill -QUIT 14809 #优雅地关闭整个服务
19
20[root@centos7 objs]# ps -ef | grep nginx
21root     12294     1  0 08:38 ?        00:00:00 nginx: master process ./nginx
22nobody   14867 12294  0 09:36 ?        00:00:00 nginx: worker process
23root     14872  6498  0 09:37 pts/0    00:00:00 grep --color=auto nginx
24


方案二

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

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

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

日志切割

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

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

 1[root@centos7 logs]# ll
 2total 24
 3-rw-r--r--. 1 root root 4691 Feb 20 10:03 access.log
 4-rw-r--r--. 1 root root 6114 Feb 20 10:03 error.log
 5-rw-r--r--. 1 root root    6 Feb 20 09:49 nginx.pid
 6-rw-r--r--. 1 root root    6 Feb 20 08:38 nginx.pid.oldbin
 7
 8[root@centos7 logs]# tail -2 access.log
 9192.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"
10192.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"
11
12[root@centos7 logs]# tail -2 error.log
132019/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"
142019/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[root@centos7 logs]# ls
2access.log  error.log  nginx.pid  nginx.pid.oldbin
3
4[root@centos7 logs]# mv access.log access.log$(date +"%Y-%m-%dT%H:%M:%SZ")
5
6[root@centos7 logs]# ls
7access.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[root@centos7 sbin]# ./nginx -s reopen
2[root@centos7 sbin]# cd ../logs/
3[root@centos7 logs]# ls
4access.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$ mv access.log access.log.old
2$ kill -USR1 master.nginx.pid 或者 nginx -s reopen
3$ sleep 1 #务必
4$ 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[root@centos7 ~]# touch test
2[root@centos7 ~]# ls -i test
334179485 test
4[root@centos7 ~]# mv test test2
5[root@centos7 ~]# ls -i test2
634179485 test2

当Nginx使用reopen的时候,

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

  2. 没有,创一个

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

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

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