一、为什么要做分布式Cacti?

现有的CACTI服务器只有一台,被监控的网络设备有332台,因此每5分钟需要对332个节点进行一次数据采集,9518个RRD文件进行写入,导致磁盘出现了严重的IO瓶颈,致使在WEB界面上的操作速度慢的让人无法忍受,严重的影响了Cacti日常管理。因此决定将其改造为分布 式。这样,一方面可以解决性能问题,另一方面可以解决异网内无公网IP地址设备的监控。

二、分布式Cacti的架构分析

整个分布式Cacti由三台服务器组成:

1、cacti78.cm3用来采集CDN的数据,同时接收CDN二层设备被动监控的数据;

主动采集CDN设备的RRD数据存放在/home/cacti/rra/cacti78.cm3目录下

被动采集CDN设备的RRD数据存放在/home/cacti/rra/passive目录下

2、cacti79.cm3用来采集主站的数据。

采主站的RRD数据存放在/home/cacti/rra/cacti79.cm3目录下

3、cacti80.cm3用来做WEB界面展示;

生成图像时用到的RRD数据通过NFS,将cacti78.cm3和cacti79.cm3采集的数据加载到本地。

三、分布式Cacti数据库分析

由于Cacti的数据库主要是用来存储配置信息的,因此我的第一个想法就是用一个数据库来支撑三台Cacti,这看起来是个不错的想法,于是 就按这种方法搭建了起了分布式Cacti。当一切配置完成之后,绘出的图让我失望了,开始时是断断续续,到了后面干脆就没了图像。一开始我并 没有想明白是怎么回事,就到处查资料,结果好不容易找到了一个方法,就是在crontab里的定期执行命令:php poller.php命令后面加个“--force”参数,强制执行这个命令,这 次让我看到了一点点的希望,终于可以绘出断断续续的图来。可为什么会断断续续的绘图呢?

通过阅读源代码,我发现原来Cacti每次采集数据的时候,不仅仅要读数据库,还要写数据库。具体的要写哪些表呢?第一个就是settings表的name字段名为poller_lastrun的行,该行后 面的value字 段存储的是上次进行数据采集的时间截,每当采集数据之前,都会读取这个值,并用当前时间截减去上次时间截,并判断是否为300秒(即数据采集间隔5分钟),如果为300秒,则进行数据采集,并将当前时间截 写入数据库;否则不进行数据采集。加了“--force”参数就是为了不进行采集间隔的判断,直接采集数据,因为有两台采集服务器同时写表,导致时间不对。 加了该参数之后图是绘出来了,可是不连续。

为了解决图不连续的问题,我再次深入研究,发现Cacti除了写这个settings的表,还写了名为poller的相关表,这些表是用来存储poller需要的一些临时数据的。由于两个采集服务器同时对这些表进行写和删除操盘,造成数据混乱。

基于以上分析,分布式Cacti只能拥有各自的数据库,而不能共享同一个库。因此采用三个库,一主两从的方法。cacti80.cm3上的数据库为主库,cacti78.cm3、cacti79.cm3上的库为从库,我们 平时的配置修改操作都在cacti80.cm3上,这个库的数据变化都会同步到其它两个库上,而对每次数据采集都会写入数据的几个表则不进行同步, 这几个表分别是:poller、poller_command、poller_output、poller_reindex、poller_time。

四、如何让两台Cacti 采集服务器进行数据采集的分工?

为了让两台服务器(cacti78.cm3、cacti79.cm3)进行数据采集的分工合作,我们首先需要改造页面源代码,就是当我们新加一台被动监控设备时,要区 别这台设备由哪台服务器来采集数据。修改后的源代码如下图,从下图的“Choose Spine Agent”后面的下拉列表中即可以选择用哪台服务器采集数据。我们规 定,所有CDN的 设备均选择“cacti78.cm3”这台服务器来采集数据;主站的设备均选择“cacti79.cm3”这台服务器来采集数据;被动监控均选择“passive”,表示不采集数据。该下拉列表将直接更改host表的disabled字段的值,由于该字段只支持两个字符,因此需要修改表结构,我修改的是支持20个字符。

由于每个采集服务器采集的数据必须放在不同目录下,以便通过NFS加载到WEB服务器(cacti80.cm3)上。因此,还需要 修改源代码lib/function.php,以便创建图片时生成的数据源路径分别属于各自的目录,比如选择cacti78.cm3采集数据,则创建图片 时生成的数据源在/home/cacti/rra/cacti78.cm3目录下;选择cacti79.cm3采集数据,则创建图片时生成的数据源在/home/cacti/rra/cacti79.cm3目录下。因此,选择由谁来采集数据一定要在创建图片之前操作,否则数据源的路径是会出错的。

两台采集服务器采集数据使用的是spine,为了能让它们各自采集自己负责的设备,还需要修改 spine.c源代码。让它每次采集只选择host表的disabled为“cacti78.cm3”或“cacti79.cm3”的设备进行数据采 集。

五、采集服务器上的定 期采集命令:

php poller.php --collect=< 采集服务器>

采集服务器是指你在WEB界面上添加一台设备时,在“Choose Spine Agent”后 面的下拉框里选择的内容。

例如:

Cacti78.cm3 上的crontab里就写

*/5 * * * * /opt/php/bin/php pollerr.php --collect=cacti78.cm3

Cacti79.cm3 上的crontab里就写

*/5 * * * * /opt/php/bin/php pollerr.php --collect=cacti79.cm3

六、实施步骤

1、创建设备时选择哪台代理采集数 据

修改include/global_form.php

"disabled" => array(

"method" => "drop_array",

"friendly_name" => "Choose Spine Agent",

"description" => "Choose a spine agent to checks for this host.",

"value" => "|arg1:disabled|",

"default" => read_disabled("hostname"),

"array" => $spine_agent

),

修改include/ global_arrays.php,添加以下内容

$spine_agent = array(

0 => "",

"cacti78.cm3" => "cacti78.cm3",

"cacti79.cm3" => "cacti79.cm3",

"passive" => "passive");

修改lib/ functions.php,添加以下内容

function read_disabled($ip) {

$disabled = db_fetch_cell("select disabled from host where hostname='$ip'");

return $disabled;

}

2.创建图片时使用什么路径

修改lib/function.php

$host = db_fetch_row("SELECT

host.id,

host.description,

host.disabled

FROM (host, data_local)

WHERE data_local.host_id=host.id

AND data_local.id=$local_data_id

LIMIT 1");

$host_spine = $host["disabled"];

$new_path = "<path_rra>/$host_spine/$host_id/$local_data_id.rrd";

$new_path = "<path_rra>/$host_spine/$host_part$ds_part" . "_" . "$local_data_id.rrd";

3.解决创建图片页面的对SNMP接口统计重新加载的Bug

修改lib/snmp.php ,在“function cacti_snmp_walk”函数下面添加以下内容

$banned_snmp_strings = array("End of MIB", "No Such"); //Add by qiudi.

4. 更改RRD路径

修改include/global.php

#$config["rra_path"] = $config["base_path"] . '/rra';

$config["rra_path"] = "/home/cacti/rra";

5.修改源代码以便通过命令行参数 来决定Spine Agent采集哪些被监控设备的数据

(1)修改poller.php源代码

找到:foreach($parms as $parameter)

加入:

case "--collect":

$spine_agent = $value;

break;

修改:

$polling_hosts = array_merge(array(0 => array("id" => "0")), db_fetch_assoc("SELECT id FROM host WHERE disabled = '$spine_agent'

ORDER BY id"));

查找:“$poller == "2") {”

修改:

if ($poller == "2") {

$command_string = read_config_option("path_spine");

$extra_args = "--collect=".$spine_agent;

(2)修改spine.c源代码

在main函 数里定义变量:

char *spine_agent = NULL;

找到:for (argv++; *argv; argv++)

加入:

else if (STRMATCH(arg,"-c") ||

STRMATCH(arg,"--collect")){

spine_agent = strdup(getarg(opt,&argv));

}

修改:qp += sprintf(qp, " WHERE disabled=''");(两处)

为:

qp += sprintf(qp, " WHERE disabled='%s'",spine_agent);