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