如何使用ELK來監控性能

EKL GUI

每當我解決一些應用性能問題的時候,我經常會看到一個服務因爲高的CPU利用率而使得一臺或者多臺服務器運行變得很是緩慢。這也許意味着它由於高負載而致使資源缺少,可是一般狀況下這實際上是代碼有bug,一個異常或者一個錯誤的流程致使過多佔用了系統資源。爲了把這些問題找出來,我不得不在NewRelic/Nagios和ELK之間查看信息。node

因此我確信我須要有一個單一的管理面板來查看從各個應用,操做系統以及網絡設備上收集來的事件組合而成的性能指標ios

爲了使用ELK來監控你平臺的性能,你須要集成一系列的工具。Probes是必須包含的組件,它運行在各個主機上用來收集各類系統性能指標。而後,採集的數據須要發送給Logstash,而後在Elasticsearch中進行彙集,最後由Kibana轉換成圖形。最終,軟件服務操做組成員使用這些圖形展現系統性能。在這篇文章中,我將分享咱們如何建構咱們的ELK軟件棧來監控服務的性能。docker

1. 收集和傳送

收集

在收集和傳送數據到Logstash中的第一個步驟,咱們使用一個名爲 Collectl 的工具。該工具來自於一個開源項目,它包含的大量的選項容許系統管理員從多個不一樣的IT系統中獲取各類指標,而且運行保存這些數據供之後分析。咱們使用它來生成,追蹤,以及保存指標數據,例如網絡吞吐量,CPU的磁盤IO等待時間,空閒的內存,以及CPU的空閒時間(指出過分/未充分使用計算機資源)。它也常被用於監控其餘類型的系統資源,例如inode的使用以及打開的socket數量。bash

Collectl命令輸出樣例服務器

Collectl命令輸出樣例

最後,Collectl使用指定格式(in plot format)將採集的指標輸出到一個日誌文件中。該開源項目知道如何收集信息可是它並不會自動發送數據到ELK軟件棧中。網絡

使用一個Docker容器

咱們把Collectl封裝到了一個Docker容器中來獲取一個Docker鏡像,該鏡像會包含了數據採集和數據發送的基本軟件。咱們使用版本4.0.0的Collectl以及下面提到的配置,這樣能夠避免一系列的問題:app

—— 爲了不數據在容器中過載,咱們只保存了當天的數據。更久的數據都是維護在ELK軟件棧中,所以你無需擔憂在容器的日誌中保存全部的數據致使的問題。socket

—— Collectl能夠以指定的時間週期收集各類採樣數據,當它會用不一樣的時間週期把數據持久到磁盤。這被稱爲刷新週期。若是數據每秒鐘都被刷新到磁盤那麼你能夠獲得近乎實時的數據。不過例如對於一個30秒的採集間隔,那麼選擇一個十分激進的採樣週期不是必須的。一個輸出格式化器會用於輸出指定格式(a plot format)的輸出,默認它會在每一行輸出多個值,每一個值用空格分開。tcp

Collectl配置文件看上去相似下面:工具

DaemonCommands = -f /var/log/collectl/performance-tab -r00:00,0 -F1 -s+YZ -oz -P --interval 30
PQuery =   /usr/sbin/perfquery:/usr/bin/perfquery:/usr/local/ofed/bin/perfquery
PCounter = /usr/mellanox/bin/get_pcounter
VStat =    /usr/mellanox/bin/vstat:/usr/bin/vstat
OfedInfo = /usr/bin/ofed_info:/usr/local/ofed/bin/ofed_info
Resize=/usr/bin/resize:/usr/X11R6/bin/resize
Ipmitool =  /usr/bin/ipmitool:/usr/local/bin/ipmitool:/opt/hptc/sbin/ipmitool
IpmiCache = /var/run/collectl-ipmicache
IpmiTypes = fan,temp,current

使用RSYSLOG

RSYSLOG是另外一個容器組件。它用來從日誌文件中提取數據,而且發送數據到ELK軟件棧中。爲了讓Logstash專一於須要字段而不是全部的數據上,這裏建議使用RSYSLOG在日誌裏增長一些元數據來對日誌進行篩選。這裏能夠在數據發送前對指標進行過濾或者增長一些信息好比實例名稱以及IP地址。附加上了時間戳後,這些信息能夠被髮送到Logstash。

一些注意點

在本步驟,有兩個問題須要注意:

1 - 時間戳: 首先,Collectl並不在採集的數據中輸出它的時間戳。所以若是你不一樣的主機運行在不一樣的時區,它們在你的ELK裏面並不會對齊。爲了解決這個問題,咱們須要查詢容器當前運行的時區,而後設置相應的時間戳。

2 - 遵循Collectl日誌文件名: 另外一個複雜的問題是Collectl輸出數據到一個文件中,可是該文件名不是固定不變的。僅僅文件名的前綴是能夠自定義的,而後Collectl自動在文件名上加上了當前日期。這個問題致使RSYSLOG不能經過文件名來監視文件,當日期切換時文件名也會改變。咱們能夠用最新版本的RSYSLOG —— 版本8來解決它,可是這裏我假設大多數用戶尚未用上這個版本。咱們建立了一個很小的腳本,它調用了老版本的RSYSLOG,該腳本在容器裏運行了一個定時的任務,該任務會連接一個指定的名稱的文件名到一個固定的日誌文件名上。而後SYSLOG只中那個固定日誌文件中提取數據,即使該文件連接的目標文件改變了也沒有關係。這就像一個指針,它在必定的時間下老是指向正確的Collectl日誌文件。

$ModLoad imfile
$InputFilePollInterval 10
$PrivDropToGroup adm
$WorkDirectory /var/spool/rsyslog
 
# Logengine access file:
$InputFileName /var/log/collectl/daily.log
$InputFileTag performance-tab:
$InputFileStateFile stat-performance
$InputFileSeverity info
$InputFilePersistStateInterval 20000
$InputRunFileMonitor
 
$template logzioPerformanceFormat,"[XXLOGZTOKENXX] <%pri%>%protocol-version% %timestamp:::date-rfc3339% XXHOSTNAMEXX %app-name% %procid% %msgid% [type=performance-tab instance=XXINSTANCTIDXX] XXOFSETXX %msg% XXUSERTAGXX\n"
if $programname == 'performance-tab' then @@XXLISTENERXX;logzioPerformanceFormat

容器檢查清單

— Collectl

  • RSYSLOG

  • 連接Collectl輸出文件的腳本

  • 腳本文件的配置

上面用到的Docker鏡像能夠在DockerHub上拉取,連接: https://registry.hub.docker.com/u/logzio/logzio-perfagent/

2. 解析數據

在收集和裝箱發送後,咱們須要作數據的解析。Collectl返回的是未結構化的日誌數據,它基本是由一系列的數字組成, Logstash Grok表達式使用這些數據來獲取每一個字段的名稱和指定的值。

Collectl的配置參數顯示設置了一個特定的輸出模式。RSYSLOG日誌配置在發送的消息中的特定位置增長了時區的信息。若是你想同時使用了這兩個配置,那Grok模式配置以下:

%{GREEDYDATA:zone_time} %{NUMBER:cpu__user_percent:int} %{NUMBER:cpu__nice_percent:int}
%{NUMBER:cpu__sys_percent:int} %{NUMBER:cpu__wait_percent:int} %{NUMBER:cpu__irq_percent:int}
%{NUMBER:cpu__soft_percent:int} %{NUMBER:cpu__steal_percent:int}
%{NUMBER:cpu__idle_percent:int} %{NUMBER:cpu__totl_percent:int} 
%{NUMBER:cpu__guest_percent:int} %{NUMBER:cpu__guestN_percent:int} 
%{NUMBER:cpu__intrpt_sec:int} %{NUMBER:cpu__ctx_sec:int} %{NUMBER:cpu__proc_sec:int} 
%{NUMBER:cpu__proc__queue:int} %{NUMBER:cpu__proc__run:int} %{NUMBER:cpu__load__avg1:float} 
%{NUMBER:cpu__load__avg5:float} %{NUMBER:cpu__load__avg15:float} %{NUMBER:cpu__run_tot:int} 
%{NUMBER:cpu__blk_tot:int} %{NUMBER:mem__tot:int} %{NUMBER:mem__used:int} 
%{NUMBER:mem__free:int} %{NUMBER:mem__shared:int} %{NUMBER:mem__buf:int} 
%{NUMBER:mem__cached:int} %{NUMBER:mem__slab:int} %{NUMBER:mem__map:int} 
%{NUMBER:mem__anon:int} %{NUMBER:mem__commit:int} %{NUMBER:mem__locked:int} 
%{NUMBER:mem__swap__tot:int} %{NUMBER:mem__swap__used:int} %{NUMBER:mem__swap__free:int} 
%{NUMBER:mem__swap__in:int} %{NUMBER:mem__swap__out:int} %{NUMBER:mem__dirty:int} 
%{NUMBER:mem__clean:int} %{NUMBER:mem__laundry:int} %{NUMBER:mem__inactive:int} 
%{NUMBER:mem__page__in:int} %{NUMBER:mem__page__out:int} %{NUMBER:mem__page__faults:int} 
%{NUMBER:mem__page__maj_faults:int} %{NUMBER:mem__huge__total:int} 
%{NUMBER:mem__huge__free:int} %{NUMBER:mem__huge__reserved:int} 
%{NUMBER:mem__s_unreclaim:int} %{NUMBER:sock__used:int} %{NUMBER:sock__tcp:int} 
%{NUMBER:sock__orph:int} %{NUMBER:sock__tw:int} %{NUMBER:sock__alloc:int} 
%{NUMBER:sock__mem:int} %{NUMBER:sock__udp:int} %{NUMBER:sock__raw:int} 
%{NUMBER:sock__frag:int} %{NUMBER:sock__frag_mem:int} %{NUMBER:net__rx_pkt_tot:int} 
%{NUMBER:net__tx_pkt_tot:int} %{NUMBER:net__rx_kb_tot:int} %{NUMBER:net__tx_kb_tot:int} 
%{NUMBER:net__rx_cmp_tot:int} %{NUMBER:net__rx_mlt_tot:int} %{NUMBER:net__tx_cmp_tot:int} 
%{NUMBER:net__rx_errs_tot:int} %{NUMBER:net__tx_errs_tot:int} %{NUMBER:dsk__read__tot:int} 
%{NUMBER:dsk__write__tot:int} %{NUMBER:dsk__ops__tot:int} %{NUMBER:dsk__read__kb_tot:int} 
%{NUMBER:dsk__write__kb_tot:int} %{NUMBER:dsk__kb__tot:int} %{NUMBER:dsk__read__mrg_tot:int} %{NUMBER:dsk__write__mrg_tot:int} %{NUMBER:dsk__mrg__tot:int} %{NUMBER:inode__numDentry:int} %{NUMBER:inode__openfiles:int} %{NUMBER:inode__max_file_percent:int} 
%{NUMBER:inode__used:int} %{NUMBER:nfs__reads_s:int} %{NUMBER:nfs__writes_s:int} 
%{NUMBER:nfs__meta_s:int} %{NUMBER:nfs__commit_s:int} %{NUMBER:nfs__udp:int} 
%{NUMBER:nfs__tcp:int} %{NUMBER:nfs__tcp_conn:int} %{NUMBER:nfs__bad_auth:int} 
%{NUMBER:nfs__bad_client:int} %{NUMBER:nfs__reads_c:int} %{NUMBER:nfs__writes_c:int} 
%{NUMBER:nfs__meta_c:int} %{NUMBER:nfs__commit_c:int} %{NUMBER:nfs__retrans:int} 
%{NUMBER:nfs__authref:int} %{NUMBER:tcp__ip_err:int} %{NUMBER:tcp__tcp_err:int} 
%{NUMBER:tcp__udp_err:int} %{NUMBER:tcp__icmp_err:int} %{NUMBER:tcp__loss:int} 
%{NUMBER:tcp__f_trans:int} %{NUMBER:buddy__page_1:int} %{NUMBER:buddy__page_2:int} 
%{NUMBER:buddy__page_4:int} %{NUMBER:buddy__page_8:int} %{NUMBER:buddy__page_16:int} 
%{NUMBER:buddy__page_32:int} %{NUMBER:buddy__page_64:int} %{NUMBER:buddy__page_128:int} 
%{NUMBER:buddy__page_256:int} %{NUMBER:buddy__page_512:int} %{NUMBER:buddy__page_1024:int}?( 
%{GREEDYDATA:user_tag})

3. 可視化

若是你運行了一個快速的ELK軟件棧,那你會幾乎同時獲得這些數據的展現。顯然這依賴於你安裝的ELK的性能,不過你能夠預期會在半分鐘以內獲得結果 - 一個最新的數據流的信息。

在Logz.io,咱們有一些預約義的報警和儀表板來展現性能指標,它們都是使用Collectl。若是你也使用Logz.io服務,請在聊天室裏找到咱們,咱們將會分享這些有用的信息。

kibana example

若是你想了解更多,請隨意在下面留言!

翻譯自這裏

相關文章
相關標籤/搜索