ELK
似乎是当前最为流行的日志收集-存储-分析的全套解决方案.
去年年初, 公司里已经在用, 当时自己还山寨
了一个统计系统(postgresql-echarts, 日志无结构化, json形式存储到postgresql, 构建统一前端配置生成, 调用统一查询接口, 具体细节), 已经过了一年有余.
一年刚好, 发生了很多事, 那套系统不知现在如何了.
在新的公司, 一切都得从0到1, 近期开始关注日志/数据上报/统计, 以及后续的数据挖掘等.
搭建, 测试并上线了一套简单的系统, 初期将所有服务器的nginx日志, 以及搜索日志进行处理.
下面主要介绍对nginx日志进行处理的过程, 不是针对elk
的介绍, 所有涉及ip的地方都改成127.0.0.1
了, 根据自己环境进行修改
数据接入和获取
Elastic栈使用Logstash和Beats来进行数据的消化和获取。
Logstash用jruby实现,有点像一个数据管道,把输入的数据进行处理,变形,过滤,然后输出到其它地方。Logstash 设计了自己的 DSL,包括有区域,注释,数据类型(布尔值,字符串,数值,数组,哈希),条件判断,字段引用等。
Logstash的数据管道包含三个步骤,Input,Filter和Output,每一步都可以通过plugin来扩展。另外Input和Output还支持配置Codecs,完成对输入输出数据的编解码工作。
Logstash支持的常见的Input包含File,syslog,beats等。Filter中主要完成数据的变形处理,可以增删改字段,加标签,等等。作为一个开源软件,Output不仅仅支持ElasticSearch,还可以和许多其它软件集成和目标,Output可以是文件,graphite,数据库,Nagios,S3,Hadoop等。
在实际运用中,logstash 进程会被分为两个不同的角色。运行在应用服务器上的,尽量减轻运行压力,只做读取和转发,这个角色叫做 shipper;运行在独立服务器上,完成数据解析处理,负责写入 Elasticsearch 的角色,叫 indexer。
logstash 作为无状态的软件,配合消息队列系统,可以很轻松地做到线性扩展。
Beats是 Elastic 从 packetbeat 发展出来的数据收集器系统。beat 收集器可以直接写入 Elasticsearch,也可以传输给 Logstash。其中抽象出来的 libbeat,提供了统一的数据发送方法,输入配置解析,日志记录框架等功能。
开源社区已经贡献了许多的beats种类。
因为Beats是使用Golang编写的,效率上很不错。
log
通过logstash shipper
读取, 转json
, 发送到redis
, 由后续的logstash indexer
进行处理步骤
1.在日志所在机器部署logstash
2.在logstash
安装目录下的patterns
中加入一个文件nginx
内容(与上面的log_format
相对应)
NGUSERNAME [a-zA-Z\.\@\-\+_%]+ NGUSER %{NGUSERNAME} NGINXACCESS %{IPORHOST:clientip} - %{NOTSPACE:remote_user} \[%{HTTPDATE:timestamp}\] \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes}|-) %{QS:referrer} %{QS:agent} %{NOTSPACE:http_x_forwarded_for}
3.增加一个logstash
配置文件: logstash-project-access-log.conf
注意, input的file, filter的grok, output的redis-key
input { file { path => [ "/data/logs/nginx/xxxx_access.log" ] start_position => "beginning" } } filter { mutate { replace => { "type" => "nginx_access" } } grok { match => { "message" => "%{NGINXACCESS}" } } date { match => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z" ] } geoip { source => "clientip" } } output { redis { host => "127.0.0.1" data_type => "list" key => "logstash:xxxx:access_log" } }
4.使用supervisor
启动shipper
.
[program:logstash_xxxx_shipper] command=/var/shell/logstash/bin/logstash -f /var/shell/logstash/configs/nginx-xxxx-shipper.conf numprocs=1 autostart=true autorestart=true log_stdout=true log_stderr=true logfile=/data/logs/logstash/logstash_xxxx_access.log
注意, input的redis为上一步redis配置, key要对应, output的elasticsearch配置, index
指定了最终es中存储对应的index, 加日期, 方便对日志进行定期删除
input { redis { host => "127.0.0.1" port => "6379" key => "logstash:xxxx:access_log" data_type => "list" codec => "json" type => "logstash-arthas-access" tags => ["arthas"] } } output { elasticsearch { host => "127.0.0.1" index => "logstash-arthas-access-%{+YYYY.MM.dd}" } }
分布集群和扩展性
ElasticSearch
ElasticSearch是为分布式设计的,有很好的扩展性,在一个典型的分布式配置中,每一个节点(node)可以配制成不同的角色,如上图所示:
Client Node,负责API和数据的访问的节点,不存储/处理数据;
Data Node,负责数据的存储和索引;
Master Node, 管理节点,负责Cluster中的节点的协调,不存储数据。
每一种角色可以通过ElasticSearch的配置文件或者环境变量来配置。每一种角色都可以很方便的Scale,因为Elastic采用了对等性的设计,也就是所有的角色是平等的,(Master Node会进行Leader Election,其中有一个是领导者)这样的设计使得在集群环境的伸缩性非常好,尤其是在容器环境,例如Docker Swarm或者Kubernetes中使用。
参考:
https://elk-docker.readthedocs.io/#elasticsearch-cluster
https://github.com/pires/kubernetes-elasticsearch-cluster
产品线
Elastic
Elastic的产品线除了大家熟悉的ELK(ElasticSearch,Logstash,Kikana),主要包含:
Beats Beats是一个开源组件,提供一个代理,把本地抓到的数据传送到ElasticSearch;
Elastic Cloud, Elasti提供的云服务;
X-Pack, Elastic的扩展组件,提供安全,告警,监控,机器学习和图处理能力。主要功能需要付费使用
剩下的其实没什么了, 启动kibana
后, 配置好指向的es
, 就可以在kibana
中查看到实时的日志数据
demo环境截图
kibana
中, 支持各种统计, 着实让人惊艳了一把.
除了基本的nginx日志, 还需要在各类url入口, 加入平台, 渠道等信息, 这样通过nginx访问日志, 可以统计到更多的信息
当然, 如果需要一些更为精确/特殊的统计, 需要自行进行数据上报的工作.
logstash forwarder
, 因为目前日志组成等较为简单, 简单处理 , 后续需要用到时再考虑logformat
和对应grok
的配置 grok
是logstash
的一个插件, 文档
Grok is currently the best way in logstash to parse crappy unstructured log data into something structured and queryable
所以, 我们在处理nginx
日志时, 需要根据具体logformat
定义对应的grok
表达式
除了上面例子中用的那套, 另一份
logformat
log_format logstash '$http_host ' '$remote_addr [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time ' '$upstream_response_time';
patterns/nginx
NGUSERNAME [a-zA-Z\.\@\-\+_%]+ NGUSER %{NGUSERNAME} NGINXACCESS %{IPORHOST:http_host} %{IPORHOST:clientip} \[%{HTTPDATE:timestamp}\] \"(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\" %{NUMBER:response} (?:%{NUMBER:bytes}|-) %{QS:referrer} %{QS:agent} %{NUMBER:request_time:float} %{NUMBER:upstream_time:float}
如果想自行定义, 可以使用 grokdebug, 将要解析的日志和配置的正则放入, 可以查看最终得到的结构化数据
初期只安装了一个 kopf, web界面查看
建议使用supervisor
对elk
进行管理,(ps. 不要用yum自带的, 版本太旧好多坑, 浪费1小时......使用pip install安装最新版本即可)
配置示例elk.conf
[program:elasticsearch] command=/var/shell/elk/elasticsearch/bin/elasticsearch numprocs=1 autostart=true autorestart=true [program:kibana] command=/var/shell/elk/kibana/bin/kibana numprocs=1 autostart=true autorestart=true [program:logstash_arthas] command=/var/shell/elk/logstash/bin/logstash -f /var/shell/elk/logstash/config/xxxx_access.conf numprocs=1 autostart=true autorestart=true log_stdout=true log_stderr=true logfile=/data/logs/elk/logstash/logstash_arthas_access.log
start_position => "beginning"
logstash, 会记录一份文件读到的位置, 在$HOME/.sincedb_xxxxx 如果要让logstash重新读取文件, 删除之即可, 重启shipper
.
但是你可能发现es中重复记录了, 这是因为, 在output
中, 没有定义存储到es时使用的document_id
, es全部当成新纪录存入, 导致数据重复
以下演示基于官方Kibana演示页面:http://demo.elastic.co/packetbeat/#/discover
8.模糊搜索
9.近似搜索