ELK收集Apache访问日志实战案例

ELK收集日志常用的有两种方式,分别是:

1. 不修改源日志的格式,而是通过logstash的grok方式进行过滤、清洗,将原始无规则的日志转换为规则的日志。
2. 修改源日志输出格式,按照需要的日志格式输出规则日志,logstash只负责日志的收集和传输,不对日志做任何的过滤清洗。

这两种方式各有优缺点,第一种方式不用修改原始日志输出格式,直接通过logstash的grok方式进行过滤分析,好处是对线上业务系统无任何影响,缺点是logstash的grok方式在高压力情况下会成为性能瓶颈,如果要分析的日志量超大时,日志过滤分析可能阻塞正常的日志输出。因此,在使用logstash时,能不用grok的,尽量不使用grok过滤功能。

第二种方式缺点是需要事先定义好日志的输出格式,这可能有一定工作量,但优点更明显,因为已经定义好了需要的日志输出格式,logstash只负责日志的收集和传输,这样就大大减轻了logstash的负担,可以更高效的收集和传输日志。另外,目前常见的web服务器,例如apache、nginx等都支持自定义日志输出格式。因此,在企业实际应用中,第二种方式是首选方案。

以上次部署的 ELK+Filebeat+Kafka+ZooKeeper 集群架构进行收集:

在这里插入图片描述apache业务服务器(可以有多个)上部署Filebeat采集日志信息。将收集到的数据信息传输到kafka集群,对数据做一定的缓冲和存储。logstash从kafka集群拉取并消费数据,对数据做一定的简单处理和分析,最后交给es集群进行存储和索引,最终展示在kibana上。

操作系统统一采用Centos7.4版本,各个服务器角色如下表所示:

IP地址主机名角色所属集群硬件配置
192.168.126.90filebeatserverapache业务服务器+Filebeat业务服务器集群2U0.5G 20G
192.168.126.91kafkazk1Kafka+ZookeeperKafka Broker集群2U1.5G 20G
192.168.126.92kafkazk2Kafka+ZookeeperKafka Broker集群2U1.5G 20G
192.168.126.93kafkazk3Kafka+ZookeeperKafka Broker集群2U1.5G 20G
192.168.126.94logstashserverLogstashLogstash集群2U1.5G 20G
192.168.126.95es1ES Master、ES DataNodeElasticsearch集群2U2G 20G
192.168.126.96es2ES Master、KibanaElasticsearch集群2U2G 20G
192.168.126.97es3ES Master、ES DataNodeElasticsearch集群2U2G 20G

下表说明了安装软件对应的名称和版本号,其中,ELK三款软件推荐选择一样的版本,这里选择的是6.5.4版本。

软件名称版本说明
JDKJDK 1.8.0_161Java环境解析器
filebeatfilebeat-6.5.4前端日志收集器
logstashlogstash-6.5.4日志收集、过滤、转发
zookeeperzookeeper-3.4.12资源调度、协作
kafkakafka_2.10-0.10.0.1消息通信中间件
elasticsearchelasticsearch-6.5.4日志存储、索引
kibanakibana-6.5.4日志展示、分析

ELK+Filebeat+Kafka+ZooKeeper环境已经部署好。

apache的日志格式与日志变量

apache支持自定义输出日志格式,但是,apache有很多日志变量字段,所以在收集日志前,需要首先确定哪些是我们需要的日志字段,然后将日志格式定下来。要完成这个工作,需要了解apache日志字段定义的方法和日志变量的含义,在apache配置文件httpd.conf中,对日志格式定义的配置项为LogFormat,默认的日志字段定义为如下内容:

[root@filebeatserver ~]# yum install httpd -y
[root@filebeatserver ~]# grep -w 'combined' /etc/httpd/conf/httpd.conf 
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    CustomLog "logs/access_log" combined

apache的日志格式与日志变量
在这里插入图片描述

自定义apache日志格式

这里定义将apache日志输出为json格式,下面仅列出apache配置文件httpd.conf中日志格式和日志文件定义部分,定义好的日志格式与日志文件如下:

[root@filebeatserver ~]# vim /etc/httpd/conf/httpd.con
LogFormat	 "{\"@timestamp\":\"%{%Y-%m-%dT%H:%M:%S%z}t\",\"client_ip\":\"%{X-Forwarded-For}i\",\"direct_ip\": \"%a\",\"request_time\":%T,\"status\":%>s,\"url\":\"%U%q\",\"method\":\"%m\",\"http_host\":\"%{Host}i\",\"server_ip\":\"%A\",\"http_referer\":\"%{Referer}i\",\"http_user_agent\":\"%{User-agent}i\",\"body_bytes_sent\":\"%B\",\"total_bytes_sent\":\"%O\"}"  access_log_json
CustomLog	 logs/access.log access_log_json

这里通过LogFormat指令定义了日志输出格式,在这个自定义日志输出中,定义了13个字段,定义方式为:字段名称:字段内容,字段名称是随意指定的,能代表其含义即可,字段名称和字段内容都通过双引号括起来,而双引号是特殊字符,需要转移,因此,使用了转移字符“\”,每个字段之间通过逗号分隔。此外,还定义了一个时间字段 @timestamp,这个字段的时间格式也是自定义的,此字段记录日志的生成时间,非常有用。CustomLog指令用来指定日志文件的名称和路径。
需要注意的是,上面日志输出字段中用到了body_bytes_sent和total_bytes_sent发送字节数统计字段,这个功能需要apache加载mod_logio.so模块,如果没有加载这个模块的话,需要安装此模块并在httpd.conf文件中加载一下即可。

验证日志输出

apache的日志格式配置完成后,重启apache,在浏览器访问http://192.168.126.90,然后查看输出日志是否正常,如果能看到类似如下内容,表示自定义日志格式输出正常:

[root@filebeatserver ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.90
[root@filebeatserver ~]# tailf /etc/httpd/logs/access.log 
{"@timestamp":"2021-08-13T13:13:05+0800","client_ip":"-","direct_ip": "192.168.126.1","request_time":0,"status":403,"url":"/","method":"GET","http_host":"192.168.126.90","server_ip":"192.168.126.90","http_referer":"-","http_user_agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0","body_bytes_sent":"4897","total_bytes_sent":"5205"}

这是没有经过反向代理进行访问的结果

配置一层反向代理服务器进行访问

[root@kafkazk1 ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.91
[root@kafkazk1 ~]# vim /etc/httpd/conf/httpd.conf 
ProxyPass / http://192.168.126.90
ProxyPassReverse / http://192.168.126.90/
[root@kafkazk1 ~]# systemctl restart httpd

在浏览器访问 http://192.168.126.91,并查看192.168.126.90(filebeat+httpd)服务器上apache日志

[root@filebeatserver ~]# tailf /etc/httpd/logs/access.log 
{"@timestamp":"2021-08-13T13:22:48+0800","client_ip":"192.168.126.1","direct_ip": "192.168.126.91","request_time":0,"status":403,"url":"/","method":"GET","http_host":"192.168.126.90","server_ip":"192.168.126.90","http_referer":"-","http_user_agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0","body_bytes_sent":"4897","total_bytes_sent":"5205"}

配置两层代理服务器进行访问

[root@kafkazk2 ~]# ifconfig ens32 | awk 'NR==2 {print $2}'
192.168.126.92
[root@kafkazk2 ~]# vim /etc/httpd/conf/httpd.conf
ProxyPass / http://192.168.126.91
ProxyPassReverse / http://192.168.126.91/
[root@kafkazk2 ~]# systemctl restart httpd

在浏览器访问 http://192.168.126.92,并查看192.168.126.90(filebeat+httpd)服务器上apache日志

[root@filebeatserver ~]# tailf /etc/httpd/logs/access.log 
{"@timestamp":"2021-08-13T14:04:20+0800","client_ip":"192.168.126.1, 192.168.126.92","direct_ip": "192.168.126.91","request_time":0,"status":403,"url":"/","method":"GET","http_host":"192.168.126.90","server_ip":"192.168.126.90","http_referer":"-","http_user_agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0","body_bytes_sent":"4897","total_bytes_sent":"5205"}

对比这三次的日志,可以看到,client_ip和direct_ip输出的异同,client_ip字段对应的变量为“%{X-Forwarded-For}i”,它的输出是代理叠加而成的IP列表,而direct_ip对应的变量为%a,表示不经过代理访问的直连IP,当用户不经过任何代理直接访问apache时,client_ip和direct_ip应该是同一个IP。

配置filebeat

filebeat是安装在apache服务器上的,配置好的filebeat.yml文件的内容:

[root@filebeatserver ~]# cd /usr/local/filebeat/
[root@filebeatserver filebeat]# vim filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /etc/httpd/logs/access.log
  fields:
    log_topic: apachelogs
name: "192.168.126.90"
output.kafka:
  enabled: true
  hosts: ["192.168.126.91:9092", "192.168.126.92:9092", "192.168.126.93:9092"]
  version: "0.10"
  topic: '%{[fields][log_topic]}'
  partition.round_robin:
    reachable_only: true
  worker: 2
  required_acks: 1
  compression: gzip
  max_message_bytes: 10000000
logging.level: debug
# 这个配置文件中,是将apache的访问日志/etc/httpd/logs/access.log内容实时的发送到kafka集群topic为apachelogs中。需要注意的是filebeat输出日志到kafka中配置文件的写法。

# 启动
[root@filebeatserver filebeat]# nohup ./filebeat -e -c filebeat.yml &
[1] 4591
nohup: ignoring input and appending output to ‘nohup.out’

测试

在浏览器访问 http://192.168.126.90,查看filebeat能否收集到日志

[root@filebeatserver filebeat]# tailf nohup.out
2021-08-13T14:21:57.368+0800	DEBUG	[publish]	pipeline/processor.go:308	Publish event: {
  "@timestamp": "2021-08-13T06:21:57.368Z",
  "@metadata": {
    "beat": "filebeat",
    "type": "doc",
    "version": "6.5.4"
  },
  "prospector": {
    "type": "log"
  },
  "input": {
    "type": "log"
  },
  "beat": {
    "name": "192.168.126.90",
    "hostname": "filebeatserver",
    "version": "6.5.4"
  },
  "host": {
    "name": "192.168.126.90"
  },
  "source": "/etc/httpd/logs/access.log",
  "offset": 21983,
  "message": "{\"@timestamp\":\"2021-08-13T14:21:54+0800\",\"client_ip\":\"-\",\"direct_ip\": \"192.168.126.1\",\"request_time\":0,\"status\":404,\"url\":\"/noindex/css/fonts/Bold/OpenSans-Bold.ttf\",\"method\":\"GET\",\"http_host\":\"192.168.126.90\",\"server_ip\":\"192.168.126.90\",\"http_referer\":\"http://192.168.126.90/noindex/css/open-sans.css\",\"http_user_agent\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0\",\"body_bytes_sent\":\"238\",\"total_bytes_sent\":\"453\"}",
  "fields": {
    "log_topic": "apachelogs"
  }
}

同时验证kafka集群能否收集并消费到这些日志数据

[root@kafkazk1 ~]# /usr/local/kafka/bin/kafka-console-consumer.sh --zookeeper 192.168.126.91:2181,192.168.126.92:2181,192.168.126.93:2181 --topic apachelogs
{"@timestamp":"2021-08-13T06:21:57.368Z","@metadata":{"beat":"filebeat","type":"doc","version":"6.5.4","topic":"apachelogs"},"source":"/etc/httpd/logs/access.log","offset":21983,"message":"{\"@timestamp\":\"2021-08-13T14:21:54+0800\",\"client_ip\":\"-\",\"direct_ip\": \"192.168.126.1\",\"request_time\":0,\"status\":404,\"url\":\"/noindex/css/fonts/Bold/OpenSans-Bold.ttf\",\"method\":\"GET\",\"http_host\":\"192.168.126.90\",\"server_ip\":\"192.168.126.90\",\"http_referer\":\"http://192.168.126.90/noindex/css/open-sans.css\",\"http_user_agent\":\"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0\",\"body_bytes_sent\":\"238\",\"total_bytes_sent\":\"453\"}","fields":{"log_topic":"apachelogs"},"prospector":{"type":"log"},"input":{"type":"log"},"beat":{"name":"192.168.126.90","hostname":"filebeatserver","version":"6.5.4"},"host":{"name":"192.168.126.90"}}

能够成功获取到日志信息

配置logstash

下面直接给出logstash事件配置文件 kafka_apache_into_es.conf 的内容:

[root@logstashserver ~]# cat /usr/local/logstash/config/kafka_apache_into_es.conf
input {
        kafka {
          bootstrap_servers => "192.168.126.91:9092,192.168.126.92:9092,192.168.126.93:9092"
          topics => ["apachelogs"]
          codec => json {
            charset => "UTF-8"
          }
          add_field => { "[@metadata][tagid]" => "apacheaccess_log" }
	}
}


filter {
    if [@metadata][tagid] == "apacheaccess_log" {
      mutate {
        gsub => ["message", "\\x", "\\\x"]		#这里的message就是message字段,也就是日志的内容。这个插件的作用是将message字段内容中UTF-8单字节编码做替换处理,这是为了应对URL有中文出现的情况。
      }
      if ( 'method":"HEAD' in [message] ) {		#如果message字段中有HEAD请求,就删除此条信息。
           drop {}
      }
      json {			#这是启用json解码插件,因为输入的数据是复合的数据结构,只是一部分记录是json格式的。
            source => "message"	#指定json格式的字段,也就是message字段。
            add_field => { "[@metadata][direct_ip]" => "%{direct_ip}"}	   #这里添加一个字段,用于后面的判断。
            remove_field => "@version"		#从这里开始到最后,都是移除不需要的字段,前面九个字段都是filebeat传输日志时添加的,没什么用处,所以需要移除。
            remove_field => "prospector"
            remove_field => "beat"
            remove_field => "source"
            remove_field => "input"
            remove_field => "offset"
            remove_field => "fields"
            remove_field => "host"
	    remove_field => "message"	  #因为json格式中已经定义好了每个字段,那么输出也是按照每个字段输出的,因此就不需要message字段了,这里是移除message字段。
        }
	 mutate {
            split => ["client_ip", ","]		#这是对client_ip这个字段按逗号进行分组切分,因为在多级代理情况下,client_ip获取到的IP可能是IP列表,如果是单个ip的话,也会进行分组,只不过是分一个组而已。
        }
       mutate {
            replace => { "client_ip" => "%{client_ip[0]}" }		 #将切分出来的第一个分组赋值给client_ip,因为client_ip是IP列表的情况下,第一个IP才是客户端真实的IP。
       }
        if [client_ip] == "-" {			#这是个if判断,主要用来判断当client_ip为"-"的情况下,当direct_ip不为"-"的情况下,就将direct_ip的值赋给client_ip。因为在client_ip为"-"的情况下,都是直接不经过代理的访问,此时direct_ip的值就是客户端真实IP地址,所以要进行一下替换。
           if [@metadata][direct_ip] not in ["%{direct_ip}","-"] {		#这个判断的意思是如果direct_ip非空。
                mutate {
                    replace => { "client_ip" => "%{direct_ip}" }
                }
            } else {
                drop{}
            }
        }
        mutate {
            remove_field => "direct_ip"		#direct_ip只是一个过渡字段,主要用于在某些情况下将值传给client_ip,因此传值完成后,就可以删除direct_ip字段了。
        }
    }
}

# 如果需要调试可以将信息输出到屏幕,无误后在输出到后端es集群
#output {
#	if [@metadata][tagid] == "apacheaccess_log" {
#		stdout {
#			codec => "rubydebug"
#		}
#	}
#}
output {
    if [@metadata][tagid] == "apacheaccess_log" {				#用于判断,跟上面input中[@metadata][tagid]对应,当有多个输入源的时候,可根据不同的标识,指定到不同的输出地址。
        elasticsearch {
         hosts => ["192.168.126.95:9200","192.168.126.96:9200","192.168.126.97:9200"]
         index => "logstash_apachelogs-%{+YYYY.MM.dd}"			#指定apache日志在elasticsearch中索引的名称,这个名称会在Kibana中用到。索引的名称推荐以logstash开头,后面跟上索引标识和时间。
        }
    }
}

# 启动
[root@logstashserver ~]# nohup /usr/local/logstash/bin/logstash -f /usr/local/logstash/config/kafka_apache_into_es.conf &
配置Kibana

filebeat收集数据到kafka,然后logstash从kafka拉取数据,如果数据能够正确发送到elasticsearch,我们就可以在Kibana中配置索引了。

[root@es2 ~]# ifconfig | awk 'NR==2 {print $2}'
192.168.126.96
# 启动
[root@es2 ~]# nohup /usr/local/kibana/bin/kibana &

浏览器访问 http://192.168.126.96:5601 登录Kibana,首先配置一个index_pattern,点击kibana左侧导航中的Management菜单,然后选择右侧的Index Patterns按钮,最后点击左上角的Create index pattern。

在这里插入图片描述在这里插入图片描述



用浏览器访问apache服务,让其产生日志后,一般需要过一小会才能在es集群中生成索引。也可以使用ES-HEAD插件查看es集群是否生成索引

在这里插入图片描述可以看到,如果发现es集群中生成了对应的索引,那么便可以继续以下步骤



在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
查看收集到的日志信息
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值