先日、PythonでCPU使用率やメモリ資料率を監視するためのアプリを作成してみました。
elsammit-beginnerblg.hatenablog.com
今回は、こちらのアプリを改造してWebアプリを作成!!
これでネットワークが接続された環境であればどこでもサーバの監視・管理ができます!!
■環境
前回と同様にPythonのバージョンは、
Python 3.7.3
とします。
また、Webアプリ作成にあたり、サーバサイドはFlaskを用います。
Flaskについてはこちらにまとめておりますので、よろしかったらご参考にしてください。
elsammit-beginnerblg.hatenablog.com
■完成形
今回作成したアプリですがこちらの通りになります。
左側グラフにはCPU使用率 & メモリ使用率を表示させ、
右側グラフには使用しているメモリや空きメモリの容量について表示させています。
最後にグラフの下部にストレージの使用量をバー表示させました。
今回はこちらのアプリに関して、サーバサイド、フロントエンドに分けてまとめていきたいと思います!!
■Webアプリ(サーバサイド編)
ではまずはサーバサイドについてまとめていきたいと思います!!
コードはこちら!!
#!/usr/bin/env python from importlib import import_module import os from flask import Flask, render_template, Response, url_for, request, redirect import psutil import numpy as np import json import csv import math app = Flask(__name__) def allwed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.context_processor def override_url_for(): return dict(url_for=dated_url_for) def dated_url_for(endpoint, **values): if endpoint == 'static': filename = values.get('filename', None) if filename: file_path = os.path.join(app.root_path, endpoint, filename) values['q'] = int(os.stat(file_path).st_mtime) return url_for(endpoint, **values) x = 1 y = {0:[0,0]} def GetResource(): #使用率取得用コールバック関数. global x mem = psutil.virtual_memory() cpu = psutil.cpu_percent(interval=0.1, percpu=False) y[x] = [mem.percent,cpu] x += 1 if x > 200: x = 0 y.clear() x2 = 1 y2 = {0:[0,0,0]} def GetResource2(): #メモリ詳細情報取得用コールバック関数. global x2 mem = psutil.virtual_memory() free = mem.free/1000000 used = mem.used/1000000 available = mem.available/1000000 y2[x2] = [free, used, available] x2 += 1 if x2 > 200: x2 = 0 y2.clear() #メイン画面用html @app.route('/') def index(): return render_template('index.html') #CPU使用率 & メモリ使用率返却用 @app.route('/UseCheck', methods=["POST"]) def RespResource(): GetResource() return Response(json.dumps(y), 200) #メモリ使用率詳細返却用 @app.route('/CheckDetail', methods=["POST"]) def RespResource2(): GetResource2() return Response(json.dumps(y2), 200) #ストレージ容量返却用 @app.route('/StorageCheck', methods=["POST"]) def RespResource3(): storage = psutil.disk_usage('/mnt/samba') jsn = { "persent":storage.percent, "total":math.floor(storage.total/1000000000), "used":math.floor(storage.used/1000000000) } return Response(json.dumps(jsn), 200) if __name__ == '__main__': w_str = "No,memory persent(%),cpu persent(%) \n" with open("output.csv", mode='w') as f: f.write(w_str) app.run(host='0.0.0.0', threaded=True)
CPUやメモリ使用率の取得に関しては先日まとめたpsutilライブラリを用いて取得しています。
使い方やコードの書き方についてはこちらをご参考ください。
elsammit-beginnerblg.hatenablog.com
今回フロント・サーバ間はPOST通信を用いておjり、サーバからのresponseはjson形式のデータとしました。
json形式のデータはそれぞれ、
・CPU使用率 & メモリ使用率(左端グラフ用データ)
{ '番号':['メモリ使用率','CPU使用率'] }
・使用しているメモリや空きメモリの容量(右端グラフ用データ)
{ '番号':['空きメモリ','使用メモリ','availableメモリ'] }
・ストレージ容量
{ "persent":使用率, "total":全体ストレージサイズ, "used":使用ストレージサイズ }
といった形式としました。
■Webアプリ(フロントエンド編)
次にフロントサイド側です。
グラフ表示を行うにあたり、Chart.jsを用いました。
html文はこちらの通りになります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js"></script> <link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}"> <title>Document</title> </head> <body> <h1>リソースチェックサイト</h1> <div id="graphArea"> <p id="graphTitle">リソース使用率</p> <canvas id="myLineChart"></canvas> </div> <div id="graphArea"> <p id="graphTitle">メモリ使用状況</p> <canvas id="myLineChart2"></canvas> </div> <div id="barArea"> <label for="storageBar">ストレージ容量:</label> <progress id="storageBar" max="100" value="70"></progress> <div id="storageSize">****GB/****GB</div> </div> <script src="{{url_for('static', filename='js/script.js')}}"></script> </body> </html>
Chart.jsを用いるために、
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js"></script>
をコールしておりかつ、postリクエストを行うにあたりjQueryを用いたかったので
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
をコールしています。
canvas上にChart.jsでのグラフ表示を行うため、
<canvas id="myLineChart"></canvas> <canvas id="myLineChart2"></canvas>
を記載しています。
次にjavascriptです。
こちらは少し長いです。。
var labels = [1,2,3,4,5,6,7,8,9,10,11,12]; var sample1 = [1.9, 2.32, 1.52, 0.79, 1.37, 1.28, 1.92, 1.44, 2.58, -0.01, 0.71, 4.25]; var sample2 = [7.01, -2.15, -7.29, 1.71, 0.72, -4.83, 2.75, 4.11, 3.08, -2.45, 3.05, -3.93]; var sample21 = [1.9, 2.32, 1.52, 0.79, 1.37, 1.28, 1.92, 1.44, 2.58, -0.01, 0.71, 4.25]; var sample22 = [7.01, -2.15, -7.29, 1.71, 0.72, -4.83, 2.75, 4.11, 3.08, -2.45, 3.05, -3.93]; var sample23 = [5.9, 8.32, -1.52, 9.79, 10.37, -1.28, 10.92, 11.44, 12.58, -0.51, 1.71, -4.25]; var ctx = document.getElementById("myLineChart"); chart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'メモリ使用率', data: sample1, borderColor: 'rgba(60, 160, 220, 1)', backgroundColor: "rgba(0,0,0,0)" },{ label: 'CPU使用率', data: sample2, borderColor: 'rgba(60, 190, 20, 1)', backgroundColor: "rgba(0,0,0,0)" }] }, options: { animation: false, scales: { xAxes: [{ scaleLabel: { display: true, labelString: '時間(秒)', fontColor: "black", fontSize: 16 }, ticks:{ max:15, min:0, stepSize:1, fontColor: "black", fontSize: 14 } }], yAxes: [{ scaleLabel: { display: true, labelString: '使用率(%)', fontColor: "black", fontSize: 16 } }] } } }); var ctx = document.getElementById("myLineChart2"); var chart2 = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'メモリ-free', data: sample21, borderColor: 'rgba(60, 160, 220, 1)', backgroundColor: "rgba(0,0,0,0)" },{ label: 'メモリ-used', data: sample22, borderColor: 'rgba(60, 190, 20, 1)', backgroundColor: "rgba(0,0,0,0)" },{ label: 'メモリ-available', data: sample23, borderColor: 'rgba(160, 90, 120, 1)', backgroundColor: "rgba(0,0,0,0)" }] }, options: { animation: false, scales: { xAxes: [{ scaleLabel: { display: true, labelString: '時間(秒)', fontColor: "black", fontSize: 16 }, ticks:{ max:15, min:0, stepSize:1, fontColor: "black", fontSize: 14 } }], yAxes: [{ scaleLabel: { display: true, labelString: 'メモリ容量(MB)', fontColor: "black", fontSize: 16 } }] } } }); function RecieveData(){ getResourceData(); getResourceData2(); getResourceData3(); chart.data.datasets[0].data = sample1; chart.data.datasets[1].data = sample2; chart.update(); chart2.data.datasets[0].data = sample21; chart2.data.datasets[1].data = sample22; chart2.data.datasets[2].data = sample23; chart2.update(); } function setChartLabel(jsn, ChartBuf){ if(Object.keys(jsn).length > 12){ ChartBuf.options.scales.xAxes[0].ticks.max = Object.keys(jsn).length; ChartBuf.options.scales.xAxes[0].ticks.min = Object.keys(jsn).length - 12; }else{ ChartBuf.options.scales.xAxes[0].ticks.max = 12; ChartBuf.options.scales.xAxes[0].ticks.min = 0; } } function getResourceData(){ $.ajax({ url: '/UseCheck', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); for(var i=0;i<Object.keys(jsn).length;i++){ sample1[i] = jsn[i][0]; sample2[i] = jsn[i][1]; labels[i] = i; } setChartLabel(jsn, chart); }).fail(function() { console.log("error"); }) } function getResourceData2(){ $.ajax({ url: '/CheckDetail', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); for(var i=0;i<Object.keys(jsn).length;i++){ sample21[i] = jsn[i][0]; sample22[i] = jsn[i][1]; sample23[i] = jsn[i][1]; labels[i] = i; } setChartLabel(jsn, chart2); }).fail(function() { console.log("error"); }) } function getResourceData3(){ $.ajax({ url: '/StorageCheck', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); var bar = document.getElementById("storageBar"); var storageSize = document.getElementById("storageSize"); bar.value = jsn["persent"]; storageSize.innerText = jsn["used"] + "GB/" + jsn["total"] + "GB"; }).fail(function() { console.log("error"); }) } setInterval(RecieveData,1000);
やっていることは、RecieveData関数を1秒ごとにコールし、
RecieveData内でjQueryによるPostリクエストを実行しています。
function getResourceData(){ $.ajax({ url: '/UseCheck', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); for(var i=0;i<Object.keys(jsn).length;i++){ sample1[i] = jsn[i][0]; sample2[i] = jsn[i][1]; labels[i] = i; } setChartLabel(jsn, chart); }).fail(function() { console.log("error"); }) } function getResourceData2(){ $.ajax({ url: '/CheckDetail', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); for(var i=0;i<Object.keys(jsn).length;i++){ sample21[i] = jsn[i][0]; sample22[i] = jsn[i][1]; sample23[i] = jsn[i][1]; labels[i] = i; } setChartLabel(jsn, chart2); }).fail(function() { console.log("error"); }) } function getResourceData3(){ $.ajax({ url: '/StorageCheck', type:'POST', dataType:"text", timeout:3000, }).done(function(data) { jsn = JSON.parse(data.toString()); var bar = document.getElementById("storageBar"); var storageSize = document.getElementById("storageSize"); bar.value = jsn["persent"]; storageSize.innerText = jsn["used"] + "GB/" + jsn["total"] + "GB"; }).fail(function() { console.log("error"); }) }
先ほどのサーバサイドで記載した通り、responseはjson形式となるので、
JSON.parseによりjsonを分割し適切な変数に格納しています。
本変数に格納したデータをChart.jsでグラフ化して表示しています。
グラフ範囲を大きくしすぎると見にくくなってしまうので、
範囲は12にし、本範囲を自動的に変化させられるようにしました。
範囲の自動変更に関してはこれからも使う技術な気がしているので、次回のブログにて細かく記載していきたいと思います!!
■最後に
ちょっと長くなってしまいました。
Webで公開したことにより、逐次見えるようになったのは少しありがたいかな?w
いったん自宅サーバに導入して使い勝手を見ていきたいと思います!!
使い勝手が悪いところは後で直そっと。