狐狸的小小窝

我是小狐狸~欢迎来我的小窝!
就是炫 狐狸小窝施工记

Logrotate 日志滚动 解决日志占用空间过大

起因

前几天发现自己的博客没有办法登录了,每次输完密码登录完都会重新跳回登录界面。一开始是怀疑登录的session出了什么问题,因为浏览器开隐身模式后就能登录进去。然而开隐身模式进后台的时候又会出现403错误,于是打算连进数据库把session删除再试试。

然而当我试图连接数据库时却一直提示失败,于是试图重启数据库服务,但是系统提示启动失败。电脑坏了怎么办?重启大法!然而数据库服务依旧无法启动,当时我也是摸不着头脑。明明以前一直好好的服务器怎么说炸就炸呢?

真相

后来我机智的看了下磁盘的占用情况:

df -hl

df -hl

然后我就震惊了!!震惊!过气博主硬盘为何被榨干!这背后究竟是人性的…(啪

经过一番折腾,终于找到了问题的所在:

du -sh .

df -hl

Nginx 的日志文件居然占用了14G的空间!Why?

论日志

日志文件通常情况下是未压缩的明文文本文件,在一个运行时间较长的系统中日志文件的体积会越来越大,甚至远超你的预期。

默认情况下一个服务的同一个日志流(如 Nginx 默认就有 error.log access.log 两个日志流)是输出到同一个日志文件中的,也就是说随着时间的推移,这一日志文件中包含的日志的天数也会越来越多,这就给查看某一特定时间段的日志带来了麻烦(试想一下在一个容量10G,包含了一整年数据的日志文件中查找5月份的日志信息,而且还没有日志辅助工具的帮助,是一件多么痛苦的事情)。并且如果日志文件占用了过大的存储空间,那么人们通常会优先删除最老的日志,保留最近一段时间的日志。如果所有日志信息都储存在同一个文件中,那么想要只删除部分数据也不是一件轻松的活。

再者,日志数据通常都是纯文本数据,并且内容重复度非常之高,如果能够将其压缩存储,那么会大大减小体积。然而日志系统是没有办法将数据追加到一个压缩过的日志文件中去的。如果要压缩日志,那么势必需要将日志数据划分成新、旧两部分,其中旧日志压缩后单独存放,新日志即是当前系统活动的日志文件,仍然是纯文本形式。

综上所述,一个好的日志系统至少需要满足以下几个需求:

  • 能够将日志按一定规律拆分成多个文件
  • 能够将非活动的日志压缩
  • 能够控制日志文件体积和数量,当条件满足时自动删除旧日志以释放空间
  • 日志文件名满足一定规律便于排序和筛选

以上的需求,通常称为日志滚动(必考)

日志滚动

道理我都懂,但是Linux的日志系统没有提供这些功能,总不能每天手动重命名、压缩、清空、删除日志吧?

天空一声巨响,英雄闪亮登场!这时候一个叫做 logrotate 的程序就派上了用处。

根据使用的系统不同,logrotate的安装方式也不尽相同,有些系统更是自带了 logrotate 并且带有很多默认的配置(比如我所使用的 Ubuntu Server)。

你可以在自己的系统上查看一下 /var/log/ 目录下的日志文件,如果见到了类似“服务名.log.0”、“服务名.log.1”等一大堆具有相同服务名称但是带有后缀序号的日志文件,那么可以99%肯定这个系统已经安装了 logrotate 并且能够工作。

logrotate

如果你的系统没有带logrotate,别担心,百度一下就能装上啦!而且配置巨简单!

在 /etc/logrotate.d/ 目录下(不同系统可能有所区别)通常会有很多配置文件,这些文件的名字通常对应了该文件所管辖的服务(文件名仅仅是便于管理,和实际内容没有必然关联)。

logrotate config

而这些配置文件的格式非常的简要,如图所示。通常第一行以目标日志文件路径起头,可以使用通配符,而后面的花括号内部则是需要应用于该日志文件的滚动规则。

其中几个常用的规则在这里稍微解释一下,通常就够用了。更详细的规则可以自行查找,不再赘述。


/var/log/nginx/*.log {
    daily  # 滚动频率,每天滚动一次。其它可选项为 hourly weekly monthly yearly
    missingok
    rotate 14  # 最多保留14次滚动,超过则删除最老的日志
    maxsize 100M  # 重要!活动日志的最大尺寸,超过该尺寸则强制滚动一次
    compress  # 重要!压缩不活动的日志,默认使用gzip
    delaycompress
    notifempty  # 如果日志为空则不滚动
    create 0640 www-data adm
    sharedscripts
    prerotate  # 在每次滚动前执行的脚本,通常是通知该服务“我要滚动日志啦!先别生成新日志咯!”
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
        run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate  # 在滚动完成后执行的脚本,通常是通知该服务“日志滚动完成啦!请把后续日志写到新的活动日志文件中去!”
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

如果发现日志滚动后 nginx 依旧在向 access.log.1 中写入日志,而新创建的 access.log 大小始终是0,那么请将上文中的 postrotate 代码改为:


service nginx rotate >/dev/null 2>&1

划重点

同学A:慢着!刚刚不是说你服务器跑的 Ubuntu Server 默认启用了 logrotate 么?那么为什么你的硬盘还是被这些日志文件吃了个精光呢?按说一共就保留14份日志,也就只有14天的份,还都是压缩过的,怎么会占用这么多呢?

我:谢尔曼M1,坐!

问题来了,这个 logrotate 的默认配置文件,少了 maxsize 100M 这一行,也就是说如果说在第二天的滚动开始前,日志就已经占用了过大的空间,以至于剩余空间不足以储存对应日志的压缩文件(因为得先压缩完才能删除原始文件,于是整个过程所需的最大空间是 原始文件体积+压缩后的体积,毕竟不可能一边压缩一边删除原始文件嘛)的话,整个日志滚动就会失败。而我的实际情况就是 access.log.0 文件占用了13G的空间(剩下的13个压缩后的日志一共才1G不到,完美的体现了压缩的好处),剩余的空间不足以对其进行压缩,于是整个日志滚动彻底失效了。

当然了,理想的情况下,如果能够将其设置成全部日志加起来的体积超过一个临界点(比如日志一共5G)那么就自动删除最老的一份日志,那是坠吼嘀!然而 logrotate 并没有提供这样的整体体积限制的功能,于是只好用一些小技巧。注意这两行:

rotate 14 # 最多保留14次滚动,超过则删除最老的日志
maxsize 100M # 重要!活动日志的最大尺寸,超过该尺寸则强制滚动一次

通过限制活动日志的体积(也就是尾号是0或者没有后缀的那个日志文件)和保留日志滚动数量,我们能够限制整体的日志大小,也即不超过 14 * 100M = 1.4G。当然这样的缺点就是如果某一天的日志特别多,那么那一天的日志会被拆分成多个(课外题:自行百度,将日志文件后缀设置为当前的日期时间)。

总结

不限制日志体积真是坑啊!

8 Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据