nginx多进程下日志切割需主进程协调usr1信号实现原子轮转,配合logrotate避免竞态;异步写入依赖内核缓冲与buffer/flush配置,非用户态线程池。

Nginx 本身不支持多进程下直接轮转日志文件(比如按时间或大小自动切割并重命名),因为多个 worker 进程会同时向同一个日志文件写入,无法由单个进程安全地 rename 或 reopen。真正的“多进程日志滚动”需要借助外部工具配合 Nginx 的信号机制来实现,核心是让所有 worker 进程重新打开日志文件,而不是直接操作正在被写的文件。
使用 logrotate + nginx -s reopen 是最常用方案
logrotate 是 Linux 系统标配的日志轮转工具,它本身不直接处理多进程并发写问题,但可通过发送 USR1 信号触发 Nginx 优雅重开日志文件:
- logrotate 按配置(如每天、按大小)移动旧日志(如 access.log → access.log.1)
- 然后执行
nginx -s reopen(等价于kill -USR1 `cat /var/run/nginx.pid`) - Nginx 主进程收到 USR1 后,通知所有 worker 进程关闭当前日志 fd,并以相同路径名重新 open 新文件(此时 logrotate 已腾出原文件名)
- 所有 worker 继续写入新的 access.log,旧日志已归档完毕,无丢失、无冲突
确保 logrotate 配置正确(关键细节)
在 /etc/logrotate.d/nginx 中需包含以下要点:
- copytruncate 不推荐:它靠截断原文件实现“伪轮转”,但 Nginx worker 可能因缓存或延迟写入导致少量日志丢失
- 必须用 create 指令确保新日志文件权限正确(如
create 0644 www-data www-data) - sharedscripts 要启用:保证 postrotate 脚本只执行一次(避免多个 worker 触发多次 reopen)
- 示例片段:
"/var/log/nginx/*.log" { daily missingok rotate 30 compress delaycompress notifempty create 0644 www-data www-data sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then nginx -s reopen 2>/dev/null || true fi endscript }
避免常见陷阱
多进程环境下容易踩坑的点:
- worker 进程数过多时,reopen 瞬间可能有短暂 I/O 峰值,但不影响服务;若日志路径磁盘满,reopen 会失败,需监控 disk space
- 不要手动
mv access.log access.log.1后再touch access.log—— Nginx 不会自动识别新文件,必须发 USR1 - Docker 环境中,若日志挂载到宿主机,仍需在容器内运行 logrotate 或由宿主机统一管理,并确保信号能送达 Nginx 主进程
- 自定义日志路径(如
access_log /data/logs/app1.log)时,logrotate 配置必须精确匹配该路径,且目录存在、权限可写
替代方案:用 systemd-journald 或外部收集器(适合云/容器场景)
如果不想维护 logrotate,也可绕过文件轮转本身:
- 将 Nginx 日志输出到 stdout/stderr(
access_log /dev/stdout),由 systemd 或容器运行时接管日志生命周期 - 用 Filebeat、Fluentd 等采集器实时读取日志文件,并按需切割上传,Nginx 保持持续写入,采集器负责消费和归档
- 优势是解耦、支持过滤/格式化;缺点是增加组件复杂度,且需确保采集器不丢日志(如用 inotify 监控 + offset 记录)











