一、为什么要做分布式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);