1 概念
我的理解,运维的核心是时时维护好目标机器(服务器、网络设备、系统软件等)使之与业务需要相适应,而自动化运维就是将运维的一系列步骤自动化。在所有步骤过程中,维护目标机一般都通过挂载在机器上的agent来实现,它是目标机上的一个口子,一个管道,让目标机完整的融入运维整个生态里。
既然我们使用Agent来维护目标机,那这些维护工作,即Agent的主要工作有哪些呢?可以想到的有:
- 接受下发资源:比如下发脚本,配置文件,应用程序,库。
- 执行本机任务:定时,不定时的执行一些任务,比如收集机器软硬件信息等,启停服务,日志分析等。
- 网络服务:比如扫描与此目标机相连的网络设备,推送数据,提供类似HTTP服务供管理工具pull数据。
- 其它自定义服务。
如果做一个适应不同运维环境的Agent,在此称之为Uniagent(Unique agent),它必须提供除上面agent功能之外的一些基础功能,方便自动化运维:
- 跨平台:支持主流的操作系统如Windows,各种Linux,Unix等。
- 核心服务+扩展支持:Uniagent是一个容器,里面内置一些常见的服务,比如执行脚本(perl,python,shell,bat),轮询SNMP网络设备,分析日志等。这里的服务是以模块(module)的形式存在的。因为环境的不可预见,Uniagent必须提供SDK,方便具体环境的人开发对应的module,然后载入Uniagent。
- 管控中心:Agent必须接受某一个管理,这里称之为Agent Manager,用来收集Agent状态,并反映到一个名为Manager Console的Web应用上来。方便运维人员在Console上下发各种指令,比如安装,卸载服务(模块),启停,安装软件,更改配置等。
这里我特别强调Uniagent,因为在大一点的公司里面,部门,产品项目众多,运维监控人员也众多,还不统一,在同一目标机上安装很多agent的形式大量存在,弊端很多。Uniagent的一个很重要的目标是在被运维的目标机上有且仅有一个Agent。
2 场景
下面以一个监控的场景来描述Uniagent的生命周期。此场景定义了一个从无到有的目标机被监控内存使用状况。
整个工作过程如下描述:
- 申请一台目标机,安装某个应用,并监控之。这个目标机在交给开发者部署应用的时候是是一台“ready to use“的机器,也就是说系统,IP,配置等ok且Uniagent已经预装,同时此机器的基本硬件信息,系统+版本已经在机器信息查询系统(以后简称MQS)里面备案。
- 目标机启动,Uniagent会向Master查询注册机(也就是Agent Manager,后面简称AM),这里Master是一个域名唯一的服务器(或服务器列表,VIP/LVS后)。Master根据Uniagent汇报上来的信息,主要是IP信息,然后根据某些策略(比如IDC机房策略),分配给其一个Agent Manager的IP。Uniagent向AM注册成功。至于Master是怎么知道有哪些Agent Manager和策略,应该有另一个系统来维护。
- Uniagent向AM发送心跳和其它信息,AM将此信息定时刷新到数据库(DB)中。
- 运维人员通过Manager Console(后简称MC)查询数据库,可以看到某个AM下维护的Uniagent列表,每个目标机的状态,系统信息, Agent Module列表等。因为运维人员要监测内存使用情况,在假定执行脚本服务(正常情况下是内置,这里为使上下文简单且完整,这个服务或Module我们暂且称之为EXESCRIPT)没有被安装的情况下,向AM下达安装EXESCRIPT模块的安装命令。
- AM在基于Uniagent送上来的目标机系统信息,结合MQS系统,在DB中找到合适的版本,对Uniagent进行EXESCRIPT模块下发。举个例子,假设目标机是Windows,发的可能是exescript.dll;如果是Linux,发的就是exescript.so。
- Uniagent向AM回报更新过后的模块信息,注意这时EXESCRIPT的子进程已经启动,已经可以提供服务了。运维人员会看到模块下发成功,他手上有一个perl脚本meminfo.pl存储在资源中心(后称RC),且通过MC能看到。他决定将这个meminfo.pl文件通过AM下发到其下所有目标机。
- Uniagent收到资源meminfo.pl的下发请求,并交给EXESCRIPT存放在EXESCRIPT模块知晓的某一文件目录。每个资源在都有一个唯一的相对地址,比如 /sys/mem/x86/meminfo.pl,结合EXESCRIPT的脚本存放目录才是一个可定位的绝对路径。
- AM被监控系统SPY下发一个监控任务,即定时的扫描脚本meminfo.pl,此任务被下发到Uniagent,开启一个调度器定时的执行脚本,所产生的结果发送到Monitor。怎么知道发送到那个Monitor? 见 第 9 步。
- Uniagent往Master查询Monitor(这个跟监控有关的数据消费者是monitor,比如跟portal有关的可能就是collector,Master会根据模块信息决定有哪些数据消费者),Uniagent连接上Monitor A和Monitor B,持续的将数据发到指定位置,这个数据传送协议,为统一,很可能采用HTTP POST。
当然,上面描述的只是安装,采集行为。对于完整的系统来说,还有任务的停止,模块的卸载,资源的删除等。上图所有的元素中,只有监控系统SPY和Monitor属于应用方,其它都需要开发或者提供(比如RC和MQS都必须提供),才能让Uniagent这个自动化过程运行起来。
3 实现
Uniagent虽然体积不能太大,但是提供容器功能,需要使用有较好抽象能力的语言,我建议采用C++来实现,因为:
- 语言特性:C++本身是标准,跨平台,支持高级数据结构和模块化编程,错误异常处理。
- 众多的库:比如STL,BOOST,Protobuf,对于Uniagent日常的系统任务,操作系统的SDK提供丰富的库让实现更简单。
- 跨语言:所有可实现多种语言接口的组件库都基于C++实现,比如COM,XPCOM,Corba等,这为以后Uniagent实现跨语言编程提供了基础支持。
能够支持C++的构建系统很多,比如automake,scons,cmake, nmake(MS build,即visual studio的那个),其中cmake具有良好的“原生”特性,即适配系统本身的构建系统,比如在windows上生成visual studio solution文件。还有cmake也经过大型项目如KDE4的实践。在Linux也有KDevelop这个cmake加C++的大杀器开发工具。
Uniagent的动态模块机制基于操作系统的DL(Dynamic Loading)即动态库加载,它在类Unix和Windows上都有实现,由于两类操作系统的二进制格式不一样,实现机制不尽相同,但Uniagent需要的功能已经足够。
因为要提供SDK,所以Uniagent的容器开发也得是库+壳的模式,类似于Apache和APR的关系,初期两个必须分开,这样库就可以包含在SDK中供其它模块开发者使用。
参考实现: