Fork me on GitHub
Suzf  Blog

How-to use Flask and mysql to build a simple monitor

License: Attribution-NonCommercial-ShareAlike 4.0 International

本文出自 Suzf Blog。 如未注明,均为 SUZF.NET 原创。

转载请注明:http://suzf.net/post/854

本文主要讲述的是: 如何使用 Flask + Mysql + Highcharts 构建一个简单的监控系统 以监控系统内存为例的简易 Demo,文档参考 运维之路 的教程。 整体思路是:

  • 收集数据
  • 存储数据
  • 展示数据

建库建表

mysql> select database();
+------------+
| database() |
+------------+
| highcharts |
+------------+
1 row in set (0.00 sec)

mysql> show create table stat;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                                                                             |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| stat  | CREATE TABLE `stat` (
  `id` int(20) unsigned NOT NULL AUTO_INCREMENT,
  `host` varchar(60) DEFAULT NULL,
  `mem_free` int(11) DEFAULT NULL,
  `mem_usage` int(11) DEFAULT NULL,
  `mem_total` int(11) DEFAULT NULL,
  `load_avg` varchar(23) DEFAULT NULL,
  `time` bigint(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=132 DEFAULT CHARSET=utf8 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

首先我们来看一下 目录结构

$tree .
.
├── flask_web_agent.py
├── flask_web.py
├── static
│   └── js
│       ├── exporting.js
│       ├── highstock.js
│       └── jquery-1.8.3.min.js
└── templates
    └── index.html

3 directories, 6 files

唠叨直接略过,直接上代码 当然这需要有一些 Python Flask html js 的基础 Agent

#/usr/bin/env python
# -*- encoding=utf-8 -*-


import time
import json
import socket
import urllib, urllib2
import inspect


class moon:
    def __init__(self):
        self.data = {}

    def getTime(self):
        return str(int(time.time()) + 8 * 3600)

    def getHost(self):
        return socket.gethostname()

    def getLoadAvg(self):
        with open('/proc/loadavg') as load_f:
             item = load_f.read().split()[:3]
             return ','.join(item)

    def getMemTotal(self):
        with open('/proc/meminfo') as mem_f:
             item = int(mem_f.readline().split()[1])
             return item / 1024

    def getMemUsage(self):
        with open('/proc/meminfo') as mem_f:
            item = int(mem_f.readline().split()[1]) - int(mem_f.readline().split()[1])
            return item / 1024

    def getMemFree(self):
        with open('/proc/meminfo') as mem_f:
            mem_f.readline()
            item = int(mem_f.readline().split()[1])
            return item / 1024

    def runAllGet(self):
        '''Auto get method to build dict'''
        for fun in inspect.getmembers(self, predicate=inspect.ismethod):
             if fun[0][:3] == 'get':
                self.data[fun[0][3:]] = fun[1]()
        return self.data

if __name__ == "__main__":
     while True:
        m = moon()
        data = m.runAllGet()

        try:
            # req = urllib2.Request("http://172.16.3.100:5000", json.dumps(data), {'Content-Type': 'application/json'})
            # f = urllib2.urlopen(req)
            url = 'http://172.16.3.100:5000'
            req = urllib2.Request(url)
            req.add_header("Content-Type","application/json")
            f = urllib2.urlopen(req, json.dumps(data))
            response = f.read()
            print response
            f.close()

        except urllib2.HTTPError,e:
            print e.code
            print e.read()
        time.sleep(60)

Server

#!/usr/bin/env python
# -*- encoding: utf-8 -*-


import json
import MySQLdb as mysql
from flask import Flask, request, render_template, g
from flask.ext.script import Manager

''' Create my app '''
app = Flask(__name__)
manager = Manager(app)

def connect_db():
    ''' Connect to database '''
    conn = mysql.connect(user="Robin", passwd="", db="highcharts", host="127.0.0.1", charset="utf8")
    return conn

@app.before_request
def before_request():
    ''' Make sure we connect the db before each connect'''
    g.db = connect_db()

@app.after_request
def after_request(response):
    g.db.close()
    return response

@app.route("/", methods=["GET", "POST"])
def hello():
    query = ""
    cur = g.db.cursor()
    if request.method == "POST":
        data = request.json
        try:
            query = "INSERT INTO `stat` (`host`,`mem_free`,`mem_usage`,`mem_total`,`load_avg`,`time`) VALUES('%s', '%d', '%d', '%d', '%s', '%d')" % (data['Host'], data['MemFree'], data['MemUsage'], data['MemTotal'], data['LoadAvg'], int(data['Time']))
            cur.execute(query)
            cur.close()
            g.db.commit()

        except mysql.Error, e:
            print "Mysql Error %d: %s" % (e.args[0], e.args[1])

        return "Saved data successed."
    else:
        return render_template("index.html")

@app.route("/data/<item>", methods=["GET"])
def getdata(item):
    cur = g.db.cursor()
    query = "SELECT `time`,%s  FROM `stat`" % item
    cur.execute(query)

    ones = [[i[0] * 1000, i[1]] for i in cur.fetchall()]
    cur.close()
    return "%s(%s);" % (request.args.get('callback'), json.dumps(ones))

if __name__ == "__main__":
    # app.run(host="0.0.0.0", port=666, debug=True)
    manager.run()

Highcharts 相关源码 可以直接从官网下载: Highcharts 相关Demo 点击这里 获取 使用highstock 绘图 ,具体模板页面内容如下:

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <!-- <meta http-equiv="refresh" content="20">-->
        <title>Flask demo - suzf.net</title>
        <script src='/static/js/jquery-1.8.3.min.js'></script>
        <script src='/static/js/highstock.js'></script>
        <script src='/static/js/exporting.js'></script>
    </head>
    <body>
        <div id="container" style="height: 500px; width: 700px; margin: 0 auto;">
            <script type="text/javascript">
                $(function () {
                var seriesOptions = [],
                    seriesCounter = 0,
                    names = ['MEM_FREE', 'MEM_USAGE', 'MEM_TOTAL'];

                /**
                 * Create the chart when all data is loaded
                 * @returns {undefined}
                 */
                function createChart() {

                    $('#container').highcharts('StockChart', {

                        rangeSelector: {
                            selected: 4
                        },

                        title: {
                            text: 'Mem info',
                            x: -20
                        },
                        yAxis: {
                            labels: {
                                formatter: function () {
                                    return (this.value > 0 ? ' + ' : '') + this.value + '%';
                                }
                            },
                            plotLines: [{
                                value: 0,
                                width: 2,
                                color: 'silver'
                            }]
                        },

                        plotOptions: {
                            series: {
                                compare: 'percent'
                            }
                        },
                       credits:{
                           enabled:true,
                           text:'suzf.net',
                           href:'http://suzf.net',
                           style: {
                                 cursor: 'pointer',
                                 color: 'green',
                                 fontSize: '10px'
                               }
                           },

                        tooltip: {
                            pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
                            valueDecimals: 2
                        },

                        series: seriesOptions
                    });
                }

                $.each(names, function (i, name) {

                    $.getJSON('http://172.16.3.100:5000/data/' + name.toLowerCase() + '?callback=?', function (data) {
                        seriesOptions[i] = {
                            name: name,
                            data: data
                        };

                        // As we're loading the data asynchronously, we don't know what order it will arrive. So
                        // we keep a counter and create the chart when all the data is loaded.
                        seriesCounter += 1;

                        if (seriesCounter === names.length) {
                            createChart();
                        }
                    });
                });
            });

            </script>
        </div>
    </body>
</html>

所有工作准备就绪,执行程序 开始测试:

python flask_web.py runserver -h 0.0.0.0 &
python flask_web_agent.py &

成果验收 flask_mem_info 后期扩展是 监控多台机器。 待续 。。。

「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」

Suzf Blog

(๑>ڡ<)☆ 谢谢 ~

使用微信扫描二维码完成支付