主要讨论内容
在本文中我们将会谈及如下用于创建Graphite监控系统的主题:
- Carbon和Whisper简介
- Whisper存储模式和聚合
- Graphite Web应用
前提条件
Amazon Web Services EC2主机。不过,你也可以使用办公室或家中已有的任何型号的计算机。
技术规格:
- 操作系统:Red Hat Enterprise Linux (RHEL) 6.5
- 实例类型:m3.xlarge
- 弹性存储区块(EBS)容量:250 GB
- Python版本:2.6.6
Carbon和Whisper简介
Carbon和Whisper。
指标项可以被发布到一个负载均衡器或直接发布到一个Carbon线程中。Carbon线程与Whisper数据库交互,将时间序列数据存储到文件系统中。
安装Carbon
Carbon实际上是一系列守护进程,组成一个Graphite安装的存储后端。这些守护进程用一个名为Twisted的事件驱动网络引擎监听时间序列数据。Twisted框架让Carbon守护进程能够以很低的开销处理大量的客户端和流量。
要安装Carbon,运行如下命令即可(假设目标系统是RHEL操作系统):
# sudo yum groupinstall "Development Tools" # sudo yum install python-devel # sudo yum install git # sudo easy_install pip # sudo pip install twisted # cd /tmp # git clone https://github.com/graphite-project/carbon.git # cd /tmp/carbon # sudo python setup.py install
/opt/graphite文件夹下将包含如下Carbon库和配置文件:
# ls -l /opt/graphite drwxr-xr-x. 2 root root 4096 May 18 23:56 bin drwxr-xr-x. 2 root root 4096 May 18 23:56 conf drwxr-xr-x. 4 root root 4096 May 18 23:56 lib drwxr-xr-x. 6 root root 4096 May 18 23:56 storage
bin文件夹下,能够找到如下三种不同类型的Carbon守护进程:
- Cache:接受通过各种协议传输来的指标项数据并以尽可能高的效率将它们写入磁盘;在接收到指标项时,将指标项值缓存在RAM中,并用底层的Whisper库按照指定的时间间隔将这些值写入磁盘。
- Relay:有两个不同的用途:将输入的指标项复制并分区。
- Aggregator:运行于cache前方,在Whisper中记录指标项之前,缓存这些指标项一段时间。
安装Whisper
Whisper是一个用于存储时间序列数据的数据库,之后应用程序可以用create,update和fetch操作获取并操作这些数据。
安装Whisper,需要运行如下命令:
# cd /tmp # git clone https://github.com/graphite-project/whisper.git # cd /tmp/whisper # sudo python setup.py install
Whisper脚本现在应该已经相应的位置:
# ls -l /usr/bin/whisper* -rwxr-xr-x. 1 root root 1711 May 19 00:00 /usr/bin/whisper-create.py -rwxr-xr-x. 1 root root 2902 May 19 00:00 /usr/bin/whisper-dump.py -rwxr-xr-x. 1 root root 1779 May 19 00:00 /usr/bin/whisper-fetch.py -rwxr-xr-x. 1 root root 1121 May 19 00:00 /usr/bin/whisper-info.py -rwxr-xr-x. 1 root root 674 May 19 00:00 /usr/bin/whisper-merge.py -rwxr-xr-x. 1 root root 5982 May 19 00:00 /usr/bin/whisper-resize.py -rwxr-xr-x. 1 root root 1060 May 19 00:00 /usr/bin/whisper-set-aggregation-method.py -rwxr-xr-x. 1 root root 969 May 19 00:00 /usr/bin/whisper-update.py
启动Carbon缓存进程
Carbon安装包中包含了关于端口号和其他多个配置参数的明确缺省值。拷贝这些已有的示例配置文件:
# cd /opt/graphite/conf # cp aggregation-rules.conf.example aggregation-rules.conf # cp blacklist.conf.example blacklist.conf # cp carbon.conf.example carbon.conf # cp carbon.amqp.conf.example carbon.amqp.conf # cp relay-rules.conf.example relay-rules.conf # cp rewrite-rules.conf.example rewrite-rules.conf # cp storage-schemas.conf.example storage-schemas.conf # cp storage-aggregation.conf.example storage-aggregation.conf # cp whitelist.conf.example whitelist.conf # vi carbon.conf
cache区段下,接收端口这一行包含一个默认值,用于通过平文本协议(plaintext protocol )接受输入指标项(如下所示):
[cache] LINE_RECEIVER_INTERFACE = 0.0.0.0 LINE_RECEIVER_PORT = 2003
执行如下命令,可以启动一个carbon-cache进程:
# cd /opt/graphite/bin # ./carbon-cache.py start Starting carbon-cache (instance a)
这个进程现在应该正在监听2003端口:
# ps -efla | grep carbon-cache 1 S root 2674 1 0 80 0 - 75916 ep_pol 00:18 ? 00:00:03 /usr/bin/python ./carbon-cache.py start # netstat -nap | grep 2003 tcp 0 0 0.0.0.0:2003 0.0.0.0:* LISTEN 2674/python
发布指标项
指标项(metric )是一种随着时间不断变化的可度量的数量,例如:
- 每秒请求数
- 请求处理时间
- CPU使用情况
datapoint
数据点(datapoint)是包含如下信息的三元组:
- 指标项名称
- 度量值
- 时间序列上某个特定的点(通常是一个时间戳)
客户端应用程序通过将数据点发送至Carbon进程发布指标项。应用程序在Carbon进程所监听的端口上建立TCP连接,然后以简单平文本格式发送数据点信息。在本文的示例中,这个端口号是2003。这个TCP连接可能会一直处于打开状态并根据需要尽可能多次重复使用。Carbon进程监听输入的数据但是并不会给客户端返回任何响应。
数据点的格式定义如下:
- 每个数据点是一行文本
- 位置0是带点的指标项名称
- 位置1是一个值
- 位置2是一个Unix时间戳
- 空格作为各个位置之间的分隔符
例如,下面是一些有效的数据点:
- carbon.agents.graphite-tutorial.metricsReceived 28198 1400509108
- carbon.agents.graphite-tutorial.creates 8 1400509110
- PRODUCTION.host.graphite-tutorial.responseTime.p95 0.10 1400509112
客户端应用程序发布指标项的方式可以有多种:
- netcat(nc)命令这样的工具,使用平文本协议发布
- 使用pickle协议
- 使用高级消息队列协议(AMQP)
- 用函数库,如Dropwizard Metrics库
为了简单起见,在本文示例中我将通过netcat命令用平文本协议发布指标项。发布如上所列的示例数据点,运行如下命令即可:
sudo yum install nc echo "carbon.agents.graphite-tutorial.metricsReceived 28198 `date +%s`" | nc localhost 2003 echo "carbon.agents.graphite-tutorial.creates 8 `date +%s`" | nc localhost 2003 echo "PRODUCTION.host.graphite-tutorial.responseTime.p95 0.10 `date +%s`" | nc localhost 2003
carbon-cache的日志文件中将包含接收到的新指标项的有关信息以及存储这些信息的位置:
# tail -f /opt/graphite/storage/log/carbon-cache/carbon-cache-a/creates.log 19/05/2014 10:42:44 :: creating database file /opt/graphite/storage/whisper/carbon/agents/graphite-tutorial/metricsReceived.wsp (archive=[(60, 129600)] xff=0.5 agg=average) 19/05/2014 10:42:53 :: creating database file /opt/graphite/storage/whisper/carbon/agents/graphite-tutorial/creates.wsp (archive=[(60, 129600)] xff=0.5 agg=average) 19/05/2014 10:42:57 :: creating database file /opt/graphite/storage/whisper/PRODUCTION/host/graphite-tutorial/responseTime/p95.wsp (archive=[(60, 1440)] xff=0.5 agg=average)
Carbon与Whisper交互,将这些时间序列数据存储到文件系统中。切换到文件系统相应的位置,确保数据文件已经创建成功:
# ls -l /opt/graphite/storage/whisper/carbon/agents/graphite-tutorial/ total 3040 -rw-r--r--. 1 root root 1555228 May 19 10:42 creates.wsp -rw-r--r--. 1 root root 1555228 May 19 10:42 metricsReceived.wsp # ls -l /opt/graphite/storage/whisper/PRODUCTION/host/graphite-tutorial/responseTime/ total 20 -rw-r--r--. 1 root root 17308 May 19 10:42 p95.wsp
whisper-info脚本获取为这些指标项创建的Whisper文件的元数据信息。
# whisper-info.py /opt/graphite/storage/whisper/PRODUCTION/host/graphite-tutorial/responseTime/p95.wsp maxRetention: 86400 xFilesFactor: 0.5 aggregationMethod: average fileSize: 17308 Archive 0 retention: 86400 secondsPerPoint: 60 points: 1440 size: 17280 offset: 28
whisper-dump是一个更完整的脚本,可以输出所有存储保留周期内的原始数据以及Whisper文件的元数据信息。
# whisper-dump.py /opt/graphite/storage/whisper/PRODUCTION/host/graphite-tutorial/responseTime/p95.wsp Meta data: aggregation method: average max retention: 86400 xFilesFactor: 0.5 Archive 0 info: offset: 28 seconds per point: 60 points: 1440 retention: 86400 size: 17280 Archive 0 data: 0: 1400609220, 0.1000000000000000055511151231257827 1: 0, 0 2: 0, 0 3: 0, 0 4: 0, 0 5: 0, 0 ... 1437: 0, 0 1438: 0, 0 1439: 0, 0
理解聚合方法,最大保留期,xFilesFactor和Whisper文件中的其他属性是相当重要的。如果现在你有一点迷茫,也不要过于担心,我将在接下来的章节中详细讨论这些属性。
Whisper存储模式和聚合
当你或你的开发者同事和系统管理员们开始发布数据点却得到了一些意想不到的结果时,可能会有一些困惑:
- 为什么我的数据点被平均了?
- 我已经在周期性地发布数据点了,为什么还是没有任何数据点?
- 我已经发布很多天数据点了,为什么我只得到一天的数据?
Whisper是如何存储数据的?
存储区(bucket)”,这些存储区是在配置文件中定义的。
例如:
- 存储器A:拥有10秒分辨率的数据点
- 存储区B:拥有60秒分辨率的数据点
- 存储区C:拥有10分钟分辨率的数据点
每个存储区还拥有一个保留期(retention)属性用于标识该存储区中的数据点应该保留的时间长度。例如:
- 存储区A:分辨率为10秒,保留时间6小时的数据点
- 存储区B:分辨率为60秒,保留时间1天的数据点
- 存储区C:分辨率为10分钟,保留时间7天的数据点
根据上述两种信息,Whisper可以进行一些简单的数学计算,计算出在每个存储区中实际需要保存多少数据点。
- 存储区A:6小时 x 60分钟/小时 x 6数据点/分钟 = 2160点
- 存储区B:1天 x 24小时/天 x 60分钟/小时 x 1数据点/分钟 = 1440点
- 存储区C:7天 x 24小时/天 x 6数据点/小时 = 1008点
whisper-dump.py脚本,会有如下输出。需要注意的是,一个archive对应一个存储区,每点秒数(seconds per point )和点数(points )属性则与我们之前的计算相匹配。
元数据:
aggregation method: average max retention: 604800 xFilesFactor: 0.5 Archive 0 info: offset: 52 seconds per point: 10 points: 2160 retention: 21600 size: 25920 Archive 1 info: offset: 25972 seconds per point: 60 points: 1440 retention: 86400 size: 17280 Archive 2 info: offset: 43252 seconds per point: 600 points: 1008 retention: 604800 size: 12096
关于聚合
当数据从一个较高精度的存储区移动到一个较低精度的存储区时,聚合开始发挥作用。让我们以前一个示例中的存储区A和存储区B为例:
- 存储区A:分辨率为10秒,保留时间6小时的数据点(较高精度)
- 存储区B:分辨率为60秒,保留时间1天的数据点(较低精度)
我们可能有一个每10秒钟发布一个数据点的应用程序。在存储区A中可以找到6小时之内发布的任何数据点。不过,如果我开始查询6小时之前发布的数据点,就可以在存储区B中找到它们。
数据点如何移动到存储区B?
用高精度值除以低精度值,以确定需要聚合的数据点的数量。
l 60秒(存储区B)/10秒(存储区A)= 6个数据点需要聚合
注:Whisper需要较高精度的值能够整除较低精度的值(也就是说,相除的结果必须是整数)。否则聚合的结果可能会不准确。
average,sum,max,min和last。聚合函数的选择取决于需要处理的数据点。例如,第95百分位的值可能应该用max函数聚合。另一方面,对于计数器来说,sum函数可能更合适。
在聚合数据点时,Whisper还处理了xFilesFactor的概念。xFilesFactor表示为了保证聚合准确,一个存储区必须包含的数据点比率。在我们之前的示例中,Whisper确定了它需要聚合6个10秒数据点。由于网络问题,应用重启等原因,可能只有4个数据点有数据而其他2个数据点是空值。
如果我们的Whisper文件的xFilesFactor是0.5,这意味着只有存在至少50%的数据点时,Whisper才会聚合数据。如果超过50%的数据点为空时,Whisper会创建一个空值聚合。在我们的例子中,即6个数据点中的4个——也就是66%。聚合函数会被应用在非空数据点上,创建聚合值。
你可以将xFilesFactor设置为0到1之间的任意值。值0表示即使只有一个有效数据点,就会执行聚合。值1则表示只有全部的数据点都有效,才会执行聚合。
/opt/graphite/conf
- /opt/graphite/conf/storage-schemas.conf
- /opt/graphite/conf/storage-aggregation.conf
默认存储模式
存储模式(storage-schemas)配置文件由多个条目组成,每个条目中包含一个模式,用于匹配指标项名称和保留期定义。默认情况下,包含两个条目:Carbon和全部其他。
carbon条目匹配以“carbon”字符串开头的指标项名称。默认情况下,Carbon守护进程每60秒发布一次它们自己内部的指标项(这一间隔是可以更改的)。例如,carbon-cache进程会发布指标项,用于标识该进程每分钟创建的指标项文件的数量。保留期的定义则表示数据点每60秒记录一次,并保存90天。
[carbon] pattern = ^carbon\. retentions = 60s:90d
全部其他条目通过指定带星号的模式捕捉全部其他与Carbon无关的指标项。这个保留期的定义表示数据点每60秒记录一次,并保存1天。
[default_1min_for_1day] pattern = .* retentions = 60s:1d
默认存储聚合
storage-aggregation配置文件也是由多个条目组成,其中包括:
- 匹配指标项名称的模式
- 一个xFilesFactor值
- 一个聚合函数
默认情况下,包含4个条目:
- 使用min聚合函数
- 至少有10%数据点才可以聚合
- 使用max聚合函数
- 至少有10%数据点才可以聚合
- 使用sum聚合函数
- 聚合的前提是至少要有一个数据点
- 使用average聚合函数
- 至少有10%数据点才可以聚合
[min] pattern = \.min$ xFilesFactor = 0.1 aggregationMethod = min [max] pattern = \.max$ xFilesFactor = 0.1 aggregationMethod = max [sum] pattern = \.count$ xFilesFactor = 0 aggregationMethod = sum [default_average] pattern = .* xFilesFactor = 0.5 aggregationMethod = average
在测试环境下,默认的存储模式和存储聚合函数可以很好的完成任务,不过真正应用到生产指标项时,可能还要修改配置文件。
存储模式修改
首先,我会修改Carbon条目。我希望Carbon每60秒记录一次指标项,并将这些指标项保存180天(6个月)。180天之后,我希望能够以10分钟的精度将这些指标项归档,再保存180天。
[carbon] pattern = ^carbon\. retentions = 1min:180d,10min:180d
在Squarespace,我们用Dropwizard框架构建RESTful的Web Service。在准生产环境和生产环境中,我们运行了许多这样的服务,所有这些服务都使用Dropwizard Metrics库以每10秒一次的速度发布应用和业务指标项。这种10秒一次的数据我会保存3天。3天后,这些数据将被聚合为1分钟数据并保存180天(6个月)。最后,6个月之后,这些数据将被聚合为10分钟数据并再保存180天。
注:如果我的指标项库以不同的速度发布数据点,我就需要修改保留的定义以匹配新的速度。
[production_staging] pattern = ^(PRODUCTION|STAGING).* retentions = 10s:3d,1min:180d,10min:180d
而Carbon,生产环境或准生产环境之外的指标项,可能只是用于测试。我会将这些数据保存1天并且假设他们会每1分钟发布一次。
[default_1min_for_1day] pattern = .* retentions = 60s:1d
修改存储聚合
我会保留默认的存储聚合条目,不过会增加几条新的条目用于以ratio,m1_rate和p95结尾的指标项。
注:新增的条目需要添加到default条目之前。
[ratio] pattern = \.ratio$ xFilesFactor = 0.1 aggregationMethod = average [m1_rate] pattern = \.m1_rate$ xFilesFactor = 0.1 aggregationMethod = sum [p95] pattern = \.p95$ xFilesFactor = 0.1 aggregationMethod = max
目前为止,你已经完成了Graphite后端的配置以匹配应用程序发布数据点的速率并且已经完全理解数据点是如何在文件系统中存储的。接下来的一章,我们将尝试用graphite-webapp将这些数据可视化。
Graphite Web应用
现在,后端组件已经成功启动并运行,并且能够用我们指定的格式存储数值型的时间序列数据,接下来我们将了解Graphite的前端组件。具体说来,我们需要查询并可视化已存储的信息的途径。
据其Github描述文件所介绍,Graphite Web应用程序是一个运行在Apache/mod_wsgi下的Django应用程序。一般来说,能够提供如下功能:
安装迷阵
graphite-web的安装真的可以称得上是一个迷阵。我已经多次尝试安装graphite-web——在RHEL,CentOS,Ubuntu和Mac OS X上——而每一次的安装步骤都是有不同的。你可以把它当成一场游戏,享受这个过程,当所有必需的依赖都成功安装完成后,你就知道你已经完成了这个迷阵。
RHEL 6.5安装指南:
# cd /tmp # git clone https://github.com/graphite-project/graphite-web.git # cd /tmp/graphite-web # python check-dependencies.py [REQUIRED] Unable to import the 'django' module, do you have Django installed for python 2.6.6? [REQUIRED] Unable to import the 'pyparsing' module, do you have pyparsing module installed for python 2.6.6? [REQUIRED] Unable to import the 'tagging' module, do you have django-tagging installed for python 2.6.6? [OPTIONAL] Unable to import the 'memcache' module, do you have python-memcached installed for python 2.6.6? This feature is not required but greatly improves performance. [OPTIONAL] Unable to import the 'txamqp' module, this is required if you want to use AMQP as an input to Carbon. Note that txamqp requires python 2.5 or greater. [OPTIONAL] Unable to import the 'python-rrdtool' module, this is required for reading RRD. 3 optional dependencies not met. Please consider the optional items before proceeding. 3 necessary dependencies not met. Graphite will not function until these dependencies are fulfilled.
目标是保证至少所有必需的依赖都安装成功。如果计划使用AMQ功能或Memcache的缓存功能,就还需要安装可选依赖。
# sudo yum install cairo-devel # sudo yum install pycairo-devel # sudo pip install django # sudo pip install pyparsing # sudo pip install django-tagging # sudo pip install python-memcached # sudo pip install txamqp # sudo pip install pytz # cd /tmp/graphite-web # python check-dependencies.py [OPTIONAL] Unable to import the 'python-rrdtool' module, this is required for reading RRD. 1 optional dependencies not met. Please consider the optional items before proceeding. All necessary dependencies are met.
必需依赖的要求后,就可以开始安装graphite-web:
# cd /tmp/graphite-web # sudo python setup.py install # ls -l /opt/graphite/webapp/ total 12 drwxr-xr-x. 6 root root 4096 May 23 14:33 content drwxr-xr-x. 15 root root 4096 May 23 14:33 graphite -rw-r--r--. 1 root root 280 May 23 14:33 graphite_web-0.10.0_alpha-py2.6.egg-info
/opt/graphite/webapp文件夹下适当的位置。
数据库初始化
web应用程序维护了一个内部数据库用于保存用户信息和仪表盘。运行如下命令初始化该数据库:
# cd /opt/graphite # export PYTHONPATH=$PYTHONPATH:`pwd`/webapp # django-admin.py syncdb --settings=graphite.settings You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use 'root'): feangulo Email address: feangulo@yaipan.com Password: Password (again): Error: Blank passwords aren't allowed. Password: Password (again): Superuser created successfully. Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s)
/opt/graphite/storage文件夹下:
# ls -l /opt/graphite/storage/graphite.db -rw-r--r--. 1 root root 74752 May 23 14:46 /opt/graphite/storage/graphite.db
Graphite Web应用设置
/opt/graphite/webapp/graphit文件夹下。将样例配置文件拷贝到该文件夹下:
# vi /opt/graphite/webapp/graphite/local_settings.py ######################### # General Configuration # ######################### TIME_ZONE = 'UTC' ########################## # Database Configuration # ########################## DATABASES = { 'default': { 'NAME': '/opt/graphite/storage/graphite.db', 'ENGINE': 'django.db.backends.sqlite3', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '' } }
到现在为止,如果你遵循前述章节的指令,现在只会有一个运行在2003端口上的carbon-cache进程和一个7002查询端口。这些是默认情况下graphite-webapp所需的端口。因此,配置文件无需任何修改。
# ps -efla | grep carbon-cache 1 S root 14101 1 0 80 0 - 75955 ep_pol May20 ? 00:00:26 /usr/bin/python ./carbon-cache.py start # netstat -nap | grep 2003 tcp 0 0 0.0.0.0:2003 0.0.0.0:* LISTEN 14101/python # netstat -nap | grep 7002 tcp 0 0 0.0.0.0:7002 0.0.0.0:* LISTEN 14101/python
不过,你也可以在设置文件中显式指定从哪个carbon-cache进程读取数据:
# vi /opt/graphite/webapp/graphite/local_settings.py ######################### # Cluster Configuration # ######################### CARBONLINK_HOSTS = ["127.0.0.1:7002:a"]
上述代码的含义是我有一个本地运行的名为‘a’的carbon-cache进程,其查询端口设置为7002。查看Carbon配置文件,将会看到如下配置:
# vi /opt/graphite/conf/carbon.conf [cache] LINE_RECEIVER_INTERFACE = 0.0.0.0 LINE_RECEIVER_PORT = 2003 CACHE_QUERY_INTERFACE = 0.0.0.0 CACHE_QUERY_PORT = 7002
注:‘a’ 是从何而来的呢?是默认分配的名字。如果要定义更多缓存,需要在配置文件中创建新的命名区块。
[cache:b] LINE_RECEIVER_INTERFACE = 0.0.0.0 LINE_RECEIVER_PORT = 2004 CACHE_QUERY_INTERFACE = 0.0.0.0 CACHE_QUERY_PORT = 7003
仪表盘和图表模版配置
Graphite Web应用中包含默认的仪表盘和图表模版。拷贝样例配置文件:
# cd /opt/graphite/conf # cp dashboard.conf.example dashboard.conf # cp graphTemplates.conf.example graphTemplates.conf
我对仪表盘配置文件作了一些修改,让图表展示区块更大。
# vi /opt/graphite/conf/dashboard.conf [ui] default_graph_width = 500 default_graph_height = 400 automatic_variants = true refresh_interval = 60 autocomplete_delay = 375 merge_hover_delay = 750
我对默认的图表模版也做了一些修改,让它有一个黑色背景和白色前景。另外我还把字体调小了一些。
# vi /opt/graphite/conf/graphTemplates.conf [default] background = black foreground = white minorLine = grey majorLine = rose lineColors = blue,green,red,purple,brown,yellow,aqua,grey,magenta,pink,gold,rose fontName = Sans fontSize = 9 fontBold = False fontItalic = False
运行Web应用程序
终于,一切准备就绪,可以运行Web应用程序了。我会在8085端口运行这个Web应用,你可以随意设置这个端口号。运行如下命令:
# cd /opt/graphite # PYTHONPATH=`pwd`/storage/whisper ./bin/run-graphite-devel-server.py --port=8085 --libs=`pwd`/webapp /opt/graphite 1>/opt/graphite/storage/log/webapp/process.log 2>&1 & # tail -f /opt/graphite/storage/log/webapp/process.log
process.log文件的输出,应该可以看到资源的加载和来自于web应用的查询。