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

原文地址:http://skaka.me/blog/2015/07/21/kamon-statsd-graphite-grafana-introduction/

1. 什麼是性能監控系統

這裏說的性能監控系統,主要側重點是監控應用系統的性能。 說直白點就是每個業務(例如註冊,登錄)的請求響應時間,請求次數等信息。 操作系統的監控不是這裏的重點,因爲業界已經有許多相當成熟的基於Linux的運維繫統。 操作系統的運維和應用系統的運維是兩碼事,應用系統的運維相對來說沒有這麼多選擇。 而對於任何線上系統來說,運維監控系統又是必不可少的。 如果你是在大公司,一般會選擇開發自己的運維繫統,而對於中小團隊,因爲人力有限,大多會採用開源的解決方案。

我在這裏要介紹的就是使用StatsD,Graphite, Grafana, Kamon搭建監控系統的方案。 這裏的Kamon只適用於基於JVM的項目,另外,如果你的項目是基於Play或Akka或Spray,可以做到不寫代碼實現監控。 因爲Kamon對這幾個框架提供了AspectJ支持,在類加載的時候插入代碼爲你完成記錄。 其他情況你需要調用Kamon API來進行數據記錄,這也非常的簡單。

2. 監控系統UI

介紹搭建步驟之前,先來看一看搭好後的界面。 轉自(https://github.com/kamon-io/docker-grafana-graphite)
這裏包含4部分的圖表。
- Actor Metrics是Akka Actor的統計圖表。因爲我的項目中沒有直接使用Akka,所以暫時忽略這一部分內容。
- Trace Metrics是業務請求的統計圖表。例如每個請求的響應時間,以及某個時間段內各請求數量的統計對比。
- OS Metrics一看就知道是操作系統的統計圖表了。
- JVM Metrics是JVM的統計圖表。

3. StatsD, Graphite, Grafana, Kamon簡介

簡單介紹一下這四個開源項目,因爲都在Github上就不貼鏈接了,
已經對這幾個項目很熟悉的可以略過。

1. StatsD

StatsD是一個用於記錄統計信息的守護進程。使用NodeJS開發,提供各種語言的客戶端API。

2. Graphite

使用Python開發,分爲三個子項目
- carbon 守護進程,接收StatsD發送過來的原始統計數據。
- whisper 用來存儲統計數據的庫。
- graphite webapp 用來圖形化展示統計數據的web項目。

3. Grafana

使用Go開發,可以直接在界面上設計統計圖表。
之前看到就是使用Grafana製作的界面。

4. Kamon

一套類庫用來記錄統計數據,使用Scala開發,提供Java和Scala API。
除了提供API,還結合AspectJ對一些框架提供自動記錄的功能,當然性能上會有損耗。

整體流程:業務系統調用Kamon的Api記錄數據,Kamon將數據發送給StatsD,
StatsD定期(默認10s)將數據彙總發送到Graphite,
當用戶訪問Grafana界面的時候,Grafana調用Graphite接口讀取數據繪製成圖形展示給用戶。

4.搭建環境

因爲用到了多個項目,並且每個項目都基於不同語言,所以安裝過程肯定不會很簡單。
使用docker的朋友直接參考這個項目(https://github.com/kamon-io/docker-grafana-graphite)
用docker鏡像會方便很多。

如果不用docker,可以參考我的安裝步驟。
以下安裝是基於Ubuntu14.04,如果是CentOS或其他系統可能某些步驟會不一樣。
另外因爲測試的時候是在開發機上,所以下面安裝的時候沒有使用獨立用戶和權限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
1.安裝需要的軟件

sudo apt-get -y install software-properties-common

sudo add-apt-repository -y ppa:chris-lea/node.js

sudo apt-get -y update

sudo apt-get -y install python-django-tagging python-simplejson python-memcache python-ldap python-cairo python-pysqlite2 python-support \

                           python-pip gunicorn nginx-light nodejs wget curl build-essential python-dev



sudo pip install Twisted==11.1.0

sudo pip install Django==1.5



# Install Elasticsearch

cd ~ && wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.3.2.deb

cd ~ && sudo dpkg -i elasticsearch-1.3.2.deb && rm elasticsearch-1.3.2.deb



2.從源碼安裝StatsD, Graphite, Grafana

# Checkout the stable branches of Graphite, Carbon and Whisper and install from there

mkdir ~/src



git clone https://github.com/kamon-io/docker-grafana-graphite.git ~/src/docker-grafana-graphite



git clone https://github.com/graphite-project/whisper.git ~/src/whisper &&\

cd ~/src/whisper                                                                   &&\

git checkout 0.9.x                                                                &&\

sudo python setup.py install



git clone https://github.com/graphite-project/carbon.git ~/src/carbon              &&\

cd ~/src/carbon                                                                    &&\

git checkout 0.9.x                                                                &&\

sudo python setup.py install



git clone https://github.com/graphite-project/graphite-web.git ~/src/graphite-web  &&\

cd ~/src/graphite-web                                                              &&\

git checkout 0.9.x                                                                &&\

sudo python setup.py install



# Install StatsD

git clone https://github.com/etsy/statsd.git ~/src/statsd                                                                        &&\

cd ~/src/statsd                                                                                                                  &&\

git checkout v0.7.2



# Install Grafana

mkdir ~/src/grafana

wget http://grafanarel.s3.amazonaws.com/grafana-1.9.1.tar.gz -O ~/src/grafana.tar.gz                   &&\

        cd ~/src/ && tar -xzf ~/src/grafana.tar.gz && mv ~/src/grafana-1.9.1 ~/src/grafana && cd - &&\

        rm ~/src/grafana.tar.gz



3.修改配置

# Configure Elasticsearch

sudo mkdir -p /tmp/elasticsearch



# Confiure StatsD

cp ~/src/docker-grafana-graphite/statsd/config.js ~/src/statsd/config.js



# Configure Whisper, Carbon and Graphite-Web

sudo cp ~/src/docker-grafana-graphite/graphite/initial_data.json /opt/graphite/webapp/graphite/

sudo cp ~/src/docker-grafana-graphite/graphite/local_settings.py /opt/graphite/webapp/graphite/

# 此時要 sudo vi /opt/graphite/webapp/graphite/local_settings.py, 把TimeZone改爲Asia/Shanghai

sudo cp ~/src/docker-grafana-graphite/graphite/carbon.conf /opt/graphite/conf

sudo cp ~/src/docker-grafana-graphite/graphite/storage-aggregation.conf /opt/graphite/conf

sudo cp ~/src/docker-grafana-graphite/graphite/storage-schemas.conf /opt/graphite/conf



sudo mkdir -p /opt/graphite/storage/whisper

sudo touch /opt/graphite/storage/graphite.db /opt/graphite/storage/index

sudo chmod 0775 /opt/graphite/storage /opt/graphite/storage/whisper

sudo chmod 0664 /opt/graphite/storage/graphite.db

cd /opt/graphite/webapp/graphite && sudo python manage.py syncdb --noinput



# Configure Grafana

cp ~/src/docker-grafana-graphite/grafana/config.js ~/src/grafana/config.js



# Add the default dashboards

mkdir ~/src/dashboards

cp ~/src/docker-grafana-graphite/grafana/dashboards/* ~/src/dashboards/



# Configure nginx

將下面的內容加入nginx.conf

  server {

    listen 80 default_server;

    server_name _;

    location / {

     # !!! change me 這裏改成你的目錄 !!!

      root /home/liubin/src/grafana;

      index index.html;

    }

    location /graphite/ {

        proxy_pass                 http://127.0.0.1:8000/;

        proxy_set_header           X-Real-IP   $remote_addr;

        proxy_set_header           X-Forwarded-For  $proxy_add_x_forwarded_for;

        proxy_set_header           X-Forwarded-Proto  $scheme;

        proxy_set_header           X-Forwarded-Server  $host;

        proxy_set_header           X-Forwarded-Host  $host;

        proxy_set_header           Host  $host;



        client_max_body_size       10m;

        client_body_buffer_size    128k;



        proxy_connect_timeout      90;

        proxy_send_timeout         90;

        proxy_read_timeout         90;



        proxy_buffer_size          4k;

        proxy_buffers              4 32k;

        proxy_busy_buffers_size    64k;

        proxy_temp_file_write_size 64k;



        add_header Access-Control-Allow-Origin "*";

        add_header Access-Control-Allow-Methods "GET, OPTIONS";

        add_header Access-Control-Allow-Headers "origin, authorization, accept";

    }



    location /elasticsearch/ {

        proxy_pass                 http://127.0.0.1:9200/;

        proxy_set_header           X-Real-IP   $remote_addr;

        proxy_set_header           X-Forwarded-For  $proxy_add_x_forwarded_for;

        proxy_set_header           X-Forwarded-Proto  $scheme;

        proxy_set_header           X-Forwarded-Server  $host;

        proxy_set_header           X-Forwarded-Host  $host;

        proxy_set_header           Host  $host;



        client_max_body_size       10m;

        client_body_buffer_size    128k;



        proxy_connect_timeout      90;

        proxy_send_timeout         90;

        proxy_read_timeout         90;



        proxy_buffer_size          4k;

        proxy_buffers              4 32k;

        proxy_busy_buffers_size    64k;

        proxy_temp_file_write_size 64k;

    }

  }



  server {

    listen 81 default_server;

    server_name _;



    open_log_file_cache max=1000 inactive=20s min_uses=2 valid=1m;



    location / {

        proxy_pass                 http://127.0.0.1:8000;

        proxy_set_header           X-Real-IP   $remote_addr;

        proxy_set_header           X-Forwarded-For  $proxy_add_x_forwarded_for;

        proxy_set_header           X-Forwarded-Proto  $scheme;

        proxy_set_header           X-Forwarded-Server  $host;

        proxy_set_header           X-Forwarded-Host  $host;

        proxy_set_header           Host  $host;



        client_max_body_size       10m;

        client_body_buffer_size    128k;



        proxy_connect_timeout      90;

        proxy_send_timeout         90;

        proxy_read_timeout         90;



        proxy_buffer_size          4k;

        proxy_buffers              4 32k;

        proxy_busy_buffers_size    64k;

        proxy_temp_file_write_size 64k;

    }



    add_header Access-Control-Allow-Origin "*";

    add_header Access-Control-Allow-Methods "GET, OPTIONS";

    add_header Access-Control-Allow-Headers "origin, authorization, accept";



    location /content {

      alias /opt/graphite/webapp/content;

    }



    location /media {

      alias /usr/share/pyshared/django/contrib/admin/media;

    }

  }



4.啓動

export GRAPHITE_STORAGE_DIR='/opt/graphite/storage'

export GRAPHITE_CONF_DIR='/opt/graphite/conf'



# run nginx

sudo /etc/init.d/nginx restart



# run carbon

sudo /opt/graphite/bin/carbon-cache.py --debug start



# run graphite-web

export PYTHONPATH='/opt/graphite/webapp'

cd /opt/graphite/webapp

sudo /usr/bin/gunicorn_django -b127.0.0.1:8000 -w2 graphite/settings.py



# run StatsD

sudo /usr/bin/node ~/src/statsd/stats.js ~/src/statsd/config.js



# run elasticsearch

sudo /etc/init.d/elasticsearch start



# run grafana

cd ~/src/dashboards

sudo /usr/bin/node dashboard-loader.js system-metrics.json welcome.json

安裝完成後,可以嘗試訪問(http://127.0.0.1),如果出現之前的界面並且沒有報錯,就代表安裝成功了。

5. 配置項目

主要是引入Kamon依賴。
因爲我們的項目基於Play,所以直接使用了Kamon-Play依賴。

1.修改build.sbt

1
2
3
4
5
6
7
8
9
val kamonVersion = "0.4.0"
//...
val dependencies = Seq(
  "io.kamon" %% "kamon-core" % kamonVersion,
  "io.kamon" %% "kamon-statsd" % kamonVersion,
  "io.kamon" %% "kamon-play" % kamonVersion,
  "io.kamon" %% "kamon-system-metrics" % kamonVersion,
  "org.aspectj" % "aspectjweaver" % "1.8.1"
)

2.修改application.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
akka {
  extensions = ["kamon.statsd.StatsD", "kamon.system.SystemMetrics"]
}

kamon {

  metric {
    tick-interval = 1 second
  }

  statsd {
    # Hostname and port in which your StatsD is running. Remember that StatsD packets are sent using UDP and
    # setting unreachable hosts and/or not open ports wont be warned by the Kamon, your data wont go anywhere.
    hostname = "127.0.0.1"
    port = 8125

    # Interval between metrics data flushes to StatsD. It's value must be equal or greater than the
    # kamon.metrics.tick-interval setting.
    flush-interval = 1 second

    # Max packet size for UDP metrics data sent to StatsD.
    max-packet-size = 1024 bytes

    # Subscription patterns used to select which metrics will be pushed to StatsD. Note that first, metrics
    # collection for your desired entities must be activated under the kamon.metrics.filters settings.
    includes {
      actor      =  [ "*" ]
      trace      =  [ "*" ]
      dispatcher =  [ "*" ]
    }

    simple-metric-key-generator {
      # Application prefix for all metrics pushed to StatsD. The default namespacing scheme for metrics follows
      # this pattern:
      #    application.host.entity.entity-name.metric-name
      # !!! 這裏改成項目名 !!!
      application = "sk-shop"
    }
  }

  play {
    include-trace-token-header = true
    trace-token-header-name = "X-Trace-Token"
  }
}

3.啓動項目

play運行時環境分爲dev環境和prod環境。 因爲Kamon-Play使用AspectJ在類加載加載的時候進行織入,AspectJ會使用自己的類加載器, 而運行在dev環境的Play項目因爲要實現熱加載,所以dev環境不能使用AspectJ。 prod一般會會使用dist命令將項目打包,可用以下方式啓動(假設項目名是shop,並且已cd進入打包後的目錄):

1
./bin/shop -J-javaagent:lib/org.aspectj.aspectjweaver-1.8.1.jar

啓動之後隨便訪問你項目幾個頁面,然後訪問127.0.0.1,如果一切正常就可以看到數據了。

6.總結

恭喜,如果做到這一步,你就已經初步的搭好你們運維繫統的架子了。 如果你的應用比較簡單,對性能要求也不高,到這一步就可以結束了。 不過,大多數的應用都對統計數據有更進一步的分析需求,例如繪製響應時間超過某一閥值(例如100ms)的餅狀圖, 或者繪製日註冊人數的直方圖。 這就需要你手動調用Kamon的API來記錄數據了,不過這相當的簡單。 如果需要設計其他的Grafana圖表,需要對Graphite的函數比較熟悉。

另外,如果應用對性能很敏感,不推薦使用AspectJ。因爲LTW(Load Time Weaving)會有一些性能損耗。 我們的項目最開始是使用的Kamon-Play和Kamon-Akka,不過後來測試發現響應時間平均要增加10%-20%, 現在已經改成直接調用Kamon API完成記錄。

Enjoy!

7.附啓動和停止腳本

1.啓動腳本 start_stats.sh

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh

export GRAPHITE_STORAGE_DIR='/opt/graphite/storage'
export GRAPHITE_CONF_DIR='/opt/graphite/conf'
nohup /opt/graphite/bin/carbon-cache.py --debug start > ~/logs/kamon/carbon.out 2>&1 &
export PYTHONPATH='/opt/graphite/webapp'
nohup /usr/bin/gunicorn_django -b127.0.0.1:8000 -w2 /opt/graphite/webapp/graphite/settings.py  > ~/logs/kamon/graphite.out 2>&1 &
nohup /usr/bin/node ~/src/statsd/stats.js ~/src/statsd/config.js  > ~/logs/kamon/graphite.out 2>&1 &
/etc/init.d/elasticsearch start
cd ~/src/dashboards
nohup /usr/bin/node dashboard-loader.js system-metrics.json  welcome.json   > ~/logs/kamon/grafana.out 2>&1 &

2.停止腳本 stop_stats.sh

1
2
3
4
5
6
7
#!/bin/sh

pkill carbon
pkill gunicorn_django
pkill statsd
/etc/init.d/elasticsearch stop
kill $(ps aux | grep 'node dashboard-loader.js' | awk '{print $2}')

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章