采集docker-container日志

主流的日志收集方案是使用ELK进行收集,要使用此方案收集docker容器的日志,网络上有很多的教程,当中不乏复制粘贴出来的文章,当中存在一些坑在网上的教程是没有提及的,例如如何过滤不需要的docker容器日志。通过自身实践(此站点使用着这个方案),使用filebeat收集日志数据,并且使用ELK进行数据处理,总结出此文章以做分享。


后续在使用过程中发现filebeat不太好用,所以转成了fluentd,请看

工具

filebeat

轻量的数据收集工具。收集数据,做一些简单的处理,之后发送到logstash进行数据进一步处理。

logstash

数据收集与处理工具。用于接收来自filebeat的数据,进行数据格式化,然后发送到elasticsearch。由于logstash是运行在jvm上,所以相比filebeat,会消耗更多资源。

elasticsearch

数据持久化与搜索。负责接收来自logstash的数据,然后保存。

kibana

数据统计可视化工具。将elasticsearch中的数据,通过图表等进行展示。

docker log

docker的标准日志输出是stdout和stderr,例如nginx的配置中,配置access_log /dev/stdout;error_log /dev/stderr;。这样就可以通过docker容器采集到nginx的日志输出。
docker启动可以指定不同的driver,其中默认的是json-file,使用json-file,可以使用docker logs $containerName来查看docker容器的日志,另外日志信息会存放到/var/lib/docker/containers/$containerId/$containerId-json.log下(此目录的生命周期跟随容器的生命周期),因此可以通过采集/var/lib/docker/containers/*/*.log来采集docker容器的信息。
关于docker log driver,详见:https://docs.docker.com/engine/extend/plugins_logging/#create-a-logging-plugin

filebeat

docker-compose

挂载docker的log目录到filebeat的容器下,另外还需要挂载一个目录到/data,用于保存采集日志文件的位置信息,默认路径是/data/registry

1
2
3
4
5
6
7
8
### docker-compose
### filebeat #############################
filebeat:
build:
context: ./filebeat
volumes:
- ${FILE_BEAT_REGISTRY_FILE}:/data
- /var/lib/docker/containers:/var/lib/docker/containers

配置

关于filebeat的配置,详见:https://www.elastic.co/guide/en/beats/filebeat/5.0/configuration-filebeat-options.html

prospectors

配置需要采集的数据的来源,如log、stdin

1
2
3
4
5
6
7
8
filebeat.prospectors:
- input_type: log
document_type: filebeat-docker-logs
paths:
- "/var/lib/docker/containers/*/*.log"
json.keys_under_root: true # 因为docker使用的log driver是json-file,因此采集到的日志格式是json格式,设置为true之后,filebeat会将日志进行json_decode处理
tail_files: true
registry_file: /var/lib/filebeat/registry

processors

filebeat采集数据后的处理配置,可以将采集到的数据进行格式化,去除不需要的字段。
/var/lib/docker/containers采集到的是当前宿主机所有container的日志,这时候会有一个坑,假如filebeat是以docker container来运行的话,那么filebeat会采集到自己的container的日志。所以在processors中进行配置,过滤掉不需要的container日志。

为docker-log日志文件加上标记

通过查看/var/lib/docker/containers发现,日志文件的命名方式是使用containerId来命名的,因此无法区分日志的容器所对应的镜像,而且日志格式如下:

1
2
3
# nginx日志
{"log":"172.21.0.6 - - [24/Feb/2018:02:03:20 +0000] \"GET /api/archive/recent HTTP/1.1\" 200 411 \"-\" \"-\"\n","stream":"stdout","time":"2018-02-24T02:03:20.807279361Z"}
{"log":"172.21.0.6 - - [24/Feb/2018:02:03:20 +0000] \"GET /api/archive HTTP/1.1\" 200 4575 \"-\" \"-\"\n","stream":"stdout","time":"2018-02-24T02:03:20.816431883Z"}

日志内容也没有可以区分镜像相关的信息,因此需要在日志内容中加上可以进行区分的信息,可以通过设置logging,增加labels信息。

1
2
3
4
5
6
7
8
9
# docker-compose
nginx:
build: ./nginx
labels:
service: platform
logging:
driver: json-file
options:
labels: "service"

labels字段表示给容器打上label信息,如上则给容器打上了一个service的labels,其值为platform
logging.options.labels配置后可以在container的label信息输出到日志信息中。
设置完成后通过使用docker inspect $containerName可以看到以下信息表示labels设置成功

然后看容器产生的日志就会发现新增加的labels信息

1
2
{"log":"172.21.0.6 - - [24/Feb/2018:02:03:20 +0000] \"GET /api/archive/recent HTTP/1.1\" 200 411 \"-\" \"-\"\n","stream":"stdout","attrs":{"service":"platform"},"time":"2018-02-24T02:03:20.807279361Z"}
{"log":"172.21.0.6 - - [24/Feb/2018:02:03:20 +0000] \"GET /api/archive HTTP/1.1\" 200 4575 \"-\" \"-\"\n","stream":"stdout","attrs":{"service":"platform"},"time":"2018-02-24T02:03:20.816431883Z"}

过滤日志,提取需要的字段

配置如下

1
2
3
4
5
6
7
8
processors:
- drop_event:
when:
not:
equals:
attrs.service: "platform"
- drop_fields:
fields: ["input_type", "offset", "source", "stream", "beat"]

由于filebeat.yml中设置了json.keys_under_root: true,所以filebeat会自动对docker container的日志文件内容进行格式化。使用drop_event将attrs.service不等于platform的事件过滤,然后删除不需要的字段input_type、offset等信息。

配置完成之后,filebeat会给logstash发送一下格式的数据

1
2
3
4
5
6
7
8
9
{
"@timestamp": "2018-02-24T06:00:03.309Z",
"attrs": {
"service": "platform"
},
"log": "172.21.0.6 - - [24/Feb/2018:05:59:54 +0000] \"GET /api/article/collect-docker-container-log/content HTTP/1.1\" 200 6787 \"-\" \"-\"\n",
"time": "2018-02-24T05:59:54.973844296Z",
"type": "filebeat-docker-logs"
}

output

配置filebeat的输出,可以配置不同的输出,例如stdout、输出到logstash、或者直接输出到elasticsearch。

1
2
3
4
output:
### Logstash as output
logstash:
hosts: ["logstash:5044"]

logstash

docker-compose

比较简单,没有什么特别,确保开启一个端口用于接收数据即可,端口默认是5044。另外需要将配置文件挂载到/etc/logstash/conf.d

1
2
3
4
5
6
#docker-compose
logstash:
build:
context: ./logstash
ports:
- "5044:5044"

1
2
3
4
5
#Dockerfile
FROM logstash:5.6

ADD conf.d/*.conf /etc/logstash/conf.d
CMD logstash -f /etc/logstash/conf.d/

配置

详见:https://www.elastic.co/guide/en/logstash/current/configuration.html
由于经过filebeat的处理之后的格式基本没有什么需要处理的了,所以logstash只需要将接收到的数据,转发到elasticsearch中即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 配置输入,接收来自filebeat的数据
input {
beats {
port => 5044
}
}

# 配置输出,输出到elasticsearch中
output {
elasticsearch {
hosts => "elasticsearch:9200"
index => "logstash-%{type}"
template_overwrite => true
}
}

elasticsearch和kibana

两者的详细配置请见:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/getting-started.html
https://www.elastic.co/guide/en/kibana/current/setup.html
docker-compose比较简单,只是挂载数据目录即可

1
2
3
4
5
6
7
elasticsearch:
image: elasticsearch
user: elasticsearch # 用elasticsearch作为运行的用户,否则会有错误提示信息,无法使用root作为运行用户
volumes:
- ${ELASTIC_DATA}:/usr/share/elasticsearch/data # 挂载数据保存的目录
ports:
- "9200:9200"

1
2
3
4
5
6
7
8
kibana:
image: kibana
ports:
- 5601:5601
links:
- elasticsearch:${ELASTIC_HOST}
environment:
- ELASTICSEARCH_URL=http://${ELASTIC_HOST}:9200 # 设置elasticsearch的访问url

以上配置完成之后,在浏览器中分别输入以下地址,检查是否配置成功。
http://localhost:9200/_cat/indices?v,看到如下

http://localhost:5601,进入kibana设置界面