New Post

Membuat Aplikasi Monitoring Server Sederhana dengan Flask dan Socket.IO

Pendahuluan

Memantau performa sebuah server terkadang dilakukan oleh beberapasysadmin dengan memeriksanya melalui command line. Asumsikan seorangsysadmin ingin melihat performa CPU dan RAM, akan menggunakan perintahfree dan cat /proc/stat. Ada juga yang memasang aplikasi server monitoringyang sudah menyediakan berbagai alat untuk memantau kinerja server. Selain melihat performa hardware terkadang ingin juga melihat beberapa file logpenting seperti log milik Apache untuk melihat akses yang masuk atau erroryang terjadi pada sebuah aplikasi web yang dijalankan di atas Apache. Kali ini kita akan mencoba membangun sebuah aplikasi sederhana yang akan memantau kinerja CPU dan RAM server berbasis Linux dan melihat logApache yang dipasang di server tersebut. Untuk mendapatkan informasi tersebut kita akan menggunakan bantuan SocketIO, sebuah library Javascript yang membantu developer untuk mengembangkan aplikasi web berbasis Websocket. Untuk mencoba pengalaman yang lebih berbeda, aplikasi ini akan dibuat menggunakan Python dan Flask.

Persiapan Alat

Untuk tutorial ini, Anda membutuhkan beberapa perlengkapan yang cukup banyak. Diantaranya adalah sebagai berikut:
  • Sistem operasi Linux yang dipasang secara lokal. Anda dapat memasangnya di VirtualBox atau VMWare, Laptop Anda sendiri, atau PC yang dijadikan server di ruangan laboratorium komputer milik instansi Anda. Saya sarankan untuk memasang Ubuntu di mesin tersebut bagi yang baru mencoba Linux, dan untuk yang sudah berpengalaman silahkan gunakan Linux favoritnya :D
  • Apache Web Server, Anda dapat memasangnya di Linux melalui package manager atau pun memasangnya dari installer
  • Text Editor, untuk menulis kode Python dan HTML
  • Web Browser, untuk menampilkan aplikasi yang akan dicoba di tutorial ini
  • Python, untuk menjalankan kode Python yang akan ditulis biasanya sudah terpasang di beberapa distro Linux (contoh cara installapt-get install python)
  • PIP, merupakan package manager untuk memasang library Python (contoh cara installapt-get install python-pip)
  • Flask, sebuah web framework Python yang mungil dan powerful (contoh cara installpip install flask)
  • Flask-SocketIO, sebuah ekstensi Flask yang dipergunakan untuk membantu Anda membangun aplikasi web berbasis Websocket dan SocketIO (contoh cara installpip install flask-socketio)
  • PSUtil, sebuah library Python yang mempermudah Anda untuk mengakses informasi hardware seperti penggunaan CPU dan RAM (contoh cara installpip install psutil)
  • SocketIO-Client, sebuah library Javascript yang mempermudah pembangunan aplikasi berbasis Websocket. Anda dapat mengunduhnya di website resmi Socket.IO
  • jQuery, sebuah library Javascript yang mempermudah Anda memanipulasi halaman HTML. Anda dapat mengunduhnya di websiteresmi jQuery
Struktur Folder dan File Aplikasi Monitoring Server berbasis Flask dan Socketio
Struktur Folder dan File Aplikasi Monitoring Server berbasis Flask dan Socketio

Membuat kode server.py

Di dalam file server.py akan ada beberapa bagian kode yang harus dipahami. Pada bagian pertama ini kita akan melakukan proses penyertaan library yang terdapat di Python. Kita akakn menggunakan library Flask, Thread, PSUtil, dan Flask-SocketIO. Kita juga akan menggunakan library seperti datetime,timerandom, dan subprocess. Berikut adalah bagian kode server.py yang melakukan proses penyertaan library:
from flask import Flask, render_template
from flask.ext.socketio import SocketIO, emit
from threading import Thread, Event
import datetime, time, random, subprocess, psutil

# akan ada kode lain sesudah kode ini
Berikutnya kita akan membuat sebuah aplikasi Flask dengan nama variabelapp kemudian kita sertakan SECRET_KEY untuk menjamin security yang dijalankan oleh proses internal Flask, Anda dapat mengisinya dengan keymilik Anda. Tidak lupa kita jalankan juga mode debug agar dapat mempermudah kita menepis error yang terdapat di aplikasi kita bilamanaerror datang. Untuk membuat socketio kita membutuhkan SocketIO()dengan melewatkan app ke dalam constructor. Untuk threading sendiri, kita buat sebuah variabel bernama thread dan membuat variabelthread_stop_event untuk mendeteksi event yang terjadi pada thread. Berikut adalah bagian kode server.py yang melakukan proses penciptaan objek global di aplikasi server monitoring kita.:
# ada kode lain sebelum kode ini

app = Flask (__name__)
app.config['SECRET_KEY'] = 'rahasia'
app.debug = True
socketio = SocketIO(app)
thread = Thread()
thread_stop_event = Event()

# akan ada kode lain sesudah kode ini
Kemudian kita akan mendefinisikan sebuah class thread yang akan melakukan beberapa fitur monitoring. Di dalam class SystemInfoThread()dibawah ini, akan terdapat 7 method yang mempunyai perannya masing - masing. Berikut adalah method - method yang terdapat di classSystemInfoThread():
  • method init merupakan constructor yang akan dieksekusi saat classpertama kali diciptakan. method ini menerima sebuah parameter yaitusocketio. Parameter tersebut akan menerima objek socketio yang dilewatkan ke class ini
  • method get_cpu_info() merupakan method yang akan mengambil informasi penggunaan CPU dalam bentuk persen. Kemudian kita kirimkan data tersebut ke client yang terkoneksi ke server Websocket kita menggunakan self.socketio.emit() yang akan diterima oleh eventreceiveMessage
  • method get_mem_info() merupakan method yang akan mengambil informasi penggunaan RAM
  • method get_swap_info() merupakan method yang akan mengambil informasi penggunaan SWAP
  • method get_apache_access_log() merupakan method yang akan mengambil informasi terbaru dari file access.log milik Apache
  • method get_apache_error_log() merupakan method yang akan mengambil informasi terbaru dari file error.log milik Apache
  • method run() merupakan method yang akan menjalankan semua proses utama dari thread ketika method start() dari suatu thread dipanggil. Di dalam method ini terdapat pemeriksaan apakah thread masih berjalan atau tidak. Kemudian terdapat delay selama 1 detik untuk setiap kali pengambilan informasi.
Berikut adalah bagian kode server.py yang mendefinisikan class thread yaituSystemInfoThread():
# ada kode lain sebelum kode ini

class SystemInfoThread(Thread):
    def __init__(self, socketio):
        self.delay = 1
        self.socketio = socketio
        super(SystemInfoThread, self).__init__()

    def get_cpu_info(self):
        output = psutil.cpu_percent(interval=0, percpu=True)
        server_time = "%s" % datetime.datetime.now()
        self.socketio.emit('receiveMessage', {'type':'cpu', 'msg': output, 'time':server_time}, broadcast=True)

    def get_mem_info(self):
        mem = psutil.virtual_memory()
        output = mem.percent
        server_time = "%s" % datetime.datetime.now()
        self.socketio.emit('receiveMessage', {'type':'mem', 'msg': output, 'time':server_time}, broadcast=True)

    def get_swap_info(self):
        swap = psutil.swap_memory()
        output = swap.percent
        server_time = "%s" % datetime.datetime.now()
        self.socketio.emit('receiveMessage', {'type':'swap', 'msg': output, 'time':server_time}, broadcast=True)

    def get_apache_access_log(self):
        output = subprocess.check_output('tail /var/log/apache2/access.log', shell=True)
        server_time = "%s" % datetime.datetime.now()
        self.socketio.emit('receiveMessage', {'type':'apache_access_log', 'msg': output, 'time':server_time}, broadcast=True)

    def get_apache_error_log(self):
        output = subprocess.check_output('tail /var/log/apache2/error.log', shell=True)
        server_time = "%s" % datetime.datetime.now()
        self.socketio.emit('receiveMessage', {'type':'apache_error_log', 'msg': output, 'time':server_time}, broadcast=True)

    def run(self):
        while not thread_stop_event.isSet():
            self.get_cpu_info()
            self.get_mem_info()
            self.get_swap_info()
            self.get_apache_access_log()
            self.get_apache_error_log()

            time.sleep(self.delay)

# ada kode lain sesudah kode ini
Setelah membuat class SysInfoThread() sekarang kita akan menentukan fungsi yang akan melayani request via HTTP dan Websocket. Untuk menampilkan halaman index kita akan memberikan route "/" pada functionindex() yang akan melakukan proses template rendering terhadap fileindex.html. Lalu kita akan mendefinisikan dua event pada socketio yaitu ketika connect dan disconnect. Pada saat event connect dipanggil, threadSystemInfoThread() akan dibentuk kemudian dijalankan. Sebelum dijalankan akan diperiksa terlebih dahulu apakah thread tersebut sudah pernah dijalankan atau belum. Kemudian untuk event disconnect hanya terdapat informasi saja untuk server bilamana ada yang memutuskan koneksi. Setelah itu baris socketio.run(app, '0.0.0.0', 5000) akan menjalankan aplikasi padaport 5000. Berikut adalah bagian kode server.py yang mendefinisikan routingdan event dari aplikasi server monitoring:
# ada kode lain sebelum kode ini

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('connect', namespace='/')
def on_connect():
    print 'Ada yang terhubung dengan server...'

    global thread
    if not thread.isAlive():
        print "Starting Thread"
        thread = SystemInfoThread(socketio)
        thread.start()

@socketio.on('disconnect', namespace='/')
def on_disconnect():
    print 'Ada yang memutus hubungan dengan server...'

if __name__ == '__main__':
    socketio.run(app, '0.0.0.0', 5000)

Membuat kode index.html

Untuk file index.html kita akan membuat sebuah judul dengan h1 kemudian ada div dengan id dashboard di dalamnya terdapat sebuah table dan divuntuk menampilkan informasi RAM, CPU, dan file log Apache. Kemudian di dalam script terdapat inisialisasi untuk konek ke Websocket. Variabel sockettersebut akan menerima sebuah event yaitu receiveMessage. Di dalam eventtersebut akan ada penentuan dalam menampilkan pesan apakah pesan yang diterima adalah cpuramswapaccess_log, atau error_log. Berikut adalah kode index.html yang akan menampilkan informasi RAM, CPU, Swap, dan file log Apache:
<!DOCTYPE html>
<html>
    <head>
        <title>Simple Server Monitoring using Python</title>
        <style>
            .apache-log {
                width: 1150px;
                height: 200px;
                background: #F5F5F5 none repeat scroll 0% 0%;
                overflow-y: scroll;
                font-size:11px;
                margin: 10px;
            }
        </style>
    </head>
    <body>
        <h1>Basic Server Monitor</h1>
        <div id="dashboard">
            <table id="cpu-mem" style="margin:10px;width:1000px">
                <thead>
                    <tr>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                    </tr>
                </tbody>
            </table>
            <div class="apache-log" id="apache-access-log">
            </div>
            <div class="apache-log" id="apache-error-log">
            </div>
        </div>
        <script src="{{ url_for('static', filename='vendors/jquery-1.9.1.min.js') }}" type="text/javascript"></script>
        <script src="{{ url_for('static', filename='vendors/socket.io.min.js') }}" type="text/javascript"></script>
        <script type="text/javascript">
            var username = "";
            var socket = io.connect('http://localhost:5000/');

            socket.on('receiveMessage', function(data){
                if (data.type == 'apache_access_log')
                {
                    $('#apache-access-log').html("<pre>"+data.msg+"</pre>");
                }
                else if (data.type == 'apache_error_log')
                {
                    $('#apache-error-log').html("<pre>"+data.msg+"</pre>");
                }
                else if (data.type == 'cpu')
                {
                    cpu_len  = data.msg.length;
                    tb_head_elem = "";
                    tb_body_elem = "";
                    for (var i = 1; i <= cpu_len; i++)
                    {
                        tb_head_elem += "<td>CPU "+i+"</td>";
                        tb_body_elem += "<td>"+data.msg[i-1]+" %</td>";
                    }

                    $('#cpu-mem thead tr').html(tb_head_elem);
                    $('#cpu-mem tbody tr').html(tb_body_elem);
                }
                else if (data.type == 'mem')
                {
                    $('#cpu-mem thead tr').append('<td>Memory</td>');
                    $('#cpu-mem tbody tr').append('<td>'+data.msg+' %</td>');
                }
            });
        </script>
    </body>
</html>

Cara Menjalankan Aplikasi

Untuk menjalakan aplikasi, Anda tinggal masuk ke dalam folder tempat dimana server.py berada kemudian jalankan perintah python server.py dan akhirnya program akan berjalan seperti pada gambar berikut ini:
Jika diakses melalui web browser akan muncul tampilan seperti pada gambar berikut:
Aplikasi Monitoring Server di tutorial ini dijalankan di sebuah browser
Aplikasi Monitoring Server di tutorial ini dijalankan di sebuah browser
Jika terjadi proses request terhadap server maka perubahan akan terjadi segera pada halaman monitoring. Jika Anda mempunyai Apache Benchmark, Anda dapat melakukan benchmark terhadap website yang tersimpan di Apache Anda dan Anda akan melihat log akan berganti karena terjadi prosesrequest secara konkuren.

Penutup

Anda dapat menambahkan grafik garis agar informasi yang ditampilkan lebih menarik, dan salah satu kekurangan yang terdapat di tutorial ini adalah logyang ditampilkan akan ditimpa oleh log baru yang diambil. Sehingga ketika ingin melihat log sebelumnya tidak bisa dilakukan. Dan tidak hanya itu, logApache yang diambil hampir semuanya ditampilkan sehingga bila dipasang diserver yang sudah dalam fase produksi dapat menciptakan peluang ketidakamanan bila informasinya jatuh ke tangan yang tidak berhak.
Dengan menyelesaikan tutorial ini, Anda sudah dapat mengenal bagaimana cara kerja Websocket yang dibangun diatas Python dan Flask. Sehingga Anda dapat mengenal bahwa aplikasi Websocket tidak hanya dapat dibangun menggunakan Node.js, menggunakan Python pun dapat dilakukan.

Referensi

  • Miguel Grinberg Blog
  • Flask Official Documentation
  • Flask-SocketIO Documentation
  • jQuery Documentation
  • PSUtil Documentation
  • Python Documentation
  • W3Schools

Tidak ada komentar