2017/12/11

Fluentd

Fluentd 將 data source 及 backend system 分離,提供兩者之間的一個 Unified Logging Layer,可讓 developers 及 data analysts 能同時使用多種資料源,同時也解決格式錯誤的資料所造成的系統變慢或解譯錯誤的問題。


Fluentd 有三種版本,全部都是以 Apache2 License 釋出。


  1. Fluentd


    社群版本,只能用 ruby gems 安裝,沒有 init scripts,如果想要修改 Fluentd 或是做更多事情,可以用這個社群版

  2. ta-agent


    這是 Treasure Data, Inc 這家公司維護並測試的版本,可直接用 rpm/deb/dmg 套件安裝,安裝時同時安裝了一些預設設定值。如果是第一次使用 Fluentd,建議安裝 ta-agent。

  3. Fluent Bit


    Fluent Bit 是 Fluentd 的 lightweight data forwarder,用在 forward 資料給 Fluentd aggregators。可安裝在 embedded system 或是嵌入到 server 系統中。


Architecture


Fluentd 的架構圖為



由於 data inputs 及 output 透過 Fluentd 中繼資料,Fluentd 這個 Unified Logging Layer 野食作為 pluggable 架構,可不斷地增加不同的 inputoutput plugins,目前已經有超過 500+ 的 plugins。


假設有 M 種 data input,N 種 data output,pluggable 架構可讓原本複雜度 O(M*N) 的系統,變成 O(M+N) 的系統。


安裝


Download Fluentd 有列出所有安裝方式的資訊。我們選擇 Installing Fluentd Using rpm Package 安裝到 CentOS 7。


產生一個新的有 sshd 的 docker machine


docker run -d \
 -p 10022:22\
 -p 80:80\
 -p 8888:8888\
 --sysctl net.ipv6.conf.all.disable_ipv6=1\
 -e "container=docker" --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --name fluentd centosssh /usr/sbin/init

在安裝前,Before Installing Fluentd 必須要先處理幾項系統設定。


  1. NTP


    要同步時間,確保 log 的 timestamp 是正確的


    CentOS 7 修改 timezone,校正時間


    timedatectl set-timezone Asia/Taipei
    /usr/sbin/ntpdate time.stdtime.gov.tw && /sbin/hwclock -w
  2. Max # of File Descriptors


    ulimit -n 65535

    vi /etc/security/limits.conf


    root soft nofile 65535
    root hard nofile 65535
    * soft nofile 65535
    * hard nofile 65535
  3. Network Kernel Parameters


    解決 TCP_WAIT 的問題,(如果在 docker 測試,會無法修改 kernel 參數,跳過這個步驟就好了,參考這邊的說明 對docker container進行內核參數調優


    vi /etc/sysctl.conf


    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.ip_local_port_range = 10240 65535

    sysctl -p 或是 reboot




以 script 安裝 FluentD,daemon 名稱為 td-agent


curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

安裝後會增加 /etc/yum.repos.d/td.repo,以及 td-agent service


啟動 daemon


systemctl enable td-agent
systemctl status td-agent

systemctl start td-agent

/etc/init.d/td-agent start
/etc/init.d/td-agent stop
/etc/init.d/td-agent restart
/etc/init.d/td-agent status

設定檔在 /etc/td-agent/td-agent.conf
預設是由 HTTP 接收 logs 轉至 stdout /var/log/td-agent/td-agent.log


發送測試資料


curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

Use Cases


  • Centralized App Logging
    收集不同語言實作的 Applcation 的 Log

  • Log Management & Search
    Fluentd + Elasticsearch 的整合替代 Splunk

  • Data Analysis
    將 Log 儲存到 Hadoop 或 MongoDB,以供後續分析處理

  • Data Archiving
    將 Log 儲存到 Amazon S3/Riak/GlusterFS Logs

  • Stream Processing

  • Windows Event Collection
    收集 Windows Event Logs (目前 stable 版本 v0.12 還不支援 Windows,要到 v0.14 才有支援)

  • IoT Data Logger


    Cloud Data Logger by Raspberry Pi 說明可在 Raspberry Pi 整合其他 Sensor 後,透過 Fluentd 收集資料。


Life of a Fluentd event


以實例解釋 event 是如何倍 Fluentd 處理的,包含 Setup, Inputs, Filters, Matches, and Labels


使用 inhttp 及 outstdout plugins 解釋 events cycle,首先修改 /etc/td-agent/td-agent.conf


# listening for HTTP Requests
<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

# print the data arrived on each incoming request to standard output
<match test.cycle>
  @type stdout
</match>

發送兩個 curl 測試


# curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

# curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

tail -f /var/log/td-agent/td-agent.log


2017-10-31 15:15:40 +0800 [info]: adding match pattern="test.cycle" type="stdout"
2017-10-31 15:15:40 +0800 [info]: adding source type="http"
2017-10-31 15:15:40 +0800 [info]: using configuration file: <ROOT>
  <source>
    @type http
    port 8888
    bind 0.0.0.0
  </source>
  <match test.cycle>
    @type stdout
  </match>
</ROOT>
2017-10-31 15:15:48 +0800 [warn]: no patterns matched tag="debug.test"
2017-10-31 15:15:58 +0800 test.cycle: {"action":"login","user":2}

Event structure

Fluentd event 包含 tag, time, record 三個部分


  • tag: event 來自哪裡
  • time: Epoch time,event 發生時間
  • record: log content,JSON object

以 apache log 為例,利用 in_tail 會由一行一行的 text line log 產生 event


192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777

tag: apache.access # set by configuration
time: 1362020400   # 28/Feb/2013:12:00:00 +0900
record: {"user":"-","method":"GET","code":200,"size":777,"host":"192.168.0.1","path":"/"}

tag 是由 a.b.c 這樣的字串組成的,用 "." 組合不同部分的字串


設定檔 td-agent.conf


  • source: input source

標準 input 有兩個: http 及 forward,可同時使用


http 將 fluentd 轉變為 HTTP endpoint,由 HTTP 接收 event message


forward 將 fluentd 轉變為 TCP endpoint,接收 TCP packets


ex:


# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
  @type forward
  port 24224
</source>

# http://this.host:8888/myapp.access?json={"event":"data"}
<source>
  @type http
  port 8888
</source>

  • match: output destination

比對 event 的 tag,並處理符合定義 tag 的 event


fluentd 的 stdout output plugin 為 file 及 forward


ex:


# Match events tagged with "myapp.access" and
# store them to /var/log/fluent/access.%Y-%m-%d
# Of course, you can control how you partition your data
# with the time_slice_format option.
<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

match 後面的參數有以下規則,依照在設定檔中的順序進行比對


    • matches a single tag part

    ex: a.* matches a.b
    a.* not match a or a.b.c

  1. ** matches zero or more tag parts


    a.** matches a, a.b and a.b.c

  2. {X,Y,Z} matches X, Y, or Z, where X, Y, and Z are match patterns


    {a,b} matches a and b
    a.{b,c}.*
    a.{b,c.**}

  3. 可用 填寫多個 patterns


    match a and b
    match a, a.b, a.b.c, and b.d


  • filter: 決定 event processing pipelines

Input -> filter 1 -> ... -> filter N -> Output


ex:


# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  @type http
  port 9880
</source>

<filter myapp.access>
  @type record_transformer
  <record>
    host_param "#{Socket.gethostname}"
  </record>
</filter>

<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

event 處理過程


收到 {"event":"data"}
-> 送到 record_transformer filter
-> 增加 "host_param" 欄位
-> {"event":"data","host_param":"webserver1"}
-> 送到 file output

  • system: 設定系統參數

<system>
  # equal to -qq option
  log_level error
  # equal to --without-source option
  without_source
  # suppress_repeated_stacktrace
  # emit_error_log_interval
  # suppress_config_dump
  
  # fluentd’s supervisor and worker process names
  process_name fluentd1
</system>

  • label: group output 及 filter for internal routing

<label @SYSTEM>
  <filter var.log.middleware.**>
    @type grep
    # ...
  </filter>
  <match **>
    @type s3
    # ...
  </match>
</label>

  • @include: include other files

# Include config files in the ./config.d directory
@include config.d/*.conf

Processing Events

在設定 Setup 後,Router Engine 就已經包含了幾個基本的 rules,內部會經過幾個步驟處理 Event。


  • Filters

可用來設定一個 rule,決定要不要接受這個 event


ex: filter test.cycle 放棄不處理 logout,這是用 @grep 處理的,判斷 action 的部分,有沒有 "logout" 這個字串


<source>
  @type http
  port 8888
  bind 0.0.0.0
</source>

<filter test.cycle>
  @type grep
  exclude1 action logout
</filter>

<match test.cycle>
  @type stdout
</match>

測試


# curl -i -X POST -d 'json={"action":"login","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

# curl -i -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle
HTTP/1.1 200 OK
Content-type: text/plain
Connection: Keep-Alive
Content-length: 0

結果在 log 裡面只有看到 login


2017-10-31 15:50:55 +0800 test.cycle: {"action":"login","user":2}

Labels

可用來定義新的 Routing sections,且不遵循 top-bottom 的順序,類似 linked references 的行為。


ex: 在 source 增加了 @label,表示要跳到 @STAGING 處理 event,而不是用上面的 filter


<source>
  @type http
  bind 0.0.0.0
  port 8880
  @label @STAGING
</source>

<filter test.cycle>
  @type grep
  exclude1 action login
</filter>

<label @STAGING>
  <filter test.cycle>
    @type grep
    exclude1 action logout
  </filter>

  <match test.cycle>
    @type stdout
  </match>
</label>

Buffers

在範例中,使用 stdout 是 non-buffered output,但在正式環境,會需要對 output 增加 buffer,例如 forward, mongodb, s3 ...


buffered output plugins 會儲存收到的 events 到 buffers,並在達到 flush condition 時,再將資料一次寫入目標。換句話說,database 可能不會馬上看到新進的 event。


Execution unit

Fluentd events 預設是在 input plugin thread 中處理的,例如 intail -> filtergrep -> outstdout pipeline,就是在 intail 的 thread 中處理的。filtergrep 及 outstdout 並沒有自己的 thread。


但 buffered output plugin 中,另外有一個自己的 thread 可處理 flushing buffer。


Sample


Collecting Tomcat logs using Fluentd and Elasticsearch


fluentd-catch-all-config


Tomcat容器日誌收集方案fluentd+elasticsearch+kilbana


安裝 fluentd 的 elasticsearch plugin


td-agent-gem install fluent-plugin-elasticsearch

定義 tomcat catalina.out 的 source


<source>
  @type tail
  format none
  path /var/log/tomcat*/localhost_access_log.%Y-%m-%d.txt
  pos_file /var/lib/google-fluentd/pos/tomcat.pos
  read_from_head true
  tag tomcat-localhost_access_log
</source>

<source>
  @type tail
  format multiline
  # Match the date at the beginning of each entry, which can be in one of two
  # different formats.
  format_firstline /^(\w+\s\d+,\s\d+)|(\d+-\d+-\d+\s)/
  format1 /(?<message>.*)/
  path /var/log/tomcat*/catalina.out,/var/log/tomcat*/localhost.*.log
  pos_file /var/lib/google-fluentd/pos/tomcat-multiline.pos
  read_from_head true
  tag tomcat.logs
</source>

<match tomcat.logs>
    @type elasticsearch
    host localhost
    port 9200
    logstash_format true
    logstash_prefix tomcat.logs
    flush_interval 1s
</match>

References


fluentd architecture


用 ElasticSearch + FluentD 打造 Log 神器與數據分析工具




logstash + kibana - Make sense of a mountain of logs


LogStash::Inputs::Syslog 性能測試與優化




使用LogHub進行日誌實時採集


Docker日志收集新方案:fluentd-pilot

Fluentd, Logstash, LogHub, Flume, Kafka


而系統的 log,其實就是眾多的 event,我們必須設法將散落在不同地方的內部或外部 log 收集並儲存起來,集中到某個系統進行管理及分析,才能簡化處理異質環境訊息處理的問題。而 Fluentd, Logstash, LogHub, Flume, Kafka 這些技術,是以不同的方式解決問題。


在營運網路服務時,可能會遇到下面這些問題


  1. 在不同的廣告通路上,取得的使用者,要評估不同的廣告得到的收益結果
  2. 使用者抱怨服務的速度太慢,但要分析是在哪一個部分出問題
  3. 發送優惠券時,要如何評估優惠券的效益
  4. 要分析什麼時候該儲備多一點貨品,或是要調配更多人力
  5. 客戶在使用過程中發生問題,要如何分析是在哪個步驟出錯

由於網路服務的系統,可能會有這些特性,連帶造成使用者在處理某個工作時,必須跨越多種異質環境。


  1. 多個促銷或銷售管道
  2. 多個使用介面,如網頁、手機或是 APP
  3. 多台雲端機器
  4. 多種開發程式語言或環境
  5. 多個作業系統平台

通常會將 Fluentd, Logstash, LogHub 在一起比較,而將 Flume, Kafka 一起比較。


LogHub 是阿里雲的 Log Service,在別的環境中,可以先不考慮這個方案。


Fluentd, Logstash 是用 ruby,而 Flume 及 Kafka 是 Java。


Fluentd 有個基本的限制,他並不保證訊息一定會被傳送,如果不能容忍訊息遺失的狀況,就不要考慮 Fluentd。


但 Logstash 及 Flume,為了保證訊息一定會被傳遞,
同樣的訊息可能會收到兩次以上。


Flume 是訊息收集系統,而 Kafka 更接近於訊息cache系統,他可以儲存一定時間內的資訊。因此可以看到很多是採用 Fluentd + Kafka + Storm/ElasticSearch 這樣混搭使用狀況。


至於 Logstash 跟 Flume 的比較,可以看 logstash vs flume 以及 請對logstash與flume做比較 這篇文章。


Logstash 重視資料的預處理,多個 input 會把資料匯總到 input 和 filter 之間的 buffer中。filter則會從buffer中讀取數據,進行過濾解析,然後儲存在 filter 和 output 之間的Buffer中。當 buffer 滿足一定的條件時,會觸發output的刷新。


而 Flume 比較重視資料的傳輸,只有封裝 event 然後就傳送,沒有資料解析處理的部份,傳輸時比較重視資料的可靠性。


References


Fluentd vs. Logstash: A Comparison of Log Collectors


日誌客戶端(Logstash,Fluentd, Logtail)橫評


深度解讀:為何要使用日誌服務LogHub替換Kafka?


公網數據採集比較(LOGHUB VS 前端機+KAFKA)


Flume和Logstash的那些事兒


日誌採集系統flume和kafka有什麼區別及聯繫,它們分別在什麼時候使用,什麼時候又可以結合?


Kafka 與 Flume的區別


Logging 日誌記錄最佳實踐


你一定需要 六款大數據採集平台的架構分析


深夜實堂:從業務需求淺談 Log aggregators

2017/12/4

statsd


statsd 是 Graphite/Carbon metrics server 的 front-end proxy,最初由 Etsy's Erik Kastner 以 Node.js 撰寫,目前已經有多種程式語言的實作版本。他是一個 event counter/aggregation service,接收 event timeings,做基本計算後,就產生 values,這可用來收集 custom application metrics,而 application 只需要不斷地發送 events。


collectd 在 5.4 版後就支援了 statsd plugin,也就是將 statsd 嵌入了 collectd。


statsd 是一個 UDP (也可換成 TCP) daemon,根據簡單的協議收集statsd客戶端發送來的數據,聚合統計之後,再定時推送給後端,如graphite和influxdb等,然後透過grafana顯示資料。


系統分成三個部分: client, server, backend。client 要植入 application 中,將相應的 metrics 發送給 statsd server。statsd server 聚合這些 metrics 後,定時發送給 backends。backends 負責儲存這些 Time Series Data,再透過適當的圖表工具展示資料。


安裝


要先安裝 nodejs,由 EPEL 安裝的是 nodejs 6.11.3-1.el7 版


yum install -y epel-release
yum install -y nodejs

如果要改安裝 nodejs 7,必須改用下面的程序


# Install Node.js 7.x repository
curl -sL https://rpm.nodesource.com/setup_7.x | bash -

# Install Node.js and npm
yum install nodejs

直接由 statsd github clone 並安裝 statsd


cd /usr/local/src

git clone https://github.com/etsy/statsd.git

cd statsd

npm install

設定


首先複製一份設定檔


cp exampleConfig.js config.js

修改 graphite 的設定


vi config.js


{
  graphitePort: 2003, 
  graphiteHost: "localhost",
  port: 8125,
  backends: [ "./backends/graphite" ]
}



修改 graphite 的設定


vi /opt/graphite/conf/storage-schemas.conf


[carbon]
pattern = ^carbon\.
retentions = 60:90d

[stats]
pattern = ^stats.*
retentions = 10s:6h,10m:7d,1d:5y

[stats_counts]
pattern = ^stats_counts.*
retentions = 10s:6h,10m:7d,1d:5y

[collectd]
pattern = ^collectd.*
retentions = 10s:6h,10m:7d,1d:5y

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

10s:6h,10m:7d,1d:5y


  • 6 hours of 10 seconds data
  • 7 days of 10 mins data
  • 5 years of 1 day data

如果 retentions 時間設定為這樣,資料會更多一些


[carbon]
pattern = ^carbon\.
retentions = 60:90d

[stats]
pattern = ^stats.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[stats_counts]
pattern = ^stats_counts.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[collectd]
pattern = ^collectd.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

10s:1d,30s:7d,1m:30d,15m:5y


  • 1 day of 10 seconds data
  • 7 days of 30 seconds data
  • 30 days of 1 minute data
  • 5 years of 15 minutes data

必須要同時修改 /opt/graphite/storage/whisper 路徑的 *.wsp 資料,可參考Whisper Scripts 文件。


# 修改 wsp size
find /opt/graphite/storage/whisper/collectd -type f -name '*.wsp' -exec whisper-resize.py --nobackup {} 10s:6h 10m:7d 1d:5y \;

# 列印 wsp file size
find /opt/graphite/storage/whisper/collectd -type f -name '*.wsp' -exec whisper-info.py {} \;

vim /opt/graphite/conf/storage-aggregation.conf


[lower]
pattern = \.lower$
xFilesFactor = 0.1
aggregationMethod = min

[min]
pattern = \.min$
xFilesFactor = 0.1
aggregationMethod = min

[upper]
pattern = \.upper(_\d+)?$
xFilesFactor = 0.1
aggregationMethod = max

[max]
pattern = \.max$
xFilesFactor = 0.1
aggregationMethod = max

[sum]
pattern = \.sum$
xFilesFactor = 0
aggregationMethod = sum

[gauges]
pattern = ^.*\.gauges\..*
xFilesFactor = 0
aggregationMethod = last

[count]
pattern = \.count$
xFilesFactor = 0
aggregationMethod = sum

[count_legacy]
pattern = ^stats_counts.*
xFilesFactor = 0
aggregationMethod = sum

[default_average]
pattern = .*
xFilesFactor = 0.3
aggregationMethod = average

  • 以 .lower .min 或 .upper .max 結尾的 metrics,只會儲存 max, min values,如果少於 10% datapoints,就只會儲存 None

  • 以 count 或 sum 結尾的 metrics,還有在 'stats_counts' 下面的 metrics,會加總所有 values,如果沒有收到資料,會儲存 None

  • 其他資料庫,會計算平均值,如果少於 30% 的 datapoint,就會儲存 None


重新啟動 graphite


systemctl restart carbon
systemctl restart graphite

啟動


有三種方式


  1. 直接在 console 啟動


    cd /usr/local/src/statsd
    node ./stats.js ./config.js
  2. 以 system service 方式啟動


    vi /usr/lib/systemd/system/statsd.service


    [Unit]
    Description=statsd daemon
    
    [Service]
    ExecStart=/usr/bin/node /usr/local/src/statsd/stats.js /usr/local/src/statsd/config.js
    ExecReload=/bin/kill -HUP $MAINPID
    KillMode=process
    
    [Install]
    WantedBy=multi-user.target

    啟動服務


    systemctl daemon-reload
    systemctl enable statsd
    systemctl start statsd
  3. 透過 npm forever-service 安裝服務


    cd /usr/local/src/statsd
    sudo npm install -g forever
    sudo npm install -g forever-service
    sudo forever-service install statsd -s stats.js -o " config.js"
    sudo service statsd start



statsd 會在 UDP:8125 運作,可檢查


netstat -nap | grep 8125

graphite 中會看到這些 metrics


stats.gauges.statsd.timestamp_lag

stats.statsd.graphiteStats.calculationtime
stats.statsd.graphiteStats.flush_length
stats.statsd.graphiteStats.flush_time
stats.statsd.graphiteStats.last_exception
stats.statsd.graphiteStats.last_flush

stats.statsd.bad_line_seen
stats.statsd.metrics_received
stats.statsd.packets_received
stats.statsd.processing_time

stats_counts.statsd.bad_line_seen
stats_counts.statsd.metrics_received
stats_counts.statsd.packets_received

statsd.numStats

Key Concepts


  • buckets
    每一個 stat 都有自己的 bucket,不需要預先定義,最後將會被轉換到 graphite,periods ( . ) 會被換成 folders

  • values
    每個 stat 都有自己的 value,解譯方式由 modifier 決定,values 一般都是 integer

  • flush
    在 flush interval timeout (config.flushInterval 定義,預設值為 10 秒)後,stats 會被 aggregted 並發送到一個 backend service


使用


stats 是使用最基本的 line protocol


<metricname>:<value> | <type>

可用 nc 測試


echo "foo:1|c" | nc -u 127.0.0.1 8125



graphite 會增加這些 metrics


stats.foo
stats_counts.foo

Metric Types


Metric Types


  • Counting

foo:1|c

把 foo 加 1,flush 後,count 會發送到後端,並 reset 為 0。


如果設定了 config.deleteCounters,在 flush 時,如果 count 是 0,就不會發送 metric 到後端


如果你使用 flush interval(10秒),並在每個間隔通過某個計數器給 statsd 傳送7次 counting。則計時器的 value (stats_counts.foo) 為 7,而 per-second value (stats.foo) 為 0.7,另外 numStats 為 7。


  • Sampling

foo:1|c|@0.1

最後面 @0.1,表示每 1/10 的時間間隔,都會發送一次 counter


  • Timing

用來記錄某個 operation 消耗多少時間


foo:320|ms

foo 要花 320ms 完成


statsd 會自動計算該 flush interval 內的 percetiles, average(mean), 標準差, sum, 上下界


在 flush interval 內,你將下列計數器 values 傳給 statsd


450
120
553
994
334
844
675
496

會計算下面的 values,並傳送給 graphite


mean_90 496
upper_90 844
sum_90 3472
upper 994
lower 120
count 8
sum 4466
mean 558.25

  • Gauges

一個被記錄的任意數值


gaugor:333|g

如果 flush 時,值沒有改變,就會再發送一次。設定 config.deleteGauges,就不會再發送一次。


在數值前面加上 + 或 -,是值的計算,而不是覆寫,這表示不能將 gauge 設定為負整數


gaugor:333|g
gaugor:-10|g
gaugor:+4|g

gaugor 結果為 333 - 10 + 4 = 327


  • Sets

在 flushes 之間,記錄發生的 events,但不重複,可用來記錄某個事件在時間區段中,有哪些使用者曾經使用過


request:1|s  // 1
request:2|s  // 1 2
request:1|s  // 1 2

  • Multi-Metric Packets

可以在一行 packet 中,以 \n 區隔多個欄位的資料。但要注意網路單一 packet 的傳輸長度上限,例如 Fast Ethernet 為 1432 (包含)。


gorets:1|c\nglork:320|ms\ngaugor:333|g\nuniques:765|s

將 statsd 整合到 collectd


雖然會減少一個 daemon,改用 collectd 同時啟動 statsd,但目前不採用這種安裝方式


修改 /etc/collectd.conf


LoadPlugin statsd

<Plugin statsd>
  Host "0.0.0.0"
  Port "8125"
#  DeleteCounters true
#  DeleteTimers   false
#  DeleteGauges   false
  DeleteSets     true
  CounterSum     true
  TimerPercentile 90.0
#  TimerPercentile 95.0
#  TimerPercentile 99.0
  TimerLower     true
#  TimerUpper     false
#  TimerSum       false
#  TimerCount     false
</Plugin>

restart collectd


systemctl restart collectd

statsd 會在 UDP:8125 運作,可用 netstat 檢查,但卻是由 collectd process 處理的


netstat -nap | grep 8125

如果用 nc 測試時


echo "foo:1|c" | nc -u 127.0.0.1 8125

會在 graphite 發現,metrics 是在 collectd 下面


collectd.testserver.statsd.count-foo
collectd.testserver.statsd.derive-foo

clients


StatsD Example Clients 這裡有多種程式語言的獨立的測試 Client


3rd Party Client Implementations 這裡有第三方 StatsD 的 Library


node-statsd 為例。


安裝 node-statsd libray


npm install -g node-statsd

撰寫測試程式,發送 api 回應時間,到 statsd 的 timeing


vi test.js


'use strict';

const StatsD = require('node-statsd'),
client = new StatsD({
  host: 'localhost',
  port: 8125
});

setInterval(function () {
  const responseTime = Math.floor(Math.random() * 100);
  client.timing('api', responseTime, function (error, bytes) {
    if (error) {
      console.error(error);
    } else {
      console.log(`Successfully sent ${bytes} bytes, responseTime: ${responseTime}`);
    }
  });
}, 1000);

執行測試程式


export NODE_PATH=/usr/lib/node_modules
node test.js

在 graphite 中可以取得 stats.timers.api.* 這些 metrics



References


StatsD wiki


statsd學習小結


StatsD!次世代系統監控的核心


使用 Statsd + Graphite 的 Monitoring 心得


聊聊 Statsd 和 Collectd 那點事!


StatsD vs collectd vs fluentd and Other Daemons You Should Know 2016/8


How do StatsD and CollectD relate?


StatsD embedded into CollectD


如何深入理解 StatsD 與 Graphite


使用 StatsD + Grafana + InfluxDB 搭建 Node.js 監控系統




How to install Node.js 7.x on Ubuntu/Debian and CentOS

2017/11/27

Grafana


Grafana 是個 metrics 資料的分析、告警及視覺化圖表的工具平台,最常用來作 Time Series Data 的圖表,也能用在收集 sensor 資料、home automation、天氣及 process control 這些領域。


安裝


參考 download Grafana 這個網頁。


在 CentOS 可用以下程序安裝


wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
sudo yum install -y initscripts fontconfig urw-fonts
sudo rpm -Uvh grafana-4.5.2-1.x86_64.rpm

軟體包含以下這幾個部分


  • /usr/sbin/grafana-server

  • /etc/init.d/grafana-server


    init.d script

  • /etc/sysconfig/grafana-server


    default file (environment vars)

  • /etc/grafana/grafana.ini


    configuration file

  • grafana-server.service


    systemd service (if systemd is available)

  • /var/log/grafana/grafana.log


    default configuration uses a log file

  • /var/lib/grafana/grafana.db


    default configuration specifies an sqlite3 database


啟動


sudo systemctl daemon-reload
sudo systemctl enable grafana-server.service
### start grafana-server
sudo systemctl start grafana-server.service

使用


網址為 http://localhost:3000
預設帳號/密碼為 admin/admin


登入後,首先要設定 DataSource,我們設定使用 graphite,Url 的部分就連到 Graphite-web 的網址



下一步是建立 DashBoard,參考 Using Graphite in Grafana 的說明,另外也可以到 Garfana Labs Dashboards 尋找適當的 template。


以 Graphite Server Metrics 為例,我們先到 Graphite Dashboard Tempates 右邊下載 JSON: graphite-server-carbon-metrics_rev1.json。



然後直接在 Grafana 網頁的 DashBoard 功能上,直接以 import 方式將剛剛的 json 匯入,就可以直接看到下面的圖表畫面。





CollectD Server Metrics


Graphite Dashboard Templates 裡面有關 CollectD Metrics 有四個,但下載後都沒辦法直接看到 CollectD 的資料圖表。


我們還是先載入一個 Template,再修改圖表的 metrics。


修改 collectd.conf,並重新啟動 collectd,主要是要增加 CPU 部分的 aggregation 計算,另外再多載入一些 Plugins。


Hostname "testserver"

FQDNLookup false
Interval 1
#Timeout 2
ReadThreads 5

LoadPlugin cpu
LoadPlugin df
LoadPlugin load
LoadPlugin memory
LoadPlugin disk
LoadPlugin interface
LoadPlugin uptime
LoadPlugin swap
LoadPlugin write_graphite
LoadPlugin processes
LoadPlugin aggregation
LoadPlugin match_regex
LoadPlugin syslog
LoadPlugin logfile

<Plugin logfile>
  LogLevel info
  # File STDOUT
  File "/var/log/collectd/collectd.log"
  Timestamp true
  PrintSeverity false
</Plugin>

<Plugin df>
  # expose host's mounts into container using -v /:/host:ro  (location inside container does not matter much)
  # ignore rootfs; else, the root file-system would appear twice, causing
  # one of the updates to fail and spam the log
  FSType rootfs
  # ignore the usual virtual / temporary file-systems
  FSType sysfs
  FSType proc
  FSType devtmpfs
  FSType devpts
  FSType tmpfs
  FSType fusectl
  FSType cgroup
  FSType overlay
  FSType debugfs
  FSType pstore
  FSType securityfs
  FSType hugetlbfs
  FSType squashfs
  FSType mqueue
  MountPoint "/"
  IgnoreSelected true
  ReportByDevice false
  ReportReserved true
  ReportInodes true
  ValuesAbsolute true
  ValuesPercentage true
  ReportInodes true
</Plugin>

<Plugin "disk">
  Disk "/^[hs]d[a-z]/"
  IgnoreSelected false
</Plugin>

<Plugin interface>
  Interface "lo"
  Interface "/^eth.*/"
  Interface "/^docker.*/"
  IgnoreSelected false
  ReportInactive true
  UniqueName false
</Plugin>

<Plugin memory>
  ValuesAbsolute true
  ValuesPercentage false
</Plugin>

<Plugin "aggregation">
  <Aggregation>
    Plugin "cpu"
    Type "cpu"
    GroupBy "Host"
    GroupBy "TypeInstance"
    CalculateAverage true
  </Aggregation>
</Plugin>

<Chain "PostCache">
  <Rule>
    <Match regex>
      Plugin "^cpu$"
      PluginInstance "^[0-9]+$"
    </Match>
    <Target write>
      Plugin "aggregation"
    </Target>
    Target stop
  </Rule>
  Target "write"
</Chain>

<Plugin write_graphite>
 <Node "example">
   Host "localhost"
   Port "2003"
   Protocol "tcp"
   ReconnectInterval 0
   LogSendErrors true
   Prefix "collectd."
   # Postfix "collectd"
   StoreRates true
   AlwaysAppendDS false
   EscapeCharacter "_"
   SeparateInstances false
   PreserveSeparator false
   DropDuplicateFields false
 </Node>
</Plugin>

在 CollectD DashBoard 的第一個 CPU Average 圖表上,點擊編輯會出現以下的畫面



Metrics #A 的部分,是原本 Template 提供的寫法,用類似的方式,加入 #B 及 #C 的部分,查看 cpu-user 及 cpu-idle 的資料,其他部分就不需要修改。


用類似的方式,修改其他 metrics 圖表,最後的結果為



Metric Editor


Using Graphite in Grafana Metrics Editor 有比較完整的 Metric 圖表功能的說明。


  1. Select metric


    因為 graphite 的樹狀 metrics 資料結構,這邊的 metric 也是一層一層選擇的

  2. Functions


    在選到 metrics 數值後,按下 + ,就可以選用某一個 graphite 的 function


    以 collectd 的 loading 為例,他是使用 graphite 提供的 aliasByNode 的函數,搭配第一個參數是 metric 資料,第二個參數是階層的數字,也就是 "shortterm"


    aliasByNode($prefix.$server.load.load.shortterm, 4)

    如果是下面這樣, legend 就會變成 "shortterm.load"


    aliasByNode($prefix.$server.load.load.shortterm, 4, -2)
  3. Nested Queries


    以這個為例,在使用 sumSeries 時,可參考到 #A 的 metrics,因為 #A 已經有四個 memory-{used,cached,free,buffered} 數值,sumSeries 會直接加總。


  4. Point consolidation


    Graphite 在傳給 Grafana 前,會先進行 consolidate,減少傳送的資料點數量,預設是用 avg 這個 function 處理,也可以利用 consolidateBy 處理。

  5. Query variable


    在 Dashboard 上面,增加一些可以調整的參數,在圖表中,以 $varname 或是 [[varname]] 的方式,參考到這些參數,就像是 DashBoard 的參數一樣。


    Graphite Templated Dashboard 中就用到了 $app, $server, $interval 三個參數。


Alert 告警


首先參考 Configuration 的內容,修改 smtp server 那個部分的設定。


vi /etc/grafana/grafana.ini


[smtp]
enabled = true
host = smtp.gmail.com:465
user = user@maxkit.com.tw
password = password
;cert_file =
;key_file =
;skip_verify = false
from_address = user@maxkit.com.tw

重新啟動 grafana


systemctl restart grafana-server

參考 Alert Rules 的說明


到 Grafana 網頁新增一個 Notification Channel



然後到 DashBoard 的圖表上,編輯某一個想要監控的指標,切換到 Alert 頁籤,設定告警的規則,這裡可以用 AND 或 OR 疊加多個 metric 規則,但 metric 不能有 Template Variables,這是比較麻煩的地方,前面都是用 variable 的方式設定 metric。



回到 metric 設定的部分,要把 variable 移除。



在檢查點,由正常狀態 變成 告警狀態時,會發送一次 email,而由告警狀態 變成 正常狀態時,會再發送一次 email。


References


高顏值監控繪圖工具 Grafana


新監控系統技術選型 Telegraf + Influxdb + Grafana + 二次開發


Graphite 和 Grafana 簡介


使用Grafana+Diamond+Graphite構造完美監控面板


使用StatsD, Graphite, Grafana, Kamon搭建可用於JVM項目的可視化性能監控系統


利用 collectd + InfluxDB + Grafana 監測系統效能


Grafana

2017/11/20

collectd


collectd 是 system statistics collection daemon,會定時由多個資料來源收集 metrics 資料,完全由 C 語言編寫,故性能高且移植性好,它也能運作在沒有腳本語言支持或者 cron daemon 的 OS 上,例如嵌入式系統,架構上除了核心程式,其他的部分,包含資料 input/output,發送通知,Logging 等等,都是以 plugin 方式處理。


collectd 通常是用來收集硬體的相關資訊,例如 CPU loading,記憶體及網路使用狀況等等,然後透過 Plugin 寫入 graphite。



安裝


collectd 是在 EPEL repository 中,可直接用 yum 安裝。


yum install -y epel-release
yum install -y collectd

systemctl enable collectd
systemctl start collectd

設定


collectd 主要設定檔在 /etc/collectd.conf,最後一行是直接 Include "/etc/collectd.d" include 一個目錄裡面的所有設定檔。所有支援的 plugin 是以 so 形式放在 /usr/lib64/collectd 這個目錄中。


以下是要修改的 collecd.conf 設定檔內容


vi /etc/collectd.conf


# 指定主機名稱,也可以寫成IP
Hostname    "testserver"
# 是否允許以 DNS 查詢主機名稱,如果 DNS 可能有錯誤,建議不要開啟
FQDNLookup   false

# 各種文件、目錄的設置
#BaseDir     "/var/lib/collectd"
#PIDFile     "/var/run/collectd.pid"
#PluginDir   "/usr/lib64/collectd"
#TypesDB     "/usr/share/collectd/types.db"

# 設置為true時,可以根據 <Plugin XXX> 自動載入 plugin,而不需要 LoadPlugin
#AutoLoadPlugin false

# 是否同時上傳 collectd 自己的狀態#CollectInternalStats false

# global 的資料收集時間間隔,單位是秒,可以在 plugin 設定中進行覆寫
Interval                 1
MaxReadInterval        180

# 單位不是秒,而是次數,實際的時間是timeout*interval
Timeout                  2

# 用於配置讀寫線程數
#WriteThreads             5
#ReadThreads              5

# 配置緩存的上下限
WriteQueueLimitLow    8000
WriteQueueLimitHigh  12000

# 建立一個Unix Socket用在命令發送,狀態查看等
LoadPlugin unixsock
<Plugin unixsock>
    SocketFile "/var/run/collectd-unixsock"
    SocketGroup "collectd"
    SocketPerms "0660"
    DeleteSocket true  # 啟動時如果存在 sock,是否嘗試刪除
</Plugin>

# 設置日誌文件,儲存到文件中,可以通過logrotate管理
LoadPlugin logfile
<Plugin logfile>
    LogLevel info
    # File STDOUT
    File "/var/log/collectd/collectd.log"  # 也可以設定為 STDOUT
    Timestamp true
    PrintSeverity false
</Plugin>

# 收集 CPU 資訊
LoadPlugin cpu

# 收集系統 Loading 資訊
LoadPlugin load

# 收集記憶體資訊
LoadPlugin memory

# 收集網路 interface 資料
LoadPlugin interface

<Plugin interface>
    Interface "eth0"
    IgnoreSelected false
    ReportInactive true
    UniqueName false
</Plugin>

# 將資料寫入 graphite
# https://collectd.org/wiki/index.php/Plugin:Write_Graphite
LoadPlugin write_graphite

<Plugin write_graphite>
 <Node "graphing">
   Host "localhost"
   Port "2003"
   Protocol "tcp"
   ReconnectInterval 0
   LogSendErrors true
   Prefix "collectd."
#   Postfix "collectd"
   StoreRates true
   AlwaysAppendDS false
   EscapeCharacter "_"
   SeparateInstances false
   PreserveSeparator false
   DropDuplicateFields false
 </Node>
</Plugin>

# disk 資訊
# https://collectd.org/wiki/index.php/Plugin:DF
LoadPlugin df
<Plugin df>
    # Device "/dev/hda1"
    # Device "192.168.0.2:/mnt/nfs"
    MountPoint "/"
    MountPoint "/home"
    # FSType "ext3"
    # IgnoreSelected false
    # ReportByDevice false
    # ReportInodes false
    # ValuesAbsolute true
    # ValuesPercentage false
</Plugin>

要建立 log 的目錄


mkdir /var/log/collectd

因應 collectd 資料,要修改 graphite 的 Carbon 設定,要注意 collectd 的 pattern 要放在 .* 的前面


vi /opt/graphite/conf/storage-schemas.conf


[carbon]
pattern = ^carbon\.
retentions = 60:90d

[collectd]
pattern = ^collectd.*
retentions = 10s:1d,30s:7d,1m:30d,15m:5y

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

啟動


重新啟動 collectd, graphite


systemctl restart collectd
systemctl restart carbon
systemctl restart graphite

結果


graphite 收到的 collectd metrics 資料為


collectd.testserver.cpu-0.cpu-XXX

collectd.testserver.df-root.df_complex-free
collectd.testserver.df-root.df_complex-reserved
collectd.testserver.df-root.df_complex-used

collectd.testserver.interface-eth0.if_dropped.rx
collectd.testserver.interface-eth0.if_dropped.tx
collectd.testserver.interface-eth0.if_errors.rx
collectd.testserver.interface-eth0.if_errors.tx
collectd.testserver.interface-eth0.if_octets.rx
collectd.testserver.interface-eth0.if_octets.tx
collectd.testserver.interface-eth0.if_packets.rx
collectd.testserver.interface-eth0.if_packets.tx

collectd.testserver.load.load.longterm
collectd.testserver.load.load.midterm
collectd.testserver.load.load.shortterm

collectd.testserver.memory.memory-buffered
collectd.testserver.memory.memory-cached
collectd.testserver.memory.memory-free
collectd.testserver.memory.memory-slab_recl
collectd.testserver.memory.memory-slab_unrecl
collectd.testserver.memory.memory-used

References


聊聊 Statsd 和 Collectd 那點事!


Collectd 簡介


使用 collectd 進行服務監控


StatsD vs collectd vs fluentd and Other Daemons You Should Know 2016/8


How do StatsD and CollectD relate?


collectd 系統資訊收集服務