python psutil库监控linux服务器

psutil监控linux服务器

psutil介绍

psutil是一个开源且跨平台的库,提供了便利的函数用来获取操作系统的信息,如cpu、内存、磁盘、网络等信息。psutil还可以用来进行进程管理,包括判断进程是否存在、获取进程列表、获取进程的详细信息等。
psutil是一个第三方的开源项目,需要先安装才能使用。

pip install psutil

linux系统的/proc目录介绍

在linux操作系统中,/proc是一个位于内存中的伪文件系统,保存的是运行时信息,如系统内存、磁盘io、设备挂载信息和硬件配置信息等。在linux系统中,许多工具的数据来源正是proc目录中的内容。

proc目录下常用文件介绍

在编写linux的监控系统时,最基本的监控包括cpu、内存、磁盘和网络等信息

  1. /proc/loadavg:保存了系统负载的平均值,前三列分别表示最近1分钟、5分钟及15分钟的平均负载。反映了当前系统的繁忙情况。
  2. /proc/meminfo:由free命令统计当前内存使用信息,可以使用文件查看命令直接读取此文件,其内容显示为两列,前者为统计属性,后者为对应的值。
  3. /proc/diskstats:磁盘设备的磁盘I/O统计信息列表
  4. /proc/net/dev:网络流入流出的统计信息,包括接收包的数量、发送包的数量,发送数据包时的错误和冲突情况等。
  5. /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'}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章