CMDB的概念
"""
CMDB: Configure Manage DataBase 中文:配置管理数据库。主要的作用是:收集服务器的基础信息(包括:服务器的主机名,ip,操作系统版本,磁盘,CPU等信息),将来提供给子系统(代码发布,工单系统等)数据
"""
CMDB的架构图方案
- agent方案
"""
将待采集的服务器看成一个agent,然后再服务器上使用python的subprocess模块执行linux相关的命令,然后分析得到的结果,将分析得到的结果通过requests模块发送给API,API获取到数据之后,进行二次比对数据,最后将比对的结果存入到数据库中,最后django起一个webserver从数据库中将数据获取出来,供用户查看
"""
- ssh方案
"""
在中控机服务器上安装一个模块叫paramiko模块,通过这个模块登录到带采集的服务器上,然后执行相关的linux命令,最后返回执行的结果,将分析得到的结果通过requests模块发送给API,API获取到数据之后,进行二次比对数据,最后将比对的结果存入到数据库中,最后django起一个webserver从数据库中将数据获取出来,供用户查看
"""
- 方案对比
"""
第一套方案的优点是: 不需要额外的增加中控机。 缺点:每新增一台服务器,就需要额外部署agent脚本。使用场景是:服务器多的情况 (1000台以上)
第二套方案的优点是:不需要额外的部署脚本。缺点:速度比较慢。使用场景是:服务器少 (1000台往下)
"""
采集客户端目录的结构设计
"""
- bin : 可执行文件 start.py / run.py
- conf: 配置文件目录 config.py
- lib : 第三方文件目录
- files: 临时测试的数据(线下)
- src /core : 核心的源代码文件目录
//- log: 记录日志 放在 /var/logs/ 下面
- test: 测试文件目录
"""
实现自定义配置以及全局配置
from conf import config # 导入自定义的配置
from . import global_settings # 导入全局配置
class MySettings():
# 集成用户自定义的配置和默认的全局配置
def __init__(self):
# 集成全局的配置
for k in dir(global_settings): # dir的作用就是将这个对象中的所有属性显示出来,以列表的形式存储
if k.isupper():
v = getattr(global_settings, k)
setattr(self, k, v)
# 集成用户自定义的配置
for k in dir(config):
if k.isupper():
v = getattr(config, k)
setattr(self, k, v)
settings = MySettings()
高内聚低耦合思想
目标:实现两套方案切换采集
第一个版本:直接在启动文件中判断
if settings.MODE == 'agent':
import subprocess
res = subprocess.getoutput('hostname')
else:
import paramiko
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='192.168.79.131', port=22, username='root', password='root')
# 执行命令
stdin, stdout, stderr = ssh.exec_command('hostname')
# 获取命令结果
result = stdout.read()
print(result)
# 关闭连接
ssh.close()
如果上述代码这样写的话,会带来如下问题:
- 耦合度太高
- 结构混乱,导致查找问题的时候不方便
- 业务逻辑代码不能写在启动文件中
那么如何解决上述存在的问题? 答:将每一个功能都封装成一个文件,比如说采集磁盘的信息,可以搞一个disk.py文件,这个文件中所有的代码都是要和采集磁盘相关的,不能有其他 的相关代码。以此类推,采集CPU的信息,也要搞一个cpu.py文件. 这种思想就是高内聚低耦合思想
第二种版本
from src.plugins.basic import Basic
from src.plugins.disk import Disk
#from src.plugins.memory import Memory
if __name__ == '__main__':
Basic().process()
Disk().process()
#Memory().process()
但是上述做法不是特别的完美,解决的方案是:将这些采集的插件写到配置文件中统一管理,参考django的中间件, 可插拔式的采集 核心的采集方法:
第三种版本
### 这是用户自定义的配置
# 将所有插件写到配置文件,不想用就注释掉即可
PLUGINS_DICT = {
'basic': 'src.plugins.basic.Basic',
'board': 'src.plugins.board.Board',
'cpu': 'src.plugins.cpu.Cpu',
'disk': 'src.plugins.disk.Disk',
'memory': 'src.plugins.memory.Memory',
}
from lib.config.conf import settings
import importlib
# 管理插件
class PluginsManager():
def __init__(self):
self.plugins_dict = settings.PLUGINS_DICT
self.debug = settings.DEBUG
# 管理配置文件的插件,采集数据, 从配置文件中读取配置
# 循环导入模块,实例化类,执行插件类对应的采集方法
def execute(self):
### 1、先从配置文件中读取配置
response = {}
for k, v in self.plugins_dict.items():
'''
k:basic
v:src.plugins.basic.Basic
'''
# 循环导入模块
'''
moudle_path:'src.plugins.basic'
class_name: 'Basic'
'''
moudle_path, class_name = v.rsplit('.', 1)
### 2 通过字符串路径去导入对应的模块,得到的是对象
m = importlib.import_module(moudle_path)
### 3导入类,实例化类,执行方法
cls = getattr(m, class_name)
res = cls().process(self.command_func, self.debug)
response[k] = res
return response
def command_func(self, cmd): # 将函数名当成内存地址传递给process,解决插件代码的冗余
if settings.MODE == 'agent':
import subprocess
res = subprocess.getoutput(cmd)
return res
else:
import paramiko
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='10.0.0.200', port=22, username='root', password='root')
# 执行命令
stdin, stdout, stderr = ssh.exec_command(cmd)
# 获取命令结果
result = stdout.read()
# print(result)
# 关闭连接
ssh.close()
return result