玩转 Linux 之:由 Nginx log rotation 聊聊 mv 的妙用

1、Nginx 下如何正确的做日志切分

今天发现有个 Nginx 日志 rotation 出来大小是 0,很奇怪,按公司的业务场景来说,这是不可能的。

瞅了下前同事留下来的 rotation 脚本,看到了这么两行,也是他们当时 rotation 的方案:

cp tracklog.ooxx.com.access.log ${log_date_dir}/tracklog.ooxx.com.access.log.${log_name_date}
> tracklog.ooxx.com.access.log

然后这个脚本会由 crontab 每 10min 定时的调度一次,每次都将日志通过 cp 截取出来,放到以时间后缀命名的文件中去,

然后清空当前文件,如此循环反复。

初看没问题,但仔细想想,这里面会存在丢数据的问题:从 cp 到 > 清空文件这段时间

丢失数据的多少会依赖你业务的并发程度、当时机器负载、cp 文件的大小共同决定。

那这个问题怎么解决呢?

由于 Nginx 自身并没有提供日志管理功能,但是它提供了一个简单的 log rotation 机制:

mv access.log access.log.0
kill -USR1 `cat master.nginx.pid`
sleep 1
gzip access.log.0    # do something with access.log.0

下面我来详细解释下上面 4 行代码的含义:

(1)首先我们不是 cp,而是 mv 当前日志成为一个归档文件(最好加上时间后缀),此时文件 access.log 的 inode 并未改变,对于 Linux 进程来说,mv 并没有使文件发生变化,而进程是按 inode 追踪文件的,而不是文件名。(关于这点请看文末的 Refer(1))。所以直到 mv 完成以及 mv 完成后, nginx 进程会一直继续读写 access.log.0 文件(其实是 access.log,不是吗?)。

(2)Nginx 内部定义了 USR1 信号,这个信号和我们所用的 kill -9 信号类似的地方在于,他们都属于 linux 信号的一种,你可以 kill -l 查看全部信号的定义,各个发行版的定义会有些许差异;不同的地方在于,-9 属于系统级别的,而 USR1 属于应用软件级别的,开发者自定义的,至于程序收到 -USR1 信号后会干什么事,开发者自己说了算,这点和 -9 这种系统级别的信号不同。所以在这里,kill -USR1 不会杀死 Nginx 进程,而 Nginx Master 进程收到  -USR1 信号后,会重新打开名为 access.log 的日志文件,由于 access.log 已经不存在了,那么 access.log 会重新建立一个这样的文件,并开始往里读写数据,也就是说读写从 access.log.0 又切回到 access.log 了,这样新的请求日志会被重新开始记录了,而这个过程是没有任何读写中断,数据丢失的。

或者平滑重启:/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reload

// 运行 nginx -s reload 命令,就会检查磁盘上的配置文件,并给主进程发送一个SIGHUP信号。

// 一旦主进程接受到一个SIGHUP,它会做两件事:

// (1) 重载配置文件、创建一组新的工作进程,新创建的工作进程立即接受连接、处理网络通信( 采用新的配置环境)。

// (2) 通知旧的工作进程优雅地推出,这些工作进程停止接受新连接。一旦当前处理的HTTP请求结束,工作进程会关闭连接。一旦所有连接关闭,工作进程就会退出。

(3)这句的意思是给一定的时间让上一步的读写切换顺利完成,以便可以进行后续的操作,对归档日志进行处理。比如有可能数据还在 nginx 的 buffer 中,没有及时写入  access.log.0 或是系统负载、IO 很高,没有及时响应切换,此时若强行对归档日志  access.log.0 进行处理,则会有数据丢失的风险。

(4)这句代码就是用户后续对归档日志  access.log.0 进行处理了,你可以 gzip、rsync 等等,随你怎么玩吧。

别看这个 rotation 过程只有 4 句简单的代码,但是它里面涉及的知识还是不少的,很多同学貌似都不理解这段过程究竟发生了什么。希望我的这段解说能让你明白这其中的来龙去脉。

文初提到的问题到这里算是有了一个简单、完美的解决方案,其实你也可以用一些三方的工具来做 log rotation,比如 logrotate,但是我觉得就我的这个按时间切分日志的需求来说,上面的 4 行代码已足够简洁了,不是么?

2、文初的问题:为什么用 mv 而不是 cp 呢?

其实我在文中已有阐述,如果你还没明白,建议读读这篇《理解 linux inode》,简单的说就是 mv 没有改变源文件的任何内容以及 inode 属性,也没有创建新文件,这样也就没有中断 nginx 进程对日志文件的读写,自然就不会有丢数据的风险了。而 cp 的话,是新建了一个目标空文件,然后去往里拷贝数据,这是需要时间的,而且无法保证对一个正在读写的文件拷贝数据的完整性。当你重定向清空源文件的时候,数据就会不同程度的丢失了。so,这里用 mv 而不是 cp,至此我想你也应当明白 cp 与 mv 的区别了。

3、最后的问题:文件对于进程的 WYSIWYG 问题

我现在有个文件 ooxx.log,每十分钟生成一个,同时有个 agent 监控进程每秒扫描一次,一旦文件出现了,立即开始传输(空文件和重复文件不会传输)。

那么现在的问题是:如何保证 agent 传输文件数据的完整性呢?

有如下几种方案:

(1)你可以创建文件锁 file.lock,生成完文件后删除,同时改 agent 源码,检测锁是否存在 (2)agent 中调用 lsof、fuse 等系统级调用检测是否还有文件句柄、文件锁未释放 (3)agent 编程语言的内置 API,比如排它锁或者锁检测机制 (4)先写 .tmp 文件,然后 mv

(5)...

你会选择哪种方案?同时各种方案它们有什么优劣呢?

这个问题就先暂且留作思考题吧~

4、SIGHUP 信号的意义与作用

刚有同学问我 SIGHUP 和 kill -9 的区别,后者属于强杀进程,可能内存中的进程数据还没持久化就结束进程,带来的危害是数据丢失,有点类似 windows 下面通过任务管理器终止进程。下面具体聊聊 SIGHUP 的问题。

The SIGHUP (“hang-up”) signal is used to report that the user's terminal is disconnected, perhaps because a network or telephone connection was broken.  SIGHUP的含义是连接断开,系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号(拿 sigaction 或者 signal 函数去注册信号处理器,或者拿 sigprocmask 之类的把信号给屏蔽掉),当收到该信号时,进程就会退出。 初衷是为了在终端挂断时告诉终端控制进程这个事件,而在守护进程中,通常用来重读配置文件。为了避免混淆,更多的情况是使用 SIGUSR1 和 SIGUSR2 来自定义实现不重启、终止进程而重新加载配置,或者其它功能。 因此如果程序中没有捕捉并处理这个信号,系统默认的动作是杀掉进程。大部分 daemon 进程都会捕捉并处理这个信号,重新读入配置文件。其它的程序没有处理,就被杀掉了。 SIGHUP会在以下3种情况下被发送给相应的进程: 1、终端关闭时,该信号被发送到session首进程以及作为job提交的进程(即用 & 符号提交的进程) 2、session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程 3、若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGSTOP或SIGTSTP信号),该信号会被发送到该进程组中的每一个进程。

4、kill -1,-1 对应的 signal 是 SIGHUP,SIGHUP对daemon是重新读取配置,对普通进程就是杀掉。一般的守护进程都会在收到这个信号时重新加载配置(本质上由开发者决定),因为 SIGHUP 本来的意义对守信进程没有意义(SIGHUP 是当控制终端失去连接时触发的信号,而守护进程没有控制终端,所以根本用不上)。 具体可以参考 nohup(1)

举例说明: 比如用户修改了 apache 配置文件,想不重起 apache 就让配置生效,可以往 apache 进程发一个 HUP 信号:killall -HUP httpd

或者 inetd 配置文件是 /etc/inetd.conf, 如果想inetd 去重读文件系统的话,可以给它发一个SIGHUP 信号。 再例如, 当你ssh到一台机器, 然后开个vim, 当你关闭这个 ssh 会话的时候 vim 就会收到 SIGHUP 信号,然后 vim 就会被终止。

参考:https://www.freebsd.org/doc/zh_CN/books/handbook/basics-daemons.html

http://httpd.apache.org/docs/2.2/stopping.html

Refer:

1、理解 linux inode

http://hi.baidu.com/leejun_2005/item/7893859e427ec9d91f427182

2、Nginx wiki:Log Rotation

http://wiki.nginx.org/LogRotation

3、How To Configure Logging and Log Rotation in Nginx on an Ubuntu VPS

https://www.digitalocean.com/community/articles/how-to-configure-logging-and-log-rotation-in-nginx-on-an-ubuntu-vps

4、linux 系统监控、诊断工具之 lsof 用法简介

http://my.oschina.net/leejun2005/blog/153584

5、python 在删除文件的时候检测该文件是否被其他线程或者进程占用?

http://segmentfault.com/q/1010000000367133

6、Linux 文件系统的实现

http://segmentfault.com/a/1190000000419225

7、进程间共享inode相互影响简单分析 

http://blog.sina.com.cn/s/blog_e59371cc0102ux75.html

8、被遗忘的Logrotate

http://blogread.cn/it/article/6354?f=wb

9、linux下cp,mv进行动态库覆盖问题分析

http://blogread.cn/it/article/6809?f=wb

10、Rotating MySQL slow logs safely

https://www.percona.com/blog/2013/04/18/rotating-mysql-slow-logs-safely/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安全领域

使用JavaScript开发物联网设备也会非常安全

本文将引导你完成一个练习,向你展示如何在 IoTivity 安全框架上使用 Java 对 OCF 设备进行快速原型设计。

48110
来自专栏FreeBuf

如何突破Windows环境限制打开“命令提示符”

如今,许多企业或组织都会通过使用受限的windows环境来减少系统表面的漏洞。系统加固的越好,那么也就意味着能被访问和使用到的功能就越少。 我最近遇到的情况是,...

2906
来自专栏JMCui

Hybris安装和各个Extention简单介绍

前言:突然想好好梳理一下这几个月所学的内容了,顺便让自己的知识有一个系统的框架。这种安装仅仅适用于开发环境,不适于生产环境。 一、  安装JDK 请安装最新的O...

47611
来自专栏进击的君君的前端之路

在sublime中安装sass编译插件

打开sublime,在preference选项下有没有package control这个选项,如果没有的话,就表示你没有Package Control 插件(一...

951
来自专栏张善友的专栏

Windows PowerShell 工具

如果尚未开始使用 Windows PowerShell,很可能您很快就会用到它。Windows PowerShell 将成为 Windows Se...

2609
来自专栏zhisheng

全文搜索引擎 Elasticsearch 集群搭建入门教程

ElasticSearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful web 接口。Elasti...

1272
来自专栏Debian社区

改善Debian Linux软件包管理的七款工具

如果你运行Debian或它的衍生版本之一(比如Linux Mint或Ubuntu),迟早会遇到apt-get和dpkg,它们是主要的软件包管理命令。然而,这些只...

1353
来自专栏蓝天

“请求未在nginx中配置的域名时,给浏览器返回508错误码”配置示例

只需要增加如下一个Server配置,可直接放在其它所有Server配置之前: server {     listen 80 default_server; ...

874
来自专栏安恒信息

安全漏洞公告

1.Dell GoAhead Web Server 登录页表单拒绝服务漏洞 Dell GoAhead Web Server 登录页表单拒绝服务漏洞发布时间:20...

3375
来自专栏Java架构师历程

ZooKeeper基本原理

ZooKeeper是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。

1585

扫码关注云+社区