日志所引发的问题

虽然2021年的今天,磁盘几乎是最不值钱的设备,但对于创业公司来说也不能大规模无脑去购买磁盘资源。于是一个不慎就会面临磁盘爆了的问题。磁盘一旦爆了,什么网站应用,数据库服务这些都会停止运行。

第一次磁盘被塞爆,主要是日志文件过大,因为前期访问量不大就没太在意这个问题,后面访问量大了,日志的增长速度加快,导致几十G的磁盘空间一下子就被塞满了。笔者也曾有过双手颤抖,把磁盘扩容,让服务重新运行的经历,那十来分钟,堪称地狱。

日志切割与归档

Linux操作系统就有一个专门做日志切割与归档的工具logrotate。它的介绍如下

logrotate is designed to ease administration of systems that generate large numbers of log files. It allows automatic rotation, compression, removal, and mailing of log files. Each log file may be handled daily, weekly, monthly, or when it grows too large.

简言之,它的作用就是周期性帮助维护者把日志切割成小份,并进行归档。

形象点说就是,假设你的日志文件所在的路径是/var/log/rails_production.log。我们可以设置每天,每周甚至每个月对这个日志进行切割。也可以设置一个最大值,当日志文件超出我们所设置的阀值(假设说是1G)的时候,日志就会被切割,生成一个名为/var/log/rails_production.log.1的文件,这个文件主要存储一些比较老的日志。一段时间之后我们会得到一堆这样的日志

/var/log/rails_production.log(当前)
/var/log/rails_production.log.1
/var/log/rails_production.log.2
/var/log/rails_production.log.3
/var/log/rails_production.log.4

每生成一个/var/log/rails_production.log.X被称作一个循环。我们可以设置N个循环过后清理一遍日志。假设我们设置为5,那么我们其实最终只会保留/var/log/rails_production.log.[1~5]5个历史日志。更老的日志会自动被删除。

简单的配置分享

logrotate的配置相对比较简单,我只简单分享一下我们的配置

/var/www/yunchang/log/production.log {
  daily
  rotate 10
  postrotate
    kill -USR1 `cat /var/www/yunchang/tmp/pids/puma.pid`
  endscript
  compress
  olddir /var/www/yunchang/log/old
}

Rails的日志我一般存放在/var/www/yunchang/log/production.log这个文件里面,上面配置的意思是即将对这个文件进行日志切割。切割周期是daily也就是每天的凌晨12点进行日志切割。老日志会进行压缩compress,并最终存放在目录/var/www/yunchang/log/old底下。要是真想节约磁盘,请务必开启压缩选项。可以看看压缩前后的大小对比

> gunzip -c production.log.4.gz > production.log.4
> ls -hla production.log.4*

-rw-rw-r-- 1 deploy deploy  10G Oct 27 08:30 production.log.4
-rw-rw-r-- 1 deploy deploy 1.3G Oct 24 00:00 production.log.4.gz

10G的日志经过压缩之后就剩大概1.3G左右。循环配置为rotate 10,也就是说循环阀值为10,每当旧的日志达到10个的时候会自动清理掉老的日志。这些都还算比较好理解,可能会有点蒙圈的是这个配置

postrotate
  kill -USR1 `cat /var/www/yunchang/tmp/pids/puma.pid`
endscript

The lines between postrotate and endscript (both of which must appear on lines by themselves) are executed (using /bin/sh) after the log file is rotated.

这其实是一个嵌入脚本,每当日志切割之后就会运行一遍。因为Rails应用正在运行的过程中会持续往/var/www/yunchang/log/production.log文件中写入日志资料。如果进程持续占用着这个文件,哪怕我们直接在磁盘上把这个文件删掉,由于进程还在,磁盘空间都无法得到释放。因此需要对Puma进行重启,这样新的进程会创建新的production.log日志文件,跟老的日志完全脱钩,磁盘也将得到释放。Puma重启的方式可以参见这份文档

尾声

磁盘溢出并不是一件好玩的事情,一旦磁盘爆了所有服务都得重新启动。在业务运行的过程中最容易导致磁盘爆掉的要数日益增长的日志记录。加磁盘只是其中的一个手段,恰当地把日志进行压缩归档也不失为一个节省运营成本的做法,希望这篇文章对您有点帮助。

参考资料

  1. logrotate: https://linux.die.net/man/8/logrotate
  2. puma restart: https://puma.io/puma4/file.restart.html