psutil監控linux服務器
psutil介紹
psutil是一個開源且跨平臺的庫,提供了便利的函數用來獲取操作系統的信息,如cpu、內存、磁盤、網絡等信息。psutil還可以用來進行進程管理,包括判斷進程是否存在、獲取進程列表、獲取進程的詳細信息等。
psutil是一個第三方的開源項目,需要先安裝才能使用。
pip install psutil
linux系統的/proc目錄介紹
在linux操作系統中,/proc是一個位於內存中的僞文件系統,保存的是運行時信息,如系統內存、磁盤io、設備掛載信息和硬件配置信息等。在linux系統中,許多工具的數據來源正是proc目錄中的內容。
proc目錄下常用文件介紹
在編寫linux的監控系統時,最基本的監控包括cpu、內存、磁盤和網絡等信息
- /proc/loadavg:保存了系統負載的平均值,前三列分別表示最近1分鐘、5分鐘及15分鐘的平均負載。反映了當前系統的繁忙情況。
- /proc/meminfo:由free命令統計當前內存使用信息,可以使用文件查看命令直接讀取此文件,其內容顯示爲兩列,前者爲統計屬性,後者爲對應的值。
- /proc/diskstats:磁盤設備的磁盤I/O統計信息列表
- /proc/net/dev:網絡流入流出的統計信息,包括接收包的數量、發送包的數量,發送數據包時的錯誤和衝突情況等。
- /proc/cupinfo:查看cpu詳細信息。
cat /proc/cpuinfo | grep 'processor'
processor : 0
processor : 1
processor : 2
processor : 3
進程目錄下常用文件介紹
proc目錄中包含了若干文件以及多個名字是數字的目錄。proc目錄下的文件保存的是整個系統的信息。名字是數字的目錄保存的是進程的信息。目錄名字是進程的id。可以通過讀取proc目錄下有多少數字命名的目錄來判斷當前系統中有多少進程。
import os
pids = [item for item in os.listdir('.') if item.isdigit() ]
lne(pids)
psutil提供的功能函數
1 cpu
與cpu相關的功能函數:
1)cpu_count默認返回邏輯cpu的個數,也可以指定logical=False獲取物理cpu的個數
>>> import psutil
>>> psutil.cpu_count()
4
>>> psutil.cpu_count(logical=False)
4
2)cpu_percent返回cpu的利用率,可以通過interval參數阻塞式地獲取interval時間範圍內的cpu利用率,否則,獲取上一次調用cpu_percent這段時間以來的cpu利用率。可以使用percpu參數指定獲取每個cpu的利用率,默認獲取整體的cpu利用率。
>>> psutil.cpu_percent()
3.2
>>> psutil.cpu_percent(percpu=True)
[3.0, 3.3, 3.3, 2.7]
>>> psutil.cpu_percent(interval=12,percpu=True)
[1.2, 1.0, 4.1, 0.8]
3)cpu_times_percent以命名元組的形式返回cpu耗費時間的比例
>>> psutil.cpu_times_percent()
scputimes(user=2.4, nice=0.0, system=0.7, idle=96.9, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
2.內存
與內存相關的功能函數
1)virtual_memory以命名元組的形式返回內存使用情況,包括總內存、可用內存、內存利用率等
>>> psutil.virtual_memory()
svmem(total=25096278016, available=22730371072, percent=9.4, used=759648256, free=17363464192, active=3338403840, inactive=2544881664, buffers=970752, cached=6972194816, shared=1226817536, slab=1589207040)
統計內存大小
import psutil
#把內存轉化爲g
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i,s in enumerate(symbols):
prefix[s] = 1 << (i+1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value,s)
return "%sB" % n
print(bytes2human(psutil.virtual_memory().total))
3 磁盤
與磁盤相關的功能函數
1)disk_partitions返回所有已經掛載的磁盤,以命名元組的形式返回。命名元組包含磁盤名稱、掛載點、文件系統類型等信息。
>>> psutil.disk_partitions()
[sdiskpart(device='/dev/sda1', mountpoint='/boot', fstype='xfs', opts='rw,relatime,attr2,inode64,noquota')]
獲取掛載的磁盤
import psutil
def get_disk_via_mountpoint(mountpoint):
disk = [ item for item in psutil.disk_partitions() if item.mountpoint == mountpoint]
return disk[0].device
get_disk_via_mountpoint('/boot')
#獲取到的掛載的磁盤
#'/dev/sda1'
2)disk_usage獲取磁盤的使用情況,包括磁盤的容量、已經使用的磁盤容量、磁盤的空間利用率等,disk_usage以命名元組的形式返回結果
>>> psutil.disk_usage('/')
sdiskusage(total=53660876800, used=38295232512, free=15365644288, percent=71.4)
>>> psutil.disk_usage("/").percent
71.4
3)disk_io_counters以命名元組的形式返回磁盤io統計信息,包括讀的次數、寫的次數、讀字節數、寫字節數等
>>> psutil.disk_io_counters()
sdiskio(read_count=35046, write_count=33076572, read_bytes=2197194752, write_bytes=264244009472, read_time=353577, write_time=60734752, read_merged_count=199, write_merged_count=1279882, busy_time=12893779)
>>> psutil.disk_io_counters(perdisk=True)
{'sda': sdiskio(read_count=17621, write_count=17605128, read_bytes=1112903168, write_bytes=132422970368, read_time=175104, write_time=28289023, read_merged_count=199, write_merged_count=1279885, busy_time=6485558), 'sda1': sdiskio(read_count=318, write_count=127432, read_bytes=27329536, write_bytes=601128448, read_time=1043, write_time=174692, read_merged_count=1, write_merged_count=0, busy_time=97592), 'sda2': sdiskio(read_count=17211, write_count=14154152, read_bytes=1084754432, write_bytes=131821841920, read_time=174047, write_time=27628845, read_merged_count=198, write_merged_count=1279885, busy_time=5933140)
4 網絡
與網絡相關的功能函數
1)net_io_counter函數以命名元組的形式返回了每塊網卡的網絡io統計信息,包括收發字節數、收發包的數量、出錯情況與刪包情況
>>> psutil.net_io_counters()
snetio(bytes_sent=5103411573141, bytes_recv=16078879303075, packets_sent=540472232, packets_recv=709825389, errin=0, errout=0, dropin=78, dropout=0)
>>> psutil.net_io_counters(pernic=True)
{'lo': snetio(bytes_sent=266314614, bytes_recv=266314614, packets_sent=1280836, packets_recv=1280836, errin=0, errout=0, dropin=0, dropout=0), 'ens160': snetio(bytes_sent=5103145328338, bytes_recv=16078613030107, packets_sent=539191761, packets_recv=708544956, errin=0, errout=0, dropin=78, dropout=0)}
2)net_connections以列表的形式返回每個網絡連接的詳細信息,可以使用該函數查看網絡連接狀態,統計連接個數以及處於特定網絡狀態的網絡連接個數
psutil.net_connections()
[sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='10.176.233.24', port=2222), raddr=addr(ip='10.72.41.65', port=50812), status='ESTABLISHED', pid=None)]
統計特定網絡狀態的連接數
>>> conns = psutil.net_connections()
>>> len([conn for conn in conns if conn.status == 'TIME_WAIT'])
4
>>> len([conn for conn in conns if conn.status == 'ESTABLISHED' and conn.raddr.ip=='10.72.41.65'])
19
3)net_if_addrs以字典的形式返回網卡的配置信息,包括ip地址或mac地址、子網掩碼和廣播地址
>>> psutil.net_if_addrs()
{'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None)]}
4)net_if_stats返回網卡的詳細信息,包括是否啓動、通信類型、傳輸速度與mtu
>>> psutil.net_if_stats()
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536), 'ens160': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=1500)}
5 users返回當前登錄用戶的信息,包括用戶名,登錄時間,終端與主機信息
>>> psutil.users()
[suser(name='yangfei', terminal='pts/1', host='upsource.aac.com', started=1590456064.0, pid=100548)]
6 boot_time以時間戳的形式返回系統的啓動時間
>>> import psutil
>>> import datetime
>>> psutil.boot_time()
1582941737.0
datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
'2020-02-29 10:02:17'
綜合案例:使用psutil實現linux服務器監控程序
使用psutil收集監控信息,包括服務器名稱、ip地址、cpu信息、內存信息、磁盤空間等信息,使用Jinja2模板渲染監控報告,使用email將監控報告發送給用戶。
import os
import socket
from datetime import datetime
import jinja2
import psutil
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.header import Header #Header是用來構建郵件
from email.mime.base import MIMEBase
from email import encoders
import time #時間模塊
import os,sys,shutil
import jinja2
import requests
import json
# 創建一個Environment對象並用他加載模板
def render(tpl_path,**kwargs):
path,filename = os.path.split(tpl_path)
return jinja2.Environment(
loader=jinja2.FileSystemLoader(path or './')
).get_template(filename).render(**kwargs)
# 統一內存大小單位爲g
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i,s in enumerate(symbols):
prefix[s] = 1 << (i+1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value,s)
return "%sB" % n
# 統計cpu信息
def get_cpu_info():
#svmem(total=8481505280, available=3200630784, percent=62.3, used=5280874496, free=3200630784)
cpu_count = psutil.cpu_count()
#返回cpu利用率
cpu_percent = psutil.cpu_percent(interval=1)
#統計用戶進程、系統進程、空閒進程的執行時間百分比
cpu_execute_time = psutil.cpu_times_percent()
user_execute_time = cpu_execute_time.user
system_execute_time = cpu_execute_time.system
idle_time = cpu_execute_time.idle
return dict(cpu_count=cpu_count,cpu_percent=cpu_percent,user_execute_time=user_execute_time,system_execute_time=system_execute_time,idle_time=idle_time)
#統計內存信息
def get_memory_info():
#svmem(total=8481505280, available=3185758208, percent=62.4, used=5295747072, free=3185758208)
virtual_mem = psutil.virtual_memory()
#把內存總量、可用內存、已用內存統一單位爲g
mem_total = bytes2human(virtual_mem.total)
mem_free = bytes2human(virtual_mem.free)
mem_used = bytes2human(virtual_mem.used)
mem_percent = virtual_mem.percent
return dict(mem_total=mem_total,mem_free=mem_free,mem_used=mem_used,mem_percent=mem_percent)
#統計磁盤信息
def get_disk_info():
#sdiskusage(total=196495798272, used=15009411072, free=181486387200, percent=7.6)
disk_usage = psutil.disk_usage('/')
#磁盤空間總量、磁盤可用空間、磁盤已用空間、磁盤空間利用率
disk_total = bytes2human(disk_usage.total)
disk_free = bytes2human(disk_usage.free)
disk_used = bytes2human(disk_usage.used)
disk_percent = disk_usage.percent
return dict(disk_total=disk_total,disk_free=disk_free,disk_used=disk_used,disk_percent=disk_percent)
#2020-05-24 05:41:06.267929
# t1=datetime.datetime.fromtimestamp(t)
# print(type(t1))
# #<class 'datetime.datetime'>
#格式化時間爲字符串
# t2 = t1.strftime(("%Y-%m-%d %H:%M:%S"))
# #2020-05-24 05:41:06
# print(t2)
#boot_time以時間戳的形式返回系統的啓動時間
def get_boot_info():
boot_time = datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%M-%d %H:%M:%S")
return dict(boot_time=boot_time)
# #users以命名元組的方式返回當前登錄用戶的信息,包括用戶名,登錄時間,終端與主機信息
# t = psutil.users()
# #[suser(name='60056283', terminal=None, host='0.0.0.0', started=1590400857.9769104, pid=None)]
# print(t)
# #suser(name='60056283', terminal=None, host='0.0.0.0', started=1590400857.9769104, pid=None)
# print(t[0])
# #1590400857.9769104
# print(t[0].started)
#返回當前登錄用戶的信息
def get_user_info():
user = psutil.users()
user_name = user[0].name
started=user[0].started
started_time = datetime.fromtimestamp(started).strftime("%Y-%M-%d %H:%M:%S")
return dict(started_time=started_time)
# 返回當前時間,主機名和主機ip地址
def get_server_name():
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
ip_add = '10.176.233.24'
host_name = socket.gethostname()
return dict(now=now,host_name=host_name,ip_add=ip_add)
#數據放到字典中
#Python 字典 update() 函數把字典參數 dict2 的 key/value(鍵/值) 對更新到字典 dict 裏。
def collect_monitor_data():
data = {}
data.update(get_server_name())
data.update(get_cpu_info())
data.update(get_memory_info())
data.update(get_disk_info())
return data
# 郵件內容
def addAttch(receiver,cc,subject,html_msg):
msg = MIMEMultipart('mixed') #採用related定義內嵌資源的郵件體
#設置郵件頭
msg['Subject'] = Header(subject)
msg['From']=Header('自動編組服務器狀態監控')
msg['To']=Header(";".join(receiver))
msg['cc']=Header(";".join(cc))
#郵件正文添加監控數據
content_html = MIMEText(html_msg, "html", "utf-8")
msg.attach(content_html)
return msg
# 發送郵件
def sendEmail(msg):
try:
server=smtplib.SMTP()
server.connect(smtpserver)
server.login(username,password)
server.sendmail(sender,receiver,msg.as_string())
server.sendmail(sender,cc, msg.as_string())
server.quit()
print("發送成功")
except:
print("發送失敗")
if __name__ == '__main__':
#獲得服務器監控數據
data = collect_monitor_data()
#渲染html模板
content = render('/home/monitor/monitor.html', **data)
sender = '[email protected]'
receiver = ['[email protected]']
subject = '監控程序部署服務器'
smtpserver = '10.176.218.30'
username = 'aac\s-dwh'
password = '123456!a'
cc = ['[email protected]','[email protected]']
# 發送郵件
msg = addAttch(receiver,cc,subject,content)
sendEmail(msg)
模板渲染的monitor.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>監控信息</title>
<STYLE TYPE="text/css" MEDIA=screen>
table.dataframe {
border-collapse:collapse;
border: 2px solid #a19da2;
/*默認居中auto顯示整個表格*/
margin: left
}
table.dataframe thead {
border: 2px solid #91c6e1;
background: #f1f1f1;
padding: 10px 10px 10px 10px;
color: #333333;
}
table.dataframe tbody {
border: 2px solid #91c6e1;
padding: 10px 10px 10px 10px;
}
table.dataframe tr {
}
table.dataframe th {
vertical-align: top;
font-size: 14px;
padding: 10px 10px 10px 10px;
color: #105de3;
font-family: arial;
text-align: center;
}
table.dataframe td{
text-align: left;
padding: 10px 10px 10px 10px;
}
body {
font-family: 宋體;
}
h1 {
color: #5db446
}
div.header h2 {
color: #0002e3;
font-family: 黑體;
}
div.content h2 {
text-align: center;
font-size: 28px;
text-shadow: 2px 2px 1px #de4040;
color: #fff;
font-weight: bold;
background-color: #008eb7;
line-height: 1.5;
margin: 20px 0;
box-shadow: 10px 10px 5pxx #888888;
border-radius: 5px;
}
h3 {
font-size: 22px;
background-color: rgba(0,2,227,0.71);
text-shadow: 2px 2px 1px #de4040;
color: rgba(239,241,234,0.99);
line-height: 1.5;
}
h4 {
color: #e10092;
font-family: 楷體;
font-size: 20px;
text-align: center;
}
td img {
/*width: 60px;*/
max-width: 300px;
max-height: 300px;
}
</style>
</head>
<body>
<p align="left">Dear all:自動編組項目監控程序部署服務器狀態推送,詳細參數請查看下錶</p>
<table border="1">
<tr><td>當前時間</td><td>{{now}}</td></tr>
<tr><td>服務器名稱</td><td>{{host_name}}</td></tr>
<tr><td>IP地址</td><td>{{ip_add}}</td></tr>
<tr><td>cpu個數</td><td>{{cpu_count}}</td></tr>
<tr><td>cpu利用率</td><td>{{cpu_percent}}%</td></tr>
<tr><td>用戶進程執行時間百分比</td><td>{{user_execute_time}}%</td></tr>
<tr><td>系統進程執行時間百分比</td><td>{{system_execute_time}}%</td></tr>
<tr><td>空閒進程執行時間百分比</td><td>{{idle_time}}%</td></tr>
<tr><td>內存總量</td><td>{{mem_total}}</td></tr>
<tr><td>內存可用空間</td><td>{{mem_free}}</td></tr>
<tr><td>內存已用空間</td><td>{{mem_used}}</td></tr>
<tr><td>內存利用率</td><td>{{mem_percent}}%</td></tr>
<tr><td>磁盤空間總量</td><td>{{disk_total}}</td></tr>
<tr><td>磁盤可用空間</td><td>{{disk_free}}</td></tr>
<tr><td>磁盤已用空間</td><td>{{disk_used}}</td></tr>
<tr><td>磁盤空間利用率</td><td>{{disk_percent}}%</td></tr>
</table>
</body>
</html>
相關知識點補充
cpu.idle
CPU利用率主要分爲用戶態,系統態和空閒態,分別表示CPU處於用戶態執行的時間,系統內核執行的時間,和空閒系統進程執行的時間,三者之和就是CPU的總時間,當沒有用戶進程、系統進程等需要執行的時候,CPU就執行系統缺省的空閒進程。CPU的利用率就是指非空閒進程佔用時間的比例,即CPU執行非空閒進程的時間 / CPU總的執行時間。
cpu.load(系統平均負載,通過top可以查看)
查看系統平均負載
cat /proc/loadavg
0.03 0.06 0.05 1/332 89115
前三個指1、5、15分鐘內的平均進程數。後面兩個指(正在運行的進程數/進程總數)和最近運行的進程ID號。
cpu.load/cores過高時會影響接口響應時間的,多出來的時間是線程等待cpu的響應時間。要求接口響應時間儘可能快的,最好確保cpu.load/cores不超過1,而對時間敏感性要求不太高時,一般要求cpu.load/cores不超過3
Jinja2模塊
Jinja2模塊有一個名爲Environment的類,這個類的實例用於存儲配置和全局對象,然後從文件系統或其他位置加載模塊。使用文件系統加載器可以直接訪問系統中的文件
def render(tpl_path,**kwargs):
path,filename = os.path.split(tpl_path)
return jinja2.Environment(
loader=jinja2.FileSystemLoader(path or './')
).get_template(filename).render(**kwargs)
Python3 字典 update() 方法
Python 字典 update() 函數把字典參數 dict2 的 key/value(鍵/值) 對更新到字典 dict 裏
update() 方法語法:
dict.update(dict2)
示例
dict = {'Name': 'Runoob', 'Age': 7}
dict2 = {'Sex': 'female' }
dict.update(dict2)
print ("更新字典 dict : ", dict)
#輸出如下:
#更新字典 dict : {'Name': 'Runoob', 'Age': 7, 'Sex': 'female'}