撰写人:李燕凌/秋荻(原创)一、为什么要做分布式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里的定期执行命令:phppoller.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 SpineAgent”后面的下拉列表中即可以选择用哪台服务器采集数据。我们规定,所有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/phppollerr.php--collect=cacti78.cm3Cacti79.cm3 上的crontab里就写*/5 * * * * /opt/php/bin/phppollerr.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 forthis 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)                WHEREdata_local.host_id=host.id                ANDdata_local.id=$local_data_id                LIMIT1");        $host_spine =$host["disabled"];$new_path ="/$host_spine/$host_id/$local_data_id.rrd";$new_path = "/$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", "NoSuch"); //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, " WHEREdisabled='%s'",spine_agent);
11-04 12:04