Ubuntu中国  - 讨论区

标题:利用 RSCT+RRDTool 实现集群的自动化监控

2010年11月01日 星期一 14:03

简介:  集群管理人员经常需要知道集群中各种资源(CPU,内存,硬盘,网络带宽等)的使用情况。现在有许多通用的集群管理工具,如 Ganglia,Performance Co- Pilot,Nagios,Cacti 等都可以实现这一目的,但没有一个工具可以监控管理人员所需要的所有内容。本文介绍了一种简单的方法利用 RSCT 和 RRDtool 来实现定制的集群监控 。并通过一个简单的示例来说明这种方法,读者可以直接使用文中提供的自动化监控脚本或是编写自己的脚本来实现所需的监控软件。此外,使用 RSCT 来完成监控对于使用 AIX 作为操作系统的集群管理人员是非常方便的。AIX 系统自带 RSCT 软件,所有你不需要在节点上安装额外的软件,只需要在管理节点上安装 RRDtool 就可以实现集群监控。

 

概述

RSCT(Reliable Scalable Cluster Technology)是一组为提高集群可用性,扩展性以及易用性提供底层支持的软件。IBM 的许多产品都依赖它,例如:HACMP, TSA, CSM 等等。RSCT 中的 RMC(Resource Monitoring and Control)子系统提供了用于管理集群或单机资源的一个通用框架,这个框架为集群管理软件提供底层的监控,查询,修改和控制集群资源的功能。

RRDtool 源于 MRTG(多路由器流量绘图器 ),是一个为监控而开发的数据库。

本文要介绍的方法就是利用 RMC 查询集群资源的功能来取得实时的监控数据,在处理完数据后,把结果保存在 RRDtool 数据库中。

RSCT 资源

要使用 RSCT,首先必须理解什么是 RSCT 的资源。在 RSCT 中,资源代表系统中物理或逻辑上可为其他组件提供服务的实体。所有的操作都是对资源进行的。一个资源可以是一个特定的文件系统、主机、网卡或进程等。相同类型的资源组成资源类。每个资源有一个或多个属性。属性有两种,静态属性和动态属性。静态属性指的是资源相对稳定的一些属性。经常可能变化的属性被称为动态属性。动态属性常常是被监控的对象。例如:IBM.EthernetDevice 是系统中以太网设备资源组成的资源类。清单 1 和清单 2 使用 lsrsrc 显示了该资源类中资源信息。


清单 1.显示资源静态属性

     # lsrsrc -Ap "IBM.EthernetDevice"
 Resource Persistent Attributes for IBM.EthernetDevice 
 resource 1: 
        Name             = "ent0"
        ActivePeerDomain = ""
        NodeNameList     = {"master.domain"} 
 resource 2: 
        Name             = "ent1"
        ActivePeerDomain = ""
        NodeNameList     = {"master.domain"}
    



清单 2.显示资源动态属性

     # lsrsrc -Ad "IBM.EthernetDevice"
 Resource Dynamic Attributes for IBM.EthernetDevice 
 resource 1: 
        RecErrorRate     = 0 
        RecDropRate      = 0 
        XmitDropRate     = 0 
        XmitErrorRate    = 0 
        XmitOverflowRate = 0 
        RecByteRate      = 1547 
        RecPacketRate    = 7 
        XmitByteRate     = 83 
        XmitPacketRate   = 0 
        RecErrors        = 0 
        RecDrops         = 0 
        XmitDrops        = 0 
        XmitErrors       = 0 
        XmitOverflows    = 0 
        RecBytes         = 490999495 
        RecPackets       = 4212256 
        XmitBytes        = 8422514234 
        XmitPackets      = 5859387 
 resource 2: 
        …
    

 

其中,以太网设备的名称和所属的节点名是静态属性,收发速率和字节数是动态属性。RSCT 提供了许多常用的资源类。通过这些资源类,用户可以方便地查看集群中各种资源的信息,通过收集这些信息实现集群监控。此外,用户还可以自定义 RSCT 中没有的资源。

RSCT 管理域

根据需要 , 可以将集群中的节点分配给不同的 RSCT 域。RSCT 域有两种,管理域(management domain) 和对等域 (peer domain)。管理域中,有一个节点是管理控制点(MCP, Management Control Point),只有 MCP 知道域中其他节点的信息。对等域中没有所谓的管理节点,所有的节点都是对等的,每一个都知道其它节点的信息。如果是出于管理的目的,可以将多个节点配置为一个 RSCT 的管理域。对等域通常被用来提供高可用性 (HA,High Availability)。出于监控目的,需要选择一个节点被选择为管理节点,并将集群中的所有需要监控节点配置在与这个管理节点同一个管理域中。这样,管理员就可以在这个管理节点上通过 RSCT 查询命令看到域中其他节点的信息,取得监控数据。

管理域资源类

管理域资源管理器定义的资源类有:

IBM.MngNode- 在管理控制点上定义被管理节点。

IBM.MCP- 在被管理节点上定义管理控制点的信息。IBM.MCP 资源被定义后会自动进行管理控制点与被管理节点之间的公钥交换,所以最好是先在管理控制点上为被管理节点创建 IBM.MngNode 资源。

IBM.MngNodeNetIF- 定义被管理节点与管理控制点通讯的网络接口信息。该类资源会随着 IBM.MngNode 类资源的创建,修改和删除被自动创建,修改和删除。每个被管理节点配置一个 IP 地址。

IBM.PublicKeyExchange- 用于交换公钥。正常情况下,在被管理节点上成功创建 IBM.MCP 资源后,管理域资源管理器会自动发起公钥交换协议(PKE,public key exchange)协议。在被管理节点上执行 refrsrc IBM.MCP 命令也可以重新发起与所有 IBM.MCP 定义的管理控制点之间的公钥交换。

建立管理域

IBM.MngNodeNetIF 和 IBM.PublicKeyExchange 是被自动创建的,所以要建立 RSCT 管理域,只要在 IBM.MngNode 和 IBM.MCP 类中定义相应资源。命令 mkrsrc/chrsrc/rmrsrc/lsrsrc 用于定义 / 修改 / 删除 / 显示资源。此外,RSCT 中有 4 个为脚本使用而设计的新命令来执行相似的功能,这 4 个命令分别是 mkrsrc-api,chrsrc-api,rmrsrc-api,lsrsrc-api。与原来的命令相比,这四个命令有以下特点:

  • 完全用 C 语言实现,执行速度更快;
  • 可以在一个调用中同时操作多个资源;
  • 命令输出被设计成更容易被脚本解析;

所以在脚本操作 RSCT 资源,建议使用这 4 个命令。

创建 RSCT 管理域的步骤如下:

  1. 在管理控制点上定义被管理节点;
           #mkrsrc-api IBM.MngNode::Name::"$node"::KeyToken::"$node"::IPAddresses::
     {"$node_ip"}::NodeID::$node_id
          

    其中,$node 指的是节点的主机名 ,$node_ip 是节点与 MCP 相连的 IP 地址,$node_id 是 RSCT 中标志一个节点 ID 号,可以通过在节点上执行 /usr/sbin/rsct/bin/lsnodeid 取得。

  2. 在被管理节点上定义管理控制点;
           #mkrsrc-api IBM.MCP::MNName::"$node"::KeyToken::"$ms"::IPAddresses::{"$ms_ip"}
     ::NodeID::$ms_node_id`
          

其中,$ms 是 MCP 的主机名 ,$ms_ip 是 MCP 与被管理节点相连的 IP 地址,$ms_node_id 是 MCP 的 ID 号。

例如:集群里有三个节点,hostname/IP/nodeID 分别为 master/192.168.0.1/0x1B04866DD460924C,node1/192.168.0.2/0x1B04866DD460924D, node2/192.168.0.2/0x1B04866DD460924E. 其中,master 节点作为 MCP。


清单 3.在 master 上创建 IBM.MngNode 资源

     #mkrsrc-api IBM.MngNode::Name::"node1"::KeyToken::"node1"::IPAddresses::
 {"192.168.0.2"}::NodeID:: 0x1B04866DD460924D 
 #mkrsrc-api IBM.MngNode::Name::"node1"::KeyToken::"node2"::IPAddresses::
 {"192.168.0.3"}::NodeID:: 0x1B04866DD460924E 
 # lsrsrc -Ap IBM.MngNode 
 Resource Persistent and Dynamic Attributes for IBM.MngNode 
 resource 1: 
        Name             = "node1"
        NodeID           = 1946828745203552845 
        KeyToken         = "node1"
        Aliases          = {} 
        IPAddresses      = {"192.168.0.2"} 
        ActivePeerDomain = ""
        NodeNameList     = {"master.domain"} 
 resource 2: 
        Name             = "node2"
        NodeID           = 1946828745203552846 
        KeyToken         = "node2"
        Aliases          = {} 
        IPAddresses      = {"192.168.0.3"} 
        ActivePeerDomain = ""
        NodeNameList     = {"master.domain"}
    



清单 4.在 node1 上创建 IBM.MCP 资源

     #mkrsrc-api IBM.MCP::MNName::"node1"::KeyToken::"master"::IPAddresses::
 {"192.168.0.1"}::NodeID::0x1B04866DD460924C 
 # lsrsrc -Ab IBM.MCP 
 Resource Persistent and Dynamic Attributes for IBM.MCP 
 resource 1: 
        MNName           = "node1"
        NodeID           = 1946828745203552844 
        KeyToken         = "master"
        IPAddresses      = {"192.168.0.1"} 
        ActivePeerDomain = ""
        NodeNameList     = {"node1.domain"}
    



清单 5.在 node2 上创建 IBM.MCP 资源

     #mkrsrc-api IBM.MCP::MNName::"node2"::KeyToken::"master"::IPAddresses::
 {"192.168.0.1"}::NodeID::0x1B04866DD460924C 
 # lsrsrc -Ap IBM.MCP 
 Resource Persistent and Dynamic Attributes for IBM.MCP 
 resource 1: 
        MNName           = "node2"
        NodeID           = 1946828745203552844 
        KeyToken         = "master"
        IPAddresses      = {"192.168.0.1"} 
        ActivePeerDomain = ""
        NodeNameList     = {"node2.domain"}
    

 

完成后,在 master 上执行 lsrsrc -Ap -a IBM.Host,可以看到被管理节点 node1,node2 的信息,表示管理域建立成功。


清单 6.检验管理域是否建立成功

     #lsrsrc -Ap -a IBM.Host 
 Resource Persistent Attributes for IBM.Host 
 resource 1: 
        Name                = "node1.domain"
        NumProcessors       = 6 
        RealMemSize         = 11811160064 
        OSName              = "AIX"
        KernelVersion       = "5.3"
        DistributionName    = "IBM"
        DistributionVersion = "5300-12-01-1016"
        Architecture        = "ppc"
        NumOnlineProcessors = 6 
        EntProcCapacity     = 300 
        NumOnVProcessors    = 3 
        NumActPProcessors   = 32 
        ActivePeerDomain    = ""
        NodeNameList        = {"node1.domain"} 
 resource 2: 
        Name                = "node2.domain"
        …
    

 

如果你的集群装有 CSM 软件,也可以通过 CSM 的 updatenode 命令来创建管理域。

RRDtool 的功能及使用

RRDtool(Round Robin Database Tool)是一个用来处理定量数据的开源高性能数据库。

RRDtool 的特性

由于 RRDtool 设计的初衷就是为监控开发的,所以与其他的数据库相比,RRDtool 有以下特性:

  • 有一个强大的绘图引擎,可以根据数据库内容,创建 png 格式的图片。图 1,图 2 和图 3 所示的就是三张通过 rrdtool graph 命令生成的网络带宽波形图。


图 1. 最近一小时的网卡收发速率
图 2. 最近一天的网卡收发速率  

图 2. 最近一天的网卡收发速率
图 3. 最近一年的网卡收发速率  

图 3. 最近一年的网卡收发速率
图 3. 最近一年的网卡收发速率  

  • 是一个基于时间序列的环形数据库。可以想象一个圆,圆周上有一些均匀分散的点。这些点就是数据存储的位置。从圆心画一条到圆周的某个点的箭头,这个箭头就是指向当前元素的指针。在当前元素被读或写之后,指针就往下一个元素移动。当所有位置都被用过,原来的位置就会被重用。这样,数据库永远不会满,不需要维护。
  • 可以被配置成存储当前值与前一个值变化速度,而不是简单地存储提供的值。
  • 要求在预定的时间间隔中取得监控数据,如果没有得到更新的数据,就将该时间段的值设为 UNKNOWN.

RRDtool 中的数据处理

RRD 数据库存储的值并不一定等于更新时提供的值。这个值是根据数据库设定对提供的值进行计算得到的。计算过程可以分为三个步骤:

  1. 根据用户定义的数据类型对提供的值进行计算,具体计算方法见表 1;


表 1.RRDtool 中的数据类型

数据类型 计算方法
GAUGE 等于提供的值,适用于提供值为速度计,温度或人数等
COUNTER 当前值与前一个值的变化速度,要求递增的值,如计数器,里程表等。如果计数器溢出,RRDtool 会检查溢出是 32 位还是 64 位的,并相应地加上合适的值。
DERIVE 当前值与前一个值的变化速度,不要求递增。除了不进行溢出处理之外,该类型的内部处理机制与 COUNTER 相同。
ABSOLUTE 假定前一个值始终为 0,求变化速度。可用来存储读取后复位的计数器。

 

  1. 整形;

    输入的时间经常会不在时间间隔的边界,所以需要根据前后两个有效的更新进行线性整形。(有效时间是被 heartbeat 设定控制的 )假定你在监视一个增长速度不均匀的计数器,每分钟检查一次,在 4:30 是 2,5:30 是 4,那么第 5 分钟的变化速度为 3。整形后得到的值称为 PDP(Primary Data Points)。

  2. 合并;

    RRDtool 最终会根据设定对 PDP 进行合并,得到 CDP(Consolidated Data Point)。CDP 可以是多个(可以是一个)PDP 的平均值、最小值、最大值以及最近的值。CDP 是 RRD 数据库中最终存储的数据。这对于输出监控数据时是非常有利的。假设每个 PDP 的时间间隔是 1 分钟,如果你想查看 1 天的数据,而且你不需要查看每一分钟具体的监控数据,只是想看一下这一个天中每半个小时的监控数据。如果 RRD 中存储的是 PDP, 在输出时就需要对当天的 PDP 每 30 个进行合并。合并需要时间,如果你需要合并的数据量更大,如 1 个月或是 1 年,那这个合并的时间会非常长。所以 RRDtool 选择在数据库更新时对 PDP 进行合并,保存 CDP。用户可以根据需要设定如何对 PDP(非 UNKNOWN)进行合并。


表 2.PDP 的合并方式

合并方式 计算方法
AVERAGE 取平均值
MIN 取最小值
MAX 取最大值
LAST 取最近的值

 

RRDtool 命令使用

1. 创建 RRD 数据库

语法:

rrdtool create filename [--start|-b start time] [--step|-s step] [--no-overwrite] [DS:ds-name:DST:dst arguments] [RRA:CF:cf arguments]

参数:

create 关键字,用于创建一个 RRD 数据库文件。

filename 文件名,扩展名最好是 .rrd

--start|-b start time 第一个记录的开始时间,默认是当前时间减 10 秒。表示方法为从 1970-01-01 UTC 到指定时间的秒数

--step|-s step 每条记录的时间间隔,默认为 300 秒

--no-overwrite 不覆盖同文件名的数据库文件

DS:ds-name:DST:dst arguments

DS 关键字,用于定义数据源。一个 RRD 文件可以有多个 DS。

ds-name 数据源名称,可以是 1 ~ 19 个 [a-zA-Z0-9_] 中的任意字符,一个 RRD 数据库文件可以有多个数据源。

DST 数据类型。支持的数据类型有:

GAUGE, COUNTER, DERIVE, ABSOLUTE,COMPUTE

DS:ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE:heartbeat:min:max

DS:ds-name:COMPUTE:rpn-expression

COMPUTE 存放对其他数据源进行公式计算的结果。

Heartbeat 心跳,两次数据源更新之间将数据源的数值确定为 UNKNOWN 前所允许的最大秒数。Min:max PDP 的最小值 / 最大值。

Rpn-expression

RRA:CF:cf arguments

RRA 关键字,用于定义 RRA(round robin archive) 档。一个 RRD 数据库文件有至少一个的 RRA 档。CDP 就被保存于 RRA 中。

CF 合并方式,可以是 AVERAGE,MIN,MAX,LAST

RRA:AVERAGE | MIN | MAX | LAST:xff:steps:rows

xff 定义合并时间间隔内允许的 PDP 为 UNKNOWN 的比例,超过这个比例时,CDP 被置为 UNKNOWN。

steps 定义多少个 PDP 合并一个 CDP。

rows 定义一个 RRA 档保存有多少条记录。

2. 更新 RRD 数据库

语法:

rrdtool {update | updatev} filename [--template|-t ds-name[:ds-name]...] [--daemon address] [--] N|timestamp:value[:value...] at-timestamp@value[:value...] [timestamp:value[:value...] … ]

参数:

update/updatev 关键字,用于更新数据库。updatev 会输出磁盘上数据库文件更新后输出更新信息。由于 updatev 需要直接的磁盘访问,所有不能与 --daemon 共用。

filename 需要更新的 RRD 数据库的文件名

--template|-t ds-name[:ds-name]...

默认情况下,需要按照数据源定义的顺序更新。该参数允许你指定要更新的数据。

--daemon address

使用缓存后台程序更新数据库,而不直接访问磁盘。

N|timestamp:value[:value...]

更新时间及数据。时间格式可以是 N| 时戳;N 表示现在;时戳以自 1970-01-01 以来的秒数来表示。 如果使用负数时间(当前时间之前多长时间),需要使用—与其它选项区分开。用 U 表示数据的值为 UNKNOWN。

at-timestamp@value[:value...]

更新时间也可以使用 AT 风格表示。

环境变量 RRDCACHED_ADDRESS 与 --daemon 选项的作用是一样的,如果两者都出现的话,前者优先。

3. 读取数据

语法:

rrdtool fetch filename CF [--resolution|-r resolution] [--start|-s start] [--end|-e end] [--daemon address]

参数:

fetch 关键字,用于取数据。

filename 需要读取的文件的文件名

CF 需要读取数据的合并方式 (AVERAGE,MIN,MAX,LAST)

--resolution|-r resolution (default is the highest resolution)

解析度,也就是你要取数据的时间间隔(单位:秒);rrdfetch 会尽量找到最匹配的 RRA。默认为数据库文件支持的最高解析度,即时间间隔最短的;如果想取的不是最高解析度的 RRA,假设要取得的是解析度为 x 的 RRA,开始时间和结束时间必须落在期望解析度的边界点上,即需要满足以下条件:

a.start 时间和 end 时间都是 x 的倍数

b.start 时间和 end 时间都在 RRA 记录的时间范围内。

--start|-s start (default end-1day)

开始时间;支持时戳和 AT 风格;默认是 1 天前。

--end|-e end (default now)

结束时间;格式与开始时间相同;默认是现在;

--daemon address

缓存后台程序地址。如果指定的话,在读取之前,会发 flush 指令给缓存后台程序将缓存的数据同步到磁盘。

2010年11月01日 星期一 14:04

 

实现自动化集群监控

在了解了 RSCT 和 RRDtool 的用法之后,本章将用一个实际的例子来说明如何实现自动化的集群监控。首先,选一个节点作为管理节点,这个节点负责收集处理其他被监控节点的信息,并向管理员提供可视的监控数据。所有的监控数据都保存在管理节点的本地的 RRD 数据库中。

环境准备

在所有节点上安装 RSCT 软件,在管理节点上安装 RRDtool 软件。RSCT 有分别支持 AIX 和 Linux 的两个版本。AIX V5 和 AIX V6 上默认安装 RSCT 软件。Linux 用户可以从

http://www14.software.ibm.com/webapp/set2/sas/f/rsct/rmc/download/home.html

下载并安装 RMC。

然后,将管理节点作为 MCP,把需要监控的节点与管理节点配置到同一个 RSCT 管理域中。

收集保存数据

编写脚本 getmetrics 收集集群中各节点信息,并把数据保存到系统的 /var/rrd 目录下。本脚本在 AIX5.3 上测试通过,读者可以直接使用或根据需要进行修改。

脚本用法:getmetrics 资源类名 属性列表 采样时间间隔

示例:取得集群中所有节点以太网卡收发数率

#getmetrics IBM.EthernetDevice RecByteRate,XmitByteRate 1

清单 7 为脚本声明全局变量及分析参数的部分。其中哈希数组 %metrix 有三个键值,依次为 { 属性名 }{ 节点名 }{ 资源名 },用来保存收集到的各节点监控数据。


清单 7. 收集脚本之初始化

     #!/usr/bin/env perl 

 @attrs = (); 
 $attr = undef; 
 $nnlist = undef; 
 $ret = undef; 
 $cmd = undef; 
 %metrix={}; 

 ($rsrc, $attrlist, $minute) = @ARGV; 
 @attrs = split /,/, $attrlist; 
 $attr = join '::', @attrs;
    

 

在示例中,RRD 数据库文件被分为两类,一类保存的是单个资源的数据,另一个保存的是多个资源数据的和。如果一个 RRD 数据库文件保存的是多个资源数据的和,那么该数据库文件中有两个数据源,sum 和 num,sum 用来记录值,num 保存资源数;如果文件只保存单个资源数据,则数据库文件只有一个数据源 sum。清单 8 封装了 rrdtool create 和 update 命令。


清单 8. 收集脚本之 RRD 命令封装函数

     #--------------------------------------------------------------- 
 # 函数名:RRD_create 
 # 描  述 : 创建 RRD 数据库文件,会覆盖已有文件
 # 输  入 : 
 #       $rrd    文件名
 #       $sum    如果 $sum!=0 会创建数据源 num 
 #       $step   每条 RRD 记录的时间间隔
 #       $start_time 记录开始时间
 #       $ds_type ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE 
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub RRD_create 
 { 
  my ($rrd, $sum, $step, $start_time, $ds_type) = @_; 
  my $output = []; 
  my $heartbeat = 8 * $step; 
  my $cmd = "create $rrd --start $start_time --step $step \ 
              DS:sum:$ds_type:$heartbeat:U:U"; 
  if($sum){ 
    $cmd = $cmd." DS:num:$ds_type:$heartbeat:U:U"; 
  } 
  $cmd = $cmd." RRA:AVERAGE:0.5:1:244 RRA:AVERAGE:0.5:24:244 \ 
           RRA:AVERAGE:0.5:168:244 RRA:AVERAGE:0.5:672:244 RRA:AVERAGE:0.5:5760:374"; 
  @$output = `rrdtool $cmd`; 
  my $line =  pop(@$output); 
  if($line =~ /ERROR/){ 
    return -1; 
  } 
  return 0; 
 } 
 #--------------------------------------------------------------- 
 # 函数名:RRD_update 
 # 描  述 : 更新已有的 RRD 数据库文件
 # 输  入 : 
 #       $rrd    文件名
 #       $sum    数据源 sum 的值
 #       $num    数据源 num 的值
 #       $process_time 更新时间
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub RRD_update 
 { 
  my ($rrd, $sum, $num, $process_time) = @_; 
  my $output = []; 
  my $cmd = "update $rrd"; 
  if($num ne "null"){ 
    $cmd = $cmd." $process_time:$sum:$num"; 
  } else { 
    $cmd = $cmd." $process_time:$sum"; 
  } 

  @$output = `rrdtool $cmd`; 
  my $line =  pop(@$output); 
  if($line =~ /ERROR/){ 
    return -1; 
  } 
  return 0; 
 } 

 #--------------------------------------------------------------- 
 # 函数名:push_data_to_rrd 
 # 描  述 : 将数据保存到 RRD 数据库
 # 输  入 : 
 #       $rrd    文件名
 #       $sum    数据源 sum 的值
 #       $num    数据源 num 的值
 #       $step   每条 RRD 记录的时间间隔
 #       $process_time 更新时间
 #       $ds_type ds-name:GAUGE | COUNTER | DERIVE | ABSOLUTE 
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub push_data_to_rrd 
 { 
  my $ret = 0; 
  my($rrd, $sum, $num, $step, $process_time, $ds_type) = @_; 
  my $summary = $num eq 'null' ? 0 : 1; 
  if(! -f $rrd){ 
    $ret = RRD_create($rrd, $summary, $step, $process_time-$step, $ds_type); 
    if($ret != 0){ 
      return $ret; 
    } 
  } 
  $ret = RRD_update($rrd, $sum, $num, $process_time); 
  return $ret; 
 }
    

 

清单 9 所示的 updaterrd 函数的功能是在 /var/rrd 下创建 RRD 数据库文件,并将 %metrix 中保存的数据更新到数据库中。保存的数据库文件在 /var/rrd 下的目录结构如下:

     |--summary 
  |-- 属性名 .rrd 
  |--... 
 |-- 节点名
  |-- 属性名 .rrd 
  |-- 属性名 _ 资源名 .rrd 
  |--...
    

 

其中,summary 目录保存的是整个集群的监控数据,在示例脚本中,是对所有节点监控数据求和。


清单 9. 收集脚本之数据保存

     #--------------------------------------------------------------- 
 # 函数名:updaterrd 
 # 描  述 : 将 %metrix 中保存的监控数据更新到 RRD 数据库
 # 输  入 : 
 #       $step   每条 RRD 记录的时间间隔
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub updaterrd 
 { 
  my $step = shift @_; 
  my $ret = 0; 
  my $attr; 
  my $nnlist; 
  my $name; 
  my $value; 
  my $rmcrrdroot = "/var/rrd"; 
  my $rrddir = undef; 
  my $rrd = undef; 
  my $process_time = `date +%s`; 
  $process_time = (int $process_time/$step)*$step; 
  my $temp = undef; 

  while(($attr, $nnlist) = each %metrix){ 
    while(($nnlist, $name) = each %{$metrix{$attr}}){ 
      if($nnlist eq 'number'){ 
        next; 
      } 
      $rrddir = "$rmcrrdroot/$nnlist"; 
      if(! -d $rrddir){ 
        `mkdir -p $rrddir`; 
      } 
      if($nnlist eq 'summary'){ 
        $rrd = "$rrddir/"."$attr.rrd"; 
        $temp = $metrix{$attr}{summary}; 
        $ret = push_data_to_rrd 
         ($rrd, $temp, $metrix{$attr}{number}, $step, $process_time, 'GAUGE'); 
        if($ret != 0){ 
          return $ret; 
        } 
      } else { 
        while(($name, $value) = each %{$metrix{$attr}{$nnlist}}){ 
          if($name eq 'number'){ 
            next; 
          } 
          if($name eq 'summary'){ 
            $rrd = "$rrddir/$attr.rrd"; 
            $temp = 
              $metrix{$attr}{$nnlist}{summary}/$metrix{$attr}{$nnlist}{number}; 
            $ret = push_data_to_rrd($rrd,$temp,\ 
              $metrix{$attr}{$nnlist}{number},$step,$process_time,'GAUGE'); 
            if($ret != 0){ 
              return $ret; 
            } 
          } else { 
            $rrd = "$rrddir/$attr"."_$name.rrd"; 
            push_data_to_rrd($rrd, $metrix{$attr}{$nnlist}{$name}, \ 
             'null', $step, $process_time, 'GAUGE'); 
            if($ret != 0){ 
              return $ret; 
            } 
          } 
        } 
      } 
    } 
  } 
  return 0;
    

 

}


清单 10. 收集脚本之解析 lsrsrc-api 输出

     #--------------------------------------------------------------- 
 # 函数名:parse_lsrsrc_output 
 # 描  述 : 解析 lsrsrc-api 的输出结果保存到 %metrix 
 # 输  入 : 
 #       $pattr  RSCT 资源类属性
 #       $output lsrsrc-api 的输出结果
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub parse_lsrsrc_output 
 { 
  my ($pattr, $output) = @_; 
  my $nnlist = undef; 
  my $name = undef; 
  my $line = undef; 
  my $attr = undef; 
  my @value = (); 
  my $i = undef; 

  foreach $line (@$output){ 
    @value = split /::/, $line; 
    $name = $value[0]; 
    $name =~ s/[^A-Za-z0-9]+/'.'/; 
    $value[1] =~ /{(\w+)}/; 
    $nnlist = $1; 
    $i = 2; 
    foreach $attr (@$pattr){ 
      $metrix{$attr}{$nnlist}{$name} = $value[$i]; 
      $metrix{$attr}{$nnlist}{summary} += $value[$i]; 
      $metrix{$attr}{$nnlist}{number} += 1; 
      $i++; 
    } 
  } 
  return 0; 
 }
    

 

主函数调用 lsrsrc-api 取得监控数据,解析保存到 /var/rrd 下的数据库文件中。


清单 11. 收集脚本之主函数

     $cmd = 
  "CT_MANAGEMENT_SCOPE=3 lsrsrc-api -i -s ${rsrc}::::Name::NodeNameList::$attr"; 
 @output = `$cmd`; 
 if($? != 0){ 
  exit ($? >> 8); 
 } 
 &parse_lsrsrc_output(\@attrs, \@output); 
 foreach $attr (keys %metrix){ 
  foreach $nnlist (keys %{$metrix{$attr}}){ 
    if(($nnlist ne 'summary') && ($nnlist ne 'number')){ 
      $metrix{$attr}{summary} += $metrix{$attr}{$nnlist}{summary}; 
      $metrix{$attr}{number} += $metrix{$attr}{$nnlist}{number}; 
    } 
  } 
 } 

 $step = $minute * 60; 
 &updaterrd($step); 

 1;
    

 

最后,使用 crontab 周期性的执行该脚本监控集群。清单 12 是一个监控集群收发速率的示例。


清单 12. 编辑 crontab

     #crontab -e 
 * * * * * getmetrics IBM.EthernetDevice RecByteRate,XmitByteRate 1
    

 

当脚本第一次执行后,你就可以在 /var/rrd 目录下看到下列文件:

     |--summary 
  |--RecByteRate.rrd 
  |--XmitByteRate.rrd 
 |--nodename 
  |--RecByteRate.rrd 
  |--RecByteRate_ent0.rrd 
  |--RecByteRate_ent1.rrd 
  |--XmitByteRate .rrd 
  |--XmitByteRate_ent0.rrd 
  |--XmitByteRate_ent1.rrd
    

 

显示数据

监控的最终目的是为了方便管理员查看集群的状态。RRDtool 提供了文本和图形两种方式显示数据库内容。清单 13 示例的脚本使用 rrdtool fetch 命令用文本方式显示监控数据的方法。


清单 13. 显示监控数据

     #!/usr/bin/env perl 
 #--------------------------------------------------------------- 
 # 函数名:RRD_fetch 
 # 描  述 : 从数据库中取数据
 # 输  入 : 
 #       $rrd    文件名
 #       $start_time 开始时间
 #       $end_time 		结束时间
 # 返回值 : 
 #       0 成功 !0 失败
 #--------------------------------------------------------------- 
 sub RRD_fetch 
 { 
  my ($rrd, $start_time, $end_time) = @_; 
  my $output = []; 
  my $resolution = undef; 
  my $cmd = undef; 
  $cmd = "fetch $rrd AVERAGE -s $start_time -e $end_time"; 
  @$output = `rrdtool $cmd`; 
  return $output; 
 } 
 #--------------------------------------------------------------- 
 # 主函数
 #--------------------------------------------------------------- 
 ($node, $attrs, $time) = @ARGV; 
 $output = undef; 
 @files = (); 
 $file = undef; 
 @attrlist = split /,/,$attrs; 
 $attr = undef; 
 $line = undef; 
 @namelist = (); 
 @timelist = (); 
 %hash = {}; 
 $name = undef; 
 $timestamp = undef; 
 $sum = undef; 
 $num = undef; 
 $end_time = `date +%s`; 
 if($time =~ /(\d+)-(\d+)/){ 
  $start_time = $end_time - $1 * 60; 
  $end_time = $end_time - $2 * 60; 
 }else { 
  $start_time = $end_time - $time * 60; 
 } 
 $rrddir="/var/rrd/$node"; 

 foreach $attr (@attrlist) { 
  @namelist = (); 
  @timelist = (); 
  %hash = {}; 
  $output = `ls -A $rrddir/$attr*`; 
  @files = split /\n/, $output; 
  foreach $file (@files) { 
    if($file =~ /$attr\.rrd$/){ 
      $name = "$attr"; 
    } elsif ($file =~ /${attr}_(\S+)\.rrd$/) { 
      $name = $1; 
    } 
    push @namelist, $name; 
    $output = RRD_fetch($file, $start_time, $end_time); 
    $line = pop(@$output); 
    if($line =~ /ERROR/){ 
      next; 
    } else { 
      push @$output, $line; 
    } 
    foreach $line (@$output){ 
      if($line =~ /[NaNQ|nan]/){ 
        next; 
      } elsif ($line =~ /^(\d+): (\S+)/){ 
        $timestamp = $1; 
        $sum = $2; 
        if(! grep {/$timestamp/} @timelist){ 
          push @timelist, $timestamp; 
        } 
        $hash{$name}{$timestamp} = sprintf "%.4f", $sum; 
      } 
    } 
  } 
  $line = join "\t", (@namelist); 
  $line = "                          ".$line; 
  print "$line\n"; 
  @timelist = sort @timelist; 
  foreach $timestamp (@timelist){ 
    $line = localtime($timestamp)."  "; 
    foreach $name (@namelist){ 
      if(exists $hash{$name}{$timestamp}){ 
        $line =$line."$hash{$name}{$timestamp}\t"; 
      } else { 
        $line = $line."-\t"; 
      } 
    } 
    print "$line\n"; 
  } 
 }
    

 

层次的集群监控

如果集群中有大量的节点,为了减少单个管理节点的负担,可以将集群划分为多个子管理域,使用多个管理节点进行层次的集群管理。如图 4 所示,Master 是主管理节点,它监控整个集群,管理员的所有的监控操作都在 Master 上进行。Sub-Master 监控 Node1 与 Node2. Sub-Master 定期收集处理 Node1 与 Node2 的监控数据并被保存在本地数据库。Master 上不直接监控 Node1,Node2,也不保存 Node1 和 Node2 的具体监控数据。当管理员需要查询 Node1 或 Node2 的监控数据时,Master 通过 Sub-Master 上的 RRD server 服务获取 Node1 或是 Node2 的信息。另外,Master 定期通过 Sub-Master 上的 RRD server 从 Sub-Master 获取该子管理域保存于 summary 目录中的数据。并将其与 Node3,Node4 的信息进行合并,形成整个集群的监控 summary 信息保存在本地 RRD 数据库中。


图 4. 层次的集群监控示意图
图 4. 层次的集群监控示意图  

RRD server 是 RRDtool 提供的一个支持远程控制的功能。RRD server 不仅可以接受 rrdtool 的所有命令还支持 cd/mkdir/pwd/ls/quit 命令。具体操作步骤如下:

  1. 选择一个没有在使用的 tcp 端口,在 /etc/services 文件中添加 rrdsrv 服务;
           rrdsrv      13900/tcp
          

  2. 在 inetd.conf 中加入:
           rrdsrv stream tcp nowait root $rrddir/rrdtool rrdtool - /var/rrd
          

    需要先创建 /var/rrd 目录,读者可以根据需要自定义路径。

  3. 重新初始化 inetd 服务。
  4. 服务启动后,就可以通过 socket, 或者是 netcat 工具远程操作 /var/rrd 目录下 RRD 文件。你也可以通过 telnet localhost 13900 做一个简单的交户测试。

通过 RRD server,用户可以像操作本地数据库一样操作在 Sub-Master 上的数据库信息,实现层次的集群监控。

小结

本文介绍了利用 RSCT 和 RRDtool 实现集群自动化监控的一种方法。文中提供了一个完整的例子来说明这种方法,读者可以直接使用它,也可以根据自己的需要进行更改,实现自己的集群监控软件

关于作者

郑爱蓉, IBM 中国软件开发中心 HPC 部门的软件工程师,主要从事集群系统管理软件的相关研发工作


 

如下红色区域有误,请重新填写。

    你的回复:

    请 登录 后回复。还没有在Zeuux哲思注册吗?现在 注册 !

    Zeuux © 2024

    京ICP备05028076号