搭建ELK环境收集日志

Posted by suleo on November 11, 2020

使用ELK收集nginx日志

一、工具简介

“ELK”是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch、Logstash 和 Kibana。

  • Elasticsearch 是一个搜索和分析引擎。
  • Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。
  • Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。

  • Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

  • Filebeat是一个轻量级的日志收集系统,能够监视指定的日志存放的目录或文件,有更新就自动收集并且发送给logstash或者ES服务器。

二、Centos安装docker

centos安装docker

  1. 移除旧的版本:

    sudo yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-selinux \
    docker-engine-selinux \
    docker-engine
    
  2. 安装一些必要的系统工具:

    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    
  3. 添加软件源信息

    sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  4. 更新 yum 缓存

    sudo yum makecache fast
    
  5. 安装 Docker-ce

    sudo yum -y install docker-ce
    
  6. 启动 Docker 后台服务

    sudo systemctl start docker
    
  7. 测试运行 hello-world

    docker run hello-world
    

三、配置与验证 (elasticsearch运行需要至少2G内存)

  1. 修改配置文件

    vim /etc/sysctl.conf

  2. 添加或者修改

    vm.max_map_count = 262144

    使生效

    sysctl -p

  3. 服务安装与运行

安装ELK有很多种方式,比如源码、rpm包,或docker;不过docker又分为了单个安装与ELK打包安装,我们通过docker打包安装,因为这样的方式相比来说最为简单,因为只需要下载镜像,然后运行起来就可以了

  • 镜像下载获取获取
    • 设置好加速地址之后,就可以开始拉取ELK镜像,参考命令如下

      docker pull sebp/elk

    • 下载镜像之后可以使用docker的命令来验证是否成功,参考命令如下

      docker images

    • 执行后docker返回结果如下

      REPOSITORY    TAG    IMAGE ID     CREATED    SIZE
          
      sebp/elk    latest  99e6d3f782ad    2 months ago  2.06GB
          
      hello-world   latest  fce289e99eb9    11 months ago 1.84kB
      
  • 容器运行

运行此容器的时候,需要将宿主机的端口转发到该容器,其中ES端口为9200,kibana端口为5601,logbate端口为5044;

由于 docker 镜像是只读的,而容器又是随时创建删除,所以建议将配置文件和数据存放在宿主机,便于后期维护,因此还需要将宿主机目录挂载到容器当中。

# 先在宿主机创建挂载目录

cd /opt

mkdir elk-data

cd elk-data

mkdir conf

mkdir elasticsearch-data

mkdir logs
# 编辑nginx配置文件

vim /usr/local/nginx/conf/nginx.conf
  1. 配置 nginx

下面是 nginx.conf 的配置内容

#user root;
worker_processes 1;
#pid    logs/nginx.pid;

events {
  worker_connections 1024;
}

http {
  include    mime.types;
  default_type application/octet-stream;
  sendfile    on;
  keepalive_timeout 65;

  # 对日志格式化成json
  log_format json '{"@timestamp":"$time_iso8601",'
​          '"@version":1,'
           '"host":"$server_addr",'
​          '"client":"$remote_addr",'
​          '"size":$body_bytes_sent,'
​          '"responsetime":$request_time,'
​          '"domain":"$host",'
​          '"url":"$uri",'
​          '"status":"$status"}';

  # 用于记录谁在什么时候做了什么
  access_log /opt/elk-data/logs/access.log  json;

  server {

​    listen    80;
​    server_name localhost;

​    #charset koi8-r;
​    #access_log logs/host.access.log main;

​    location / {
​      root  html;
​      index index.html index.htm;
​      # proxy_pass http://127.0.0.1:8000
​    }

​    error_page  500 502 503 504 /50x.html;
​    location = /50x.html {
​      root  html;
​    }
  }
}
  • 测试 nginx 文件配置的正确性

    /usr/local/nginx/sbin/nginx -t `

    出现下面这两行代码说明正确

    nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok

    nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

  1. logstash配置

logstash配置主要有三个地方要处理,首先是输入源在什么位置,然后是对数据进行过滤或者格式化,最后是需要将数据输出到什么地方;在下方的配置只做了其中两项,编辑配置文件命令参考如下

  • 修改宿主机刚才挂载的目录,在目录下创建conf目录,建立test.conf文件

    ` vim /opt/elk-data/conf/test.conf`

  • 下面是 test.conf 的配置内容

    input {
      file {
    ​    path => "/opt/logs/access.log" # 填写容器内部log文件路径
    ​    codec => "json"
      }
    }
      
    output {
      elasticsearch {
    ​    hosts => ["127.0.0.1:9200"] 
      }
      stdout { 
    ​    codec => rubydebug 
      }
    }
      
    input {
      file {
    ​    path => "/opt/logs/access.log" # 填写容器内部log文件路径
    ​    codec => "json"
      }
    }
    output {
      elasticsearch {
    ​    hosts => ["127.0.0.1:9200"] 
      }
      stdout { 
    ​    codec => rubydebug 
      }
    }
    
  1. 启动logstash

前面已经将日志格式与logstash配置好,现在需要启动logstash开始收集日志,logstash挂载到容器内,需要先启动容器

# 开启三个端口传递信息,挂载主机的三个路径下的所有文件,给容器重命名为elk,镜像为sebp/elk
docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -v /opt/elk-data/conf:/opt/conf -v /opt/elk-data/logs:/opt/logs -v /opt/elk-data/elasticsearch-data:/var/lib/elasticsearch -it -d --name elk sebp/elk
  • 启动logstash之前需要先进入容器里面,进入容器参考命令如下:

    docker exec -it elk /bin/bash

  • 删除02-beats-input.conf 关于ssl的三行设置,否则客户端需要另外配置证书相关配置项(重新创建容器都需要删除这三行设置)

    vim /etc/logstash/conf.d/02-beats-input.conf

  • 需要关闭自动开启的 logstash,检查logstash,elasticsearch状态,保持elasticsearch开启,logstash关闭

    service logstash stop

  • 进入容器之后,需要启动logstash来收集数据,启动的时候需要带两个参数进去,第一个是logstash的数据暂存位置,第二个是使用的配置文件,因此构造的命令如下所示:

    # 如果配置文件有改动会自动加载

    ` /opt/logstash/bin/logstash -f /opt/conf/test.conf –config.reload.automatic `

  • 另起一个窗口请求nginx:

    curl localhost:80

四、绘图配置与展示

当数据导入之后,才可以使用kibana的图形化来查看数据了,所以首先确认一下ES中是否有数据,确认有数据后就可以进行绘图配置,配置完成之后就可以进行筛选日志等操作了。

  1. ES数据检查

    数据添加到ES服务器当中后,可以通过ES服务提供的URL来查看其中的数据,URL地址如下所示:

    http://localhost:9200/_search?pretty

    就会看到刚刚产生的日志内容,当看到total数量变大,并在下面的数据项中看到了nginx日志信息时,则代表导入数据成功了。

  2. kibana索引配置

    通过浏览器访问kibana,URL地址如下

    http://127.0.0.1:5601/app/kibana#/management/kibana/index?_g=()

    • 点击左侧导航栏的Discover,便可进入创建索引模式界面,可以选择索引查看相对应数据

    • 点击左侧导航栏的Management链接,点击页面Index Pattern链接,点击右上方Create index pattern,方框里面输入你创建的索引名字,点击Next step便创建kibana的索引完成,此时再次点击左侧导航栏的Discover链接,便可以看到刚才创建索引的一些视图,数据。

      img

在图中有一个input输入框,可以在里面填写筛选所需要的关键词;如果没有筛选出结果,也可检查左侧的时间筛选项是否设置正确,默认显示最近十五分钟产生的数据 。

img

此处有对应的数据,说明收集成功。

五、filebeat客户端配置

filebeat 是用来转发和集中日志数据的轻量级服务。能监视指定的日志文件和位置。收集日志文件,并将它们转发到logstash或elasticsearch

另装一个虚拟机作为客户端,上面的docker,logstash作为服务器。

部署流程:

img

  1. 客户端配置

    # filebeat文件配置:filebeat.yml
       
    filebeat.inputs:
    	- type: log
    	enabled: true
    	paths:
       	- /usr/local/nginx/logs/access.log # 指定需要收集的日志文件的路径
    fields:
       indexname: nginx-access
    fields:
     host_ip: ip  #发送本机出口ip,便于服务器端方便筛选
    output.logstash:
      hosts: ["ip:port"]  #指定日志服务器的ip:port
    
  2. nginx文件配置(部分配置:json格式化,log存储路径)

    /usr/local/nginx/conf/nginx.conf

    log_format json '{"@timestamp":"$time_iso8601",'
    ​          '"@version":1,'
    ​          '"host":"$server_addr",'
    ​          '"client":"$remote_addr",'
    ​          '"size":$body_bytes_sent,'
    ​          '"responsetime":$request_time,'
    ​          '"domain":"$host",'
    ​          '"url":"$uri",'
    ​          '"status":"$status"}';
       
     access_log /usr/local/nginx/logs/access.log json;
    
    • nginx改了配置需要重启下:

      /usr/local/nginx/sbin/nginx -s reload

  3. 服务器端配置

    # logstash文件配置:
       
    input {
     beats {
      port => 5044     # 设置专用端口用于接收各个来源的日志
     }
    }
       
    output {
      elasticsearch {
    ​    index => "log_%{+YYYY.MM.dd}"
    ​    hosts => ["127.0.0.1:9200"]   # 发送到本机的elasticsearch上
      }
      stdout { codec => rubydebug }
    }
    
  4. 客户端启动nginx,filebeat 收集日志,服务端启动logstash,发送并展示日志信息

  • 重启启动nginx(如果修改过nginx配置文件,需要重启后再启动)

  • 发起请求 :curl localhost:80

    img

  • 启动成功后查看日志文件中是否收集到日志

    [root@localhost filebeat-7.4.0-linux-x86_64]# cat /usr/local/nginx/logs/access.log
      
    {"@timestamp":"2019-12-26T09:22:51+08:00","@version":1,"host":"127.0.0.1","client":"127.0.0.1","size":612,"responsetime":0.000,"domain":"localhost","url":"/index.html","status":"200"}
    
  • 客户端启动 filebeat

    ./filebeat -e -c filebeat.yml -d "publish"

    img

  • 服务端启动logstash

    /opt/logstash/bin/logstash -f /opt/conf/logstash.conf --config.reload.automatic

    img

  • 查看kibana,确认ES是否收集到

img

  1. filebeat 日志分类,logstash过滤

    # nginx.conf配置
       
    user nobody;
    worker_processes 1;
       
    error_log logs/error.log;
    #error_log logs/error.log notice;
    #error_log logs/error.log info;
       
    pid     logs/nginx.pid;
       
    events {
      worker_connections 1024;
    }
       
    http {
       
      include    mime.types;
      default_type application/octet-stream;
       
      #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
       
      #         '$status $body_bytes_sent "$http_referer" '
       
      #         '"$http_user_agent" "$http_x_forwarded_for"';
       
      #access_log logs/access.log main;
       
      sendfile    on;
      #tcp_nopush   on; 
       
      #keepalive_timeout 0;
      keepalive_timeout 65;
      #gzip on;
         
      log_format json '{"@timestamp":"$time_iso8601",''"@version":1,''"host":"$server_addr",''"client":"$remote_addr",''"size":$body_bytes_sent,''"responsetime":$request_time,''"domain":"$host",''"url":"$uri",''"status":"$status"}';
       
      access_log /usr/local/nginx/logs/access.log json;
      error_log /usr/local/nginx/logs/error.log;
       
      server {
    ​    listen    80;
    ​    server_name localhost;\#charset koi8-r;\#access_log logs/host.access.log main;
    ​    location / {
    ​      root  html;
    ​      index index.html index.htm;}\#error_page 404       /404.html;\# redirect server error pages to the static page /50x.html
    ​    \#
    ​    error_page  500 502 503 504 /50x.html;
    ​    location = /50x.html {
    ​      root  html;}
    }
       
    
    filebeat.yml配置:
       
    filebeat.inputs:
    	- type: log
      enabled: true
      paths:
        - /usr/local/nginx/logs/access.log # 指定需要收集的日志文件的路径
      fields:
        indexname: nginx-access
       
        
       
    	- type: log
      enabled: true
    	paths:
    		- /usr/local/nginx/logs/error.log # 指定需要收集的日志文件的路径
      fields:
        indexname: nginx-error
       
      fields:
        host_ip: ip  #发送本机出口ip,便于服务器端方便筛选
       
    output.logstash:
      hosts: ["ip:5044"]  #指定日志服务器的ip:port
    
    # logstash.conf配置:
       
    input {
     beats {
      port => 5044
     }
    }
       
    filter {
     mutate {
      rename => { "[host][name]" => "host" }
     }
       
     if [fields][indexname] == "nginx-error" {
       grok {
    ​     match => {"message" => "%{DATESTAMP:datetime}\s+\[%{LOGLEVEL:loglevel}\]\s+%{GREEDYDATA:error_reason}"}
       }
     }
        
     else if [fields][indexname] == "nginx-access" {
       grok {
    ​      match => { "message" => "%{NGINXACCESS}" }
       }
     }
        
     date {
       match => [ "timestamp", "YY/MM/dd HH:mm:ss","YYYY-MM-dd HH:mm:ss", "dd/MMM/YYYY:HH:mm:ss Z" ]
     }
        
     mutate {
       remove_field => ["host", "tags", "@version", "type"]
     }
        
    }
       
    output {
      elasticsearch {
    ​    action => "index"
    ​    index => "log_%{+YYYY.MM.dd}"
    ​    hosts => ["127.0.0.1:9200"]
      }
      stdout { codec => rubydebug }
    }
    
  • 服务端logstash启动:

    /opt/logstash/bin/logstash -f /opt/conf/logstash.conf --config.reload.automatic

  • 客户端filebeat启动:

    ./filebeat -e -c filebeat.yml -d "publish"

  • logstash收集并打印出日志:

  • img

  • kibana显示:

    img

六、常见错误总结及解决方法

  1. 启动 nginx 后 access.log 收集不到日志

修改配置文件后记得重启才能生效 : /usr/local/nginx/sbin/nginx -s reload

  1. CentOs的系统时间和本地网络时间不一致(多八小时)

需要配置成NTP同步状态:

  • 使用 date 命令,查看系统时间和当前时间是否一致,如果不一致,就需要使用ntp同步标准时间,ntp:网络时间协议(network time protol)

    安装:yum install ntp

    同步:ntpdate pool.ntp.org

  • 使用timedatectl查看是否同步

    [root@localhost ~]# timedatectl
      
       Local time: Tue 2019-12-24 16:37:04 CST
      
     Universal time: Tue 2019-12-24 08:37:04 UTC
      
    ​    RTC time: Tue 2019-12-24 16:37:04
      
    ​    Time zone: Asia/Shanghai (CST, +0800)
      
       NTP enabled: yes
      
    NTP synchronized: yes
      
     RTC in local TZ: yes
      
       DST active: n/a
    
  • 查看下面三个是否都是yes,如果缺了就使用下面的命令:

    NTP enabled: yes

    timedatectl set-ntp on

​ NTP synchronized: yes

​ 将当前时间和日期写入BIOS,避免重启后失效

hwclock -w

RTC in local TZ: yes

timedatectl set-local-rtc 1

  1. Logstash收集不到日志数据
  • 原因1: 新建容器,进入容器后忘了删除ssl的三行配置

    解决:删除ssl的三行配置(上文有步骤)

  • 原因2: 日志文件没有更新,日志读取的偏移量没有改变,所以认为没有新的日志,就不进行新的收集

    解决:可以在logstash.conf中新增两行代码

    input {
      file {
    ​    path => ["/logs/access.log"]
    ​    start_position => "beginning" # 从头读文件
    ​    sincedb_path => "/dev/null"  # 偏移量放入Linux黑洞中,所以# 永远从头开始读文件
      }
    }