发布时间:2026-09-15 12:31 更新时间:2026-09-15 12:31 阅读量:0
很多站长上线初期不太管 Nginx 的 access.log,等到某天收到磁盘告警,才发现单个日志文件已经涨到十几 GB,用 tail 打开都卡。访问日志本身是排查问题的第一手资料,不能一删了之,但也不能让它无限膨胀。合理的做法是让日志按天切割、压缩归档、按份数过期清理,这套动作在绝大多数 Linux 发行版上由 logrotate 完成,配合 Nginx 的信号机制即可实现。
先理解一个前提:Nginx 进程启动后会一直持有当前日志文件的文件描述符。如果你直接 rm access.log,磁盘空间并不会立刻释放,因为进程还握着这个 inode,只有等 Nginx 重启或重新打开日志文件,空间才会真正归还。同时新请求仍会写向那个已被删除的 inode,日志等于写进了黑洞。
正确做法是「重命名 + 通知重开」。把当前日志改名为带日期的归档文件,然后给 Nginx 主进程发送信号,让它关闭旧句柄、按配置里的路径重新打开一个新文件。常用的信号有两个:USR1 用于重新打开日志文件,配合 nginx -s reopen 使用;HUP 用于重载配置,会重启 worker 进程,开销略大。切割日志场景下优先用 USR1,不中断正在处理的连接。
logrotate 的 postrotate 脚本就是干这件事的地方,它会在轮转完成后执行,把信号发给正在运行的 Nginx。
多数发行版的 Nginx 包会自带 /etc/logrotate.d/nginx,但默认策略往往是「每周轮转、保留 4 份」,对访问量大的站点远远不够。可以改成按天轮转,并加上压缩与日期后缀。编辑 /etc/logrotate.d/nginx,写入类似下面的内容:
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
dateformat -%Y%m%d
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
逐项说明几个关键参数。daily 表示每天轮转一次,logrotate 由 cron 或 systemd timer 触发,实际执行时间通常在凌晨,具体以本机 /etc/cron.daily/logrotate 或 timer 配置为准。rotate 14 保留 14 份归档,加上当前文件共 15 个,按天计算大约留两周。compress 开启 gzip 压缩,纯文本日志通常能压到原体积的十分之一左右;delaycompress 让最近一份归档暂不压缩,方便刚切割后还想快速 grep 的场景。dateext 加 dateformat 让归档文件名带上日期,比默认的 .1、.2 后缀直观得多。
create 0640 www-data adm 中的用户组要按你的实际环境填写,Debian/Ubuntu 上 Nginx 一般以 www-data 运行,CentOS/RHEL 系通常是 nginx 用户,写错会导致新日志文件权限不对、Nginx 无法写入。可以先执行 ps -o user= -C nginx | sort -u 确认运行用户。
postrotate 里那句判断不能省:先确认 pid 文件存在再发信号,避免 Nginx 未运行时 kill 报错。注意信号是发给 master 进程的,worker 会随之重新打开日志。sharedscripts 保证通配符匹配到多个日志文件时,postrotate 只执行一次,而不是每匹配一个文件执行一遍。
配置改完不要等第二天,先用调试模式跑一遍,确认没有语法问题:
# 干跑一次,输出会告诉你将要做什么,不实际改动文件
logrotate -d /etc/logrotate.d/nginx
强制立即轮转一次,用于验证配置是否生效
logrotate -vf /etc/logrotate.d/nginx
执行完 -vf 后,去日志目录看一眼:应该出现类似 access.log-20260915 的归档文件,以及一个新建的、体积归零的 access.log。再发一个请求,确认新日志里有内容写入,说明 USR1 信号生效、句柄已重开。如果新日志一直为空而归档还在变大,多半是 postrotate 没执行或信号没发对,检查 pid 文件路径是否与实际一致,可用 nginx -t 确认配置里的 pid 指令指向哪里。
磁盘占用控制除了靠 rotate 份数,还有几个实用手段。一是给日志目录单独分区或挂载点,避免日志写满拖垮系统盘;二是把不常看的日志级别调高,比如在 nginx.conf 里对健康检查、静态资源这类高频请求关闭 access_log,减少无效写入;三是归档文件可以再压缩或转存到对象存储,本地只留最近几天。可以用下面这条命令快速看清谁在占空间:
du -sh /var/log/nginx/* | sort -h | tail -n 10
另外提醒一点,logrotate 的日期后缀在同一天多次强制轮转时,默认会因文件名冲突而跳过或覆盖,测试时可以先手动删除生成的归档再跑,生产环境按天一次不会遇到这个问题。若站点访问量极大、单日日志就有数 GB,按天切割仍嫌文件过大,可以考虑按小时轮转,把 daily 换成 hourly 并相应调整保留份数,具体支持情况以本机 logrotate 版本的 man 手册为准。
小结一下:Nginx 日志治理的核心就三步——用 logrotate 定时改名归档,用 USR1 信号让 Nginx 重开新文件,用 rotate 份数和 compress 控制磁盘水位。配置改好后先 logrotate -d 干跑验证,再 -vf 强制轮转一次确认信号生效,之后交给定时任务自动运行即可。下一步建议顺手检查一下 /etc/logrotate.d/ 下其他服务的配置,把保留策略统一,避免某天又被某个没管过的日志文件顶满磁盘。
| 📑 | 📅 |
|---|---|
| Let's Encrypt 证书自动续期失败排查:日志、80端口校验与 reload 钩子 | 2026-09-13 |
| Docker 磁盘占用过高清理实战:overlay2、容器日志与悬空镜像 | 2026-09-12 |
| Nginx 502/504 排查实战:从 upstream 超时到 php-fpm 进程池 | 2026-09-12 |
| MySQL 出现 Too many connections 怎么办:定位、连接池与超时调优 | 2026-09-11 |
| Linux用户与权限管理实战:sudoers、SUID/SGID与最小权限 | 2026-09-10 |
| Docker Compose 部署多容器应用实战:环境变量、数据卷与重启策略配置要点 | 2026-09-15 |
| MySQL慢查询日志开启与优化入门 | 2026-09-15 |
| 服务器定时任务实战:crontab 语法与不生效排查 | 2026-09-15 |
| Nginx 499 状态码排查实战:客户端断连与 upstream 超时的区别 | 2026-09-16 |
| 容器内存超限被 OOM Kill 排查:指标、cgroup 与堆参数对应 | 2026-09-16 |