ELK:对于docker使用logstash收集nginx日志到elasticsearch的步骤小结
阅读原文时间:2021年04月21日阅读:2

基于CentOs7

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

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

一、Centos安装docker(https://docs.docker.com/install/linux/docker-ce/centos/)

移除旧的版本:

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

安装一些必要的系统工具:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

添加软件源信息:

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

更新 yum 缓存:

sudo yum makecache fast

安装 Docker-ce:

sudo yum -y install docker-ce

启动 Docker 后台服务:

sudo systemctl start docker

测试运行 hello-world

docker run hello-world

二.​​​​​​  配置与验证 (elasticsearch运行需要至少2G内存)

修改配置文件      vim /etc/sysctl.conf

      tips:如果报错:vim: command not found    参照: https://www.jianshu.com/p/93fe144f5739

rpm -qa |grep vim

vim-minimal-7.0.109-6.el5

vim-common-7.0.109-7.2.el5

vim-enhanced-7.0.109-7.2.el5

#如果上面三条少了其中的某一条,就直接安装所有包

yum -y install vim*

添加或者修改      vm.max_map_count = 262144

使生效                 sysctl -p

1 服务安装与运行

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

2.  镜像下载获取获取

设置好加速地址之后,就可以开始拉取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


#在结果当中可以看出,ELK镜像已经下载下来,占用了将近2GB空间

3.  容器运行

运行此容器的时候,需要将宿主机的端口转发到该容器,其中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       0.0.0.0:80;
        server_name  localhost;

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

        location / {
            modsecurity on;
            modsecurity_rules_file  /usr/local/nginx/conf/modsecurity_includes.conf;
            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 

出现下面这两行代码说明正确
Password:
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

tips:    可以使用  ps aux|grep nginx   命令来查看nginx配置路径

2) 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 
    }
}

3) 进入容器,启动logstash

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

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
# 开启三个端口传递信息,挂载主机的三个路径下的所有文件,给容器重命名为elk,镜像为sebp/elk

启动logstash之前需要先进入容器里面,进入容器参考命令如下:

docker exec -it elk /bin/bash

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

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

需要关闭自动开启的 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链接,便可以看到刚才创建索引的一些视图,数据。

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

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

四.filebeat客户端配置

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

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

部署流程:

1、192.168.194.5(客户端配置)

filebeat文件配置:filebeat.yml

filebeat.inputs:
- type: log
  enabled: true
  paths:
      - /usr/local/nginx/logs/access.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: nginx-access

fields:
  host_ip: 192.168.194.5    #发送本机出口ip,便于服务器端方便筛选

output.logstash:
   hosts: ["192.168.194.6:5044"]   #指定日志服务器的ip:port

 nginx文件配置(部分配置:json格式化,log存储路径):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

2、192.168.194.6(服务器端配置)

 logstash文件配置:

input {
  beats {
    port => 5044         # 设置专用端口用于接收各个来源的日志
  }
}
output {
    elasticsearch {
        index => "log_%{+YYYY.MM.dd}"
        hosts => ["127.0.0.1:9200"]     # 发送到本机的elasticsearch上
    }
    stdout { codec => rubydebug }
}

3、客户端启动nginx,filebeat 收集日志,服务端启动logstash,发送并展示日志信息

启动nginx(之前修改过nginx,需要重启后再启动)

curl localhost:80

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

[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"

服务端启动logstash

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

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

4、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;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

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

- type: log
  enabled: true
  paths:
      - /var/log/jsac.agentjsac.info.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-agentjsac-info

- type: log
  enabled: true
  paths:
      - /var/log/jsac.agentjsac.error.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-agentjsac-error

- type: log
  enabled: true
  paths:
      - /var/log/jsac.agentjsac.debug.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-agentjsac-debug

- type: log
  enabled: true
  paths:
      - /var/log/jsac.agentjsac.warn.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-agentjsac-warn

- type: log
  enabled: true
  paths:
      - /var/log/jsac.driver-install.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-driver-install

- type: log
  enabled: true
  paths:
      - /var/log/jsac.tsthostapi.debug.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-tsthostapi.debug

- type: log
  enabled: true
  paths:
      - /var/log/jsac.tsthostapi.error.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-tsthostapi.error

- type: log
  enabled: true
  paths:
      - /var/log/jsac.update.info.log  # 指定需要收集的日志文件的路径
  fields:
      indexname: jsac-update.info

fields:
  host_ip: 192.168.194.5    #发送本机出口ip,便于服务器端方便筛选

output.logstash:
   hosts: ["192.168.194.6: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}" }
      }
  }
  else if "agentjsac" in [fields][indexname]  {
      grok {
          match => { "message" => "%{DATESTAMP:datetime} %{LOGLEVEL:loglevel} %{GREEDYDATA:reason}" }
      }
  }
  else if "tsthostapi" in [fields][indexname]  {
      grok {
          match => { "message" => "%{GREEDYDATA:description}" }
      }
  }
  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收集并打印出日志:

kibana显示:

=======================================================================================================

nginx的error.log日志常见的几个错误解决方法

参照:https://blog.51cto.com/chenx1242/1769724

error.log:

[emerg] 20952#0: unexpected "}" in /usr/local/nginx/conf/nginx.conf:87
# 这句话就说明在nginx.conf的87行里有一个 } 是错误的,检查一下}是不是多余了,或者;少了,这个错误的级别是emergency;

[emerg] 21023#0: "root" directive is duplicate in /usr/local/nginx/conf/nginx.conf:86
# 这句话就是说明在nginx.conf的第86行里root设定重复了,级别同样是emergency。以上两个都是书写的问题,很好纠正;

[notice] 21045#0: signal process started
# 这个意思是nginx已经在运行的状态下,被执行启动,这个不算致命错误;

nginx: [alert] could not open error log file: open()"/usr/local/nginx/logs/error.log" failed (13:Permissiondenied)
# 这个是说当前用户没有权限写入error.log的日志,解决方法要来权限就行了;

nginx: [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: No such file or directory)
# nginx提示无法找到nginx.pid这个文件了
#使用#/usr/local/nginx/sbin/nginx -c  /usr/local/nginx/conf/nginx.conf,重新启动一下,就自动生成pid文件了。

=======================================================================================================

=======================================================================================================

=======================================================================================================