摘要:快速搭建日志收集版本進(jìn)行文章的第二次修改,包括了之前的簡(jiǎn)單方案的升級(jí)過程。分割線快速搭建日志收集第一版本新項(xiàng)目短時(shí)間來實(shí)現(xiàn)日志采集。
快速搭建elk日志收集 kafka版本
進(jìn)行文章的第二次修改,包括了之前的簡(jiǎn)單方案的升級(jí)過程。
因?yàn)闃I(yè)務(wù)的不斷更新升級(jí),為了保證線上業(yè)務(wù)也能正常使用elk服務(wù),并且使得elk的服務(wù)和線業(yè)務(wù)流解耦(即避免直接寫入es的方式可能會(huì)帶來的耗時(shí)影響)所以我們采用了下面最新的方案,也是常規(guī)方案
方案業(yè)務(wù)層 >> kafka隊(duì)列 >> logstash 消費(fèi) >> elasticsearch
業(yè)務(wù)層將日志寫入到kafka隊(duì)列,同事logstash可以開啟多個(gè)線程,啟用同一個(gè)group_id來對(duì)kafka進(jìn)行消費(fèi),將讀取到的日志進(jìn)行解析后,寫入到elasticsearch中,并且按照索引模板進(jìn)行解析。
優(yōu)點(diǎn)業(yè)務(wù)層可以直接寫入到kafka隊(duì)列中,不用擔(dān)心elasticsearch的寫入效率問題。
缺點(diǎn)比起之前的簡(jiǎn)單版本,需要保證kafka隊(duì)列、logstash的高可用(雖然logstash掛掉后,可以重啟后重新讀取隊(duì)列日志)
搭建整個(gè)搭建過程,寫入kafka是非常簡(jiǎn)單的,這里遇到的問題是logstash和elasticsearch索引模板帶來的困擾。
logstash內(nèi)置一套模板
elasticsearch 本身可以自定義一套模板
如何確定使用誰的模板呢?
網(wǎng)上找到的資料,建議采用將模板配置在elasticsearch側(cè),這樣就不用每個(gè)logstash進(jìn)行一個(gè)模板配置文件的維護(hù)。
官網(wǎng)的文檔kafka的input插件
https://www.elastic.co/guide/...
input { kafka { // 需要讀取的kafka隊(duì)列集群配置 bootstrap_servers => "xxx.xxx.xxx.xxx:9092" // 配置的消費(fèi)者的group名稱,因?yàn)橥粋€(gè)組內(nèi)的消費(fèi)消息不會(huì)重復(fù) group_id => "logstash-group" // 主題配置 topics => "kibana_log" // 從未消費(fèi)過的偏移量開始 auto_offset_reset =>"earliest" } } // 重要,下面多帶帶講 filter { json { source => "message" } } output { // stdout可以省略,這個(gè)是為了命令行模式下方便調(diào)試 stdout{ codec => rubydebug } elasticsearch { // es 集群 hosts=>"xxx.xxx.xxx.xxx:9200" // 重要:取消logstash自定義模板功能,進(jìn)而強(qiáng)制使用es的內(nèi)置模板 manage_template=>false // 需要匹配的模板名稱 index=>"logstash-dev-%{+YYYY.MM.dd}" } }
先解釋下filter配置,當(dāng)我們從kafka讀取消息的時(shí)候,消息體是通過message字段來進(jìn)行傳遞的,所以message是一個(gè)字符串,但是我們的es索引模板可能會(huì)非常復(fù)雜,所以我們需要對(duì)其進(jìn)行json解析后,再交給es。否則es收到的之后一個(gè)message字段。
filter { json { source => "message" } }
再說下模板配置,首先通過kibana的devtool向es中寫入了一個(gè)模板,我區(qū)分了兩套環(huán)境dev、prod。
Php 實(shí)現(xiàn)寫入這里字段都進(jìn)行了strval轉(zhuǎn)義,為什么呢?這和下面要講的動(dòng)態(tài)模板有關(guān)聯(lián)的。往下看
$position = YnUtil::getPosition(); $urlData = parse_url(Wii::app()->request->url); $path = $urlData["path"] ?? ""; $params = [ "category" => strval($category), "appType" => strval(YnUtil::getAppType()), "appVersion" => strval(YnUtil::getAppVersion()), "host" => strval(Wii::app()->request->hostInfo), "uri" => strval(Wii::app()->request->url), "uid" => strval(Wii::app()->user->getUid()), "path" => strval($path), "server" => strval(gethostname()), "geoip" => [ "ip" => strval(Yii::$app->request->userIP), "location" => [ "lat" => floatval($position["latitude"]), "lon" => floatval($position["longitude"]), ], ], "userAgent" => strval(Wii::app()->request->userAgent), "message" => is_array($message) ? Json::encode($message) : strval($message), // "@timestamp" => intval(microtime(true) * 1000), ];索引模板
下面的模板是寫入到es里面的自定義模板,為了防止索引規(guī)則名稱沖突,這里將order置為1。
我們先來看下第一個(gè)模板(這個(gè)是不推薦的,因?yàn)楹芊爆?,但是類型很?qiáng)制有效)
PUT _template/logstash-dev { "index_patterns": "logstash-dev*", "aliases": {}, "order":1, "mappings": { // 這里使用logs是因?yàn)閘ogstash默認(rèn)的type類型 "logs": { // 動(dòng)態(tài)模板 "dynamic_templates": [ { "string_fields": { "match": "*", "match_mapping_type": "string", "mapping": { "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } }, "norms": false, "type": "text" } } } ], // 這里對(duì)屬性進(jìn)行了類型設(shè)置 "properties": { "@timestamp": { "type": "date" }, "@version": { "type": "keyword" }, "appType": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "appVersion": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "category": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "geoip": { "dynamic": "true", "properties": { "ip": { "type": "ip" }, "latitude": { "type": "half_float" }, "location": { "type": "geo_point" }, "longitude": { "type": "half_float" } } }, "host": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "message": { "type": "text", "norms": false }, "server": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "uid": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "uri": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "path": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "userAgent": { "type": "text", "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } }, "settings": { "index": { "number_of_shards": "1" } } }動(dòng)態(tài)模板概念
上面的模板不是最優(yōu)的,但是卻是一種嘗試,模板里面針對(duì)每個(gè)屬性做了設(shè)置,這樣客戶端只需要寫入對(duì)應(yīng)的屬性就好了。但是如何動(dòng)態(tài)配置呢?如果想加入一個(gè)字段,難道還要修改模板么? 這里引入了動(dòng)態(tài)模板的概念
Only the following datatypes can be automatically detected: boolean, date, double, long, object, string. It also accepts * to match all datatypes.
動(dòng)態(tài)映射
https://www.elastic.co/guide/...
動(dòng)態(tài)模板(注意看match和match_mapping_type)
https://www.elastic.co/guide/...
映射屬性
https://www.elastic.co/guide/...
然后我們給出了一個(gè)全新的模板
全新索引模板這個(gè)模板里面只針對(duì)特殊的屬性進(jìn)行了設(shè)置,其他的都是通過動(dòng)態(tài)模板擴(kuò)展的,下面看下效果。
PUT _template/logstash-dev { "index_patterns": "logstash-dev*", "aliases": {}, "order":1, "mappings": { "logs": { "dynamic_templates": [ { "string_fields": { "match": "*", "match_mapping_type": "string", "mapping": { "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } }, "norms": false, "type": "text" } } } ], "properties": { "@timestamp": { "type": "date" }, "@version": { "type": "keyword" }, "geoip": { "dynamic": "true", "properties": { "ip": { "type": "ip" }, "latitude": { "type": "half_float" }, "location": { "type": "geo_point" }, "longitude": { "type": "half_float" } } } } } }, "settings": { "index": { "number_of_shards": "1" } } }
我們上面說了,全都進(jìn)行了strval轉(zhuǎn)義,為什么呢?因?yàn)閯?dòng)態(tài)模板里面,匹配的是string類型,如果我們寫入的是一個(gè)int類型,那么就不會(huì)進(jìn)行自動(dòng)擴(kuò)展了。試驗(yàn)后表明,會(huì)生成一個(gè)int類型的message字段,這樣是不合理的。最終生成的效果是如下圖的。
分割線 =============================
快速搭建elk日志收集 第一版本新項(xiàng)目短時(shí)間來實(shí)現(xiàn)日志采集。
資源一臺(tái)8G 4核 500G硬盤 服務(wù)器
部署方案項(xiàng)目部署在4臺(tái)服務(wù)器,每臺(tái)服務(wù)器通過phpsdk直接寫入一臺(tái)es服務(wù)器中。
(在本次部署中,沒有使用logstash的功能)
沒有使用異步隊(duì)列,導(dǎo)致直接寫入es可能會(huì)影響業(yè)務(wù)邏輯,但是目前只會(huì)在開發(fā)和測(cè)試環(huán)境使用。
組件主要利用elasticsearch 和 kibana
要使用x-pack做安全校驗(yàn),包括給kibana加入登錄授權(quán)功能
Elasticsearch
https://www.elastic.co/cn/pro...
Elasticsearch-clients
這里包含的多種語言的sdk包
https://www.elastic.co/guide/...
Kibana
https://www.elastic.co/cn/pro...
Logstash
https://www.elastic.co/cn/pro...
X-pack
安裝流程說明很詳細(xì),秘鑰生成后記得保存下,并且加入x-pack后,kibana和elasticsearch的通訊,需要修改配置文件。另外phpsdk也需要加入秘鑰,后面說明。
https://www.elastic.co/cn/pro...
es的模板超級(jí)復(fù)雜的,所以我們要利用標(biāo)準(zhǔn)的現(xiàn)有的模板,需要從logstash中提取一個(gè)。
解壓下載的logstash-6.1.2.tar,執(zhí)行搜索命令
$ find ./ -name "elasticsearch-template*" ./vendor/bundle/jruby/2.3.0/gems/logstash-output-elasticsearch-9.0.2-java/lib/logstash/outputs/elasticsearch/elasticsearch-template-es2x.json ./vendor/bundle/jruby/2.3.0/gems/logstash-output-elasticsearch-9.0.2-java/lib/logstash/outputs/elasticsearch/elasticsearch-template-es5x.json ./vendor/bundle/jruby/2.3.0/gems/logstash-output-elasticsearch-9.0.2-java/lib/logstash/outputs/elasticsearch/elasticsearch-template-es6x.json
會(huì)發(fā)現(xiàn)有2x 5x 6x三個(gè)模板,這里我們選擇6x,要根據(jù)你的es版本來選擇。
然后創(chuàng)建索引,索引要在數(shù)據(jù)寫入之前創(chuàng)建,因?yàn)橐o每個(gè)字段設(shè)置類型。
https://www.elastic.co/guide/...
按照文檔的方式,將獲取到的6x通過curl的方式寫入到es
Es開啟外網(wǎng)訪問因?yàn)槟J(rèn)的es配置是開啟了localhost:9200端口,用于執(zhí)行RESTFUL,但是本次我們采用php-sdk的方式,直接寫入es,就要求每臺(tái)業(yè)務(wù)服務(wù)器,都能訪問到es。
# ---------------------------------- Network ----------------------------------- # # Set the bind address to a specific IP (IPv4 or IPv6): # 修改此處的host配置為0.0.0.0,這樣所有的請(qǐng)求都可以接入進(jìn)來 network.host: 0.0.0.0 # # Set a custom port for HTTP: # #http.port: 9200使用client sdk
下載地址
https://www.elastic.co/guide/...
try { $hosts = [ // 這個(gè)地方要填寫x-pack分配的密碼和用戶名 "http://{用戶名}:{密碼}@192.168.1.11:9200", // HTTP Basic Authentication // "http://user2:[email protected]:9200" // Different credentials on different host ]; $client = ClientBuilder::create()->setHosts($hosts)->build(); $position = YnUtil::getPosition(); $params = [ "index" => "logstash-yn-" . date("Ymd"), // elastic6版本有個(gè)bug,每個(gè)索引只能有一個(gè)type類型 "type" => "xxxx", "id" => md5(rand(0, 999999999)) . (microtime(true) * 1000), "body" => [ // 索引創(chuàng)建好后,寫入數(shù)據(jù)一定要注意類型,不然會(huì)報(bào)錯(cuò),我這里都會(huì)進(jìn)行格式化一遍 // 類別作為一個(gè)主要字段用于區(qū)分日志 "category" => strval($category), "appType" => strval(YnUtil::getAppType()), "appVersion" => strval(YnUtil::getAppVersion()), "host" => strval($hostInfo), "uri" => strval($url), "uid" => strval($user->getUid()), "server" => strval(gethostname()), "geoip" => [ "ip" => strval($ip), // 這個(gè)很重要,可以實(shí)現(xiàn)geo可視化圖形 "location" => [ "lat" => floatval($position["latitude"]), "lon" => floatval($position["longitude"]), ], ], "userAgent" => strval($userAgent), "message" => Json::encode($message), // 這里一定要寫入毫秒時(shí)間戳 "@timestamp" => intval(microtime(true) * 1000), ], ]; $client->index($params); } catch (Exception $e) { }
安裝好x-pack后,sdk也要配置用戶名和密碼
日志的索引,按照日期來構(gòu)建名稱
elastic6版本,每個(gè)索引只能有一個(gè)type類型,這是一個(gè)bug
geoip是logstash的模板中定義的字段,可以實(shí)現(xiàn)geo可視化(稍后解釋模板)
@timestamp 這里一定要毫秒時(shí)間戳
要捕獲報(bào)錯(cuò)日志,不要影響業(yè)務(wù)邏輯
模板說明官方說明
https://www.elastic.co/guide/...
我們這里以6x作為模板
{ // 將這個(gè)模板應(yīng)用于所有以 logstash- 為起始的索引。 "template": "logstash-*", "version": 60001, "settings": { "index.refresh_interval": "5s" }, "mappings": { "_default_": { // 動(dòng)態(tài)模板說明,很重要,配置了動(dòng)態(tài)模板后,我們可以添加任意字段 // https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html "dynamic_templates": [{ // 信息字段 官方說這里可以自定義 The template name can be any string value. "message_field": { "path_match": "message", "match_mapping_type": "string", "mapping": { "type": "text", "norms": false } } }, { // 字符串字段說明 "string_fields": { // 匹配所有 "match": "*", // 并且字段類型是string的 "match_mapping_type": "string", // "mapping": { "type": "text", // 這里應(yīng)該是和受歡迎程度評(píng)分相關(guān) "norms": false, "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } }], // 定義好的屬性說明 "properties": { // 時(shí)間字段,這個(gè)很重要,不然kibana不會(huì)出現(xiàn)時(shí)間相關(guān)的查詢控件 "@timestamp": { "type": "date" }, "@version": { "type": "keyword" }, // 這個(gè)可以只寫入properies里面的任意一個(gè)字段 "geoip": { "dynamic": true, "properties": { "ip": { "type": "ip" }, // 我只是用了這個(gè)location "location": { "type": "geo_point" }, "latitude": { "type": "half_float" }, "longitude": { "type": "half_float" } } } } } } }遇到的問題 索引創(chuàng)建太復(fù)雜怎么辦?
使用logstash內(nèi)置的模板
添加了時(shí)間字段后,創(chuàng)建數(shù)據(jù)不顯示?檢查是否是毫秒時(shí)間戳
安裝x-pack后,clientsdk向es寫入數(shù)據(jù)報(bào)錯(cuò),提示校驗(yàn)失?。?/b>需要在鏈接中添加用戶名和密碼
類型和模板不匹配導(dǎo)致錯(cuò)誤?寫入類型,一定要和索引模板中定義的一致,不然肯定報(bào)錯(cuò)!
同一個(gè)索引,添加多個(gè)type報(bào)錯(cuò)?elasticsearch6的 bug,官方承諾在7進(jìn)行修復(fù)
kibana加入登錄驗(yàn)證?安裝x-pack吧
如何保證高可用,無人值守?使用supervisor
如何清理elasticsearch老數(shù)據(jù)?我的方案是:定時(shí)腳本來清理7天之前的索引DELETE logstash-xxxx
如何快速執(zhí)行curl查詢?在kibana中有一個(gè)Dev Tools 可以執(zhí)行curl,并且看到結(jié)果
如何格式化kibana的日期格式?在kibana菜單的Management->Index Patterns中可以管理
結(jié)束不詳細(xì)的地方,可以留言
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28196.html
摘要:日志監(jiān)控和分析在保障業(yè)務(wù)穩(wěn)定運(yùn)行時(shí),起到了很重要的作用。本文搭建的的是一個(gè)分布式的日志收集和分析系統(tǒng)。對(duì)于隊(duì)列上的這些未處理的日志,有不同的幾臺(tái)進(jìn)行接收和分析。再由統(tǒng)一的進(jìn)行日志界面的展示。如等配置文件可以配置,等日志報(bào)表可視化熟練 ELK簡(jiǎn)介ELKStack即Elasticsearch + Logstash + Kibana。日志監(jiān)控和分析在保障業(yè)務(wù)穩(wěn)定運(yùn)行時(shí),起到了很重要的作用。比...
閱讀 3038·2023-04-25 18:06
閱讀 3307·2021-11-22 09:34
閱讀 2869·2021-08-12 13:30
閱讀 2058·2019-08-30 15:44
閱讀 1671·2019-08-30 13:09
閱讀 1639·2019-08-30 12:45
閱讀 1726·2019-08-29 11:13
閱讀 3617·2019-08-28 17:51