Graphite

Calvin Xiao edited this page Apr 9, 2014 · 87 revisions

1. 介绍

Graphite是一个Github项目,没有自己的独立域名网站,文档也host在专门存放文档的网站上。

Graphite 专注于两个最简单的任务: 作为一个便捷且Scalable的Network Service,以精度随时间递减的方式存储Metrics数据,并支持以丰富的函数获取它们,以图片或者JSON的格式。

Graphite基于Python,由三部分组成:

  • Carbon:基于Twist的TCP Server,负责接收数据,用简单到死的文本协议,因为够简单,所以Yammer Codahale Metrics、Netflix Servo、StatsD、Collectd、Logstash,人人都有一个到Graphite的插件。
  • Whisper: 类库,将数据存储成RRD(round-robin-databas)文件,数据粒度随时间递减,可以存储更长时间的其据。其继承者是Ceres,不再固定文件大小。
  • Graphite-Web:基于Django的Web应用,支持Restful的URL获取图片或JSON数据,URL里可以带各种有用的函数

基于Python的单点性能可能不高,但Graphite有Cache、HA、Scalable方面的专门设计。

2. Install Graphite 0.9.12 on Windows

3. Functions & URL

3.1 URL示例

Image: http://localhost:8080/render?from=-10minutes&target=system.loadavg_1min&width=500&height=300
Json: http://localhost:8080/render?from=-10minutes&target=system.loadavg_1min&format=json
CSV: http://localhost:8080/render?from=-10minutes&target=system.loadavg_1min&format=csv

3.2 重要函数

Graphite的魅力在于它有非常多让人眼花缭乱的函数,每当有什么需求时,都可以先尝试翻看一下它的函数列表。这里稍微列举一些。

1. url上的通配符:
url中可以带有星星或选择符,会选择出多个counter来,也就诞生了一系列其他需求:

&target=rest.getUser.count.*          
&target=rest.get*.count.succeeded     
&target=rest.getUser.count.{succeeded,failed}
&target=rest.server[1-20].getUser.count    

注意,因为url中的每个.代表了实际存储文件的一个目录层次,graphite的代码也是用unix的fnmatch一层层目录的推进匹配,所以不能用rest.* 选出 rest.getUser.count

  • 将星星代表的多个counter通过某个方式选出一个值来,比如 maxSeries,sumSeries,averageSeries。sumSeries(rest.getUser.count.*),将星代表的counter相加成一个值。
  • groupByNode,url中有两颗星星,将第1颗星星代表的多个Series组合,而第2颗代表的则分开来显示。
    groupByNode(ganglia.by-function...cpu.load5,2,"sumSeries"),第一颗星代表服务器,第2颗星代表服务器,此函数将显示每台Server上所有函数的CPU总和。averageSeriesWithWildcards,sumSeriesWithWildcards也有类似效果。
  • aliasByNode,将星星中的counter的名称做Alias,比如aliasByNode(rest.getUser.count.*, 1),alias就是succeeded或failed。更高级是aliasSub,可以用正则表达式取出counter名称里的某部分做alias。
  • exclude,单独剔除某个Counter,比如exclude(servers*.threads,"server02")
  • 只显示某些counter,比如highestAverage,highestMax,只显示值最高(还可以是最低)的几个counter,又或者currentAbove,currentBelow,以last值(还可以是max,min,avg值)做条件过滤。
  • asPercent,以几个counter相对的百分比来显示。

2. 图形增强:

  • legendValue(rest.getuser.latency, 'avg', 'max'),图形下面显示数据的最大小值、平均值(可以是last, avg, total, min, max)。
  • threshold(60, "latency 60ms threshold","red"),在图形里显示警戒线。
  • consolidateBy(rest.getuser.latency, 'max'),如果看一周的曲线,图上的每个点包含了多个datapoint的数据,不用本函数,默认行为是算平均值,可以用本函数设为取max,min,sum等。

3. 其他:

  • summarize(rest.getuser.latency, "1min"),按1分钟进行聚合而不是原来的10秒。聚合的方式默认是sum,也可以是avg,max,last。
  • 将自增长的Counter转化为TPS: perSecond(rest.getuser.totalCount),但当前版本此函数好像不存在,一个解决方式是:scaleToSeconds(deriviative(rest.getuser.totalCount),1) ,第一个函数先算出两个点之间的差值,第二个平均到每秒。
  • integral(rest.getUser.count),显示所有datapoint的逐渐累积值。
  • hitcount, 将rate换算成totalCount,能跨越各种时间长度。
  • Timeshift, 可以比较当前数值和一周前的数值(两条线): &target=alias(summarize(rest.getuser.count, "1min"), "today")&target=alias(summarize(rest.getuser.count, "1min"),"1w"), "last week")。
  • movingAverage, 几个datapoint的移动平均值。

3.3 URL参数

  1. 时间
    关于时间各种定义见URL文档。可以定义绝对时间,比如from=04:00_19890601。可以定义一些奇葩的写法,比如&from=6pm+today等于今天六点以后,&from=january+1等于1月1号以后,&from=monday等于离现在最近的周一以后。 from默认是24小时以前,until默认是now。

  2. noCache=true
    结果默认会按local_settings.py中的DEFAULT_CACHE_DURATION 缓存60秒,如果要每10秒刷新看一次最新值,应设置noCache=true。 另外,如果有配置memcached,缓存在memcached中,否则在Graphite-WebApp里。

  3. drawNullAsZero=true
    没有流量,没有数据点插入时,在Graphite里数据是Null,图中的曲线会有断点,设置url参数drawNullAsZero=true会把断点变成0。另外函数中也有keepLastValue(),让null等于最后一次的值,或者transformNull(webapp.pages.*.views,-1),可以让默认值为-1。

4. Carbon & Whisper

####4.1 端口与协议

  • Carbon默认在2003端口启动TCP的,接收plain text数据,可配成UDP。
  • Carbon默认在2004端口,接收以Python的Pickle格式的数据。
  • Carbon默认在7002端口,支持Cache数据的查询。

####4.2 数据写入流程 Metric数据来临时,将写入不同的whisper文件,carbon.conf中有MAX_UPDATES_PER_SECOND=500的限制,如果海量数据来临时,数据就不会立刻写到文件里,而是被Buffer起来。然后系统会按照谁有最多的没写入的data point排序,利用Whisper比RRDTool可以一次写入多个data point的优势,一次写入多个data point到一个数据文件。

当Graphite-Web画图时,不但会去读whisper文件,还会通过7002端口,查询Carbon中没来得及写入文件的Buffer。

如果数据来临的间隔低于Graphite刷新的间隔,Graphite只会取最后一个值。比如retentions = 10s:1d,而客户端每5秒发来一条数据,前一条数据会被丢弃。

####4.2 数据压缩流程 数据的分布定义storage-schemas.conf中,默认是按10秒一个数据的方式,存一天的数据。一天前的数据就没了。

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

Statsd建议改成从现在到6个小时前每10秒一个数据,从7个小时前到7天前每1分钟一个数据,7天前到5年前每10分钟一个数据,5年前的数据丢弃。

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

将10秒的数据降为1分钟数据时,默认是算平均值,但也可以按合计值,最大值,最小值等,一切尽在storage-aggregation.conf。

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

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

####4.3 Whisper文件操作 因为文件不是顺序写,所以硬盘使用SSD的性能更好。 C:\Python27\Scripts\Whisper-* 脚本,可以对Whisper文件 查看、更新、Info、 在retention更改后重新更改大小。

5. DashBoard

5.1 overview

官方的DashBoard不算太好,因此有下列的替代品:

  1. [Grafana] (http://grafana.org/) 和ElasticSearch的Kibana同一个风格,是目前最好的DashBoard。
  2. 其他的Dashboard都不太好,比如Descartes是Graphite的维护者Jason Dixon 做的一个替代官方DashBoard,Ruby编写。三方的Dashboard小结
  3. 自己定制一个动态页面,用URL嵌入Graphite吐出来的图片。
  4. 获取Graphite的Json,在浏览器用D3.js引擎重新渲染,Giraffe是一个典型的实现,Demo,当然,也可以完全自己用D3.js定制。

Grafana既有用户体验良好的Dashboard,也支持用户动态创建编辑保存DashBoard,是最省心省力的选择。

5.2 Grafana

5.2.1 安装

Grafana是一个纯粹的html/js 应用,丢进任何Web容器里即可。不过Grafana需要ajax 查询Graphite-Web内容,会有跨域访问的限制,需要修改Graphite Web的容器支持跨域访问,不过我的做法是直接把Grafana丢进Graphite webapp目录,修改一下webapp\graphite\urls.py文件,比如添加一句 ··· ('^grafana/(?P.*)$', 'django.views.static.serve', {'document_root' : settings.GRAPHITE_ROOT+'/webapp/grafana'}), ···

Grafana目前的版本更新非常迅速,可经常留意。

再然后copy config.sample.js 成 config.js,就可以访问http://graphite/grafana/index.html

5.2.2 Tips

  • 它要求ElasticsSearch来存储多个DashBoard的定义文件,如果实在没有装ElasticSearch,也可以JSON文件的干活,Export出JSON文件(Save ->Advanced->Export Schema),然后上传回它的/app/dashboards目录。
  • 一般人不喜欢黑色,可选择白色风格的DashBoard,在右上角的DashBoard Configuration里选择style为light。
  • 和Kibana一样,在时间轴上拖拉一段时间来细化时间的选择是最炫的演示效果。
  • 如果需要动态的DashBoard,也可以用javascript来实现。Scripted dashboards
  • Templating也是它的一大功能,比如可以快速切换多台服务器,Templating
  • 一些界面相关的function,图形设置里已直接支持,不需要再写成function,比如Legends,Threshold,draw null as zero。
  • Y轴智能单位支持ms和byte的智能转换,比如转换为Kb,Mb,Gb。

6. HA 与 Scalable

Clustering Graphite Blog

Carbon-Relay 支持垂直或者水平的分区,将Metric发送到不同的Carbon-Cache实体上(同名的Metric总是发送到同一个Cache实体)。在carbon.conf的[relay]中配置RELAY_METHOD = consistent-hashing是水平分区,配置RELAY_METHOD = rules,然后配置relay-rules.conf中按Metric Name的正则表达式是垂直分区。
relay-rules.conf示例:

[example]
pattern = ^mydata\.foo\..+
servers = 10.1.2.3, 10.1.2.4:2004  //会复制到所有server上(HA)

Carbon-Relay 如果采用consistent-hashing,还可以在carbon.conf里定义factory=2, 复制数据到两台服务器上的Carbon-Cache,实现HA。

为了使用更多的CPU,一台机器上可以起一个Graphite-Web实例,多个Carbon-Cache实例,而使用同一个Whisper文件目录,因为sharding的原因,两个实例是不会写同一个whiper文件的,此时,Graphite-Web的local_settings.py里的CARBONLINK_HOSTS里要配置多个carbon。

Graphite-Web 先读本地的whisper文件,如果读到了Metric就走正常流程,如果本地没有这个Metric,就通过 local_settings.py里的CLUSTER_SERVERS查询其他Graphite-Web Server。

HA环境下,如果一台机器倒了,再重启之后如何从另一台机器上同步最新数据?Cluster环境下,如果增加后端的Carbon-Cache实例,一致性哈希到每台机上的metric会有变化,如何重新Balance已存在的数据? 幸亏有一个carbonate项目把这个脏活干了,比如carbon-sync, 看这个命令的名字就感觉亲切。

LOCAL_IP="$1"

for h in $(carbon-hosts) ; do  //访问所有host
  (
    ssh $h -- carbon-list |       //列出该host上所有metric
    carbon-sieve -n $LOCAL_IP |   //再列出本机有的metric
    carbon-sync -s $h             //同步重合的metric
  ) &
done

Carbon-Aggregrator,如果数据从多台服务器过来,但你其实只想看个总数,并不关心每台服务器上的计数,又或者数据过来的时间低于Graphite的datapoint的间隔,你又不想丢弃之前的数据,就可以放一个Carbon-Aggregrator在Carbon-Cache前面,类似StatsD的做一次聚合,把聚合后的数据交给Carbon-Cache,大幅降低其IO。不过Carbon-Aggregrator 只支持Sum和Avg两种计算方式。配置文件在carbon.conf的[aggregator]段落和aggregation-rules.conf

<env>.applications.<app>.all.requests (60) = sum <env>.applications.<app>.*.requests

上例,将多台server过来的数据,做一次60秒的聚合,名字叫<env>.applications.<app>.all.requests。其中<xxx>会用来自动匹配具体的名字,而*就会用来做聚合。

以上内容均为纸上谈兵,待验证。

7. Graphite vs RRDTool

Whisper vs RRDTool

  • Graphite的旧版FAQ这里已经专门提到了两者的比较。
  • Graphite自承RRDTool的单纯写入速度比它的Whisper快2-3倍,读快2-5倍,因为C与Python的区别。
  • 但Whisper因为有时间戳,可以插入不规则发生的数据和已过去的数据。Whisper还可以同时写入批量数据,见上。

Graphite vs RRDTool

  • 对我们项目来说,Graphite提供TCP写入和Http读取的网络服务,当然比RRDTool基于文件操作灵活,RRDTool的rrdcached和rrdcgi还没去做测试。
  • 对我们项目来说,更重要的是支持正则的灵活配置方式,可以更好的配置Dynamic Counter,或者多个节点上的数据输入,在取数据时可以url里带星号来进行汇总,见上。
  • Graphite支持函数,在用途上更有潜力。

8. 用户

  • 大客户:Github、Instagram、豆瓣、Etsy等
  • 云服务:playfairapp statsd + Graphite

9. 参考资料

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.