Ubuntu服务器上跑容器,最怕两件事:应用日志写满磁盘导致服务挂掉,以及排查问题时面对杂乱无章的日志无从下手。这不是危言耸听,Docker默认的日志驱动是json-file,它会无限制地往/var/lib/docker/containers/下的json.log文件里写数据,直到把你的根分区撑爆。而Kubernetes环境下的容器日志,如果不加限制,kubelet的日志轮转策略也常常跟不上日志产生的速度。解决思路很明确:在源头限制日志大小,在系统层面做兜底清理,同时建立一套可用的收集方案让日志不在本地堆积。
理解容器日志的存储位置与增长特性在Ubuntu系统上,Docker容器的标准输出和标准错误会被捕获并存储。默认情况下,这些日志文件位于/var/lib/docker/containers/<容器ID>/目录下,文件名通常是<容器ID>-json.log。你可以用docker inspect命令查看某个容器的日志路径。这个文件会不断追加写入,除非你配置了日志轮转,否则它会一直增长到磁盘满为止。containerd作为Kubernetes常用的容器运行时,其日志默认存放在/var/log/pods/和/var/log/containers/目录下,同样面临无限增长的问题。很多运维人员直到收到磁盘告警才发现问题,这时候/var分区可能已经100%占用了。
配置Docker日志轮转策略最有效的办法是在Docker daemon层面配置全局的日志驱动选项。编辑/etc/docker/daemon.json文件,加入log-driver和log-opts配置。如果文件不存在就新建一个。一个典型的配置如下:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
这里的含义是单个日志文件最大10MB,保留最近3个文件。这样每个容器的日志最多占用30MB磁盘空间。修改后需要重启Docker服务使配置生效:sudo systemctl restart docker。需要注意的是,这个配置只对新创建的容器生效,已经运行的容器需要删除后重新创建才会应用新的日志轮转策略。对于Docker Compose管理的服务,也可以在docker-compose.yml中为每个服务单独指定日志配置,优先级高于daemon全局配置。
处理已存在的大型日志文件配置好轮转策略后,那些已经膨胀的日志文件不会自动变小。你需要手动清理它们。直接删除json.log文件不是最佳做法,因为Docker daemon仍然持有该文件的句柄,删除后空间不会立即释放。正确的方法是使用truncate命令清空文件内容而不删除文件:
sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log
这条命令会遍历所有容器的日志文件并将其大小置零。执行后磁盘空间会立即释放,而且不会影响正在运行的容器。如果你确认某些容器已经不再需要,可以直接删除整个容器和相关的日志文件。定期执行这条命令可以作为一种临时应急手段,但治本还是要靠日志轮转配置。
Kubernetes环境下的日志管理在Kubernetes集群中,每个Pod的容器日志由kubelet负责管理。kubelet会定期检查容器日志的大小,并根据配置进行轮转。相关的配置参数在kubelet的启动参数或配置文件中设置,主要包括--container-log-max-size和--container-log-max-files。例如,在/var/lib/kubelet/config.yaml中可以这样配置:
containerLogMaxSize: "10Mi" containerLogMaxFiles: 5
修改后需要重启kubelet服务。和Docker类似,这个配置只影响新创建的Pod。对于已经存在的Pod,你需要重建它们才能应用新的日志轮转策略。此外,Kubernetes节点上还可能堆积大量的已退出容器、未使用的镜像和孤儿卷,这些也会占用可观的磁盘空间。使用docker system prune或crictl rmi --prune等命令可以清理这些资源。
搭建集中式日志收集方案即使配置了本地日志轮转,日志最终还是会丢失历史数据。对于需要长期保留、集中查询的场景,必须把日志发送到外部存储。Loki加Promtail是当前比较轻量且与Ubuntu生态契合的方案。Promtail作为日志采集代理部署在每台服务器上,负责读取容器日志文件并推送给Loki存储,Grafana负责查询和可视化。
安装Promtail很简单,先从官方仓库下载二进制文件或使用包管理器安装。配置文件/etc/promtail/config.yml的核心部分是scrape_configs,需要定义如何发现和采集容器日志。一个针对Docker容器的配置片段如下:
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
这个配置利用Docker的服务发现机制,自动发现所有运行中的容器并采集其日志。Promtail会为每条日志自动添加容器名称等标签,方便后续在Grafana中按容器筛选查询。对于Kubernetes环境,Promtail可以使用kubernetes_sd_configs来发现Pod,配置思路类似,但标签重写规则会更复杂一些,需要提取命名空间、Pod名称、容器名称等元数据。
使用Vector作为高性能替代方案如果日志量非常大,Promtail的资源消耗可能成为瓶颈。Vector是一个用Rust编写的高性能日志采集器,内存占用极低,吞吐量很高。在Ubuntu上安装Vector可以直接使用官方提供的APT仓库。Vector的配置文件使用TOML格式,一个采集Docker日志并输出到控制台(调试用)的配置如下:
[sources.docker] type = "docker_logs" [sinks.print] type = "console" inputs = ["docker"] encoding.codec = "json"
实际生产环境中,你会把sink配置为Loki、Elasticsearch或Kafka等存储后端。Vector支持丰富的转换和处理功能,可以在采集端就对日志进行解析、过滤和格式化,减少后端存储的压力。比如可以用VRL(Vector Remap Language)提取日志中的错误级别、请求路径等字段,方便后续告警和分析。
系统级磁盘清理与监控除了容器日志,Ubuntu系统本身也会积累各种临时文件、旧内核和软件包缓存。定期执行apt autoremove和apt clean可以清理不再需要的软件包和下载缓存。journald的日志也可能占用大量空间,可以通过修改/etc/systemd/journald.conf来限制其大小:
SystemMaxUse=500M SystemMaxFileSize=100M
然后重启systemd-journald服务。对于/var/log下的其他系统日志,logrotate已经内置了轮转策略,但你可以根据需要调整/etc/logrotate.d/下的配置文件,缩短轮转周期或减小保留的归档数量。
光靠手动清理不够,你需要一套自动化的磁盘监控机制。一个简单的做法是编写cron脚本,定时检查磁盘使用率,超过阈值时自动执行清理并发送告警。下面是一个示例脚本,检查根分区使用率超过80%时触发清理:
#!/bin/bash
USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $USAGE -gt 80 ]; then
echo "Disk usage is ${USAGE}%, cleaning up..."
truncate -s 0 /var/lib/docker/containers/*/*-json.log 2>/dev/null
docker system prune -f
apt clean
fi
把这个脚本加入crontab,每小时执行一次,就能在问题恶化前自动介入。更完善的做法是接入Prometheus加Node Exporter监控磁盘使用率,配合Alertmanager设置告警规则,这样你可以在磁盘满之前就收到通知,从容处理。
日志收集中的性能考量在部署日志采集代理时,要注意它对宿主机性能的影响。Promtail和Vector默认会跟踪日志文件的尾部,这个操作本身开销很小。但如果你的容器日志写入速度极快,采集端需要及时跟上,否则会造成采集延迟。Vector在多核机器上可以利用多线程并行处理,吞吐量远超Promtail。另外,采集代理自身也会产生日志和临时文件,记得把它的日志也纳入轮转管理。
网络带宽是另一个容易被忽略的因素。如果日志量巨大,把原始日志全部发送到远端存储会占用大量带宽。可以在采集端做采样或过滤,只发送WARNING级别以上的日志,或者对高基数日志做聚合。Vector在这方面特别灵活,它的transform组件可以在事件流中实时过滤和修改数据。
总结最佳实践把这几件事做好,Ubuntu服务器上的容器日志问题基本就能控制住:第一,Docker daemon或kubelet必须配置日志轮转,这是最后一道防线;第二,部署Promtail或Vector把日志实时推送到Loki等集中存储,本地只保留少量热数据;第三,用cron脚本或监控系统自动清理磁盘,避免人工救火;第四,定期审查日志保留策略,根据实际查询需求和存储成本调整轮转参数。这些措施组合起来,既能保证排查问题时有日志可查,又不会让磁盘空间成为定时炸弹。
