手把手系列 - 搭建 efk 7 收集 docker 容器日志
目录
- Docker容器最佳实践
- 手把手系列 - 搭建 EFK 7 收集 docker 容器日志
- 手把手系列 - 搭建 EFK 8 收集 docker 容器日志
- 手把手系列 - 使用docker容器搭建efk8
前言
本文书写时间:2022-06-30 以最新 7 的版本 7.17.4 搭建一套 docker 容器的日志收集系统。
elasticsearch: 7.17.4
filebeat: 7.17.4
kibana: 7.17.4
阅读本文前,请对 docker 日志有个正确的认识,请参考:
系统版本
System: CentOS Linux release 7.9.2009 (Core)
Kernel: 3.10.0-1160.el7.x86_64
要求:主机可访问互联网,本文采用 yum 安装。
注意:'>' 为命令输入提示符 '>' 后为输入的命令
docker 版本
Docker-CE
* Server Version: 20.10.7
* Storage Driver: overlay2
安装过程
主机名 | ip地址 |
---|---|
efk-node | 192.168.1.101 |
安装顺序如下:
- 系统初始化
- docker-ce
- elasticsearch
- kibana
- filebeat
系统初始化
系统初始化分为以下几步:
- 修改主机名
- 关闭selinux 和 firewalld
- 配置国内yum源
- 校对时间
修改主机名
>hostnamectl set-hostname efk-node
>hostname efk-node
>echo "192.168.1.101 efk-node" >> /etc/hosts
#断开会话重新连接
root@efk-node(192.168.1.101)/root>hostname
efk-node
关闭selinux 和 firewalld
### 关闭 selinux
>sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
>systemctl disable firewalld
>reboot
配置国内yum源
>cd /etc/yum.repos.d/
#centos-7源
>curl http://mirrors.aliyun.com/repo/Centos-7.repo -o ./Centos-7.repo
>sed -i '/aliyuncs/d' Centos-7.repo
#docker-ce源
>curl http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -o ./docker-ce.repo
#epel-7源
>curl http://mirrors.aliyun.com/repo/epel-7.repo -o ./epel-7.repo
#efk源
>cat << EOF > elasticstack.repo
[elasticstack]
name = elasticstack
gpgcheck = 0
baseurl = https://mirrors.tuna.tsinghua.edu.cn/elasticstack/yum/elastic-7.x/
EOF
校对时间
>yum install -y ntpdate
>ntpdate tiger.sina.com.cn
Docker-ce
- 安装docker-ce
>yum install -y docker-ce
- 添加docker-ce 配置
>mkdir /etc/docker/
>cat << 'EOF' > /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"exec-opts": ["native.cgroupdriver=systemd"],
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
],
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
EOF
- 启动 docker
>systemctl enable docker; systemctl start docker
查看docker 信息
>cat << 'EOF' >> /etc/sysctl.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
>sysctl --system
>docker info
Elasticsearch
Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。
- 安装elasticsearch
> yum install -y elasticsearch
- 修改默认配置文件
>cd /etc/elasticsearch/
# 修改操作前,一定要做好源文件的备份
>cp -a elasticsearch.yml elasticsearch.yml.orig
# 修改后的配置文件
>egrep -v "^#|^$" elasticsearch.yml
path.data: /var/lib/elasticsearch //es数据存储目录
path.logs: /var/log/elasticsearch //es日志存储目录
network.host: 192.168.1.101 //es监听地址
http.port: 9200 //es端口
### 这两项需要配置,否则启动会报错 ###
discovery.seed_hosts: ["efk-node"] // 配置候选节点通信
cluster.initial_master_nodes: ["efk-node"] // 集群初始化提供master地址
### 解决跨越问题 ###
http.cors.enabled: true
http.cors.allow-origin: "*"
- 启动elasticsearch
>systemctl enable elasticsearch; systemctl start elasticsearch
启动完成后,elasticsearch会监听 9200 和 9300
>netstat -ntplu | egrep java
tcp6 0 0 192.168.1.101:9200 :::* LISTEN 4305/java
tcp6 0 0 192.168.1.101:9300 :::* LISTEN 4305/java
使用 curl 访问 9200
>curl 192.168.1.101:9200
{
"name" : "efk-node",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "hZ7Y40MKQ4aDwSbUYZBRpg",
"version" : {
"number" : "7.17.5", //elasticsearch版本
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "8d61b4f7ddf931f219e3745f295ed2bbc50c8e84",
"build_date" : "2022-06-23T21:57:28.736740635Z",
"build_snapshot" : false,
"lucene_version" : "8.11.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
Kibana
Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。
- 安装kibana
>yum install -y kibana
- 修改 kibana 配置文件
>cd /etc/kibana/
#修改前,备份源文件
>cp -a kibana.yml kibana.yml.orig
>egrep -v "^#|^$" kibana.yml
server.host: "192.168.1.101" //监听地址
elasticsearch.hosts: ["http://192.168.1.101:9200"] //连接es地址,不修改kibana浏览器访问会无法打开
i18n.locale: "zh-CN" //汉化
- 启动kibana
> systemctl enable kibana ; systemctl start kibana
kibana监听地址默认为:5601
> netstat -ntplu | egrep 5601
tcp 0 0 192.168.1.101:5601 0.0.0.0:* LISTEN 5241/node
- 通过浏览器访问kibana
Filebeat
- 安装 filebeat
>yum install -y filebeat
到此,所有安装的软件包都已经安装就位,接下来就是配置的修改及调整。所有软件包如下:
>rpm -qa | egrep "docker-ce|elasticsearch|kibana|filebeat"
filebeat-7.17.5-1.x86_64
docker-ce-cli-20.10.7-3.el7.x86_64
docker-ce-rootless-extras-20.10.7-3.el7.x86_64
elasticsearch-7.17.5-1.x86_64
kibana-7.17.5-1.x86_64
docker-ce-20.10.7-3.el7.x86_64
配置调试过程
本次以收集 nginx 容器日志为例,进行调试配置。
- 编写收集容器日志的 filebeat 配置文件
### 一定要在 /etc/filebeat/ 目录下编写配置文件,否则无法使用调试模式启动。
>cd /etc/filebeat/
在编写 filebeat 配置文件之前,得了解下 filebeat 配置文件结构,强烈建议参考官方文档:
https://www.elastic.co/guide/en/beats/filebeat/7.17/directory-layout.html
通过目录,可以简单理解,filebeat 分为 inputs 和 output 两部分,首先查看 inputs
这里存在 Container 和 docker 两部分,点开查看,首先查看 Docker input
docker input 在 7.2.0 版本中已经被弃用,所以这里建议使用 container
这里有直接的配置示例,直接抄写即可。
接下来,查看 output 部分。
因为,我们处于配置调试节点,所以将日志收集直接打印在控制台展示,如果觉得数据没问题,再进行配置到 elasticsearch 中,所以这里直接查看 Console
直接抄写 output 配置
综上,目前的 filebeat 配置文件如下:
>cat /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
output.console:
pretty: true
通过 filebeat 调试模式启动。
>filebeat -e -c docker-nginx.yml
2022-06-30T15:21:30.805+0800 INFO [input] log/input.go:171 Configured paths: [/var/lib/docker/containers/*/*.log] {"input_id": "2e4d48c5-ae36-4472-8901-1d5564b3f22c"}
2022-06-30T15:21:30.805+0800 INFO [crawler] beater/crawler.go:148 Starting input (ID: 14033481556371524303)
2022-06-30T15:21:30.805+0800 INFO [crawler] beater/crawler.go:106 Loading and starting Inputs completed. Enabled inputs: 1
出现如上信息,并占用控制台则表示启动成功,等待着日志的打印。
既然要收集 nginx 容器的日志,就得有 nginx 容器,这里启动 nginx 容器
>docker run --name ngx -p 80:80 -d nginx:alpine
当容器启动成功后,filebeat 控制台就会打印如下信息日志:
{
"@timestamp": "2022-06-30T07:24:52.053Z",
"@metadata": {
"beat": "filebeat",
"type": "_doc",
"version": "7.17.5"
},
"log": {
"file": {
"path": "/var/lib/docker/containers/ce21b205f5f38165f6c26487bbcad5010edc23fc893b1c658782c0c3c46924a5/ce21b205f5f38165f6c26487bbcad5010edc23fc893b1c658782c0c3c46924a5-json.log"
},
"offset": 2444
},
"stream": "stderr",
"message": "2022/06/30 07:24:52 [notice] 1#1: start worker process 35",
"input": {
"type": "container"
},
"agent": {
"type": "filebeat",
"version": "7.17.5",
"hostname": "efk-node",
"ephemeral_id": "5436287d-984a-4b94-b2ea-43903321cf82",
"id": "42ed1620-c7e6-4352-9f2b-a8f0e57c7446",
"name": "efk-node"
},
"ecs": {
"version": "1.12.0"
},
"host": {
"name": "efk-node"
}
}
出现这样的信息,说明filebeat 已经捕获到了 容器的日志信息
接下来就要对日志信息进行筛选,大段的日志信息有很多我们并不需要过多的去关注,因此需要剔除无用的信息,继续查看官方文档:
在上面除了,Input 和 output 还有一个 Processors 处理器,这个 Processors 也可以理解为 filter ,筛选器
在 Processors 子类中,找到了 drop_fields 删除字段配置,打开查看。
通过官方示例查看,when 当...时候... 这里就是通过条件筛选进行字段的删除,目前我们这里没有条件筛选,尝试直接删除字段。
注意:通过官方文档查看 processors 是与 input 和 output 平级的,所以注意格式。
于是,配置文件又修改为如下:
# /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- drop_fields:
fields: ["log","agent","ecs"]
output.console:
pretty: true
在刚才的控制台 ctrl + c 结束,重新启动
>filebeat -e -c docker-nginx.yml
浏览器访问 nginx 后,查看控制台:
{
"@timestamp": "2022-06-30T07:42:33.163Z",
"@metadata": {
"beat": "filebeat",
"type": "_doc",
"version": "7.17.5"
},
"input": {
"type": "container"
},
"host": {
"name": "efk-node"
},
"stream": "stdout",
"message": "192.168.1.2 - - [30/Jun/2022:07:42:33 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\" \"-\""
}
到目前为止,已经提取出来对我们有用的日志信息,接下来就可以存储到 elasticsearch 中,查看官方文档如何存储到 es 中。
很简单就存储到 es 中,注意:这里我们并没有开启 https ,只需写成 http即可。修改 filebeat 配置如下:
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- drop_fields:
fields: ["log","agent","ecs"]
# output.console:
# pretty: true
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
重启控制台
ctrl+c
>filebeat -e -c docker-nginx.yml
浏览器访问 nginx 容器生成日志,然后通过 curl 查看 es 中所有索引信息:
>curl http://192.168.1.101:9200/_cat/indices?v
不难发现,下面这一条就是我们通过 filebeat 存储进去的日志信息。这个索引名怎么看怎么奇怪,因为存储的是nginx 容器的日志,所以做好将索引名修改为 nginx 相关的名字,这样就可以通过索引名确定存储的数据信息。
查看 output -> elasticsearch 官方文档
修改 配置文件如下:
### /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- drop_fields:
fields: ["log","agent","ecs"]
# output.console:
# pretty: true
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
index: "docker-nginx-%{+yyyy.MM.dd}"
重启控制台
ctrl+c
>filebeat -e -c docker-nginx.yml
2022-06-30T16:16:45.273+0800 INFO instance/beat.go:685 Home path: [/usr/share/filebeat] Config path: [/etc/filebeat] Data path: [/var/lib/filebeat] Logs path: [/var/log/filebeat] Hostfs Path: [/]
2022-06-30T16:16:45.273+0800 INFO instance/beat.go:693 Beat ID: 42ed1620-c7e6-4352-9f2b-a8f0e57c7446
2022-06-30T16:16:45.273+0800 ERROR instance/beat.go:1014 Exiting: setup.template.name and setup.template.pattern have to be set if index name is modified
Exiting: setup.template.name and setup.template.pattern have to be set if index name is modified
出现了报错信息:
如果索引名被修改,就必须配置 setup.template.name and setup.template.pattern
查询了下度娘,修改配置文件如下:
### /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- drop_fields:
fields: ["log","agent","ecs"]
# output.console:
# pretty: true
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
index: "docker-nginx-%{+yyyy.MM.dd}"
setup.ilm.enabled: false
setup.template.name: "docker"
setup.template.pattern: "docker-*"
重启控制台
>filebeat -e -c docker-nginx.yml
浏览器访问后,查看索引
filebeat 索引已经被手动删除,这里生成了 docker-nginx-* 索引
接下来就可以通过 kibana 展示出来。
这样就将 nginx 容器的日志信息收集展示出来了。
因为 filebeat 一直使用的是 调试模式 filebeat -e -c docker-nginx.yml
模式来做的,因此将 docker-nginx.yml 配置信息整合到 主配置文件 filebeat.yml 中。
整合后的 filebeat.yml 如下:
# ============================== Filebeat inputs ===============================
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
# ============================== Filebeat modules ==============================
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: true
# Period on which files under path should be checked for changes
#reload.period: 10s
# ======================= Elasticsearch template setting =======================
setup.template.settings:
index.number_of_shards: 1
#index.codec: best_compression
#_source.enabled: false
# ================================== General ===================================
setup.ilm.enabled: false
setup.template.name: "docker"
setup.template.pattern: "docker-*"
# =================================== Kibana ===================================
# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
# This requires a Kibana endpoint configuration.
setup.kibana:
# Kibana Host
# Scheme and port can be left out and will be set to the default (http and 5601)
# In case you specify and additional path, the scheme is required: http://localhost:5601/path
# IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
host: "192.168.1.101:5601"
# Kibana Space ID
# ID of the Kibana Space into which the dashboards should be loaded. By default,
# the Default Space will be used.
#space.id:
# ================================== Outputs ===================================
# Configure what output to use when sending the data collected by the beat.
# ---------------------------- Elasticsearch Output ----------------------------
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
index: "docker-nginx-%{+yyyy.MM.dd}"
# ================================= Processors =================================
# processors:
# - add_host_metadata:
# when.not.contains.tags: forwarded
# - add_cloud_metadata: ~
# - add_docker_metadata: ~
# - add_kubernetes_metadata: ~
processors:
- drop_fields:
fields: ["log","agent","ecs"]
启动服务:
systemctl start filebeat
进阶配置过程
现在的数据就简化了很多,只保留对我们有用的信息。接下来就要考虑几个问题
-
nginx 日志分为 access.log 和 error.log 如何区分存储?
-
如果多个容器如果区分不同的容器日志?
stdout 及 stderr 存入不同索引
对于 nginx来说,可能只需要关注 错误日志,如何区分存储呢?其实查看上面控制台输出的日志内容不难发现一个字段:stream
{
"@timestamp": "2022-06-30T07:42:33.163Z",
"@metadata": {
"beat": "filebeat",
"type": "_doc",
"version": "7.17.5"
},
"input": {
"type": "container"
},
"host": {
"name": "efk-node"
},
"stream": "stdout", //这个字段
"message": "..."
}
其实对于 nginx 这样的容器,遵循一个标准,标准输出到 stdout 标准错误输出到 stderr 可进入容器查看:
~ # ls -l /var/log/nginx/
total 0
lrwxrwxrwx 1 root root 11 Jun 22 19:20 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 Jun 22 19:20 error.log -> /dev/stderr
所以,对于 stream: stderr 就是需要关注的 错误日志,所以根据字段条件进行区分,修改 filebeat 配置文件如下:
### /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- drop_fields:
fields: ["log","agent","ecs"]
# output.console:
# pretty: true
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
# index: "docker-nginx-%{+yyyy.MM.dd}"
indices:
- index: "docker-nginx-access-%{+yyyy.MM.dd}"
when.contains:
stream: "stdout"
- index: "docker-nginx-error-%{+yyyy.MM.dd}"
when.contains:
stream: "stderr"
setup.ilm.enabled: false
setup.template.name: "docker"
setup.template.pattern: "docker-*"
注意:调试 filebeat 时,建议先关闭 filebeat服务
>systemctl stop filebeat
>filebeat -e -c docker-nginx.yml
通过浏览器访问nginx 一个并不存在的 uri 让其生成错误日志,查看 索引:
根据了不同的条件存入不不同的索引中,这样可以通过 kibana 来展示出来。
docker-nginx-error-* 也如上添加展示。
这样, 就可以通过不同的索引展示各种所需日志数据。
总结
该部分解决的问题是:单容器,正常日志和错误日志分类存放的问题。
多容器及不同业务之间的日志汇集需要看下一个部分。
不同业务容器存入不同索引
容器使用场景,肯定会存在不同的程序或者不同的业务都运行于容器当中。举个最简单的架构 lnmp ,nginx 和 php 的日志如果存储在同一个索引里,日后查询排错还不如直接查看源日志文件,这就违背了搭建日志收集系统的初衷。
通过上面 filebeat 中 inputs 可以看到是 通过解析容器日志目录来获取日志信息的,而容器的名称和ID 都会随着生命周期而变动的,因此无法像物理或者虚拟主机一样通过IP联系起来。这个时候就需要为容器打标签,通过打标签的形式区分不同业务的容器集合。
注意:做这一部分示例前,请将运行中的容器关闭并删除、关闭 filebeat
docker rm -f `docker ps -aq`
systemctl stop filebeat
为容器打标签
- docker 直接启动打标签
docker run --name nginx -p 80:80 --label service=nginx --log-opt labels=service -d nginx:alpine
- 通过docker-compose 打标签
### docker-compose.yml
version: "3"
services:
nginx:
container_name: "nginx"
image: nginx:alpine
environment:
- "TZ=Asia/Shanghai"
labels:
service: nginx
logging:
options:
labels: "service"
ports:
- "80:80"
上面两种方式,任意执行一种。
通过浏览器访问 http://192.168.1.101/ , 查看容器日志:
>tail -1 /var/lib/docker/containers/b066871ed799d21c3633341ec15dcfdc712067b483b7a4c7e3b3f909cf380940/b066871ed799d21c3633341ec15dcfdc712067b483b7a4c7e3b3f909cf380940-json.log | jq
{
"log": "192.168.1.2 - - [01/Jul/2022:01:51:17 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\" \"-\"\n",
"stream": "stdout",
"attrs": {
"service": "nginx"
},
"time": "2022-07-01T01:51:17.717295543Z"
}
日志中多有 attrs 属性字段,就是打的标签,然后可通过 filebeat output 中 when 条件来过滤。
示例:
需求:有两个不同的 web 服务, 一个开启80端口,一个开启 8080 端口,需要对它们日志存储到不同的索引里。
这里使用 docker-compose 启动容器,docker-compose如下:
### /root/manifests/docker-compose.yml
version: "3"
services:
# 80 web 服务
nginx:
container_name: "nginx"
image: nginx:alpine
environment:
- "TZ=Asia/Shanghai"
labels:
service: nginx # 标记
logging:
options:
labels: "service"
ports:
- "80:80"
# 8080 web 服务
httpd:
container_name: "httpd"
image: httpd:latest
environment:
- "TZ=Asia/Shanghai"
labels:
service: httpd # 标记
logging:
options:
labels: "service"
ports:
- "8080:80"
使用 dokcer-compose 启动
>docker-compose up -d
>docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
httpd "httpd-foreground" httpd running 0.0.0.0:8080->80/tcp, :::8080->80/tcp
nginx "/docker-entrypoint.…" nginx running 0.0.0.0:80->80/tcp, :::80->80/tcp
然后编写 filebeat 配置文件:
#文件目录: /etc/filebeat/docker-compose-web.yml
filebeat.inputs:
- type: container
enabled: true
paths:
- '/var/lib/docker/containers/*/*.log'
output.elasticsearch:
hosts: ["http://192.168.1.101:9200"]
indices:
- index: "web-nginx-access-%{+yyyy.MM.dd}"
when.contains:
# 日志中包括 docker.attrs.service==nginx 且 stream=stdout 存入web-nginx-access-%{+yyyy.MM.dd}
docker.attrs.service: "nginx"
stream: "stdout"
- index: "web-nginx-error-%{+yyyy.MM.dd}"
when.contains:
# 日志中包括 docker.attrs.service==nginx 且 stream=stderr 存入web-nginx-error-%{+yyyy.MM.dd}
docker.attrs.service: "nginx"
stream: "stderr"
- index: "web-httpd-access-%{+yyyy.MM.dd}"
when.contains:
# 日志中包括 docker.attrs.service==httpd 且 stream=stdout 存入web-httpd-access-%{+yyyy.MM.dd}
docker.attrs.service: "httpd"
stream: "stdout"
- index: "web-httpd-error-%{+yyyy.MM.dd}"
when.contains:
# 日志中包括 docker.attrs.service==httpd 且 stream=stderr 存入web-httpd-error-%{+yyyy.MM.dd}
docker.attrs.service: "httpd"
stream: "stderr"
processors:
- drop_fields:
fields: ["log","ecs","agent"]
setup.kibana:
host: "192.168.1.101:5601"
setup.ilm.enabled: false
setup.template.name: "web"
setup.template.pattern: "web-*"
启动:
>cd /etc/filebeat
>filebeat -e -c docker-compose-web.yml
通过浏览器访问后,查看 索引
这里就通过不同的条件将不同业务不同类型的日志进行了分开存储,然后在 kibana 如上配置,就可以分类查看。