Nginx 日志切割的正确姿势:logrotate、USR1 和 VPS 磁盘边界

为宿主机 Nginx 配置 logrotate 与日志重新打开,区分每天检查、文件大小阈值和归档份数,说明预览与强制轮转的区别,并单独限制 Docker 标准输出日志。

·9 minNginxlogrotateVPSLinux
把应用部署到土耳其|BRNCHOST · 土耳其 VDS
云服务器,积分可续期|雨云 · 国内外节点 · 积分兑换权益
低价年付,大流量 VPS|RackNerd · SSD 存储 · 1Gbps 端口
香港轻量,搭个小站|晚安云 · 香港云服务器
香港 VPS,大带宽可选|野草云 · BGP 直连
大陆访问,精品线路|搬瓦工 · CN2 GIA / CTGNet 套餐
资料归档,交给 AI 整理|WorkBuddy · 本地文件处理
建站起步,先看应用镜像|腾讯云 · 轻量应用服务器
CN2 GIA,中国方向优化|DMIT · Premium 网络
双 ISP 原生住宅 IP|丽萨主机 · 美国 9929 精品线路
高频 CPU,多地部署|Evoxt · 云服务器 · 每周异地备份
京东云轻量云主机:129元/年,新人专享,限购1台

网站还在正常访问,VPS 的磁盘却一天天减少。检查发现是 access.log 变大后,直接把文件改名,往往又会遇到第二个问题:Nginx 继续往旧文件写,新文件一直是空的。

日志轮转需要完成两件事:保存旧文件,再让进程重新打开当前日志。宿主机上的 Nginx 可以用 logrotate 配合重新打开日志的信号处理;容器标准输出由 Docker 的日志驱动保存,配置方法不同。先确认日志落在哪里,再改保留规则。

先查路径,别同时配置两套规则

下面讨论 Linux 宿主机安装的 Nginx,日志目录由 root 管理。1Panel 内的容器、自己编译安装的 Nginx、Docker 镜像中的日志路径,都需要按实际部署方式另行确认。

先查看版本和配置:

nginx -v
logrotate --version
sudo nginx -T

nginx -T 会检查并输出完整配置,其中可能有内网地址或认证信息,不要把输出原样公开。查找 access_log、error_log 和 pid,确认是否用了不同目录、多份配置或另一套启动参数。安装在非默认目录的实例,后面的命令也要带上对应配置和前缀。

再检查已有轮转规则:

sudo cat /etc/logrotate.conf
sudo ls -l /etc/logrotate.d/
sudo cat /etc/logrotate.d/nginx

最后一条如果提示不存在,先确认这个发行版或安装方式有没有其他规则文件。软件包可能已经安排了轮转,不应再建一个文件,重复匹配同一份日志。保留现有规则的副本,再修改其内容;也不要把原来的脚本替换掉之前就假定它没有作用。

日志目录可以先做一次容量检查:

sudo du -sh /var/log/nginx
df -h /var/log/nginx
df -i /var/log/nginx

路径要换成实际目录。du 帮助判断文件占用,df -h 查看所在文件系统的剩余空间,df -i 查看 inode。日志只是可能的占用来源,数据库、上传文件和备份也可能在同一块盘上。

改名以后,为什么还在写旧文件

进程打开文件后使用文件描述符写入。文件改名并不会让描述符自动转向你新建的 access.log。即使删除了目录里的文件名,进程仍可能持有打开的文件,因此目录看起来小了,文件系统空间却没有立即释放。

Nginx 的日志轮转说明要求先改名,再向主进程发送 USR1 重新打开日志。命令行也提供相应操作:

sudo nginx -s reopen

这个命令会依据 Nginx 的配置找到主进程。只有一个标准安装实例时较好处理;多实例或自定义 -c、-p 启动方式,需要使用相同的配置和前缀,避免把信号发给另一个实例。不要照搬网上某个固定 PID 路径,因为它可以由配置改变。

重新打开日志不需要为了这件事停止网站。它与重新加载配置是两个不同动作:轮转时要处理日志文件,修改配置时才考虑配置重载。若重新打开失败,应检查实际 PID、进程、目录权限和错误日志,不把异常吞掉后继续当成成功。

Nginx 官方文档中的日志重新打开说明

图中是 Nginx 官方文档的日志轮转段落,说明改名后向主进程发送 USR1;它不是服务器执行结果。

给宿主机 Nginx 配一份轮转规则

确认 /var/log/nginx 确实是本机日志目录,现有规则也没有重复覆盖后,可以按下面的内容调整。示例采用数字后缀,每天轮转,并在检查时对超过 100 MB 的文件提前轮转。七份归档是一个容量规划示例,不代表所有站点都应保存七天。

/var/log/nginx/*.log {
    daily
    maxsize 100M
    rotate 7
    missingok
    notifempty
    compress
    delaycompress
    nodateext
    sharedscripts
    postrotate
        /usr/sbin/nginx -s reopen
    endscript
}

/usr/sbin/nginx 是示例安装路径,先用 command -v nginx 核对。如果现有发行版规则使用辅助脚本,保留其中适用于你的实例和服务状态的处理,再判断是否需要替换。本例要求轮转时 Nginx 正在运行;服务停止时 reopen 可能失败,日志轮转任务会报告错误,不能在这种情况下把它理解为服务已经恢复。

示例没有指定 create 的用户和组,让 Nginx 在重新打开时建立日志文件。若已有规则使用 create,应按实际主进程、工作进程和目录权限核对,不能随意套用另一个发行版的 www-data 或 nginx 用户。日志目录如果由普通用户控制,也需要单独设计 su 和权限;不要把这份 root 管理目录的示例直接放到用户可写路径里。

missingok 容许某份日志不存在,notifempty 跳过空文件;compress 压缩归档,delaycompress 把最近一份归档的压缩推迟到下一次轮转。最近的旧文件暂时不压缩,有利于保留重新打开前的过渡写入,但不能替代正确的 reopen。

sharedscripts 让这一组实际发生轮转后只运行一次后处理脚本。nodateext 则选择编号归档,避免继承全局日期后缀。一天内可能轮转多次时,只有日期的文件名容易发生重名;不要一边提高检查频率,一边保留不足以区分多次轮转的归档名。

100 MB 是检查条件,不是磁盘配额

maxsize 100M 不会常驻后台盯着文件。logrotate 每次运行时才判断是否满足条件。如果每天凌晨只执行一次,中午暴增的日志仍可能一直增长到下一次检查。

检查频率决定了两次判断之间可能积累多少日志。假设访问量异常时每小时新增 200 MB 日志,一小时检查一次也可能让文件越过 100 MB;每天检查一次更不可能把文件限制在这个值。这只是容量估算,实际增长量要从自己的文件和时间记录中确定。

size 与按时间轮转的规则有优先关系,不宜把 daily 加上 size 后就认定同时实现两个条件。本例选择 maxsize,用于在时间条件之外增加大小条件。具体语义可在服务器上的 man logrotate 中查看,并与安装版本对应。

rotate 7 保存的是轮转次数。一天轮转三次,七份归档覆盖的时间就少于七天。压缩效果也取决于日志内容,不能据此保证磁盘占用固定。需要保留一个月用于排障或审计时,应计算增长量、轮转频率和异地归档容量,不把文件份数直接当成保留天数。

查清究竟是谁运行 logrotate

在 systemd 管理的机器上,可以先查看:

systemctl list-timers --all
systemctl cat logrotate.timer
systemctl cat logrotate.service

如果没有这些单元,检查系统的 cron 安排。它们属于不同的调度方式,安装和启用情况由发行版决定。配置文件里写了 hourly,也不会自行创建每小时运行的任务。

需要提高频率时,先确定现有任务是不是已经在运行,避免再加 cron 与 timer 并行处理同一份规则。本例的 maxsize 在更频繁调用时才有机会更早发现大文件;其他被同一个任务包含的规则也会被检查,应一并审阅。

轮转状态文件帮助记录上次处理时间,不能每次都随意换一个空状态文件来绕过已有记录。并行任务还可能争用状态锁。长期调度应使用统一入口,临时验证也要知道自己究竟读了哪份配置和状态。

验证规则时,先预览,再安排真实轮转

先使用 debug 模式查看判断与配置错误:

sudo logrotate -d /etc/logrotate.conf

这一步不会真正改名、压缩日志,也不会更新状态文件。它适合检查重复匹配、权限或语法问题,但不能证明 Nginx 已经重新打开文件。

需要观察正常执行结果时,可以运行:

sudo logrotate -v /etc/logrotate.conf

它会执行当前满足条件的规则,可能处理其他系统日志;不是另一种只读预览。若日志还没到轮转条件,输出可能显示不需要轮转。不要为了得到一行“成功”就反复强制处理全部配置。

确实需要强制验证 Nginx 规则时,先确认归档保留策略、服务运行状态和当前文件副本,再针对实际规则文件安排:

sudo logrotate -v -f /etc/logrotate.d/nginx

单独读取这个文件不一定继承 /etc/logrotate.conf 的全局设置,所以应保证被验证的规则包含所需选项,并理解它与日常统一入口的区别。强制执行可能淘汰最旧归档,不能在缺少历史副本时随手尝试。

轮转后,通过自己的实际网址发出一次请求,再检查新日志的时间和内容。确认当前文件增长、旧归档不再继续增长,并检查后处理脚本是否报错。若目录占用减少而磁盘仍满,继续排查仍被进程持有的已删除文件,不能只看文件名判断空间已经释放。

Docker 标准输出交给 Docker 处理

Nginx 在容器里运行时,镜像可能把日志连接到标准输出和标准错误。这时宿主机目录中的 access.log 轮转规则,并不能限制 Docker 保存的输出。

先查看目标容器使用的驱动:

docker inspect -f '{{.HostConfig.LogConfig.Type}}' CONTAINER_NAME

替换容器名。采用 Compose 且使用 json-file 时,可以把下面的配置合并到对应服务:

logging:
  driver: json-file
  options:
    max-size: "20m"
    max-file: "5"

将它放在需要限制日志的服务下,不能放在 Compose 根层。修改后要按原来的部署流程重建相应容器才能生效;仅重启旧容器或者修改 Docker 守护进程默认值,不会自动改变已有容器的日志参数。重建前确认挂载数据和维护时间。

Docker 提醒内部日志文件应由守护进程管理,不应再用宿主机 logrotate 去修改那些文件。若容器把日志写进挂载目录,则还要核对容器内部的日志重新打开方式;标准输出和挂载目录同时保存日志时,也会同时消耗空间。

宿主机文件轮转、Docker 输出限制和异地归档分别设置。保留下来的日志还应有明确用途:能定位错误、覆盖需要回查的时间,同时不会挤占业务数据和备份。记录真实增长量、任务执行结果与可用空间,再据此调整阈值和归档份数。