ELK 似乎是當(dāng)前最為流行的日志收集-存儲(chǔ)-分析的全套解決方案.
去年年初, 公司里已經(jīng)在用, 當(dāng)時(shí)自己還山寨 了一個(gè)統(tǒng)計(jì)系統(tǒng)(postgresql-echarts, 日志無(wú)結(jié)構(gòu)化, json形式存儲(chǔ)到postgresql, 構(gòu)建統(tǒng)一前端配置生成, 調(diào)用統(tǒng)一查詢接口, 具體細(xì)節(jié)), 已經(jīng)過了一年有余.
一年剛好, 發(fā)生了很多事, 那套系統(tǒng)不知現(xiàn)在如何了.
在新的公司, 一切都得從0到1, 近期開始關(guān)注日志/數(shù)據(jù)上報(bào)/統(tǒng)計(jì), 以及后續(xù)的數(shù)據(jù)挖掘等.
搭建, 測(cè)試并上線了一套簡(jiǎn)單的系統(tǒng), 初期將所有服務(wù)器的nginx日志, 以及搜索日志進(jìn)行處理.
下面主要介紹對(duì)nginx日志進(jìn)行處理的過程, 不是針對(duì)elk 的介紹, 所有涉及ip的地方都改成127.0.0.1 了, 根據(jù)自己環(huán)境進(jìn)行修改
1. nginx日志 -> logstash shipper -> redis
在centos 使用yum 安裝nginx 后, 默認(rèn)/etc/nginx/nginx.conf 中的日志格式定義為:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
然后在具體server 配置中使用
access_log /data/logs/nginx/{PROJECT_NAME}_access.log main;
此時(shí), 我們需要做的是, 將access log 通過logstash shipper 讀取, 轉(zhuǎn)json , 發(fā)送到redis , 由后續(xù)的logstash indexer 進(jìn)行處理
步驟
1.在日志所在機(jī)器部署logstash
2.在logstash 安裝目錄下的patterns 中加入一個(gè)文件nginx
內(nèi)容(與上面的log_format 相對(duì)應(yīng))
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.增加一個(gè)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 啟動(dòng)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
2. redis -> logstash indexer -> elasticsearch
注意, input的redis為上一步redis配置, key要對(duì)應(yīng), output的elasticsearch配置, index 指定了最終es中存儲(chǔ)對(duì)應(yīng)的index, 加日期, 方便對(duì)日志進(jìn)行定期刪除
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}"
}
}
3. elasticsearch -> kibana
剩下的其實(shí)沒什么了, 啟動(dòng)kibana 后, 配置好指向的es , 就可以在kibana 中查看到實(shí)時(shí)的日志數(shù)據(jù)
demo環(huán)境截圖
kibana 中, 支持各種統(tǒng)計(jì), 著實(shí)讓人驚艷了一把.
除了基本的nginx日志, 還需要在各類url入口, 加入平臺(tái), 渠道等信息, 這樣通過nginx訪問日志, 可以統(tǒng)計(jì)到更多的信息
當(dāng)然, 如果需要一些更為精確/特殊的統(tǒng)計(jì), 需要自行進(jìn)行數(shù)據(jù)上報(bào)的工作.
后續(xù)
- 更多的類型的日志聚合, 包括各類訪問日志, 統(tǒng)計(jì)上報(bào)日志等, 日志落地成文件, 永久留存, 轉(zhuǎn)入es中, 只留存三個(gè)月
- 如何對(duì)各類數(shù)據(jù)進(jìn)行拆分/匯總
- ELK整體部署/運(yùn)維/擴(kuò)容等, 包括數(shù)據(jù)清理
- 基于ES日志的業(yè)務(wù)自定義統(tǒng)計(jì)后臺(tái)(kibana無(wú)法滿足一些具體業(yè)務(wù)的統(tǒng)計(jì)需求)
- 為什么不使用
logstash forwarder , 因?yàn)槟壳叭罩窘M成等較為簡(jiǎn)單, 簡(jiǎn)單處理 , 后續(xù)需要用到時(shí)再考慮
其他
1. 關(guān)于logformat 和對(duì)應(yīng)grok 的配置
grok 是logstash 的一個(gè)插件, 文檔
Grok is currently the best way in logstash to parse crappy unstructured log data into something structured and queryable
所以, 我們?cè)谔幚?code>nginx日志時(shí), 需要根據(jù)具體logformat 定義對(duì)應(yīng)的grok 表達(dá)式
除了上面例子中用的那套, 另一份
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}
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}
如果想自行定義, 可以使用 grokdebug, 將要解析的日志和配置的正則放入, 可以查看最終得到的結(jié)構(gòu)化數(shù)據(jù)
2. elasticsearch插件
初期只安裝了一個(gè) kopf, web界面查看
3. supervisor
建議使用supervisor 對(duì)elk 進(jìn)行管理,(ps. 不要用yum自帶的, 版本太舊好多坑, 浪費(fèi)1小時(shí)......使用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
4. logstash坑
start_position => "beginning"
logstash, 會(huì)記錄一份文件讀到的位置, 在$HOME/.sincedb_xxxxx 如果要讓logstash重新讀取文件, 刪除之即可, 重啟shipper .
但是你可能發(fā)現(xiàn)es中重復(fù)記錄了, 這是因?yàn)? 在output 中, 沒有定義存儲(chǔ)到es時(shí)使用的document_id , es全部當(dāng)成新紀(jì)錄存入, 導(dǎo)致數(shù)據(jù)重復(fù)
|