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'}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章