今天回到家突然发现刚买的一台 VPS 根目录空间 100% 被占用了,使用 du 一层层查看占用最后发现是 /var/lib/docker/containers
下有一个 json 日志文件占用了 20G 大小。
简单的查了一下发现 Docker 容器的日志都会被记录在宿主机的 /var/lib/docker/containers/
路径下。而我有一个容器因为不停地输出日志,没多久就占了很大空间。
Docker 容器日志
在默认情况下 Docker 容器的日志会输出到一个 json-file 文件中,容器输出到 stdout
和 stderr
的内容加上时间戳会被记录到宿主机。
这些日志文件在宿主机的 /var/lib/docker/containers/
文件夹下,以这样的形式命名:
/var/lib/docker/containers/<container id>/<container id>-json.log
限制 Docker 容器日志大小
修改 Docker 配置 vi /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {"max-size": "10m", "max-file": "3"}
}
注意修改之后需要重启 Docker 使之生效 sudo systemctl restart docker
。
或者针对个别容器设置,在 docker-compose.yml
中:
注意需要版本2及以上
version: '2'
services:
app:
container_name: app
image: node
restart: always
logging:
driver: "json-file"
options:
max-file: "5" // number of files or file count
max-size: "10m" // file size
或者命令行:
docker run --log-opt max-size=10m --log-opt max-file=5 my-app:latest
Docker 日志策略和最佳实践
通过应用自己管理日志
应用自己去管理自己的日志,比如使用 Logging Framework,在 Java 中通常使用 log4j 将日志打印到一个远端的中心化地方,这样就可以绕过 Docker 和操作系统。这种方式给予了开发者更多的控制权。
为了保存日志数据,你可以配置一个持久化的存储或将日志转发到一个远程日志的系统,比如 Elastic Stack 或 Sematext Cloud,但是基于应用的日志框架存在的问题便是如果部署了多个容器,那么你需要一个方式来告诉日志系统,哪些日志属于哪个容器。
通过 Data Volumes 记录日志
你可以在容器内部创建一个目录,然后将该目录挂载到宿主机上,那么一些长期或共享使用的数据可以长久的存储。你可以复制,备份,或者从其他容器访问这些数据。也可以在多个容器之间共享这些 volume。
但是使用 Data Volume 存在的问题是,很难将这些容器迁移到其他宿主机而不丢失数据。
通过 Docker Logging Driver 记录日志
在 Docker 下,另外一个记录日志的方式是使用 logging drivers。不像 Data Volumes, Docker logging driver 会从容器的 stdout 和 stderr 输出中直接读取数据。默认的配置会将这些日志记录到宿主机的一个文件中,但是改变 logging driver 可以允许你将事件转发给 syslog, gelf, journald 或其他 endpoints。
因为容器不再需要读写日志文件,可以提升一定的性能。但是也有一些弊端,Docker log 命令只能在 json-file
log driver 下使用;log diver 有一些功能限制,日志文件只能被传输而不能被解析;当 TCP 服务不可达时,容器会 shut down。
使用专用的日志容器记录日志
另一个解决方案是通过一个专用的、独立的日志容器来记录和手机日志,这非常适用于微服务架构。这个优势在于这完全不依赖与宿主机。相反,专用的日志容器可以允许你在 Docker 的环境中管理日志文件。他会自动从其他容器收集日志,监控,分析,并且将他们转存到一个中心存储上。
这种方式使得我们可以轻易地将容器在不同的宿主机中移动,并且可以非常轻松的扩展日志基础设施,只需要增加日志容器即可。
Logging Using the Sidecar Approach
和专用的日志容器类似,使用日志容器,但是不同点在于,每一个应用容器都有专用的日志容器,允许你对每一个应用的日志进行自定义。第一个容器会将日志文件打印到 volume,然后日志文件会日志容器打上标签,然后再被传送到日志管理系统。
使用 sidecar 的一个主要的优势是,你可以为每一个 log 增加额外自定义的标签,可以更好地确定其来源。
同样也有一些劣势,设置或扩容可能会变得非常复杂和困难,并且需要更多的资源。你需要确保应用容器和 sidecar 容器是一起进行工作的,否则可能会造成数据丢失。
复习一下日志相关命令:
- docker logs containerName
- docker logs -f containerName
- 显示 CPU 和内存使用 docker stats
- 显示具体容器的 CPU 和内存使用 docker stats containerName1 Name2
- 显示容器中运行的进程 docker top containerName
- Docker events: docker events
- 显示存储使用:docker system df
Logging Driver
Logging driver 是 Docker 用来才运行的容器和服务中收集数据的机制,这使得数据更容易进行分析。当一个新的容器被创建,Docker 会自动使用 json-file log driver 作为默认。同时,你可以使用 logging driver plugins 来和其他日志工具进行交互。
下面是一个通过自定义 logging driver 来和 syslog 交互的例子:
docker run -–log-driver syslog –-log-opt syslog-address=udp://syslog-server:514 alpine echo hello world
如何配置 Docker Logging Driver
有两个选项:
- 为所有的容器设置默认的 logging driver
- 为每一个容器设置 logging driver
上面提过,默认的 logging driver 是 JSON 文件,其他选项有 logagent, syslog, fluentd, journald, splunk 等等。你可以通过修改 Docker configuration 文件 来进行配置:
# /etc/docker/daemon.json
{
"log-driver": "journald"
}
然后再执行:
systemctl restart docker
使之生效。
或者,你可以通过命令来为每一个容器单独设置
docker run --log-driver syslog --log-opt syslog-address=udp://syslog-server:514 alpine echo hello word
Logging Driver 选项
选项解释:
- logagent:这是一个通用的 log shipper,Logagent Docker image 是一个提前配置好 Log 收集的镜像,Logagent 不仅会收集日志,还会收集诸如镜像名字,容器ID,容器名字,Swarm service 等 meta-data 或 Kubernetes meta-data。并且它可以处理多行日志,可以解析容器的日志等等
- syslog: 将日志转发到 syslog 服务
- journald:将容器日志发送到 systemd journal
- fluentd: 将日志信息发送到 Fluentd 收集器
- elf:将容器的日志写到 Graylog Extended Log Format(GELF) 端,比如 GrayLog 或 Logstash
- awslogs:将日志发送到 AWS CloudWatch Logs
- splunk:通过 HTTP Event Collector(HEC)将日志写到 Splunk
- cplogs:将日志发送到 Google Cloud Platform(GCP)Logging
- logentries: 将日志写到 Rapid7 Logentries
- etwlogs: 将日志写到 Event Tracing for Windows(ETW)