Docker容器用久了,很多人都会遇到同一个问题:磁盘空间莫名被吃掉几十个G,一查发现是/var/lib/docker/containers下面的json日志文件在疯狂膨胀。容器的stdout输出全部会写进这些json-file日志里,如果应用本身打日志很勤快,又没做任何限制,单个日志文件涨到十几G都很常见。本文介绍如何用logrotate这套老牌的日志管理工具来解决容器日志的切割与清理问题。

先了解Docker默认的日志机制
Docker默认使用json-file日志驱动,容器的标准输出和标准错误都会被写入宿主机上的一个json文件中,路径类似/var/lib/docker/containers/<容器ID>/<容器ID>-json.log。很多初学者以为日志在容器内部,其实不然,真正落盘的位置在宿主机上。
这个默认行为本身没有任何大小限制,也就是说容器只要一直跑、一直输出,日志就会无限增长。Docker其实提供了原生的控制参数,可以在daemon.json或者docker run时直接设置:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}修改之后重启Docker服务生效。这个方案简单直接,对新建的容器立即起作用。但它也有局限:一是只对新容器生效,已经存在的旧容器需要重建;二是它只做切割不做压缩,占用的磁盘空间依然不小;三是日志轮转的粒度固定,没法按天、按周来组织日志文件。而logrotate在这些方面都更灵活。
用logrotate管理容器日志的完整配置
logrotate是Linux下最经典的日志轮转工具,绝大多数发行版都自带,如果没有可以用包管理器装一下:yum install logrotate或者apt-get install logrotate。它的配置文件放在/etc/logrotate.d/目录下,每个应用一个独立的配置文件,互不干扰。
针对Docker容器日志,可以新建一个配置文件/etc/logrotate.d/docker-containers,内容如下:
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
size 100M
missingok
delaycompress
copytruncate
dateext
dateformat -%Y%m%d-%s
}各项参数的含义值得逐个说明。rotate 7表示最多保留7份历史日志,超出的自动删除;daily指定每天切割一次,也可以换成weekly或monthly;compress开启gzip压缩,配合delaycompress可以让最近一次切割的日志延迟一天再压缩,方便排查问题;size 100M表示单文件超过100M也触发切割,和daily是或的关系;missingok让日志文件不存在时不报错;dateext和dateformat让切割后的文件带上日期后缀,便于辨认。
最关键的是copytruncate这个参数。容器正在运行时,Docker守护进程持有日志文件的句柄,如果直接把日志文件改名或删除,Docker还会继续往原句柄写入,导致空间不释放。copytruncate的处理方式是先复制一份副本,再把原文件清空,这样句柄不受影响,容器无感知。它的缺点是复制和清空之间存在一个极小的时间窗口,可能丢失几行日志,对绝大多数业务来说可以接受。
配合定时任务与挂载目录日志的处理
logrotate本身不负责定时执行,它依赖cron来触发。多数系统安装后已经自带了/etc/cron.daily/logrotate这个脚本,每天自动跑一次。如果需要更频繁的轮转,比如每小时检查一次,可以自己在crontab里加一条:
# 每小时执行一次日志轮转检查 0 * * * * /usr/sbin/logrotate /etc/logrotate.conf
执行完可以手动验证一下配置是否正确:logrotate -d /etc/logrotate.d/docker-containers是dry-run模式,只打印将要执行的动作不实际操作;去掉-d参数改为-f则强制立即执行一次轮转,方便确认效果。
除了Docker自管理的json日志,实际项目中更常见的还有容器通过volume挂载到宿主机的应用日志,比如把/app/logs挂到宿主机的/data/myapp/logs。这类日志同样可以用logrotate管理,配置方式完全一样,只需把路径换掉:
/data/myapp/logs/*.log {
rotate 14
daily
compress
missingok
notifempty
copytruncate
}notifempty表示日志为空文件时跳过轮转,避免产生一堆无意义的空压缩包。如果应用支持发送信号重新打开日志文件(比如Nginx收到USR1信号会重开日志),也可以不用copytruncate,改用postrotate脚本来处理,这样能做到日志零丢失:
/data/myapp/logs/*.log {
daily
rotate 14
compress
missingok
sharedscripts
postrotate
docker exec myapp nginx -s reopen > /dev/null 2>&1 || true
endscript
}sharedscripts保证多个日志文件匹配时脚本只执行一次,postrotate里的命令在轮转完成后运行,通过docker exec通知容器内的应用重新打开日志句柄。
两种方案怎么选
一般来说,如果是简单的场景、只想防止磁盘被打爆,直接配置Docker的max-size和max-file就够了,不需要额外装任何东西。但如果需要按日期归档日志、需要压缩节省空间、需要更长的保留周期和更灵活的轮转策略,logrotate明显更合适。两者也可以同时使用:Docker侧设置一个宽松的兜底限制防止极端情况,logrotate侧做精细化的切割归档,这是生产环境中比较稳妥的组合做法。
最后提醒一点,如果使用的是Kubernetes环境,容器日志管理通常交给日志采集 sidecar 或者集群层面的方案来处理,logrotate更适合单机Docker或者小规模自建的场景。工具本身没有绝对的好坏,关键是根据自己的部署形态选择合适的日志管理策略,并养成定期检查磁盘占用的习惯,别等磁盘爆了才想起来日志这回事。
logrotateDocker日志管理容器日志切割修改时间:2026-09-15 17:20:35