目录

隐藏
  1. Docker 日志都在哪里?怎么收集?
  2. 不同容器的日志汇聚到 fluentd 后如何区分?
Docker 日志都在哪里?怎么收集?
日志分两类,一类是 Docker 引擎日志;另一类是 容器日志。

Docker 引擎日志

Docker 引擎日志 一般是交给了 Upstart(Ubuntu 14.04) 或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者一般通过 jounarlctl -u docker 来读取。不同系统的位置都不一样,SO上有人总结了一份列表,我修正了一下,可以参考:

系统 日志位置
Ubuntu(14.04) /var/log/upstart/docker.log
Ubuntu(16.04) journalctl -u docker.service
CentOS 7/RHEL 7/Fedora journalctl -u docker.service
CoreOS journalctl -u docker.service
OpenSuSE journalctl -u docker.service
OSX ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/log/d‌ocker.log
Debian GNU/Linux 7 /var/log/daemon.log
Debian GNU/Linux 8 journalctl -u docker.service
Boot2Docker /var/log/docker.log
容器日志

容器的日志 则可以通过 docker logs 命令来访问,而且可以像 tail -f 一样,使用 docker logs -f 来实时查看。如果使用 Docker Compose,则可以通过 docker-compose logs <服务名> 来查看。

如果深究其日志位置,每个容器的日志默认都会以 json-file 的格式存储于 /var/lib/docker/containers/<容器id>/<容器id>-json.log 下,不过并不建议去这里直接读取内容,因为 Docker 提供了更完善地日志收集方式 - Docker 日志收集驱动。

关于日志收集,Docker 内置了很多日志驱动,可以通过类似于 fluentd, syslog 这类服务收集日志。无论是 Docker 引擎,还是容器,都可以使用日志驱动。比如,如果打算用 fluentd 收集某个容器日志,可以这样启动容器:

$ docker run -d \
--log-driver=fluentd \
--log-opt fluentd-address=10.2.3.4:24224 \
--log-opt tag="docker.{{.Name}}" \
nginx
其中 10.2.3.4:24224 是 fluentd 服务地址,实际环境中应该换成真实的地址。

具体使用 fluentd 的方法,请参考我写的一组 fluentd 日志收集的例子:

https://coding.net/u/twang2218/p/docker-example/git/tree/master/fluentd

不同容器的日志汇聚到 fluentd 后如何区分?
有两种概念的区分,一种是区分开不同容器的日志,另一种是区分开来不同服务的日志。

区分不同容器的日志是很直观的想法。运行了几个不同的容器,日志都送向日志收集,那么显然不希望 nginx 容器的日志和 MySQL 容器的日志混杂在一起看。

但是在 Swarm 集群环境中,区分容器就已经不再是合理的做法了。因为同一个服务可能有许多副本,而又有很多个服务,如果一个个的容器区分去分析,很难看到一个整体上某个服务的服务状态是什么样子的。而且,容器是短生存周期的,在维护期间容器生存死亡是很常见的事情。如果是像传统虚拟机那样子以容器为单元去分析日志,其结果很难具有价值。因此更多的时候是对某一个服务的日志整体分析,无需区别日志具体来自于哪个容器,不需要关心容器是什么时间产生以及是否消亡,只需要以服务为单元去区分日志即可。

这两类的区分日志的办法,Docker 都可以做到,这里我们以 fluentd 为例说明。

version: '2'
services:
web:
image: nginx:1.11-alpine
ports:
- "3000:80"
labels:
section: frontend
group: alpha
service: web
image: nginx
base_os: alpine
logging:
driver: fluentd
options:
fluentd-address: "localhost:24224"
tag: "frontend.web.nginx.{{.Name}}"
labels: "section,group,service,image,base_os"
这里我们运行了一个 nginx:alpine 的容器,服务名为 web。容器的日志使用 fluentd 进行收集,并且附上标签 frontend.web.nginx.<容器名>。除此以外,我们还定义了一组 labels,并且在 logging 的 options 中的 labels 中指明希望哪些标签随日志记录。这些信息中很多一部分都会出现在所收集的日志里。

让我们来看一下 fluentd 收到的信息什么样子的。

{
"frontend.web.nginx.service_web_1": {
"image": "nginx",
"base_os": "alpine",
"container_id": "f7212f7108de033045ddc22858569d0ac50921b043b97a2c8bf83b1b1ee50e34",
"section": "frontend",
"service": "web",
"log": "172.20.0.1 - - [09/Dec/2016:15:02:45 +0000] \"GET / HTTP/1.1\" 200 612 \"-\" \"curl/7.49.1\" \"-\"",
"group": "alpha",
"container_name": "/service_web_1",
"source": "stdout",
"remote": "172.20.0.1",
"host": "-",
"user": "-",
"method": "GET",
"path": "/",
"code": "200",
"size": "612",
"referer": "-",
"agent": "curl/7.49.1",
"forward": "-"
}
}
如果去除 nginx 正常的访问日志项目外,我们就可以更清晰的看到有哪些元数据信息可以利用了。

{
"frontend.web.nginx.service_web_1": {
"image": "nginx",
"base_os": "alpine",
"container_id": "f7212f7108de033045ddc22858569d0ac50921b043b97a2c8bf83b1b1ee50e34",
"section": "frontend",
"service": "web",
"group": "alpha",
"container_name": "/service_web_1",
"source": "stdout",
}
}
可以看到,我们在 logging 下所有指定的 labels 都在。我们完全可以对每个服务设定不同的标签,通过标签来区分服务。比如这里,我们对 web 服务指定了 service=web 的标签,我们同样可以对数据库的服务设定标签为 service=mysql,这样在汇总后,只需要对 service 标签分组过滤即可,分离聚合不同服务的日志。

此外,我们可以设置不止一个标签,比如上面的例子,我们设置了多组不同颗粒度的标签,在后期分组的时候,可以很灵活的进行组合,以满足不同需求。

此外,注意 frontend.web.nginx.service_web_1,这是我们之前利用 --log-opt tag=frontend.web.nginx.<容器名> 进行设定的,其中 <容器名> 我们使用的是 Go 模板表达式 {{.Name}}。Go 模板很强大,我们可以用它实现非常复杂的标签。在 fluentd 中,项可以根据标签来进行筛选。

这里可以唯一表示容器的,有容器 ID container_id,而容器名 container_name 也从某种程度上可以用来区分不同容器。因此进行容器区分日志的时候,可以使用这两项。

还有一个 source,这表示了日志是从标准输出还是标准错误输出得到的,由此可以区分正常日志和错误日志。

现在我们可以知道,除了容器自身输出的信息外,Docker 还可以为每一个容器的日志添加很多元数据,以帮助后期的日志处理中应对不同需求的搜索和过滤。

在后期处理中,fluentd 中可以利用或者插件根据 tag 或者其它元数据进行分别处理。而日志到了 ElasticSearch 这类系统后,则可以用更丰富的查询语言进行过滤、聚合。