Ruby 在 Rails 服务中进行日志切割与归档

lanzhiheng · 2021年10月27日 · 最后由 sg552sg552 回复于 2021年10月27日 · 587 次阅读

简单分享一下如何使用 logrotate 对 Rails 服务的日志进行切割与归档。


日志所引发的问题

虽然 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

在 Rails6 之前我一般用 log4r, 有自带的 daily log logrotate 也不错

sg552sg552 回复

系统级的解决方案还是有意义的。一招鲜,吃遍天。可以同样适用与 sidekiq,或者其他服务的日志。就类似 弄明白 systemd 之后,就跟 进程自己的 daemon 模式说 byebye 了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号