1. 程式人生 > >使用 psutil 和 MongoDB 做系統監控

使用 psutil 和 MongoDB 做系統監控

注:原文地址 psutil and MongoDB for System Monitoring

這篇入門文章描述了怎樣建立一系列的圖表來監控一臺或多臺伺服器的負載。使用 Python(psutil 和 bottle),MongoDB 和 jquery。不管你使用什麼樣的資料庫或 WEB 框架,思路都是一樣的。

在最後,你將有一個 web 頁面為每臺伺服器展示圖表,圖表中顯示了 cpu, memory, 和 disk usage。

大綱

  • Part 0: Get the Tools
  • Part 1: Get the Data

    • About the MongoDB Collection.
    • The Data Gathering Code
  • Part 2: Set up the bottle Server
  • Part 3: Display the Data with jqplot

    • The HTML Page
    • The JavaScript (jqplot) Code

我們需要監控兩臺 FreeBSD 伺服器來確保它們是正常的,執行期間沒有記憶體和磁碟使用率問題。 為了這篇文章的目的,這兩臺伺服器的名字是 example01 和 example02。

注意:這些恰好是相同的機器執行 MongoDB 副本集,它們中的一臺執行 web 服務。沒有理由它們必須是相同的機器 - 你完全可以讓 MongoDB 執行在一臺不同的伺服器上,而不是你想監控的其中一臺。這對於 web 服務也是一樣的 - 可以在任何伺服器上,不是必須得在你監控的其中一臺伺服器上。

我不想登入每臺伺服器,然後執行 topds 來找出發生了什麼。我想要一個有最新圖表的 web 頁面可以看一眼,每臺伺服器一個頁面。

整個工作流遵循以下三個步驟:

  • 獲取系統資料存入 MongoDB(使用 psutilcron
  • 設定一個 Web 伺服器來查詢 MongoDB 的資料(使用 bottle
  • 編寫一個簡單的 Web 頁面來展示這些資料(jqplot + AJAX)

此處輸入圖片的描述

你可以在 GitHub 工程 psmonitor 上獲取所有的程式碼。在這篇文章中,我使用一些程式碼片段。

在第一部分,你在一個 cron 任務中使用 psutil Python 包每五分鐘寫系統資訊到 MongoDB 的一個 capped collection 中。這 5 分鐘是完全任意的 - 你可以選擇你喜歡的任意時段。我正在監控的系統,幾分鐘提供足夠細的粒度。這部分把資料存入 MongoDB。

在第二部分, bottle 應用程式發出一個請求給 MongoDB,然後得到一個 JSON 資料格式的響應。這是在客戶端的 HTML 頁面和 MongoDB 資料直接建立了一個代理。

在第三部分,你有一個 HTML 檔案匹配到你想監控的每臺伺服器上。該檔案載入 jqplot 併發出一個 AJAX 請求給 bottle 應用程式。這部分是波動的原因 - 我們獲取儲存在 MongoDB 中的負載資料的時間序列圖表。

這個我們建立的其中一個圖表的示例:

此處輸入圖片的描述

你可以為你想要的任何資料建立圖表。我為每臺機器使用的圖表是:

  • cpu user percent
  • cpu system percent
  • cpu irq
  • cpu nice percent
  • disk space free
  • memory free

Part 0: Get the Tools

以下的工具需要通過安裝來實現,但是你可以使用一個不同的資料庫或是 Web 框架,如果你已經有的話。

psutil 是一個非常好用的跨平臺的系統監控工具,如果想了解更多,請移步至其專案主頁,你可以通過 pip 安裝它。

NoSQL 資料庫 MongoDB 是一個開源的文件型資料庫。如果你有自然適合 JSON 結構的資料,MongoDB 會是一款非常適合儲存資料的工具。對我來說,它已經成為了首選的工具,當我隨時發現需要儲存 JSON 格式資料的時候。有趣的是,我越使用它,越能找到新的用途。MongoDB 的文件型結構是驚人的有用,並且可以為許多資訊型別提供 map 。

bottle Web 框架是一個用 Python 寫的沒有任何依賴的小型框架。你可以通過 pip 安裝它。你可以使用包括開發伺服器讓事情繼續,稍後把它放在一個不同的後端伺服器。我把我的放在 Apache 伺服器,一旦我得到我希望他們的。我稍後將寫一篇文章關於這個的設定。

jqplot jquery 外掛使得生產圖表更容易,加上它們看起來也不錯,並且它們是統一的很容易的比較圖表。比如,當一個程序波動起來的時候,在圖表中很容易看出來,因為你可以同時看到 cpu 和 記憶體飆升,這兩個圖表是同樣的 X 軸位置。你可以使用這個外掛做很多事情,這個練習僅僅只是皮毛。

Part 1: Get the Data

當我們想遵循這樣的模式建立一個數據結構的時候,在這步中你可以新增和刪除任何 psutil 支援的資料。終點是會看 jqplot 圖表的使用者,因此在你的腦海裡保持資料結構,jqplot 想要的是一個雙元素列表的列表,其中一個是時間,像這樣:

[[datetime1, y-value1], [datetime2, y-value2], and so on.]

那個結構不是收集資料的最有效方式,但是記住你的初心是什麼一直是很重要的。我們將收集資料並且改變資料結構以便給 jqplot 需要的資料結構。

當第一步我不知道我需要什麼和使用什麼,我有更多一些測量。我認為,隨著時間的流逝,我看到機器的實際需求將改變,並且它非常容易改變。

{
  'server': servername,
  'datetime': datetime.now(),
  'disk_root': ,
  'phymem':,
  'cpu': {'user':, 'nice':, 'system':, 'idle':, 'irq':,},
}

下面是 python 程式碼從系統獲取資料(使用 psutil )到 MongoDB 資料庫。我在 MongoDB 中為每個被檢測到的機器建立了一個集合。

你可以在 MongoDB 中建立一個單獨的 document,包含了每臺機器的資料;這個依賴於你的需求。因為我想每臺機器有一個單獨的頁面,我用同樣的方式拆分資料。如果你想把所有機器的圖表渲染到一個頁面中的話,你或許想讓所有機器的資料在一個 document 中。

About the MongoDB Collection

我有一個三個成員的 MongoDB 副本,設定名為 rs1。儲存資料的機器恰巧是我監控的伺服器,example01 和 example02。這第三個是一個仲裁者,不儲存資料。這些 mongoDB 伺服器不需要監控,它們可以在任何地方。

我有一個數據庫 reports,我們將把新的 collections 放入這個資料庫。對於每臺機器,我將有一個 collection 來包含它的負載資料:1440 分每天,每 5 分組抽樣一次,並儲存 2 天的資料,我們需要 576 條記錄(documents)。

(1440/5)*2 = 576 records per server

我不確定我最後要使用多少資料,因此我預估了 2k 每個 document,我為每個 document 預估一個比較大的大小, 因為這僅僅是個開始,後面我需要收集更多的資料,結果是 2k 真的是非常慷慨了,每個記錄的平均大小是 200 bytes 左右,但是我沒有包含任何的網路資料(並且 disk space 是低耗的)。

576 documents @ 2048 bytes per doc = 1,179,648 bytes

對於每一臺機器需要監控的機器,我建立了一個固定集合的最大大小上限 1179648 和一個最大的數量上限 576 documents:

use reports
db.createCollection('example01', {capped:true, size:1179648, max:576})
db.createCollection('example02', {capped:true, size:1179648, max:576})

通過使用一個固定集合,我們將保證資料是以插入順序儲存的,隨著時間流逝,老的 documents 會自動刪除,因此我們始終有最新的 48 小時資料。

The Data Gathering Code

首先,做必須的 imports 並且連線到 MongoDB 例項:

from datetime import datetime
import psutil
import pymongo
import socket

conn = pymongo.MongoReplicaSetClient(
    'example01.com, example02.com',
    replicaSet='rs1',
)
db = conn.reports

現在為你想要的每一個數據呼叫 psutil

def main():
    cpu = psutil.cpu_times_percent()
    disk_root = psutil.disk_usage('/')
    phymem = psutil.phymem_usage()

建立一個字典包含你需要的時間序列結構資料。

    doc = dict()
    doc['server'] = socket.gethostname()
    doc['date'] = datetime.now()
    doc['disk_root'] = disk_root.free, 
    doc['phymem'] = phymem.free

    doc['cpu'] = {
        'user': cpu.user, 
        'nice': cpu.nice,
        'system': cpu.system, 
        'idle': cpu.idle,
        'irq': cpu.irq
    }

最後,把這個字典作為一個 document 新增進匹配的 MongoDB 集合中。它將被轉換成一個 BSON document 當它被插入資料庫的時候,但是結構是一樣的。

    if doc['server'] == 'example01.com':
        db.example01.insert(doc)
    elif doc['server'] == 'example02':
        db.example02.insert(doc)

這裡你有程式碼來獲取資料並且儲存進資料庫集合中。所有剩下的部分是執行程式碼自動完成的。

在你想監控的每臺伺服器上設定一個定時任務,每 5 分鐘執行一次:

*/5 * * * * /path/to/psutil_script

每個 MongoDB 集合包含 48 小時的系統性能資料,隨你操弄。

Part 2: Set up the bottle Server

建立一個 bottle 應用程式來查詢 MongoDB集合。

連線 MongoDB,在收到每個請求伺服器的資料後,給每個對應的伺服器響應格式化的資料。

from bottle import Bottle
import pymongo
load = Bottle()

conn = pymongo.MongoReplicaSetClient(
    'example01.com, example02.com',
    replicaSet='rs1',
)
db = conn.reports

這是一個路由,一個 url 連線。當一個請求進來,從 url 中獲取伺服器的名字,然後建立並返回一個合適的資料結構(jqplot 需要的)。

@load.get('/<server>')
def get_loaddata(server):
    data_cursor = list()
    if server == 'example02':
        data_cursor = db.example02.find()
    elif server == 'example01':
        data_cursor = db.example01.find()

    disk_root_free = list()
    phymem_free = list()
    cpu_user = list()
    cpu_nice = list()
    cpu_system = list()
    cpu_idle = list()
    cpu_irq = list()

    for data in data_cursor:
        date = data['date']
        disk_root_free.append([date, data['disk_root'])
        phymem_free.append([date, data['phymem'])
        cpu_user.append([date, data['cpu']['user']])
        cpu_nice.append([date, data['cpu']['nice']])
        cpu_system.append([date, data['cpu']['system']])
        cpu_idle.append([date, data['cpu']['idle']])
        cpu_irq.append([date, data['cpu']['irq']])

    return {
            'disk_root_free': disk_root_free,
            'phymem_free': phymem_free
            'cpu_user': cpu_user,
            'cpu_irq': cpu_irq,
            'cpu_system': cpu_system,
            'cpu_nice': cpu_nice,
            'cpu_idle': cpu_idle,
            }

Part 3: Display the Data with jqplot

HTML Page

HTML 頁是非常簡單的。

  1. 在樣式表中讀取.
  2. 編寫一個 div 儲存每個圖表。這以下的示例中我僅僅展示了 cpu_user 資料。該模式對於其他變數是一樣的。
  3. 載入 javascript。

你可以把 javascript 從 psmonitor.js 中直接放入頁面,或者是以一個檔案的形式呼叫它(如示例那樣):

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Load Monitoring</title>
<link rel="stylesheet" type="text/css" href="/css/jquery.jqplot.css" />
<style>div {margin:auto;}</style>
</head>
<body>

    <div id="cpu_user" style="height:400px;width:800px; "></div>

<script src="http://code.jquery.com/jquery-latest.min.js" ></script>
<script type="text/javascript" src="/js/jquery.jqplot.js"></script>
<script type="text/javascript" src="/js/jqplot.json2.js"></script>
<script type="text/javascript" src="/js/jqplot.dateAxisRenderer.js"></script>
<script type="text/javascript" src="/js/jqplot.highlighter.js"></script>
<script type="text/javascript" src="/js/jqplot.cursor.js"></script>
<!--[if lt IE 9]>
  <script language="javascript" type="text/javascript" src="excanvas.js"></script>
<![endif]-->


<script type="text/javascript" src="/js/psmonitor.js" />


</body>
</html>

The JavaScript (jqplot) Code

完整的程式碼在 GitHub 工程中,但是這有 jqplot 程式碼片段用來設定顯示資料。機器 example01 執行的 web 伺服器將返回 json 格式的負載資料。同樣,web 伺服器可以執行在任何機器上,在我的示例中,它發生在被我們監控的伺服器中的一臺。

每個 plot 的程式碼遵循了相同的模式:

  1. 發起一個 AJAX 請求呼叫執行著 bottle 應用的伺服器。
  2. 把你想圖表化的資料放入一個變數。
  3. 把這個變數傳遞給 jqplot。

程式碼中的 url 包含了 example01 字串:

url: "http://example01/load/example01"

example01 的第一個例項是 web 伺服器的地址,因為該機器上執行著 bottle 應用。第二個例項是我們想要資料的這臺伺服器的名字。伺服器名字()被傳遞進 bottle 路由(get_loaddata)檢索 MongoDB 記錄(documents):

$(document).ready(function(){
    var jsonData = $.ajax({
      async: false,
      url: "http://example01/load/example01",
      dataType:"json"
    });

    var cpu_user = [jsonData.responseJSON['cpu_user']];

    $.jqplot('cpu_user',  cpu_user, {
        title: "CPU User Percent: EXAMPLE01",
        highlighter: {show: true, sizeAdjust: 7.5},
        cursor: {show: false},
        axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer, 
              tickOptions:{formatString:"%a %H:%M"}}},
        series:[{lineWidth:1, showMarker: false}]
    });
});

該 javascript 劃分 CPU 使用者百分比;你可以用相同的方式新增另外的 plots,僅僅需要改變變數的名字和標題。