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'}