CMDB是什么?中文名叫配置管理数据库,以下解释摘自wiki百科:

配置管理数据库(Configuration management database)是与IT系统所有组件相关的信息库。它包含IT基础架构配置项的详细信息。

CMDB记录配置项,以及它们的重要属性和之间的关系。


简单地说,CMDB是记录IT系统中所有类别及其具体属性,以及它们之间的关联关系的。如果你只有那么几台设备,那手指就能数得过来了,如果有几十台设备,那几张表也够了,而如果成百上千台,甚至种类就多达几十种呢?必须得有个资产管理系统才行了吧,要不然你怎么知道这台设备的前世今生,以及它出了问题应该找谁(哪家供应商)。


而资产管理系统就够了吗?设备要用起来,就不能是孤立的,多种设备,以及设备上的系统,软件必须是连接起来才有意义,但资产系统显然管不了这种关联关系。


而如果使用传统的关系数据库,很简单的想法是一张表对应一种设备类型,表的列就是这种设备的属性,那问题来了,我加了一种设备怎么办?我需要在数据库里新建一张表,这个我还能做,因为我是个系统管理员嘛,这么简单的DBA入门工作还是能做的,但程序怎么办?它能认出这新加的这张表吗?我可不懂java, javascript...于是我只好付钱给我的软件供应商,让他再帮我加这一类设备。而作为软件提供商,就会面临各种各样稀奇古怪的设备类型,那软件开发人员能把这些类型都内置在里面吗?显然不可能。再一种情况,以前我的资产管理系统对于PC服务器,只需要记录它的型号,配置就行了,突然老板跟我说今年要作固定资产登记,每台机器加个标签,上面是固定资产编号。我又得叫软件提供商提供新版本了...


简单是说,传统的资产管理系统,设备类别不可增,设备属性也不能增。或者,都可增,但开发代价太大了,不是博主这种屌丝公司的小作坊开发团队能hold得住的,其性能及可维护性也不好。于是博主(从此,我就是我了,不再是那个甲方清闲的系统管理员了)的作了点调研,结合自己判断,用关系数据库(rDBMS)来做CMDB是死路一条。而以Neo4j为代表的图形数据库为轻量级的CMDB快速开发提供了一线生天。

有兴趣可以读读这位国外的仁兄写的博文:

http://zkybase.org/blog/2011/12/06/why-im-pretty-excited-about-using-neo4j-for-a-cmdb-backend/

为什么他为找到Neo4j来做CMDB数据存储端而兴奋?我看了也很兴奋!Neo4j太切合CMDB的需求了!它是NoSQL的,节点采用Key-Value的方式保存数据,这一点创造性解决设备类别(CMDB里叫CI类)的扩展性,可以随意加一种CI类,也可以随意在CI类里加一个属性,而且它不会影响原有数据,也无所谓空与非空。另外,Neo4j原来主要用于社交网络的,就是小明认识小红,而小红是她妈妈的女儿,她妈妈是她爸爸的妻子,同时也是老板的秘书,那小红与老板有什么关系就可以查出来了。人与人之间的关系太复杂,而设备与设备之间的关系(CI项与CI项之间的关系)也类似,应用系统依赖于数据库节点和中间件节点,而数据库节点和中间件节点都依赖于操作系统,操作系统运行在虚拟机上,虚拟机又依赖于物理服务器,物理服务器又与存储相连... 那么,应用系统与存储其实也是有关联的。这样的连接关系,如果用关系数据库来表示,应该怎么设计呢?我是没这个天赋去想了。


很快又悲观地发现,用图形数据库来做CMDB的就那位Willie Wheeler一个...但我还是打算尝试一下,特别是作为一个能写代码的产品经理。


不得不说python是个很好的胶水语言,非常适合个人或者小作坊,以下是我做的一些测试以及结果:

from py2neo import node,rel
from py2neo import neo4j
g=neo4j.GraphDatabaseService('http://localhost:7474/db/data')
import cmdb.py2neo_function as neofunction
# data initialize
#storage
a=g.create(
node(ci_class='storage',city='Shanghai',district='PuDong',name='storagea',rack=1,capacity=300),
node(ci_class='storage',city='Shanghai',district='PuDong',name='storageb',rack=1,capacity=300),
node(ci_class='storage',city='Shanghai',district='PuDong',name='storagec',rack=1,capacity=300),
node(ci_class='storage',city='Shanghai',district='PuDong',name='storaged',rack=1,capacity=300),
node(ci_class='storage',city='Shanghai',district='PuDong',name='storagee',rack=1,capacity=300))
#small_server
b=g.create(node(ci_class='small_server',city='Shanghai',district='PuDong',name='small_servera',cpu_count=16,mem_GB=64),
node(ci_class='small_server',city='Shanghai',district='PuDong',name='small_serverb',cpu_count=16,mem_GB=64),
node(ci_class='small_server',city='Shanghai',district='PuDong',name='small_serverc',cpu_count=16,mem_GB=64),
node(ci_class='small_server',city='Shanghai',district='PuDong',name='small_serverd',cpu_count=16,mem_GB=64),
node(ci_class='small_server',city='Shanghai',district='PuDong',name='small_servere',cpu_count=16,mem_GB=64))
#lpar
c=g.create(node(ci_class='lpar',city='Shanghai',district='PuDong',name='lpara',cpu_count=1,mem_GB=2),
node(ci_class='lpar',city='Shanghai',district='PuDong',name='lparb',cpu_count=1,mem_GB=2),
node(ci_class='lpar',city='Shanghai',district='PuDong',name='lparc',cpu_count=1,mem_GB=2),
node(ci_class='lpar',city='Shanghai',district='PuDong',name='lpard',cpu_count=1,mem_GB=2),
node(ci_class='lpar',city='Shanghai',district='PuDong',name='lpare',cpu_count=1,mem_GB=2))
#db_instance
d=g.create(node(ci_class='db_instance',name='dbi_a',type='db2',version='9.1'),
node(ci_class='db_instance',name='dbi_b',type='db2',version='9.1'),
node(ci_class='db_instance',name='dbi_c',type='db2',version='9.1'),
node(ci_class='db_instance',name='dbi_d',type='db2',version='9.1'),
node(ci_class='db_instance',name='dbi_e',type='db2',version='9.1'))
#was_node
d=g.create(node(ci_class='was_node',name='was_nodea',version='6.1'),
node(ci_class='was_node',name='was_nodeb',version='6.1'),
node(ci_class='was_node',name='was_nodec',version='6.1'),
node(ci_class='was_node',name='was_noded',version='6.1'),
node(ci_class='was_node',name='was_nodee',version='6.1'))
#application
e=g.create(node(ci_class='was_node',name='app_a'),
node(ci_class='was_node',name='app_b'),
node(ci_class='was_node',name='app_c'),
node(ci_class='was_node',name='app_d'),
node(ci_class='was_node',name='app_e'))
#get the nodes
#get all storeage nodes
storage_list=neofunction.SearchNodes(g,'ci_class','storage')
#get all small_server nodes
small_server_list=neofunction.SearchNodes(g,'ci_class','small_server')
#get all lpar nodes
lpar_list=neofunction.SearchNodes(g,'ci_class','small_server')
#get all was nodes
was_list=neofunction.SearchNodes(g,'ci_class','was_node')
#get all db_instance nodes
dbi_list=neofunction.SearchNodes(g,'ci_class','db_instance')
#get all app nodes
app_list=neofunction.SearchNodes(g,'ci_class','application')
#create relations
#relationship type:  depend_on, running_on, connect_to
g.create((small_server_list[0],'depend_on',storage_list[0]))
small_server_a=neofunction.SearchNodes(g,'name','small_servera')[0]
for i in rage(5):
    g.create((lpar_list[i],'depend_on',small_server_list[i]))
    g.create((dbi_list[i],'running_on',lpar_list[i]))
    g.create((was_list[i],'running_on',lpar_list[i]))
    g.create((app_list[i],'depend_on',dbi_list[i]))
    g.create((app_list[i],'depend_on',was_list[i]))
#search all lpars which depends on the specific small server
#get one small_server
n1=Get_node_by_id(2304)
lpar_list=Get_relationship_nodes(g,n1,'depend_on')
for lpar in lpar_list:
    print lpar[name] + ' is depend on '+n1['name']


以上代码,我对storage,小型机, lpar,数据库节点,中间件节点,应用这几个CI类都创建了5个CI项,然后尝试建立了一些关联关系。得到的图如下:

使用图形数据库,颠覆性解决CMDB实现难点(一)_NoSQL


如果我要查找一个应用节(2332)点到那个存储服务器节点(2299)的所有关系,那么通过cypher查询,我可以得到这样的结果:

使用图形数据库,颠覆性解决CMDB实现难点(一)_NoSQL_02


如何?这是neo4j自带的网页工具展示图形,CMDB的展示当然要做得好看点,脑补一下,把圆圈里的数字根据它的某个属性换成一种设备的图片,是不是得到一个简单的从应用到存储服务器的影响关系分析图了?