JupyterHub Dashboard

This is a simple web app that provides a dashboard view of users and the GPU resources they are currently utilizing.

Built as a Flask app there are three files that make up the entire project. First, the primary application file contains the following.

from flask import Flask, render_template import subprocess import re from io import StringIO import csv import json import pprint pp = pprint.PrettyPrinter() app = Flask(__name__) def get_jupyter_pods(): pods = [] try: process = subprocess.run( ['kubectl', 'get', 'pods', '-n', 'jhub'], stdout=subprocess.PIPE) pod_info = process.stdout.decode('utf-8') pod_info_squeeze = re.sub(r' {2,}', ' ', pod_info) f = StringIO(pod_info_squeeze) reader = csv.DictReader(f, delimiter=' ') for row in reader: if 'jupyter' in row['NAME']: pods.append(row['NAME']) except: return [] return pods def get_pod_data(name): try: process = subprocess.run( ['kubectl', 'get', 'pod', name, '-n', 'jhub', '-o', 'json'], stdout=subprocess.PIPE) obj = process.stdout.decode('utf-8') return json.loads(obj) except: return None def get_pod_user(pod): for item in pod['spec']['containers'][0]['env']: if item['name'] == 'JUPYTERHUB_USER': return item['value'] return None def get_pod_image(pod): for item in pod['spec']['containers'][0]['env']: if item['name'] == 'JUPYTER_IMAGE': return item['value'] return None def get_pod_node(pod): return pod['spec']['nodeName'] def get_pod_gpus(pod): resource_requests = pod['spec']['containers'][0]['resources']['requests'] if 'nvidia.com/gpu' in resource_requests: return resource_requests['nvidia.com/gpu'] else: return None @app.route('/') def dashboard(): data = [] pods = get_jupyter_pods() for pod in pods: pod_data = get_pod_data(pod) user = get_pod_user(pod_data) image = get_pod_image(pod_data) node = get_pod_node(pod_data) gpus = get_pod_gpus(pod_data) data.append({ 'user': user, 'image': image, 'node': node, 'gpus': gpus }) return render_template('dashboard.html', data=data) if __name__ == '__main__': app.run(host='0.0.0.0', port=8080)
Code language: Python (python)

The second is a simple css style file with the following contents. It should be located in static/style.css.

h1 { padding: 20px; } table { border-collapse: collapse; } div.table { padding-left: 20px; } td, th { text-align: left; padding: 15px 10px 15px 10px; border: 0px; border-bottom: 1px solid #ddd; } th { background-color: #444; color: white; } tr:hover { background-color: #eee; }
Code language: CSS (css)

The final file is an html template with the following contents. It should be located in templates/dashboard.html.

<!doctype html> <html> <head> <link rel="stylesheet" href="static/style.css"> </head> <title>JupyterHub Dashboard</title> <h1>JupyterHub Dashboard</h1> <div class="table"> {% if data %} <table> <tr> <th>User</th> <th>Node</th> <th>GPUs</th> <th>Image</th> </tr> {% for item in data %} <tr> <td>{{ item['user'] }}</td> <td>{{ item['node'] }}</td> <td>{{ item['gpus'] }}</td> <td>{{ item['image'] }}</td> </tr> {% endfor %} </table> {% else %} <div>No launched servers</div> {% endif %} </div> </html>
Code language: HTML, XML (xml)

The web app can be run with the following command.

python dashboard-app.py
Code language: CSS (css)

Alternatively, the app can be run automatically on reboot as a service. Create the file /etc/systemd/system/jupyterhub-dashboard.service with the following contents.

[Unit] Description=Web app that displays a simple dashboard describing the utilization of JupyterHub resources. [Service] User=[username] WorkingDirectory=/path/to/app/dir ExecStart=/usr/bin/python3 /path/to/app/dashboard-app.py [Install] WantedBy=multi-user.target
Code language: Bash (bash)

Then run the following commands to enable the service.

sudo systemctl enable jupyterhub-dashboard.service service jupyterhub-dashboard start service jupyterhub-dashboard status
Code language: Bash (bash)

Leave a comment

Your email address will not be published. Required fields are marked *