
使用ElasticSearch將近3個(gè)月了,在使用過程中,陸陸續(xù)續(xù)踩了不少坑,每次覺得無法逾越時(shí),心里都想放棄,一是因?yàn)檫@東西要完全掌握不是那么容易,需要花很多時(shí)間;二是如果繼續(xù)使用曾經(jīng)用過的zabbix,說不定可以很快滿足眼前的需求,從而可以抽身做其他事情。但堅(jiān)持下來,就一定能從坑里爬起來,從而對(duì)這個(gè)系統(tǒng)更加了解,并利用這頭”猛獸”幫助我做更多事情。原因很簡(jiǎn)單,ElasticSearch除了是一個(gè)分布式數(shù)據(jù)庫,還是一個(gè)擴(kuò)展性和可用性都很強(qiáng)的近實(shí)時(shí)搜索引擎。
目前為止,踩過以下幾個(gè)坑:
- 集群搭建不成功
- 未使用內(nèi)網(wǎng)IP,導(dǎo)致恢復(fù)緩慢
- 未使用隊(duì)列及l(fā)ogstash,導(dǎo)致數(shù)據(jù)丟失
- Master和DataNode未分離,導(dǎo)致集群不穩(wěn)定
- Logstash吞吐量問題
- Logstash如何創(chuàng)建Mapping
- head插件安裝錯(cuò)誤
犯了這么多錯(cuò)誤基本上都是使用不當(dāng)、以偏概全的原因,以為看了一點(diǎn)文檔,就憑直覺可以猜測(cè)到系統(tǒng)的所有內(nèi)容,造成了后續(xù)問題的不斷涌現(xiàn),下面就逐一說一下淌坑過程
集群搭建不成功
一開始是在單機(jī)上玩ElasticSearch的,上生產(chǎn)環(huán)境肯定要使用它的集群功能,但文檔說只需要在elasticsearch.yml中設(shè)置cluster.name和node.name即可,ElasticSearch節(jié)點(diǎn)啟動(dòng)時(shí)會(huì)自動(dòng)發(fā)現(xiàn)集群并加入到集群,但全部設(shè)置完畢后,竟無法使各個(gè)節(jié)點(diǎn)組成集群,最后發(fā)現(xiàn)這種方法只在一臺(tái)機(jī)器上有效,而要組成集群,需要在每臺(tái)節(jié)點(diǎn)做以下配置:
discovery.zen.ping.unicast.hosts: ["Node1_IP:9300", "Node2_IP:9300", "Node3_IP:9300"]
ElasticSearch一般會(huì)開兩個(gè)端口,一個(gè)是集群維護(hù)端口,如上面的9300; 一個(gè)是對(duì)外提供的端口,默認(rèn)是9200。
未使用內(nèi)網(wǎng)IP,導(dǎo)致恢復(fù)緩慢
在部署集群時(shí),我挑選了幾臺(tái)配置相近的同網(wǎng)段機(jī)器,但當(dāng)時(shí)其中一臺(tái)機(jī)器操作系統(tǒng)沒有加載內(nèi)網(wǎng)網(wǎng)卡,為了偷一下懶,便直接使用了外網(wǎng)IP,集群是跑起來了,運(yùn)行了一段時(shí)間也沒有什么問題,但隨著流量越來也大,終于有一天Master突然down掉了,我當(dāng)時(shí)心想,ElasticSearch集群本身具有故障轉(zhuǎn)移功能,馬上會(huì)分配一個(gè)Master節(jié)點(diǎn),然后我只需要把原先的Master節(jié)點(diǎn)重啟即可,然而重啟了之后,通過head頁面查看恢復(fù)情況,發(fā)現(xiàn)集群長(zhǎng)時(shí)間處于黃色狀態(tài)(有replica shard丟失),而丟失的shard一直處于未分配狀態(tài),并沒有如我預(yù)期的:?jiǎn)?dòng)后,該節(jié)點(diǎn)可以重用在磁盤上的shard數(shù)據(jù),上報(bào)給Master,不需要數(shù)據(jù)拷貝,立馬恢復(fù)為綠色狀態(tài)。過了幾分鐘后,我發(fā)現(xiàn)恢復(fù)的shard數(shù)正以緩慢的速度增加,便推導(dǎo)出這樣的錯(cuò)誤結(jié)論:ElasticSearch在某一臺(tái)機(jī)器掛掉后,只會(huì)從primary shard復(fù)制數(shù)據(jù),即便節(jié)點(diǎn)迅速恢復(fù),也不會(huì)復(fù)用該節(jié)點(diǎn)上的數(shù)據(jù),如果是這種實(shí)現(xiàn)方式,我認(rèn)為這種恢復(fù)速度是無法接受的,頓時(shí)產(chǎn)生了無法繼續(xù)使用下去的念頭,當(dāng)時(shí)的心情是無比失落的。
而ElasticSearch的使用是相當(dāng)廣泛的,我們熟知的世界最大同性交友網(wǎng)站也是使用它來實(shí)現(xiàn)搜索功能的,所以可以斷定是我的使用方法有問題,要么是我選的版本不穩(wěn)定,要么是其他的原因,而穩(wěn)定版一般出問題的幾率不大,因此在換版本之前,需要找其他方面的原因,很久之前看過一篇google的slide,上面有一頁介紹了不同介質(zhì)數(shù)據(jù)的傳輸速度:外網(wǎng)的傳輸速度在10ms級(jí)別,而內(nèi)網(wǎng)卻在20微秒級(jí),這種速度的差異便會(huì)造成以下幾個(gè)方面的影響
- 集群無法提供正常服務(wù):因?yàn)槊總€(gè)請(qǐng)求,ElasticSearch節(jié)點(diǎn)都會(huì)經(jīng)過轉(zhuǎn)發(fā)和收集兩個(gè)過程,如果使用外網(wǎng)網(wǎng)卡,便會(huì)造成延遲大,訪問量上不去,而流量到達(dá)一定程度后,集群很快便無法提供正常服務(wù)
- 由于ES集群已經(jīng)無法正常服務(wù),所以down機(jī)、恢復(fù)困難一系列綜合癥的情況便會(huì)陸續(xù)發(fā)生
后續(xù)我將集群切到了內(nèi)網(wǎng)中,再測(cè)試重啟某一個(gè)節(jié)點(diǎn),便不會(huì)再出現(xiàn)恢復(fù)一個(gè)節(jié)點(diǎn)需要半天的情況了。

未使用隊(duì)列或logstash,導(dǎo)致數(shù)據(jù)丟失
最初用到的架構(gòu)非常簡(jiǎn)單: 使用ES(ElasticSearch縮寫)集群作為存儲(chǔ),beats和rsyslog作為shipper向ES集群發(fā)送數(shù)據(jù),使用這種架構(gòu)的主要原因是配置簡(jiǎn)單,ES本身是一個(gè)高可用集群,直接把數(shù)據(jù)發(fā)過去就好。而自己心里還產(chǎn)生了為什么會(huì)有ELK架構(gòu),感覺Logstash是多余的想法,在發(fā)生了幾次down機(jī)之后,才發(fā)現(xiàn)之前的想法很傻很天真,之前的架構(gòu)也有明顯的問題

- 在某一個(gè)節(jié)點(diǎn)down掉后,如果不馬上恢復(fù),在不了解beats負(fù)載均衡機(jī)制的前提之下,很難判斷數(shù)據(jù)還會(huì)不會(huì)發(fā)送給down掉的節(jié)點(diǎn),而新增一個(gè)節(jié)點(diǎn),需要修改所有beat的配置,即這里至少要使用一個(gè)負(fù)載均衡器給所有ES節(jié)點(diǎn)做負(fù)載均衡
- ES是一個(gè)高可用集群,但目前還沒有足夠的使用經(jīng)驗(yàn),所以可能今后還會(huì)出現(xiàn)集群故障的問題,而出故障,很可能造成數(shù)據(jù)的丟失,為了避免這種情況發(fā)生,需要在beats和ES集群之間構(gòu)建一套可持久化的隊(duì)列,最簡(jiǎn)單的隊(duì)列是redis,而logstash放在redis兩邊分別作為生產(chǎn)者和消費(fèi)者。想到的方案便是
beats->logstash->redis->logstash->ES,這樣便解決了丟數(shù)據(jù)的問題,當(dāng)然最新版的logstash可以將數(shù)據(jù)持久化到磁盤上,也許可以對(duì)此模型進(jìn)行簡(jiǎn)化
Logstash和Redis的使用都非常簡(jiǎn)單,這里就不一一介紹,值得注意的是,如果要使用redis做持久化,需要使用Redis的List的方式,而不是Sub-Pub的方式,以下是具體的架構(gòu)圖,箭頭的方向是數(shù)據(jù)流動(dòng)的方向。

Master和DataNode未分離,導(dǎo)致集群不穩(wěn)定
在ES集群中,節(jié)點(diǎn)分為Master、DataNode、Client等幾種角色,任何一個(gè)節(jié)點(diǎn)都可以同時(shí)具備以上所有角色,其中比較重要的角色為Master和DataNode:
- Master主要管理集群信息、primary分片和replica分片信息、維護(hù)index信息。
- DataNode用來存儲(chǔ)數(shù)據(jù),維護(hù)倒排索引,提供數(shù)據(jù)檢索等。
可以看到元信息都在Master上面,如果Master掛掉了,該Master含有的所有Index都無法訪問,文檔中說,為了保證Master穩(wěn)定,需要將Master和Node分離。而構(gòu)建master集群可能會(huì)產(chǎn)生一種叫做腦裂的問題,為了防止腦裂,需要設(shè)置最小master的節(jié)點(diǎn)數(shù)為eligible_master_number/2 + 1
腦裂的概念:
如果你有2個(gè)Master候選節(jié)點(diǎn),并設(shè)置最小Master節(jié)點(diǎn)數(shù)為1,則當(dāng)網(wǎng)絡(luò)抖動(dòng)或偶然斷開時(shí),2個(gè)Master都會(huì)認(rèn)為另一個(gè)Master掛掉了,他們都被選舉為主Master,則此時(shí)集群中存在兩個(gè)主Master,即物理上1個(gè)集群變成了邏輯上的2個(gè)集群,而當(dāng)其中一個(gè)Master再次掛掉時(shí),即便它恢復(fù)后回到了原有的集群,在它作為主Master期間寫入的數(shù)據(jù)都會(huì)丟失,因?yàn)樗厦婢S護(hù)了Index信息。
根據(jù)以上理論,我對(duì)集群做了如下更改,額外選取3個(gè)獨(dú)立的機(jī)器作為Master節(jié)點(diǎn),修改elasticsearch.yml配置
node.master = true
node.data = false
discovery.zen.minimum_master_nodes = 2
修改其他節(jié)點(diǎn)配置,將其設(shè)置為DataNode,最后挨個(gè)重啟
node.master = false
node.data = true
Logstash吞吐量問題
在使用了新的架構(gòu)后,我發(fā)現(xiàn)了當(dāng)流量上來后,Redis的隊(duì)列會(huì)持續(xù)增長(zhǎng),消費(fèi)速度跟不上生產(chǎn)速度,造成的問題是數(shù)據(jù)在Redis中堆積,圖表展示有大量的延遲。解決這個(gè)問題有以下幾個(gè)思路
- 可能是ES插入速度太慢,需要調(diào)整參數(shù)提升插入性能
- 可能是Logstash吞吐量低,需要增加每次向Redis拿數(shù)據(jù)的緩存、增加向ES輸出的緩存、增加線程數(shù)、增加每次批量操作的
content length等
對(duì)于ES調(diào)優(yōu)中,我調(diào)整了線程數(shù),增加線程隊(duì)列,增大shard數(shù),但都沒有解決問題。
而Logstash調(diào)優(yōu),我首先調(diào)整了LS_HEAP_SIZE參數(shù),讓Logstash可以同時(shí)處理大量的數(shù)據(jù),然后主要專注在調(diào)整Logstash的Input和Output插件參數(shù)上,插件中可以設(shè)置線程數(shù)、batch_count數(shù)值等,而當(dāng)我將Redis插件參數(shù)改為batch_count=>10000后,發(fā)現(xiàn)隊(duì)列不再一直增長(zhǎng)了,它會(huì)漲到一定程度后,瞬間減少到2-3位數(shù),即隊(duì)列的長(zhǎng)度在一定范圍內(nèi)浮動(dòng),當(dāng)時(shí)欣喜若狂,以為自己解決了,但跑了大概5個(gè)小時(shí)候,發(fā)現(xiàn)隊(duì)列又開始不斷增長(zhǎng)了,問題并沒有得到解決。而產(chǎn)生解決了的假象應(yīng)該是我增加了Logstash內(nèi)存的原因,數(shù)據(jù)只是先把Logstash內(nèi)存填滿,再開始填隊(duì)列,而填滿Logstash內(nèi)存花了幾個(gè)小時(shí),關(guān)鍵的Logstash到ES的吞吐量還是沒有上去,在access日志中,無論如何也無法讓bulk API的content length增加,如下圖中的長(zhǎng)度一直維持在2K左右。

最后,我采用了替換Logstash版本的策略,更新了時(shí)下最新的5.1.1版本,由于新版的配置和舊版配置不一樣,所以認(rèn)真研究了一下配置,在這個(gè)過程中,我發(fā)現(xiàn)了一個(gè)-b參數(shù)可以修改批量插入的大小,也許就是我需要的。果然,將這個(gè)參數(shù)由默認(rèn)的125改為了1000,順利的解決了這個(gè)難題,同時(shí)也證明了并不是版本問題,還是使用問題,而這個(gè)參數(shù)也正是修改content length的方法,順便說一下,如果你使用nginx作為負(fù)載均衡器,你需要同時(shí)增加client_max_body_size參數(shù),避免產(chǎn)生content length過大而返回413錯(cuò)誤碼。
Logstash如何創(chuàng)建Mapping
當(dāng)使用Logstash進(jìn)行轉(zhuǎn)發(fā)時(shí),有可能你的數(shù)據(jù)都在一個(gè)Index中,當(dāng)然你也可以設(shè)置不同的Index,這篇文章中就有根據(jù)type來劃分Index的方法,不管劃不劃分Index,都會(huì)默認(rèn)生成一個(gè)或多個(gè)mapping結(jié)構(gòu),mapping結(jié)和不同的type即對(duì)應(yīng)MySQL中的數(shù)據(jù)庫和表結(jié)構(gòu)信息,當(dāng)然我這里不是為了說明它們的區(qū)別,而是我們無法自定義字段的類型。
這會(huì)產(chǎn)生各種各樣的問題,比如它會(huì)默認(rèn)產(chǎn)生analyzed類型的string字段,會(huì)自動(dòng)將帶有連接符的字符串分為兩個(gè)字符串輸出,即"idc-1"這樣的字符串會(huì)輸出為"idc"和"1",這并不是我想要的,讓我相當(dāng)困擾,而Mapping在生成后是無法修改字段的,除非你換一個(gè)新的字段。
解決這個(gè)問題的方法并不在mapping上,而我卻花了很多時(shí)間在這個(gè)上面,最終答案卻是使用template,在template中可以定義你需要的mapping,這樣便解決以上問題。到此,我還是不能完全理解里面的機(jī)制,以后抽空了解后再補(bǔ)上。
head插件安裝失敗
上文有介紹head插件,它是一個(gè)可以顯示集群狀態(tài)及操作ES集群的UI,可以取代官方的X-Pack,后者只有30天的試用期,因?yàn)閯?chuàng)業(yè)公司,能用免費(fèi)的盡量采用免費(fèi)的。在集群中,有幾個(gè)節(jié)點(diǎn)安裝該插件會(huì)失敗,提示:Unable to veryfy checksum for download plugin ...,google上查了一圈仍然沒有找到解決辦法,最后試著手動(dòng)將該插件下載然后解壓到/usr/share/elasticsearch/plugins/目錄下,并將目錄改為head即可解決該問題。
以上問題是我這段時(shí)間來碰到的坑,每個(gè)都花了不少時(shí)間去解決,自己也比較幸運(yùn),花在上面的時(shí)間沒有白費(fèi)。因?yàn)閭€(gè)人覺得這個(gè)技術(shù)棧實(shí)在是比較好,而資料主要以英文的為主,把自己的經(jīng)歷寫下來,希望今后不再犯同樣的錯(cuò)誤,也希望可以幫助其他使用該技術(shù)的同學(xué)。