From 4d9ffa212e71107b905ca85706c2abd469c9b904 Mon Sep 17 00:00:00 2001
From: lxbhahaha <32586299+lxbhahaha@users.noreply.github.com>
Date: Wed, 16 Oct 2024 13:48:18 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7=E4=BD=BF?=
=?UTF-8?q?=E7=94=A8=E6=83=85=E5=86=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 4 +++-
app.py | 39 ++++++++++++++++++++++++++++++++++++---
index.html | 42 +++++++++++++++++++++++++++++++++++-------
3 files changed, 74 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 52b7ea4..2d0240e 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ pip install flask flask-cors paramiko -i https://pypi.tuna.tsinghua.edu.cn/simpl
因为本质上是通过ssh连接服务器,然后通过命令来获取相应的信息,有的命令可能服务器系统上不自带需要另外安装,否则无法获取到对应的数据。
- **ifstat**,用于获取网络数据的工具,可通过apt安装(如果不需要显示网络数据则不用安装)。并且需要在服务器上运行一下命令,查看哪个网卡才是主要的,写到配置文件里去(如果不需要查看网络信息可以不写)。
+- **gpustat**,用于获取显卡上用户的使用情况,也可通过apt安装。
- **nvidia驱动**,需要需要安装N卡的驱动,能够通过`nvidia-smi`来获取显卡信息即可(AMD的应该就没办法了)。
其中这个ifstat查看网卡的步骤如下:通过apt安装完成之后,在终端输入`ifstat`,可以看到类似下面的输出(ctrl+c停止),因为一般会不只一个网卡,而且名称也会不一样。此时可以看一下哪个名称的网卡有数据变化,比如下方的就是`eno2`,可以写到配置文件里。
@@ -89,4 +90,5 @@ document.addEventListener('DOMContentLoaded', function() {
有域名的话也可以搞一个反向代理,可参考 [服务器上使用Nginx部署网页+反向代理](http://blog.lxblxb.top/archives/1723257245091)。
# 3. 其他
-- 永辉帮忙搞了一下顶部checkbox布局的问题
\ No newline at end of file
+- `永辉`帮忙搞了一下顶部checkbox布局的问题。
+- 参考`治鹏`的方法加了每张显卡的用户使用的情况。
\ No newline at end of file
diff --git a/app.py b/app.py
index 64d6235..8c1ce2e 100644
--- a/app.py
+++ b/app.py
@@ -76,14 +76,48 @@ def get_gpus_info(client, timeout, info_list:list=None):
'free_mem': free_mem,
'util_gpu': util_gpu,
'util_mem': util_mem,
- 'temperature': temperature
+ 'temperature': temperature,
+ 'users': {}
})
+ # 读取用户使用信息
+ try:
+ gpustat_cmd = 'gpustat --json'
+ stdin, stdout, stderr = client.exec_command(gpustat_cmd, timeout=timeout)
+ gpustat_output = stdout.read().decode()
+
+ # 确保 gpustat 输出不是空的
+ if not gpustat_output:
+ raise ValueError("gpustat did not return any output.")
+
+ gpustat_info = json.loads(gpustat_output)
+
+ # 确保解析的 gpustat 信息格式正确
+ if 'gpus' not in gpustat_info:
+ raise ValueError("Parsed gpustat info does not contain 'gpus' key.")
+
+ # 解析进程信息 -----------------------------
+ for gpu in gpustat_info['gpus']:
+ idx = gpu['index']
+ processes = gpu.get('processes', []) # 使用 get() 方法避免 KeyError
+ for process in processes:
+ username = process['username']
+ gpu_memory_usage = process['gpu_memory_usage'] # 占用的显存
+ # 找到对应的 GPU,将用户及其显存使用情况记录下来
+ for gpu_result in result:
+ if gpu_result['idx'] == idx:
+ if username not in gpu_result['users']:
+ gpu_result['users'][username] = 0
+ gpu_result['users'][username] += gpu_memory_usage
+ except Exception as e:
+ if info_list is not None:
+ info_list.append(f'gpu user: {e}')
+
return result
except Exception as e:
if info_list is not None:
info_list.append(f'gpus: {e}')
- None
+ return None
def get_storage_info(client, timeout, path_list, info_list:list=None):
try:
@@ -157,7 +191,6 @@ def keep_check_one(server: dict, shared_data_list: dict, server_title: str, inte
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(server['ip'], port=server['port'], username=server['username'], password=server.get('password', None), key_filename=server.get('key_filename', None), timeout=interval*3)
- cmd = 'nvidia-smi --query-gpu=index,name,memory.total,memory.used,memory.free,utilization.gpu,utilization.memory,temperature.gpu --format=csv'
shared_data_list[server_title]['err_info'] = None
re_try_count = 0
diff --git a/index.html b/index.html
index bf61059..0c034c6 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,23 @@
}
.gpu-info {
margin-top: 10px;
+ border: 1px solid #ccc; /* 边框 */
+ border-radius: 8px; /* 圆角 */
+ padding: 10px; /* 内边距 */
+ margin-bottom: 15px; /* 下边距 */
+ background-color: #f9f9f9; /* 背景颜色 */
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影 */
+ }
+
+ .user-info {
+ margin-top: 10px; /* 上边距 */
+ font-size: 14px; /* 字体大小 */
+ color: #555; /* 字体颜色 */
+ }
+
+ .user-item {
+ color: #007bff; /* 用户名颜色 */
+ font-weight: bold; /* 加粗 */
}
/* 头部样式 */
@@ -76,7 +93,7 @@
// 请求服务器获取GPus数据
function fetchData() {
- fetch('http://lxblxb.top:15002/all_data')
+ fetch('http://127.0.0.1:15002/all_data')
// 获取服务器和显卡数据
.then(response => response.json()) // 解析 JSON 响应
.then(data => {
@@ -239,9 +256,23 @@
colorDot = redDot;
}
gpuInfo.innerHTML = '' + gpu.idx + ' - ' + gpu.gpu_name + colorDot + '
'
- + 'Temperature: ' + gpu.temperature + '°C
'
- + 'Memory: ' + gpu.used_mem + ' / ' + gpu.total_mem + " MB" + '
'
- + 'Utilization: ' + gpu.util_gpu + '%';
+ + '温度: ' + gpu.temperature + '°C
'
+ + '显存: ' + gpu.used_mem + ' / ' + gpu.total_mem + " MB" + '
'
+ + '利用率: ' + gpu.util_gpu + '%';
+
+ // 添加用户使用信息
+ if ('users' in gpu) { // 检查是否有用户信息
+ let userInfo = document.createElement('div');
+ userInfo.classList.add('user-info');
+ userInfo.innerHTML = "使用情况:";
+
+ for (const [username, pid] of Object.entries(gpu.users)) {
+ userInfo.innerHTML += `${username} (${pid}) `;
+ }
+
+ gpuInfo.appendChild(userInfo); // 将用户信息添加到GPU信息中
+ }
+
serverCard.appendChild(gpuInfo);
});
// 分割线
@@ -251,9 +282,6 @@
// 错误信息
if ('err_info' in serverData[key])
{
- // 分割线
- add_bar(serverCard);
-
let errInfo = document.createElement('div');
errInfo.classList.add('error-info');
errInfo.innerHTML = 'error info
' + serverData[key].err_info;