CMDB


IT運維的分類

​ IT運維,指的是對已經搭建好的網絡,軟件,硬件進行維護。運維領域也是細分的,有硬件運維和軟件運維。

​ 硬件運維主要包括對基礎設施的運維,比如機房的設備,主機的硬盤,內存這些物理設備的維護。

​ 軟件運維主要包括系統運維和應用運維,系統運維主要包括對OS,數據庫,中間件的監控和維護,這些系統介於設備和應用之間,應用運維主要是對線上業務系統的運維。

傳統運維痛點

日程工作繁瑣

​ 日常運維工作是比較繁瑣的,研發同學會經常需要到服務器上查日志,重啟應用,或者是說今天上線某個產品,需要部署下環境。這些瑣事是傳統運維的大部分工作。

應用運行環境不統一

​ 在部署某應用后,應用不能訪問,就會聽到開發人員說,在我的環境運行很好的,怎么部署到測試環境后,就不能用了,因為各類環境的類庫不統一
還有一種極端情況,運維人員習慣不同,可能憑自己的習慣來安裝部署軟件,每種服務器上運行軟件的目錄不統一。

運維及部署效率低下

​ 想想運維人員需要登陸到服務器上執行命令,部署程序,不僅效率很低,並且非常容易出現人為的錯誤,一旦手工出錯,追溯問題將會非常不容易。

無用報警信息過多

​ 經常會收到很多報警信息,多數是無用的報警信息,造成運維人員經常屏蔽報警信。
​ 另外如果應用的訪問速度出了問題,總是需要從系統、網絡、應用、數據庫等一步步的查找原因。

資產管理和應用管理混亂

​ 資產管理,服務管理經常記錄在excel、文本文件或者wiki中,不便於管理,老員工因為比較熟,不注重這些文檔的維護,只有靠每次有新員工入職時,資產才能夠更正一次。

自動化運維平台的特性

​ 針對傳統運維的痛點,我們可以知道自動化運維需要支持哪些功能

運維自動化最重要的就是標准化一切

  • OS的選擇統一化,同一個項目使用同樣的OS系統部署其所需要的各類軟件
  • 軟件安裝標准化,例如JAVA虛擬機,php,nginx,mysql等各類應用需要的軟件版本,安裝目錄,數據存放目錄,日志存放目錄等
  • 應用包目錄統一標准化,及應用命名標准化
  • 啟動腳本統一目錄和名字,需要變化的部分通過參數傳遞
  • 配置文件標准化,需要變化的部分通過參數傳遞
  • 日志輸出,日志目錄,日志名字標准化
  • 應用生成的數據要實現統一的目錄存放
  • 主機/虛擬機命名標准化,虛擬機管理使用標准化模板
  • 使用docker比較容易實現軟件運行環境的標准化

資產管理系統(CMDB)

CMDB是所有運維工具的數據基礎

CMDB包含的功能

  1. 用戶管理,記錄測試,開發,運維人員的用戶表
  2. 業務線管理,需要記錄業務的詳情
  3. 項目管理,指定此項目用屬於哪條業務線,以及項目詳情
  4. 應用管理,指定此應用的開發人員,屬於哪個項目,和代碼地址,部署目錄,部署集群,依賴的應用,軟件等信息
  5. 主機管理,包括雲主機,物理機,主機屬於哪個集群,運行着哪些軟件,主機管理員,連接哪些網絡設備,雲主機的資源池,存儲等相關信息
  6. 主機變更管理,主機的一些信息變更,例如管理員,所屬集群等信息更改,連接的網絡變更等
  7. 網絡設備管理,主要記錄網絡設備的詳細信息,及網絡設備連接的上級設備
  8. IP管理,IP屬於哪個主機,哪個網段, 是否被占用等

CMDB實現的四種方式

Agent實現方式

​ Agent方式,可以將服務器上面的Agent程序作定時任務,定時將資產信息提交到指定API錄入數據庫。

Agent

​ 其本質上就是在各個服務器上執行subprocess.getoutput()命令,然后將每台機器上執行的結果,返回給主機API,然后主機API收到這些數據之后,放入到數據庫中,最終通過web界面展現給用戶。

優點:速度快

缺點:需要為每台服務器部署一個Agent程序

場景:服務器多的時候

ssh實現方式(基於Paramiko模塊)

​ 中控機通過Paramiko(py模塊)登錄到各個服務器上,然后執行命令的方式去獲取各個服務器上的信息。

ssh

優點:無Agent

缺點:速度慢

場景:服務器少的時候

如果在服務器較少的情況下,可應用此方法

import paramiko
   
# 創建SSH對象
ssh = paramiko.SSHClient()
# 允許連接不在know_hosts文件中的主機
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 連接服務器
ssh.connect(hostname='c1.salt.com', port=22, username='root', password='123')
   
# 執行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 獲取命令結果
result = stdout.read()
   
# 關閉連接
ssh.close()

saltstack方式

saltstack

​ 此方案本質上和第二種方案大致是差不多的流程,中控機發送命令給服務器執行。服務器將結果放入另一個隊列中,中控機獲取將服務信息發送到API進而錄入數據庫。

優點:快,開發成本低

缺點:依賴於第三方工具

常見的面試問題

你們公司為什么要做CMDB?

a.公司要做自動化運維相關的系統,CMDB是必做的一個項目

b.之前采取的是Excel表格進行管理數據,因此修改數據的話會變的很亂

2.CMDB的架構是什么?

agent、ssh類、salt-stack

你在這個項目中負責哪一塊?

數據采集

​ a.參考了Django的配置文件的設置(整合了自定義的配置文件以及默認的配置文件)

​ b.參考了Django的中間件的寫法和用法,完成了一套可插拔式的采集插件

數據分析

數據展示(圖表展示)

開發的過程中遇到了哪些問題?怎么解決的?

a.linux的命令不熟悉 -->百度或者問運維

b.字符串導模塊 -->importlib

c.溝通問題(產品經理)

d.唯一標識問題

你們這個項目做了多長時間?多少個人?多少服務器?

1、 5~6個月

​ 1個月:調研 排期 溝通 設計方案 選擇方案

​ 1~2個月:先上一個1.0版本(不要求代碼寫的多好,要求寫的快,最主要的功能都要有)

​ 2~3個月:不斷的迭代項目(包括需求增加,代碼的迭代,前端的展示剛開始用的原生的js第二次升級迭代使用Vue,自己寫接口(drf))

2、一般后端2-3個人 前端1個人

3、創業公司:幾十台

代碼部分及問題

post的數據獲取

​ 如果header:content-type:application/x-www-form-urlencoded

​ request.body才會將數據封裝給request.POST方法

a.requests.post(settings.API_URL,data=json.dumps(res))

b.requests.post(settings.API_URL,json=res)

核心代碼:

hostnameList = self.get_hostnames()
from concurrent.futures import ThreadPoolExecuyor
p = ThreadPoolExecuyor(10)
for hostname in hostnameList:
    p.submit(self.task,hostname)

唯一標識的問題

現在的做法:

​ 取SN作為唯一標識,來 獲取老的數據

改進的做法:

​ 1.業務邏輯解決:如果公司不采集虛擬機的信息,直接用sn

​ 2.用hostname做唯一標識

agent需要遵守的步驟:

a.裝機完成之后,需要給每一台服務器分配一個唯一的主機名
b.將分配好的主機名錄入到web管理系統中
#因此,在沒有收集資產的情況下,數據庫中已經錄好了主機的主機名
c.將采集的客戶端代碼放到服務器的某一個目錄下面,然后開始定時執行代碼腳本
b.開始第一次采集
#將主機名(c1.com)記錄到一個文件中
e.將服務器分配給開發用
f.12點開始收集資產信息,11.59,修改了主機名,c1.com-->c2000.com,匯報數據的時候,永遠以第一次采集的文件中的主機名為標准

API的認證

第一種方式:

​ 服務器

server_token = "bdsjabfjsldabjfdsnbajfndjakfnjdsanfjd"
client_token = requests.META.get("HTTP_TOKEN")
if server_token != client_token:
    return HttpResponse("非法用戶")

​ 客戶端

import requests
client_token = "bdsjabfjsldabjfdsnbajfndjakfnjdsanfjd"
res = requests.get('http://127.0.0.1:8000/api/',headers={"Token":client_token})

第二種方式

加密和加鹽

if server_time - client_time > 5:
            return HttpResponse('你超時了')
        tmp = "%s|%s" % (server_token, client_time)
        m = hashlib.md5()
        m.update(bytes(tmp, encoding='utf-8'))
        server_md5 = m.hexdigest()
        if client_md5 != server_md5:
            return HttpResponse('數據被修改了')
        if client_md5_token in key_record:
            return HttpResponse('已經被訪問過了')
        else:
            key_record[client_md5_token] = client_time + 10
        return HttpResponse('非常重要的信息')

服務端目錄結構設計

api:接受采集到的數據

backend:后台數據管理展示界面

repository:專門管理db數據表的

數據表的設計

from django.db import models


# Create your models here.
class UserProfile(models.Model):
    name = models.CharField(verbose_name=u'姓名', max_length=32)
    email = models.EmailField(verbose_name=u'郵箱')
    phone = models.CharField(verbose_name=u'座機', max_length=32)
    mobile = models.CharField(verbose_name=u'手機', max_length=32)
    password = models.CharField(verbose_name=u'密碼', max_length=64)

    class Meta:
        verbose_name = '用戶表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class UserGroup(models.Model):
    name = models.CharField(max_length=32, unique=True)
    users = models.ManyToManyField(to='UserProfile')

    class Meta:
        verbose_name = '用戶組表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class BusinessUnit(models.Model):
    name = models.CharField(verbose_name=u'業務線', max_length=64, unique=True)
    contact = models.ForeignKey(to='UserGroup', verbose_name=u'業務聯系人', related_name='c')
    manager = models.ForeignKey(to='UserGroup', verbose_name=u'系統管理員', related_name='m')

    class Meta:
        verbose_name = '業務線表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class IDC(models.Model):
    name = models.CharField(verbose_name=u'機房', max_length=32)
    floor = models.IntegerField(verbose_name=u'樓層', default=1)

    class Meta:
        verbose_name = '機房表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Tag(models.Model):
    name = models.CharField(verbose_name=u'標簽', max_length=32, unique=True)

    class Meta:
        verbose_name = '標簽表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Server(models.Model):
    device_type_choices = (
        (1, '服務器'),
        (2, '交換機'),
        (3, '防火牆'),
    )
    device_status_choices = (
        (1, '上架'),
        (2, '在線'),
        (3, '離線'),
        (4, '下架'),
    )
    device_type_id = models.IntegerField(verbose_name=u'服務器類型', choices=device_type_choices, default=1)
    device_status_id = models.IntegerField(verbose_name=u'服務器狀態', choices=device_status_choices, default=1)
    cabinet_num = models.CharField(verbose_name=u'機櫃號', max_length=30, null=True, blank=True)
    cabinet_order = models.CharField(verbose_name=u'機櫃中序號', max_length=30, null=True, blank=True)
    idc = models.ForeignKey(to="IDC", verbose_name=u'IDC機房', null=True, blank=True)
    business_unit = models.ForeignKey(to='BusinessUnit', verbose_name=u'屬於的專業線', null=True, blank=True)
    tag = models.ManyToManyField(to='Tag')
    hostname = models.CharField(verbose_name=u'主機名', max_length=128, unique=True)
    sn = models.CharField(verbose_name=u'SN號', max_length=64, db_index=True)
    manufacturer = models.CharField(verbose_name=u'制造商', max_length=64, null=True, blank=True)
    model = models.CharField(verbose_name=u'型號', max_length=64, null=True, blank=True)
    manager_id = models.GenericIPAddressField(verbose_name=u'管理IP', null=True, blank=True)
    os_platform = models.CharField(verbose_name=u'系統', max_length=16, null=True, blank=True)
    os_version = models.CharField(verbose_name=u'系統版本', max_length=32, null=True, blank=True)
    cpu_count = models.IntegerField(verbose_name=u'CPU個數', null=True, blank=True)
    cpu_physical_count = models.IntegerField(verbose_name=u'CUP物理個數', null=True, blank=True)
    cpu_model = models.CharField(verbose_name=u'CPU型號', max_length=128, null=True, blank=True)
    create_at = models.DateTimeField(auto_now_add=True, blank=True)

    class Meta:
        verbose_name = '服務器表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hostname


class Disk(models.Model):
    slot = models.CharField(verbose_name='插槽位', max_length=8)
    model = models.CharField(verbose_name=u'磁盤型號', max_length=32)
    capacity = models.CharField(verbose_name=u'磁盤容量GB', max_length=32)
    pd_type = models.CharField(verbose_name=u'磁盤類型', max_length=32)
    server_object = models.ForeignKey(to='Server', related_name='disk')

    class Meta:
        verbose_name = '硬盤表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.slot


class NIC(models.Model):
    name = models.CharField(verbose_name=u'網卡名稱', max_length=128)
    hwaddr = models.CharField(verbose_name=u'網卡mac地址', max_length=64)
    netmask = models.CharField(max_length=64)
    ipaddrs = models.CharField(verbose_name=u'ip地址', max_length=256)
    up = models.BooleanField(default=False)
    server_object = models.ForeignKey(to='Server', related_name='nic')

    class Meta:
        verbose_name = '網卡表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Memory(models.Model):
    slot = models.CharField(verbose_name=u'插槽位', max_length=32)
    manufacturer = models.CharField(verbose_name=u'制造商', max_length=32, null=True, blank=True)
    model = models.CharField(verbose_name=u'型號', max_length=64)
    capacity = models.FloatField(verbose_name=u'容量', null=True, blank=True)
    sn = models.CharField(verbose_name=u'內存SN號', max_length=64, null=True, blank=True)
    speed = models.CharField(verbose_name=u'速度', max_length=16, null=True, blank=True)
    server_object = models.ForeignKey(to='Server', related_name='memory')

    class Meta:
        verbose_name = '內存表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.slot


class AssetRecord(models.Model):
    assert_object = models.ForeignKey(to='Server', related_name='ar')
    content = models.TextField(null=True)
    creator = models.ForeignKey(to='UserProfile', null=True, blank=True)
    create_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = '資產記錄表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return "%s-%s-%s" % (
            self.assert_object.idc.name, self.assert_object.cabinet_num, self.assert_object.cabinet_order)


class ErrorLog(models.Model):
    assert_object = models.ForeignKey(to='Server', null=True, blank=True)
    title = models.CharField(max_length=16)
    content = models.TextField()
    create_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = '錯誤日志表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

數據清洗入庫

磁盤清洗:

增:新的slot有,老的slot沒有,將新的slot插入到數據庫中

刪:新的沒有,老的有,將老的slot對應的數據行從數據庫中刪掉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM