IT運維的分類
IT運維,指的是對已經搭建好的網絡,軟件,硬件進行維護。運維領域也是細分的,有硬件運維和軟件運維。
硬件運維主要包括對基礎設施的運維,比如機房的設備,主機的硬盤,內存這些物理設備的維護。
軟件運維主要包括系統運維和應用運維,系統運維主要包括對OS,數據庫,中間件的監控和維護,這些系統介於設備和應用之間,應用運維主要是對線上業務系統的運維。
傳統運維痛點
日程工作繁瑣
日常運維工作是比較繁瑣的,研發同學會經常需要到服務器上查日志,重啟應用,或者是說今天上線某個產品,需要部署下環境。這些瑣事是傳統運維的大部分工作。
應用運行環境不統一
在部署某應用后,應用不能訪問,就會聽到開發人員說,在我的環境運行很好的,怎么部署到測試環境后,就不能用了,因為各類環境的類庫不統一
還有一種極端情況,運維人員習慣不同,可能憑自己的習慣來安裝部署軟件,每種服務器上運行軟件的目錄不統一。
運維及部署效率低下
想想運維人員需要登陸到服務器上執行命令,部署程序,不僅效率很低,並且非常容易出現人為的錯誤,一旦手工出錯,追溯問題將會非常不容易。
無用報警信息過多
經常會收到很多報警信息,多數是無用的報警信息,造成運維人員經常屏蔽報警信。
另外如果應用的訪問速度出了問題,總是需要從系統、網絡、應用、數據庫等一步步的查找原因。
資產管理和應用管理混亂
資產管理,服務管理經常記錄在excel、文本文件或者wiki中,不便於管理,老員工因為比較熟,不注重這些文檔的維護,只有靠每次有新員工入職時,資產才能夠更正一次。
自動化運維平台的特性
針對傳統運維的痛點,我們可以知道自動化運維需要支持哪些功能
運維自動化最重要的就是標准化一切
- OS的選擇統一化,同一個項目使用同樣的OS系統部署其所需要的各類軟件
- 軟件安裝標准化,例如JAVA虛擬機,php,nginx,mysql等各類應用需要的軟件版本,安裝目錄,數據存放目錄,日志存放目錄等
- 應用包目錄統一標准化,及應用命名標准化
- 啟動腳本統一目錄和名字,需要變化的部分通過參數傳遞
- 配置文件標准化,需要變化的部分通過參數傳遞
- 日志輸出,日志目錄,日志名字標准化
- 應用生成的數據要實現統一的目錄存放
- 主機/虛擬機命名標准化,虛擬機管理使用標准化模板
- 使用docker比較容易實現軟件運行環境的標准化
資產管理系統(CMDB)
CMDB是所有運維工具的數據基礎
CMDB包含的功能
- 用戶管理,記錄測試,開發,運維人員的用戶表
- 業務線管理,需要記錄業務的詳情
- 項目管理,指定此項目用屬於哪條業務線,以及項目詳情
- 應用管理,指定此應用的開發人員,屬於哪個項目,和代碼地址,部署目錄,部署集群,依賴的應用,軟件等信息
- 主機管理,包括雲主機,物理機,主機屬於哪個集群,運行着哪些軟件,主機管理員,連接哪些網絡設備,雲主機的資源池,存儲等相關信息
- 主機變更管理,主機的一些信息變更,例如管理員,所屬集群等信息更改,連接的網絡變更等
- 網絡設備管理,主要記錄網絡設備的詳細信息,及網絡設備連接的上級設備
- IP管理,IP屬於哪個主機,哪個網段, 是否被占用等
CMDB實現的四種方式
Agent實現方式
Agent方式,可以將服務器上面的Agent程序作定時任務,定時將資產信息提交到指定API錄入數據庫。

其本質上就是在各個服務器上執行subprocess.getoutput()命令,然后將每台機器上執行的結果,返回給主機API,然后主機API收到這些數據之后,放入到數據庫中,最終通過web界面展現給用戶。
優點:速度快
缺點:需要為每台服務器部署一個Agent程序
場景:服務器多的時候
ssh實現方式(基於Paramiko模塊)
中控機通過Paramiko(py模塊)登錄到各個服務器上,然后執行命令的方式去獲取各個服務器上的信息。

優點:無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方式

此方案本質上和第二種方案大致是差不多的流程,中控機發送命令給服務器執行。服務器將結果放入另一個隊列中,中控機獲取將服務信息發送到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對應的數據行從數據庫中刪掉
