CMDB

IT運維的分類

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

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

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

傳統運維痛點

日程工做繁瑣

​ 平常運維工做是比較繁瑣的,研發同窗會常常須要到服務器上查日誌,重啓應用,或者是說今天上線某個產品,須要部署下環境。這些雜事是傳統運維的大部分工做。mysql

應用運行環境不統一

​ 在部署某應用後,應用不能訪問,就會聽到開發人員說,在個人環境運行很好的,怎麼部署到測試環境後,就不能用了,由於各種環境的類庫不統一
還有一種極端狀況,運維人員習慣不一樣,可能憑本身的習慣來安裝部署軟件,每種服務器上運行軟件的目錄不統一。linux

運維及部署效率低下

​ 想一想運維人員須要登錄到服務器上執行命令,部署程序,不只效率很低,而且很是容易出現人爲的錯誤,一旦手工出錯,追溯問題將會很是不容易。nginx

無用報警信息過多

​ 常常會收到不少報警信息,多數是無用的報警信息,形成運維人員常常屏蔽報警信。
​ 另外若是應用的訪問速度出了問題,老是須要從系統、網絡、應用、數據庫等一步步的查找緣由。web

資產管理和應用管理混亂

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

自動化運維平臺的特性

​ 針對傳統運維的痛點,咱們能夠知道自動化運維須要支持哪些功能sql

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

  • 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.惟一標識問題

大家這個項目作了多長時間?多少我的?多少服務器?

一、 5~6個月

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

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

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

二、通常後端2-3我的 前端1我的

三、創業公司:幾十臺

代碼部分及問題

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對應的數據行從數據庫中刪掉

相關文章
相關標籤/搜索