我对 ElasticStack 可以说是既熟悉又陌生,说熟悉是因为很久以前就已经开始使用 ELK 来分析日志了,说陌生是因为以前的 ELK 环境都是同事搭建的,我主要是看看 Kibana 面板而已。随着 V5 的发布,ELK 全面进化为 ElasticStack,该自己动手了。
实际操作前最好大致浏览一下官方文档,以便对 ElasticStack 各个组件的作用有一个基本概念,如果看完文档还没搞清楚,那么至少要看明白下面这张图:
ElasticStack
整个流程相当简单,首先服务器通过 Filebeat 把数据上报给 Logstash,然后把分析后把数据保存到 ElasticSearch 里,最后用户通过 Kibana 浏览数据。
废话少说,接下来让我们按顺序安装 ElasticStack 的各个组件,不过安装前我们需要确保系统已有 Java 且版本足够新,一般我习惯用包管理工具安装这种系统级工具:
代码语言:javascript复制shell> yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel
同时记得创建一个系统账号(比如叫 elastic)以便后续运行服务使用。
第一步:安装 ElasticSearch
代码语言:javascript复制shell> tar zxvf elasticsearch-<VERSION>.tar.gz
shell> mv elasticsearch-<VERSION> /usr/local/elasticsearch
shell> /usr/local/elasticsearch/bin/elasticsearch
服务启动后,我们可以通过 elasticsearch 提供的 API 来确认一下基本信息:
代码语言:javascript复制shell> curl http://localhost:9200/
{
"name" : "...",
"cluster_name" : "...",
"cluster_uuid" : "...",
"version" : {
"number" : "...",
"build_hash" : "...",
"build_date" : "...",
"build_snapshot" : false,
"lucene_version" : "..."
},
"tagline" : "You Know, for Search"
}
缺省情况下,elasticsearch 服务会监听 9200 端口,如果你想自定义监听地址和端口,那么可以设置 elasticsearch.yml 配置文件中的 network.host 和 http.port 选项。
如果你的 Linux 内核版本比较低,那么在启动的时候可能会遇到如下错误:
system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk
此时可以通过在配置文件里加入:「bootstrap.system_call_filter: false」来规避问题。
至此,elasticsearh 的安装算是基本结束了,我们可以确认一下服务健康与否:
代码语言:javascript复制shell> curl http://localhost:9200/_cluster/health?pretty
{
"cluster_name" : "test",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 0,
"active_shards" : 0,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
提醒:当你在生产环境中运行 elasticsearch 的时候,一定要留意 jvm.options 配置文件的设置,特别是里面的 Xms 和 Xmx,通常是建议把它们设置成内存的一半,太小的话可能不够用,太大的话可能会增加 GC 时间。
第二步:安装 Kibana
代码语言:javascript复制shell> kibana-<VERSION>-linux-x86_64.tar.gz
shell> mv kibana-<VERSION>-linux-x86_64 /usr/local/kibana
shell> /usr/local/kibana/bin/kibana
Kibana 服务会监听 5601 端口。新版 Kibana 会在首页的 Add Data to Kibana 里教你如果按模板把数据展示出来,包括 Logging 的形式,当然,你也可以自己按要求配置。
第三步:安装 Logstash
代码语言:javascript复制shell> tar zxvf logstash-<VERSION>.tar.gz
shell> mv logstash-<VERSION> /usr/local/logstash
shell> /usr/local/logstash/bin/logstash -f /path/to/logstash.conf
关键在于配置文件 logstash.conf 的编写,它包含 input、filter、output 三部分:
代码语言:javascript复制input {
beats {
host => "<HOST>"
port => 5044
}
}
filter {
grok {
match => {
"message" => "%{HTTPD_COMBINEDLOG} (?:%{NUMBER:time:float}|-)"
}
}
kv {
prefix => "arg_"
source => "request"
field_split => "&?"
remove_field => [ "request" ]
}
urldecode {
all_fields => true
}
date {
match => ["timestamp" , "dd/MMM/YYYY:HH:mm:ss Z"]
}
}
output {
elasticsearch {
hosts => ["<HOST>:9200"]
user => "<USER>"
password => "<PASSWORD>"
}
}
说明:Logstash 缺省会在 Elasticsearch 里创建 logstash-%{ YYYY.MM.dd} 索引,如果需要修改,那么可以在 output 里通过 index 指令设置,不过需要注意的是,需要在 Kibana 里创建新索引(Management -> Index Patterns -> Add New)
其中,input 通过 5044 端口接收 Filebeat 发送的数据,output 把分析后的数据发送到远端 elasticsearch 的 9200 端口,难点在于 filter 的编写,下面以 nginx 日志为例来说明,缺省情况下,nginx 包含了 combined 的日志格式,也就是:
代码语言:javascript复制log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
不过我们通常还关心响应时间,所以我们多记录一个 $upstream_response_time:
代码语言:javascript复制log_format logstash '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$upstream_response_time';
在 filter 中又属 grok 最难懂,不过有很多例子,其中也包含了 COMBINED 正则,再配上 Dev Tools 里的 Grok Debugger,并不难搞定,如果有问题可以把 input,output 改成调试模式来纠错:
代码语言:javascript复制input{
stdin {}
}
output {
stdout { codec => rubydebug }
}
如果出现 grok 不解析 HTTPD_COMBINEDLOG 的情况,那么可以用 COMBINEDAPACHELOG 替换。以我的环境为例,缺省情况下包含的匹配模式都被放到目录:vendor/bundle/jruby/1.9/gems/logstash-patterns-core-4.0.2/patterns 里,你也可以通过 patterns_dir 来扩展它。
其它诸如 kv,urldecode,date 等等的说明,参考官方文档即可。不过其中有一个不易察觉的问题需要说明一下,那就是在 kv 切割字段后,缺省会统统识别为 string 类型,比如 uid=123&foo=bar 在被 kv 处理后,uid 也会被当作 string,而不是我们希望的 integer,虽然我们可以通过加入 mutate 来强制转换,不过字段往往是动态的,硬编码不灵活,这里面有一个小技巧,那就是利用 elasticsearch 的 numeric_detection 功能来自动检测,篇幅所限,具体就不展开了,大家有一个印象即可。
第四步:安装 Filebeat
有很多种 Beat,比如 Packetbeat,Winlogbeat,Metricbeat,因为我们是通过 Nginx 日志来收集数据,所以我们选择的是 Filebeat,配置文件 /etc/filebeat/filebeat.yml大致如下:
代码语言:javascript复制filebeat.prospectors:
- input_type: log
paths:
- /path/to/nginx/logstash/format/log/file
output.logstash:
hosts: ["<HOST>:5044"]
在我们安装 ELK 的过程中,都是使用手动启动服务的方式,不过需要说明的是 ELK 都是内存消耗大户,保不齐就会出现 OOM 之类的意外情况,所以推荐用 supervisor 管理:
代码语言:javascript复制[program:elasticsearch]
command=/usr/local/elasticsearch/bin/elasticsearch
numprocs=1
autostart=true
autorestart=true
user=elastic
[program:logstash]
command=/usr/local/logstash/bin/logstash -f /path/to/logstash.conf
numprocs=1
autostart=true
autorestart=true
user= elastic
[program:kibana]
command=/usr/local/kibana/bin/kibana
numprocs=1
autostart=true
autorestart=true
user= elastic
如此,一个基本可用的 ElasticStack 就算是配置好了,理论上 ELK 三部分都是可以水平扩展的,如果性能有问题,一般加节点就行了,下面给一个实际的日志统计图表:
TIME
通过分析日志里的 $upstream_response_time 数据,我们可以把响应时间分割到多个区间里:90%,95%,99%,如此比单纯取全体平均值更合理。
此外,ElasticStack 缺省就支持地图功能,我们可以在 Logstash 的 filter 里设置:
代码语言:javascript复制geoip {
source => "IP Field Name"
}
然后在 Kibana 里加 tilemap 图表即可(如下图:杭州有人再狂刷页面,嗷嗷红):
Tile Map
需要说明的是,缺省地图是英文的,可以在 Kibana 里设置成高德的 tilemap 地址:
代码语言:javascript复制tilemap.url: "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}"
结束前再提醒一下大家,如果是按日期索引的日志,那么最好定期清除旧索引:
代码语言:javascript复制shell> curl -XDELETE http://ip:port/logstash-
$(date -d '-10days' '%Y.%m.%d')