logrotate轮转后服务挂掉大概率是postrotate中kill或reload操作失败所致,需查journalctl -u logrotate及服务日志确认信号是否被正确响应,再用lsof验证日志文件句柄是否刷新,同时检查sharedscripts是否缺失导致重复reload。

logrotate 轮转时服务进程被 kill 怎么确认
服务在 logrotate 执行后挂掉,大概率不是“服务自己崩了”,而是轮转脚本里写了 kill -USR1 或 systemctl reload 之类操作,但目标进程没正确响应信号,或者 reload 失败后服务静默退出。先别急着改配置,得确认是不是它干的。
检查方法很简单:
- 查
/var/log/messages或journalctl -u logrotate,搜关键词logrotate和reload、kill、failed - 看轮转时间点前后 2 分钟内,对应服务的日志有没有报错,比如
nginx: signal process started后没下文,或Failed to reload nginx.service - 用
lastcomm | grep logrotate(需启用 acct)或ausearch -m execve -i | grep logrotate(需 auditd 开启)追溯实际执行命令
postrotate 里 reload 失败却不报错的坑
很多配置看着没问题:postrotate 里写 systemctl reload nginx,但一旦 reload 实际失败(比如配置语法错、端口被占),systemctl 默认不抛出错误,logrotate 也照常退出,服务就卡在旧进程不重启、新进程没起来的“半死”状态。
真正安全的做法是加判断和显式失败处理:
- 把
systemctl reload nginx换成systemctl try-reload-or-restart nginx,它会在 reload 失败时自动 fallback 到 restart - 或者手动加检查:
systemctl reload nginx || systemctl restart nginx - 更稳妥:在
postrotate块末尾加if ! systemctl is-active --quiet nginx; then exit 1; fi,让 logrotate 整体失败,避免后续轮转继续执行
日志文件句柄没释放导致服务写日志失败
这是最隐蔽也最常被忽略的一类:logrotate 把 /var/log/nginx/access.log 重命名了,但 nginx 进程还拿着旧文件的 fd 在写,结果新日志一直为空,而旧文件名已变,你查 ls /var/log/nginx/ 看不到它,以为“日志没了”,其实是写进了一个带日期后缀的归档文件里(比如 access.log-20260907)——这时候服务没挂,但日志断了,排查方向全偏。
验证方式很直接:
- 用
lsof -p $(pgrep nginx) | grep access.log,看 nginx 进程打开的到底是不是当前路径下的文件 - 如果输出里是
access.log-20260907这种,说明句柄没刷新,必须发信号让它 reopen - Nginx 要
kill -USR1,rsyslog 要kill -HUP,Java 应用得看具体日志框架是否支持动态 reload
logrotate 配置里 sharedscripts 没配对导致多实例冲突
当一个配置块管理多个日志路径(比如同时切 /var/log/nginx/*.log),又没加 sharedscripts,logrotate 会为每个匹配到的文件单独执行一遍 postrotate ——也就是说,systemctl reload nginx 可能被连跑 3 次,中间任意一次失败或超时,都可能让服务状态异常。
正确写法必须成对出现:
- 有
prerotate就得配endscript,有postrotate也一样 - 多个日志共用一套 reload 逻辑,一定要加
sharedscripts和endscript - 示例片段:
"/var/log/nginx/*.log" { daily missingok rotate 30 compress sharedscripts postrotate systemctl try-reload-or-restart nginx > /dev/null 2>&1 || true endscript }
sharedscripts 不是可选项,是防止并发 reload 的关键开关;漏掉它,服务不死也抖三抖。











