copytruncate 是 logrotate 配置文件中的一个指令,用于在轮转日志时采用 “先复制,再清空” 的策略,而不是默认的 “先重命名,再新建” 策略。
工作原理
默认情况下(没有 copytruncate),logrotate 会:
- 将当前日志文件
access.log重命名为access.log.1 - 创建一个新的空文件
access.log - 向应用程序发送信号(如
kill -USR1),让其重新打开新的日志文件
而使用 copytruncate 时:
- 复制:将
access.log的内容完整复制到access.log.1 - 清空:将
access.log文件的大小截断(truncate)为 0,保留原文件的 inode、权限和所有者
整个过程中,日志文件的文件名和 inode 编号都不变。
为什么要用 copytruncate?
有些应用程序无法被通知重新打开日志文件,或者不支持通过信号等方式优雅地切换日志文件。例如:
- 老旧的自研程序,日志句柄一直被持有
- 某些数据库(如旧版 MySQL)如果直接重命名日志文件,它仍会继续写入旧文件(文件名变了,但 inode 没变,文件描述符还在)
- 在容器环境中,进程无法接收外部信号
copytruncate 可以让程序始终写入同一个文件(inode 不变),无需任何配合即可实现轮转。
优缺点
| 优点 | 缺点 |
|---|---|
| 无需停止服务,无需发送信号 | 可能丢失日志:在复制和截断之间的极短时间内写入的日志行,可能会丢失 |
| 对程序透明,无需修改配置 | I/O 双倍消耗:复制整个日志文件(通常很大)会占用磁盘读写 |
| 保留原文件 inode,某些监控系统更稳定 | 如果日志文件非常大,复制操作可能导致磁盘 I/O 飙升 |
配置示例
/var/log/nginx/access.log {
daily
rotate 7
copytruncate # 使用复制+清空,而不是重命名
compress
missingok
}
与默认方式(create+postrotate)的对比
| 特性 | 默认方式(重命名+新建) | copytruncate 方式 |
|---|---|---|
| 是否需要程序配合 | 需要(发送信号,程序重新打开文件) | 不需要 |
| 是否可能丢日志 | 极少(信号发送到程序重新打开之间的瞬间) | 可能(复制与截断之间写入的数据) |
| 磁盘 I/O | 低(仅重命名,无数据复制) | 高(每次轮转都要完整复制日志文件) |
| 文件 inode | 改变(新文件 inode 不同) | 不变 |
| 推荐场景 | 绝大多数现代应用(Nginx、Apache、rsyslog) | 无法发送信号的遗留程序 |
在 Nginx 中的建议
不要对 Nginx 日志使用 copytruncate。Nginx 完美支持 USR1 信号来重新打开日志文件,使用默认方式(postrotate 中 kill -USR1)更安全、高效,且不会丢日志。
如果错误地对 Nginx 使用了 copytruncate,当访问量很大时,access.log 可能在复制过程中被持续写入,导致:
- 复制操作永远跟不上写入速度
- 磁盘 I/O 飙升
- 日志文件实际上无法被清空(因为写入速度超过复制速度)
使用 copytruncate 的正确场景
- 一个老旧的二进制程序,无法停止或重启,且不支持重新打开日志文件
- 日志文件不大(几十 MB 以内)
- 对偶尔丢失几行日志不敏感(如非关键业务日志)
注意事项
copytruncate和create指令是互斥的,不要同时使用- 如果日志文件非常大(几 GB),考虑使用默认方式或改用日志切割工具如
cronolog、newsyslog - 截断(truncate)操作并不是原子的,极少数情况下可能损坏文件,因此建议保留
create方式作为首选
总结:copytruncate 是一种“向下兼容”的轮转方式,适合无法配合的应用;对于 Nginx 等主流服务,永远优先使用默认的重命名+信号方式。


2万+

被折叠的 条评论
为什么被折叠?



