目前 Linux 下有一些使用 Python 語言編寫的 Linux 系統監控工具 比如 inotify-sync(檔案系統安全監控軟體)、glances(資源監控工具)在實際工作中,Linux 系統管理員可以根據自己使用的伺服器的具體情況編寫一下簡單實用的指令碼實現對 Linux 伺服器的監控。 本文介紹一下使用 Python 指令碼實現對 Linux 伺服器 CPU 記憶體 網路的監控指令碼的編寫。
Python 版本說明
Python 是由 Guido van Rossum 開發的、可免費獲得的、非常高階的解釋型語言。其語法簡單易懂,而其物件導向的語義功能強大(但又靈活)。Python 可以廣泛使用並具有高度的可移植性。本文 Linux 伺服器是 Ubuntu 12.10, Python 版本 是 2.7 。如果是 Python 3.0 版本的語法上有一定的出入。另外這裡筆者所說的 Python 是 CPython,CPython 是用 C 語言實現的 Python 直譯器,也是官方的並且是最廣泛使用的Python 直譯器。除了 CPython 以外,還有用 Java 實現的 Jython 和用.NET 實現的 IronPython,使 Python方便地和 Java 程式、.NET 程式整合。另外還有一些實驗性的 Python 直譯器比如 PyPy。CPython 是使用位元組碼的直譯器,任何程式原始碼在執行之前先要編譯成位元組碼。它還有和幾種其它語言(包括 C 語言)互動的外部函式介面。
工作原理:基於/proc 檔案系統
Linux 系統為管理員提供了非常好的方法,使其可以在系統執行時更改核心,而不需要重新引導核心系統,這是通過/proc 虛擬檔案系統實現的。/proc 檔案虛擬系統是一種核心和核心模組用來向程式(process)傳送資訊的機制(所以叫做“/proc”),這個偽檔案系統允許與核心內部資料結構互動,獲取有關程式的有用資訊,在執行中(on the fly)改變設定(通過改變核心引數)。與其他檔案系統不同,/proc 存在於記憶體而不是硬碟中。proc 檔案系統提供的資訊如下:
- 程式資訊:系統中的任何一個程式,在 proc 的子目錄中都有一個同名的程式 ID,可以找到 cmdline、mem、root、stat、statm,以及 status。某些資訊只有超級使用者可見,例如程式根目錄。每一個單獨含有現有程式資訊的程式有一些可用的專門連結,系統中的任何一個程式都有一個單獨的自連結指向程式資訊,其用處就是從程式中獲取命令列資訊。
- 系統資訊:如果需要了解整個系統資訊中也可以從/proc/stat 中獲得,其中包括 CPU 佔用情況、磁碟空間、記憶體對換、中斷等。
- CPU 資訊:利用/proc/CPUinfo 檔案可以獲得中央處理器的當前準確資訊。
- 負載資訊:/proc/loadavg 檔案包含系統負載資訊。
- 系統記憶體資訊:/proc/meminfo 檔案包含系統記憶體的詳細資訊,其中顯示實體記憶體的數量、可用交換空間的數量,以及空閒記憶體的數量等。
表 1 是 /proc 目錄中的主要檔案的說明:
表 1 /proc 目錄中的主要檔案的說明
檔案或目錄名稱 |
描 述 |
---|---|
apm |
高階電源管理資訊 |
cmdline |
這個檔案給出了核心啟動的命令列 |
CPU info |
中央處理器資訊 |
devices |
可以用到的裝置(塊裝置/字元裝置) |
dma |
顯示當前使用的 DMA 通道 |
filesystems |
核心配置的檔案系統 |
ioports |
當前使用的 I/O 埠 |
interrupts |
這個檔案的每一行都有一個保留的中斷 |
kcore |
系統實體記憶體映像 |
kmsg |
核心輸出的訊息,被送到日誌檔案 |
mdstat |
這個檔案包含了由 md 裝置驅動程式控制的 RAID 裝置資訊 |
loadavg |
系統平均負載均衡 |
meminfo |
儲存器使用資訊,包括實體記憶體和交換記憶體 |
modules |
這個檔案給出可載入核心模組的資訊。lsmod 程式用這些資訊顯示有關模組的名稱,大小,使用數目方面的資訊 |
net |
網路協議狀態資訊 |
partitions |
系統識別的分割槽表 |
pci |
pci 裝置資訊 |
scsi |
scsi 裝置資訊 |
self |
到檢視/proc 程式程式目錄的符號連線 |
stat |
這個檔案包含的資訊有 CPU 利用率,磁碟,記憶體頁,記憶體對換,全部中斷,接觸開關以及賞賜自舉時間 |
swaps |
顯示的是交換分割槽的使用情況 |
uptime |
這個檔案給出自從上次系統自舉以來的秒數,以及其中有多少秒處於空閒 |
version |
這個檔案只有一行內容,說明正在執行的核心版本。可以用標準的程式設計方法進行分析獲得所需的系統資訊 |
下面本文的幾個例子都是使用
Python 指令碼
讀取
/proc 目錄中的主要檔案來實現實現對 Linux 伺服器的監控的 。
使用 Python 指令碼實現對 Linux 伺服器的監控
對於 CPU(中央處理器)監測
指令碼 1 名稱 CPU1.py,作用獲取 CPU 的資訊。
清單 1.獲取 CPU 的資訊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#!/usr/bin/env Python from __future__ import print_function from collections import OrderedDict import pprint def CPUinfo(): ''' Return the information in /proc/CPUinfo as a dictionary in the following format: CPU_info['proc0']={...} CPU_info['proc1']={...} ''' CPUinfo=OrderedDict() procinfo=OrderedDict() nprocs = 0 with open('/proc/CPUinfo') as f: for line in f: if not line.strip(): # end of one processor CPUinfo['proc%s' % nprocs] = procinfo nprocs=nprocs+1 # Reset procinfo=OrderedDict() else: if len(line.split(':')) == 2: procinfo[line.split(':')[0].strip()] = line.split(':')[1].strip() else: procinfo[line.split(':')[0].strip()] = '' return CPUinfo if __name__=='__main__': CPUinfo = CPUinfo() for processor in CPUinfo.keys(): print(CPUinfo[processor]['model name']) |
簡單說明一下清單 1,讀取/proc/CPUinfo 中的資訊,返回 list,每核心一個 dict。其中 list 是一個使用方括號括起來的有序元素集合。List 可以作為以 0 下標開始的陣列。Dict 是 Python 的內建資料型別之一, 它定義了鍵和值之間一對一的關係。OrderedDict 是一個字典子類,可以記住其內容增加的順序。常規 dict 並不跟蹤插入順序,迭代處理時會根據鍵在雜湊表中儲存的順序來生成值。在 OrderedDict 中則相反,它會記住元素插入的順序,並在建立迭代器時使用這個順序。
可以使用 Python 命令執行指令碼 CPU1.py 結果見圖 1
1 2 |
# Python CPU1.py Intel(R) Celeron(R) CPU E3200 @ 2.40GHz |
圖 1.執行清單 1
也可以使用 chmod 命令新增許可權收直接執行 CPU1.py
1 2 |
#chmod +x CPU1.py # ./CPU1.py |
對於系統負載監測
指令碼 2 名稱 CPU2.py,作用獲取系統的負載資訊
清單 2 獲取系統的負載資訊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/usr/bin/env Python import os def load_stat(): loadavg = {} f = open("/proc/loadavg") con = f.read().split() f.close() loadavg['lavg_1']=con[0] loadavg['lavg_5']=con[1] loadavg['lavg_15']=con[2] loadavg['nr']=con[3] loadavg['last_pid']=con[4] return loadavg print "loadavg",load_stat()['lavg_15'] |
簡單說明一下清單 2:清單 2 讀取/proc/loadavg 中的資訊,import os :Python 中 import 用於匯入不同的模組,包括系統提供和自定義的模組。其基本形式為:import 模組名 [as 別名],如果只需要匯入模組中的部分或全部內容可以用形式:from 模組名 import *來匯入相應的模組。OS 模組 os 模組提供了一個統一的作業系統介面函式,os 模組能在不同作業系統平臺如 nt,posix 中的特定函式間自動切換,從而實現跨平臺操作。
可以使用 Python 命令執行指令碼 CPU1.py 結果見圖 2 # Python CPU2.py
圖 2.執行清單 2
對於記憶體資訊的獲取
指令碼 3 名稱 mem.py,作用是獲取記憶體使用情況資訊
清單 3 獲取記憶體使用情況
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env Python from __future__ import print_function from collections import OrderedDict def meminfo(): ''' Return the information in /proc/meminfo as a dictionary ''' meminfo=OrderedDict() with open('/proc/meminfo') as f: for line in f: meminfo[line.split(':')[0]] = line.split(':')[1].strip() return meminfo if __name__=='__main__': #print(meminfo()) meminfo = meminfo() print('Total memory: {0}'.format(meminfo['MemTotal'])) print('Free memory: {0}'.format(meminfo['MemFree'])) |
簡單說明一下清單 3:清單 3 讀取 proc/meminfo 中的資訊,Python 字串的 split 方法是用的頻率還是比較多的。比如我們需要儲存一個很長的資料,並且按照有結構的方法儲存,方便以後取資料進行處理。當然可以用 json 的形式。但是也可以把資料儲存到一個欄位裡面,然後有某種標示符來分割。 Python 中的 strip 用於去除字串的首位字元,最後清單 3 列印出記憶體總數和空閒數。
可以使用 Python 命令執行指令碼 mem.py 結果見圖 3。 # Python mem.py
圖 3.執行清單 3
對於網路介面的監測
指令碼 4 名稱是 net.py,作用獲取網路介面的使用情況。
清單 4 net.py 獲取網路介面的輸入和輸出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#!/usr/bin/env Python import time import sys if len(sys.argv) > 1: INTERFACE = sys.argv[1] else: INTERFACE = 'eth0' STATS = [] print 'Interface:',INTERFACE def rx(): ifstat = open('/proc/net/dev').readlines() for interface in ifstat: if INTERFACE in interface: stat = float(interface.split()[1]) STATS[0:] = [stat] def tx(): ifstat = open('/proc/net/dev').readlines() for interface in ifstat: if INTERFACE in interface: stat = float(interface.split()[9]) STATS[1:] = [stat] print 'In Out' rx() tx() while True: time.sleep(1) rxstat_o = list(STATS) rx() tx() RX = float(STATS[0]) RX_O = rxstat_o[0] TX = float(STATS[1]) TX_O = rxstat_o[1] RX_RATE = round((RX - RX_O)/1024/1024,3) TX_RATE = round((TX - TX_O)/1024/1024,3) print RX_RATE ,'MB ',TX_RATE ,'MB' |
簡單說明一下清單 4:清單 4 讀取/proc/net/dev 中的資訊,Python 中檔案操作可以通過 open 函式,這的確很像 C 語言中的 fopen。通過 open 函式獲取一個 file object,然後呼叫 read(),write()等方法對檔案進行讀寫操作。另外 Python 將文字檔案的內容讀入可以操作的字串變數非常容易。檔案物件提供了三個“讀”方法: read()、readline() 和 readlines()。每種方法可以接受一個變數以限制每次讀取的資料量,但它們通常不使用變數。 .read() 每次讀取整個檔案,它通常用於將檔案內容放到一個字串變數中。然而 .read() 生成檔案內容最直接的字串表示,但對於連續的面向行的處理,它卻是不必要的,並且如果檔案大於可用記憶體,則不可能實現這種處理。.readline() 和 .readlines() 之間的差異是後者一次讀取整個檔案,象 .read() 一樣。.readlines() 自動將檔案內容分析成一個行的列表,該列表可以由 Python 的 for … in … 結構進行處理。另一方面,.readline() 每次只讀取一行,通常比 .readlines() 慢得多。僅當沒有足夠記憶體可以一次讀取整個檔案時,才應該使用 .readline()。最後清單 4 列印出網路介面的輸入和輸出情況。
可以使用 Python 命令執行指令碼 net.py 結果見圖 4 #Python net.py
圖 4.執行清單 4
監控 Apache 伺服器程式的 Python 指令碼
Apache 伺服器程式可能會因為系統各種原因而出現異常退出,導致 Web 服務暫停。所以筆者寫一個 Python 指令碼檔案:
清單 5 crtrl.py 監控 Apache 伺服器程式的 Python 指令碼
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env Python import os, sys, time while True: time.sleep(4) try: ret = os.popen('ps -C apache -o pid,cmd').readlines() if len(ret) < 2: print "apache 程式異常退出, 4 秒後重新啟動" time.sleep(3) os.system("service apache2 restart") except: print "Error", sys.exc_info()[1] |
設定檔案許可權為執行屬性(使用命令 chmod +x crtrl.py),然後加入到/etc/rc.local 即可,一旦 Apache 伺服器程式異常退出,該指令碼自動檢查並且重啟。 簡單說明一下清單 5 這個指令碼不是基於/proc 偽檔案系統的,是基於 Python 自己提供的一些模組來實現的 。這裡使用的是 Python 的內嵌 time 模板,time 模組提供各種操作時間的函式。
總結
在實際工作中,Linux 系統管理員可以根據自己使用的伺服器的具體情況編寫一下簡單實用的指令碼實現對 Linux 伺服器的監控。本文介紹一下使用 Python 指令碼實現對 Linux 伺服器 CPU 、系統負載、記憶體和 網路使用情況的監控指令碼的編寫方法。