手把手系列 - 搭建 efk 8 收集 docker 容器日志

目录


  1. Docker容器最佳实践
  2. 手把手系列 - 搭建 EFK 7 收集 docker 容器日志
  3. 手把手系列 - 搭建 EFK 8 收集 docker 容器日志
  4. 手把手系列 - 使用docker容器搭建efk8

前言


查看网络上真正搭建使用 efk 8.0 版本的并不多,于是本文记录搭建过程。

本文书写时间:2022-07-018.2.2 搭建一套 docker 容器的日志收集系统。

elasticsearch: 8.2.2
filebeat: 8.2.2
kibana: 8.2.2

阅读本文前,请对 docker 日志有个正确的认识,请参考:

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

安装顺序如下:

  1. 系统初始化
  2. docker-ce-20.10.7
  3. elasticsearch-8.2.2
  4. kibana-8.2.2
  5. filebeat-8.2.2

系统初始化

系统初始化分为以下几步:

  1. 修改主机名
  2. 关闭selinux 和 firewalld
  3. 配置国内yum源
  4. 校对时间

修改主机名

>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-8.x/
EOF

校对时间

>yum install -y ntpdate
>ntpdate tiger.sina.com.cn 

Docker-ce

  1. 安装docker-ce
>yum install -y docker-ce
  1. 添加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
  1. 启动 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是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。

  1. 安装elasticsearch
yum install -y elasticsearch-8.2.2

安装 elasticsearch 时候会出现如下提示:

image-20220706171507124

注意:这里用户名为:elastic ,密码可以修改,但是这里最好也记录一下。

用户名:elastic
密 码:j7CopSyDuOGhan*ZmI*u
  1. 修改默认配置文件
>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"]	// 配置候选节点通信
### 开启x-pack 安全功能 及 https 功能
xpack.security.enabled: true
xpack.security.enrollment.enabled: true
xpack.security.http.ssl:	//启用https
  enabled: true
  keystore.path: certs/http.p12
xpack.security.transport.ssl:	//
  enabled: true
  verification_mode: certificate
  keystore.path: certs/transport.p12
  truststore.path: certs/transport.p12
cluster.initial_master_nodes: ["efk-node"]	// 集群初始化提供master地址
http.host: 0.0.0.0
### 解决跨越问题 ###
http.cors.enabled: true
http.cors.allow-origin: "*"
  1. 启动elasticsearch
systemctl enable elasticsearch; systemctl start elasticsearch

启动完成后,elasticsearch会监听 92009300

>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

elasticsearch-8.2.2 中,无法使用 http 访问, 需要使用: https://192.168.1.101:9200/

image-20220701173955193

image-20220706171703472

image-20220706171715445

Kibana

Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

  1. 安装 kibana
>yum install -y kibana-8.2.2
  1. 修改 kibana 配置文件
>egrep -v "^#|^$" kibana.yml
server.port: 5601	# 端口
server.host: "0.0.0.0"	# 监听端口
elasticsearch.hosts: ["http://localhost:9200"]	# es链接
logging:
  appenders:
    file:
      type: file
      fileName: /var/log/kibana/kibana.log
      layout:
        type: json
  root:
    appenders:
      - default
      - file
pid.file: /run/kibana/kibana.pid
i18n.locale: "zh-CN"	# 汉化
  1. 启动 kibana
> systemctl enable kibana ; systemctl start kibana

kibana监听地址默认为:5601

>netstat -ntplu | egrep 5601
tcp        0      0 0.0.0.0:5601            0.0.0.0:*               LISTEN      5784/node
  1. 通过浏览器访问

image-20220706172840150

在centos7上获取命令如下:

> /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana

执行以后,将 token 复制到【注册令牌】中。

image-20220706173056210

点击 【配置 Elastic】 后,会跳出验证码:

image-20220706173150240

查看 kibana 验证码:

image-20220706173214304

输入验证码后,等待 kibana 验证完毕

image-20220706173240388

完成后,出现登录界面

image-20220706173258323

用户名密码就是安装 elasticsearch 生成的用户名密码:

用户名:elastic
密 码:j7CopSyDuOGhan*ZmI*u

image-20220706173358537

Filebeat

  1. 安装 filebeat
>yum install -y filebeat-8.2.2

到此,所有安装的软件包都已经安装就位,接下来就是配置的修改及调整。所有软件包如下:

> rpm -qa | egrep "docker-ce|elasticsearch|kibana|filebeat"
elasticsearch-8.2.2-1.x86_64
filebeat-8.2.2-1.x86_64
docker-ce-20.10.7-3.el7.x86_64
kibana-8.2.2-1.x86_64
docker-ce-cli-20.10.7-3.el7.x86_64
docker-ce-rootless-extras-20.10.7-3.el7.x86_64

配置调试过程

本次以收集 nginx 容器日志为例,进行调试配置。

  1. 编写收集容器日志的 filebeat 配置文件
### 一定要在 /etc/filebeat/ 目录下编写配置文件,否则无法使用调试模式启动。
>cd /etc/filebeat/

在编写 filebeat 配置文件之前,得了解下 filebeat 配置文件结构,强烈建议参考官方文档:

https://www.elastic.co/guide/en/beats/filebeat/8.2/directory-layout.html

image-20220706173936963

通过目录,可以简单理解,filebeat 分为 inputs 和 output 两部分,首先查看 inputs

image-20220706174045336

对比 7.14 版本的 filebeat,在8.2.2中已经弃用了 docker 模块,只能使用 container

image-20220706174555586

这里有直接的配置示例,直接抄写即可。

接下来,查看 output 部分。

image-20220706174723049

因为,我们处于配置调试节点,所以将日志收集直接打印在控制台展示,如果觉得数据没问题,再进行配置到 elasticsearch 中,所以这里直接查看 Console

image-20220706174636580

直接抄写 output 配置

综上,目前的 filebeat 配置文件如下:

>cat /etc/filebeat/docker-nginx.yml
filebeat.inputs:
- type: container
  paths:
    - '/var/lib/docker/containers/*/*.log'

output.console:
  pretty: true

通过 filebeat 调试模式启动。

/etc/filebeat> filebeat -e -c docker-nginx.yml

既然要收集 nginx 容器的日志,就得有 nginx 容器,这里启动 nginx 容器

>docker run --name ngx -p 80:80 -d nginx:alpine

当容器启动成功后,filebeat 控制台就会打印如下信息日志:

{
  "@timestamp": "2022-07-06T09:54:34.861Z",
  "@metadata": {
    "beat": "filebeat",
    "type": "_doc",
    "version": "8.2.2"
  },
  "host": {
    "name": "efk-node"
  },
  "agent": {
    "ephemeral_id": "21665b96-2ac4-4213-ae68-5be81c505ef9",
    "id": "90fe1004-6c5c-4755-9372-a154c57bf136",
    "name": "efk-node",
    "type": "filebeat",
    "version": "8.2.2"
  },
  "ecs": {
    "version": "8.0.0"
  },
  "log": {
    "offset": 2444,
    "file": {
      "path": "/var/lib/docker/containers/4c4e1811670c5745ad1307bf21ddfec7e63dd140388a012c07729da2308c2375/4c4e1811670c5745ad1307bf21ddfec7e63dd140388a012c07729da2308c2375-json.log"
    }
  },
  "stream": "stderr",
  "message": "2022/07/06 09:54:34 [notice] 1#1: start worker process 36",
  "input": {
    "type": "container"
  }
}

出现这样的信息,说明filebeat 已经捕获到了 容器的日志信息

接下来就要对日志信息进行筛选,大段的日志信息有很多我们并不需要过多的去关注,因此需要剔除无用的信息,继续查看官方文档:

image-20220706180738051

在上面除了,Inputoutput 还有一个 Processors 处理器,这个 Processors 也可以理解为 filter ,筛选器

image-20220706180802085

Processors 子类中,找到了 drop_fields 删除字段配置,打开查看。

image-20220706180820901

通过官方示例查看,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-07-06T10:05:49.917Z",
  "@metadata": {
    "beat": "filebeat",
    "type": "_doc",
    "version": "8.2.2"
  },
  "stream": "stdout",
  "message": "192.168.1.2 - - [06/Jul/2022:10:05:49 +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\" \"-\"",
  "input": {
    "type": "container"
  },
  "host": {
    "name": "efk-node"
  }
}

到目前为止,已经提取出来对我们有用的日志信息,接下来就可以存储到 elasticsearch 中,查看官方文档如何存储到 es 中。

image-20220706181839108

在 8 的版本中, elasticsearch 采用https 并开启了用户认证,这是区别 7 版本唯一的不同。官方文档也是采用三种不同的形式进行认证。

通过查证,可使用如下方式写入 elasticsearch :

output.elasticsearch:
  hosts: ["https://[ES地址]:9200"]
  ssl.verification_mode: "none"
  username: "elastic"
  password: "[密码]"

结合之前 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: ["https://192.168.1.101:9200"]
  ssl.verification_mode: "none"
  username: "elastic"
  password: "j7CopSyDuOGhan*ZmI*u"
  
### 加入以下内容,output.elasticsearch.index 才会生效
setup.ilm.enabled: false            # 关闭索引生命周期
setup.template.enabled: false       # 允许自动生成index模板
setup.template.overwrite: true      # 如果存在模块则覆盖
#setup.template.name: "docker"       # 生成index模板的名称
#setup.template.pattern: "docker-*"  # 生成index模板匹配的index格式

重启控制台

ctrl+c
>filebeat -e -c docker-nginx.yml

浏览器访问 nginx 容器生成日志,然后浏览器查看 es 中所有索引信息

image-20220707112620936

我们通过 filebeat 存储进去的日志信息。这个索引名怎么看怎么奇怪,因为存储的是nginx 容器的日志,所以做好将索引名修改为 nginx 相关的名字,这样就可以通过索引名确定存储的数据信息。

image-20220706182606523

修改 配置文件如下:

### /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: ["https://192.168.1.101:9200"]
  ssl.verification_mode: "none"
  username: "elastic"
  password: "j7CopSyDuOGhan*ZmI*u"
  index: "docker-nginx-%{+yyyy.MM.dd}"
  
### 加入以下内容,output.elasticsearch.index 才会生效
setup.ilm.enabled: false            # 关闭索引生命周期
setup.template.enabled: false       # 允许自动生成index模板
setup.template.overwrite: true      # 如果存在模块则覆盖
#setup.template.name: "docker"       # 生成index模板的名称
#setup.template.pattern: "docker-*"  # 生成index模板匹配的index格式

重启控制台

>filebeat -e -c docker-nginx.yml

浏览器访问后,查看索引

image-20220707113002397

这里生成了 docker-nginx-* 索引

接下来就可以通过 kibana 展示出来。

image-20220707113150441

image-20220707113217283

image-20220707113238176

image-20220707113327848

这样就将 nginx 容器的日志信息收集展示出来了。

进阶配置过程

现在的数据就简化了很多,只保留对我们有用的信息。接下来就要考虑几个问题

  1. nginx 日志分为 access.log 和 error.log 如何区分存储?

  2. 如果多个容器如果区分不同的容器日志?

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: ["https://192.168.1.101:9200"]
  ssl.verification_mode: "none"
  username: "elastic"
  password: "j7CopSyDuOGhan*ZmI*u"
  indices:
  - index: "docker-nginx-access-%{+yyyy.MM.dd}"
    when.contains:
      stream: "stdout"
  - index: "docker-nginx-error-%{+yyyy.MM.dd}"
    when.contains:
      stream: "stderr"
  
### 加入以下内容,output.elasticsearch.index 才会生效
setup.ilm.enabled: false            # 关闭索引生命周期
setup.template.enabled: false       # 允许自动生成index模板
setup.template.overwrite: true      # 如果存在模块则覆盖
#setup.template.name: "docker"       # 生成index模板的名称
#setup.template.pattern: "docker-*"  # 生成index模板匹配的index格式

image-20220707113736039

根据了不同的条件存入不不同的索引中,这样可以通过 kibana 来展示出来。

image-20220707113914434

这样, 就可以通过不同的索引展示各种所需日志数据。

总结

该部分解决的问题是:单容器,正常日志和错误日志分类存放的问题。

多容器及不同业务之间的日志汇集需要看下一个部分。

不同业务容器存入不同索引

容器使用场景,肯定会存在不同的程序或者不同的业务都运行于容器当中。举个最简单的架构 lnmp ,nginx 和 php 的日志如果存储在同一个索引里,日后查询排错还不如直接查看源日志文件,这就违背了搭建日志收集系统的初衷。

通过上面 filebeat 中 inputs 可以看到是 通过解析容器日志目录来获取日志信息的,而容器的名称和ID 都会随着生命周期而变动的,因此无法像物理或者虚拟主机一样通过IP联系起来。这个时候就需要为容器打标签,通过打标签的形式区分不同业务的容器集合。

注意:做这一部分示例前,请将运行中的容器关闭并删除、关闭 filebeat

docker rm -f `docker ps -aq`
systemctl stop filebeat

为容器打标签

  1. docker 直接启动打标签
docker run --name nginx -p 80:80 --label service=nginx --log-opt labels=service -d nginx:alpine
  1. 通过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'


processors:
- drop_fields:
   fields: ["log","ecs","agent"]


output.elasticsearch:
  hosts: ["https://192.168.1.101:9200"]
  ssl.verification_mode: "none"
  username: "elastic"
  password: "j7CopSyDuOGhan*ZmI*u"
  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"

### 加入以下内容,output.elasticsearch.index 才会生效
setup.ilm.enabled: false            # 关闭索引生命周期
setup.template.enabled: false       # 允许自动生成index模板
setup.template.overwrite: true      # 如果存在模块则覆盖
#setup.template.name: "docker"       # 生成index模板的名称
#setup.template.pattern: "docker-*"  # 生成index模板匹配的index格式

启动:

>cd /etc/filebeat
>filebeat -e -c docker-compose-web.yml

通过浏览器访问后,查看 索引

image-20220707114654517

这里就通过不同的条件将不同业务不同类型的日志进行了分开存储,然后在 kibana 如上配置,就可以分类查看。

image-20220707114927760

posted @ 2022-07-07 11:52  hukey  阅读(1061)  评论(0编辑  收藏  举报