为什么nginx 的日志就无法正常执行logrotate呢?nginx的logrotate配置没问题,手动的执行logrotate也没有问题,而且crond程序每天也都正常执行,包括系统本身的日志都正常轮替? 诡异……

问题

最近遇到一个很诡异的问题,nginx 的日志使用logrotate 不按计划执行。多次排查,经过几天的观察也无效果。

分析

先分成几步吧。一步一步找问题。我先列出以下几个点:

Nginx日志logrotate(轮替)

Nginx的日志,默认使用rpm或yum安装的情况下,是自动会添加logrotate文件的,我们不用特意的配置。但在编译的nginx中,我们需要手动添加logrotate文件的,不然日志是无法切割的。

运行机制

首先,系统在启动时,会自动加载运行crond,默认系统也已经安装了logrotate 程序。它会根据设置对指定的文件进行轮替切割。即 crond + logrotate 实现了日志的自动轮转,不用我们写脚本控制日志的轮替了。

默认的cron 配置位于 /etc目录下:

ls /etc/cron*
cron.d/       cron.daily/   cron.deny     cron.hourly/  cron.monthly/ crontab       cron.weekly/  

从上面文件夹的名字可以看出,默认的cron 计划可以按天、小时、周、月来执行。

logrotate配置

默认logrotate的cron文件位于/etc/cron.daily/logrotate 下。即每天执行。

可以看下文件内容:

#!/bin/sh

/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0

其中需要注意的是,上面的这个文件/etc/logrotate.conf,该文件是logrotate服务的主配置文件。

我们看下该文件的内容:

# see "man logrotate" for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
dateext

# uncomment this if you want your log files compressed
#compress

# RPM packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp and btmp -- we'll rotate them here
/var/log/wtmp {
    monthly
    create 0664 root utmp
	minsize 1M
    rotate 1
}

/var/log/btmp {
    missingok
    monthly
    create 0600 root utmp
    rotate 1
}

# system-specific logs may be also be configured here.

然后我们可以关注下 include 指令,这里是rpm安装包安装完后,会把自己的logrotate配置放入到该子目录中,是不是很像nginx的虚拟主机配置?

这个文件是默认的配置,当然我们可以在子文件中,添加相关的选项覆盖默认的配置。

Nginx logrotate配置

我们看下/etc/logrotate.d/nginx 的内容:

"/data/nginx/logs/*.log"
{
    daily
    rotate 7
    missingok
    dateext
    compress
    delaycompress
    sharedscripts
    create 0640 nginx nginx
    postrotate
        if [ -f /usr/local/nginx/logs/nginx.pid ]; then kill -USR1 `cat /usr/local/nginx/logs/nginx.pid` ; fi
    endscript
}

里面的指令网上一查就知道了,不过这里还是简单说明记录下。

daily			每日执行
rotate			保留轮替的7份,先进先出,超过7就删除之前的文件
missingok		忽略错误
dateext			定义日期后缀格式,这里也可以修改默认值*dateext format -%Y-%m-%D*
compress		是否压缩
delaycompress	延迟压缩
create			定义新日志文件的属性
postrotate		轮转日志文件之后执行的脚本
sharedscripts	如果该文件中定义了多个文件需要logrotate,那么该指令只会执行一次postrotate脚本
测试验证

可以通过手动执行验证logrotate配置

logrotate -d /etc/logrotate.conf

-d: 开启debug模式,不会实际执行轮转文件

在这里都是没有问题的,在第二天观察的时候,发现还是不行。然后又修修补补,各种改nginx的logrotate文件,发现还是各种不行。

检查selinux

在和朋友聊了这么诡异的问题之后,突然聊到了selinux, 对,就是我们在接触linux时,一般各种文档都会提示禁用selinux.

检查下是否开启了selinux:

# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Max kernel policy version:      31

果然确实是开启的状态。看下审计日志(拒绝和关联的系统调用记录到/var/log/audit/audit.log)

grep nginx /var/log/audit/audit.log | grep logs

输出如下面的内容:

type=AVC msg=audit(1574651401.550:22184): avc:  denied  { getattr } for  pid=26928 comm="logrotate" path="/data/nginx/logs/access.log" dev="sda3" ino=68228788 scontext=system_u:system_r:logrotate_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0
type=AVC msg=audit(1574651401.567:22185): avc:  denied  { getattr } for  pid=26928 comm="logrotate" path="/data/nginx/logs/access.log" dev="sda3" ino=68228788 scontext=system_u:system_r:logrotate_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0

可以把上面的时间戳复制下,通过以下命令看下具体发生的时间:

date -d "@1574651401.567"

调试下selinux,安装selinux tools 工具:

yum install policycoreutils-python -y

我们使用刚安装好的selinux工具,audit2allow 可以从拒绝的操作的日志中生成SELinux策略允许规则。

grep nginx /var/log/audit/audit.log | audit2allow -m nginx -a

或者直接使用以下命令,可以看到系统目前出现过的audit日志

audit2allow -w -a

我们这里先直接关闭selinux吧。验证下。

setenforce 0

永久关闭selinux,需要修改selinux的配置文件/etc/sysconfig/selinux:

sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux

验证

第二天再看下,日志已经成功轮替了,确认了下就是selinux的“锅”. 另外文章底部的参考链接有开启selinux让日志logrotate正常 工作的方式。有需要的可以看下。

参考链接

audit2allow

nginx-log-rotation-without-disabling-selinux

nginx-selinux-configuration