python flusk 监控

创建虚拟环境目录 python3 -m venv /sda1/xunjian/venv # 激活虚拟环境 source /sda1/xunjian/venv/bin/activate # 激活后终端会显示 (venv)

  1. 创建虚拟环境(在当前目录):

    bash

    复制代码
    python3 -m venv venv
  2. 激活虚拟环境

    bash

    复制代码
    source venv/bin/activate  # 激活后终端会显示 (venv)
  3. 在虚拟环境中安装依赖(此时无权限限制):

    bash

    复制代码
    pip install flask psutil
  4. 运行脚本(需在虚拟环境中):

    bash

    复制代码
    python xun.py

完成后,若要退出虚拟环境,执行:

bash

复制代码
deactivate

虚拟环境能完全隔离项目依赖,避免影响系统 Python 环境,是标准且安全的做法。

安装依赖(在虚拟环境中):

复制代码
pip install flask paramiko

import psutil
import subprocess
import time
import paramiko
from flask import Flask, render_template_string, jsonify
from threading import Thread, Lock

app = Flask(__name__)
lock = Lock()

# 监控配置:本地 + 远程机器(修改远程机器SSH信息)
MONITOR_CONFIG = {
    "local": {
        "name": "本地服务器",
        "type": "local"
    },
    "remote-1": {
        "name": "远程服务器 (192.168.1.7)",
        "type": "remote",
        "ip": "192.168.1.7",
        "username": "your_ssh_username",  # 替换为远程SSH用户名
        "password": "your_ssh_password",  # 替换为远程SSH密码
        "services": ["nginx", "mysql", "ssh"]  # 远程监控服务
    }
}

# 本地监控服务列表
LOCAL_SERVICES = ["nginx", "mysql", "docker"]


# -------------------------- 工具函数:单位转换(核心修复) --------------------------
def convert_to_gb(value_str):
    """将带单位的存储值(如462M、2.5G)转换为GB的浮点数"""
    if not value_str:
        return 0.0
    value_str = value_str.strip().upper()
    if 'G' in value_str:
        return float(value_str.replace('G', ''))
    elif 'M' in value_str:
        return float(value_str.replace('M', '')) / 1024  # M转G
    elif 'K' in value_str:
        return float(value_str.replace('K', '')) / (1024 **2)  # K转G
    else:
        return 0.0


# -------------------------- 本地机器监控 --------------------------
def get_local_system_info():
    """获取本地系统资源信息(修复状态判断)"""
    # CPU
    cpu_percent = psutil.cpu_percent(interval=1)
    cpu_cores = psutil.cpu_count(logical=False)
    
    # 内存
    memory = psutil.virtual_memory()
    memory_used = round(memory.used / (1024** 3), 2)
    memory_total = round(memory.total / (1024 **3), 2)
    
    # 磁盘(根目录)
    disk = psutil.disk_usage('/')
    disk_used = round(disk.used / (1024** 3), 2)
    disk_total = round(disk.total / (1024 **3), 2)
    
    # Swap
    swap = psutil.swap_memory()
    swap_used = round(swap.used / (1024** 3), 2)
    swap_total = round(swap.total / (1024 **3), 2)
    
    return {
        "cpu": {"percent": cpu_percent, "cores": cpu_cores},
        "memory": {"used": memory_used, "total": memory_total, "percent": memory.percent},
        "disk": {"used": disk_used, "total": disk_total, "percent": disk.percent},
        "swap": {"used": swap_used, "total": swap_total, "percent": swap.percent}
    }


def check_local_service_status(service):
    """检查本地服务状态"""
    try:
        result = subprocess.run(
            f"systemctl is-active {service}",
            shell=True,
            capture_output=True,
            text=True
        )
        return result.stdout.strip() == "active"
    except:
        for p in psutil.process_iter(["name"]):
            if service in p.info["name"].lower():
                return True
        return False


# -------------------------- 远程机器监控(核心修复) --------------------------
def ssh_exec_command(ip, username, password, command):
    """通过SSH执行远程命令"""
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(ip, username=username, password=password, timeout=10)
        stdin, stdout, stderr = ssh.exec_command(command)
        output = stdout.read().decode().strip()
        error = stderr.read().decode().strip()
        return output, error
    except Exception as e:
        return None, str(e)
    finally:
        ssh.close()


def get_remote_system_info(config):
    """获取远程系统资源信息(修复单位转换错误)"""
    ip = config["ip"]
    username = config["username"]
    password = config["password"]
    
    # CPU使用率(修复命令,兼容更多系统)
    cpu_output, err = ssh_exec_command(
        ip, username, password, 
        "grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage}'"
    )
    cpu_percent = float(cpu_output) if cpu_output and cpu_output.replace('.', '', 1).isdigit() else 0.0
    
    # 内存信息(处理M/GB单位)
    mem_output, err = ssh_exec_command(
        ip, username, password, 
        "free -h | grep Mem | awk '{print $3, $2}'"  # 只取已用和总量(如"462M 1.8G")
    )
    mem_used = 0.0
    mem_total = 0.0
    mem_percent = 0.0
    if mem_output and len(mem_output.split()) == 2:
        mem_used_str, mem_total_str = mem_output.split()
        mem_used = convert_to_gb(mem_used_str)
        mem_total = convert_to_gb(mem_total_str)
        mem_percent = (mem_used / mem_total) * 100 if mem_total > 0 else 0.0
    
    # 磁盘信息(处理M/GB单位)
    disk_output, err = ssh_exec_command(
        ip, username, password, 
        "df -h / | tail -1 | awk '{print $3, $2}'"  # 已用和总量(如"5.8G 6.3G")
    )
    disk_used = 0.0
    disk_total = 0.0
    disk_percent = 0.0
    if disk_output and len(disk_output.split()) == 2:
        disk_used_str, disk_total_str = disk_output.split()
        disk_used = convert_to_gb(disk_used_str)
        disk_total = convert_to_gb(disk_total_str)
        disk_percent = (disk_used / disk_total) * 100 if disk_total > 0 else 0.0
    
    # Swap信息(处理M/GB单位)
    swap_output, err = ssh_exec_command(
        ip, username, password, 
        "free -h | grep Swap | awk '{print $3, $2}'"
    )
    swap_used = 0.0
    swap_total = 0.0
    swap_percent = 0.0
    if swap_output and len(swap_output.split()) == 2:
        swap_used_str, swap_total_str = swap_output.split()
        swap_used = convert_to_gb(swap_used_str)
        swap_total = convert_to_gb(swap_total_str)
        swap_percent = (swap_used / swap_total) * 100 if swap_total > 0 else 0.0
    
    return {
        "cpu": {"percent": round(cpu_percent, 1), "cores": 0},
        "memory": {"used": round(mem_used, 2), "total": round(mem_total, 2), "percent": round(mem_percent, 1)},
        "disk": {"used": round(disk_used, 2), "total": round(disk_total, 2), "percent": round(disk_percent, 1)},
        "swap": {"used": round(swap_used, 2), "total": round(swap_total, 2), "percent": round(swap_percent, 1)}
    }


def check_remote_service_status(config, service):
    """检查远程服务状态(确保返回布尔值)"""
    ip = config["ip"]
    username = config["username"]
    password = config["password"]
    
    # 先检查systemctl状态
    output, err = ssh_exec_command(ip, username, password, f"systemctl is-active {service}")
    if output == "active":
        return True
    # 再检查进程
    output, err = ssh_exec_command(ip, username, password, f"pgrep -x {service} >/dev/null 2>&1 && echo 'running'")
    return output == "running"


# -------------------------- 数据更新线程 --------------------------
def update_monitor_data():
    """定期更新所有机器的监控数据(修复状态显示)"""
    while True:
        with lock:
            # 本地机器数据(明确标记为online)
            try:
                local_system = get_local_system_info()
                local_services = [
                    {"name": s, "is_running": check_local_service_status(s)}
                    for s in LOCAL_SERVICES
                ]
                monitor_data["local"] = {
                    "name": MONITOR_CONFIG["local"]["name"],
                    "system": local_system,
                    "services": local_services,
                    "update_time": time.strftime('%Y-%m-%d %H:%M:%S'),
                    "status": "online"  # 本地默认在线
                }
            except Exception as e:
                monitor_data["local"] = {
                    "name": MONITOR_CONFIG["local"]["name"],
                    "error": str(e),
                    "status": "offline",
                    "update_time": time.strftime('%Y-%m-%d %H:%M:%S')
                }
            
            # 远程机器数据(完善错误处理)
            remote_config = MONITOR_CONFIG["remote-1"]
            try:
                remote_system = get_remote_system_info(remote_config)
                # 确保服务列表不为空
                remote_services = []
                for s in remote_config["services"]:
                    try:
                        remote_services.append({
                            "name": s,
                            "is_running": check_remote_service_status(remote_config, s)
                        })
                    except:
                        remote_services.append({
                            "name": s,
                            "is_running": False
                        })
                monitor_data["remote-1"] = {
                    "name": remote_config["name"],
                    "system": remote_system,
                    "services": remote_services,
                    "update_time": time.strftime('%Y-%m-%d %H:%M:%S'),
                    "status": "online"
                }
            except Exception as e:
                # 即使出错,也返回空服务列表避免前端undefined
                monitor_data["remote-1"] = {
                    "name": remote_config["name"],
                    "error": f"连接失败: {str(e)}",
                    "services": [{"name": s, "is_running": False} for s in remote_config["services"]],
                    "status": "offline",
                    "update_time": time.strftime('%Y-%m-%d %H:%M:%S')
                }
        
        time.sleep(10)  # 每10秒更新一次


# 初始化监控数据存储
monitor_data = {}
# 启动更新线程
update_thread = Thread(target=update_monitor_data, daemon=True)
update_thread.start()


# -------------------------- Web页面与API --------------------------
@app.route('/')
def index():
    return render_template_string('''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>多机器监控仪表盘</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        .machine-card {
            border: 1px solid #e2e8f0;
            border-radius: 0.5rem;
            padding: 1rem;
            margin-bottom: 1rem;
        }
        .status-running { color: #10b981; }
        .status-stopped { color: #ef4444; }
        .status-online { color: #10b981; }
        .status-offline { color: #ef4444; }
    </style>
</head>
<body class="bg-gray-50 p-4">
    <h1 class="text-2xl font-bold mb-6">多机器监控仪表盘</h1>
    
    <div id="monitor-data">
        <!-- 数据将通过JS动态填充 -->
    </div>

    <script>
        function formatData() {
            fetch('/api/data')
                .then(res => res.json())
                .then(data => {
                    let html = '';
                    for (let key in data) {
                        const machine = data[key];
                        // 处理未定义的系统数据
                        const system = machine.system || {
                            cpu: {percent: 0},
                            memory: {used: 0, total: 0},
                            disk: {used: 0, total: 0},
                            swap: {used: 0, total: 0}
                        };
                        // 处理未定义的服务
                        const services = machine.services || [];
                        
                        html += `
                        <div class="machine-card">
                            <h2 class="text-xl font-semibold mb-4">
                                ${machine.name} 
                                <span class="text-sm ml-2 ${machine.status === 'online' ? 'status-online' : 'status-offline'}">
                                    ${machine.status === 'online' ? '在线' : '离线'}
                                </span>
                            </h2>
                            ${machine.error ? `<p class="text-red-500 mb-4 text-sm">${machine.error}</p>` : ''}
                            
                            <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
                                <div class="bg-white p-3 rounded shadow">
                                    <h3 class="text-sm text-gray-500">CPU使用率</h3>
                                    <p class="text-xl font-bold">${system.cpu.percent.toFixed(1)}%</p>
                                </div>
                                <div class="bg-white p-3 rounded shadow">
                                    <h3 class="text-sm text-gray-500">内存使用</h3>
                                    <p class="text-xl font-bold">${system.memory.used.toFixed(2)}G/${system.memory.total.toFixed(2)}G</p>
                                </div>
                                <div class="bg-white p-3 rounded shadow">
                                    <h3 class="text-sm text-gray-500">磁盘使用</h3>
                                    <p class="text-xl font-bold">${system.disk.used.toFixed(2)}G/${system.disk.total.toFixed(2)}G</p>
                                </div>
                                <div class="bg-white p-3 rounded shadow">
                                    <h3 class="text-sm text-gray-500">Swap使用</h3>
                                    <p class="text-xl font-bold">${system.swap.used.toFixed(2)}G/${system.swap.total.toFixed(2)}G</p>
                                </div>
                            </div>
                            
                            <div class="mb-2">
                                <h3 class="text-lg font-medium mb-2">服务状态</h3>
                                <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2">
                                    ${services.map(s => `
                                        <div class="bg-white p-2 rounded shadow flex justify-between items-center">
                                            <span>${s.name}</span>
                                            <span class="${s.is_running ? 'status-running' : 'status-stopped'}">
                                                <i class="fa ${s.is_running ? 'fa-check-circle' : 'fa-times-circle'}"></i>
                                                ${s.is_running ? '运行中' : '已停止'}
                                            </span>
                                        </div>
                                    `).join('')}
                                </div>
                            </div>
                            
                            <p class="text-xs text-gray-500">最后更新: ${machine.update_time}</p>
                        </div>`;
                    }
                    document.getElementById('monitor-data').innerHTML = html;
                });
        }

        // 初始加载和定时刷新
        formatData();
        setInterval(formatData, 10000);
    </script>
</body>
</html>
    ''')


@app.route('/api/data')
def api_data():
    with lock:
        return jsonify(monitor_data)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
相关推荐
不写八个14 分钟前
GoLang教程005:switch分支
开发语言·后端·golang
ZeroToOneDev19 分钟前
Java(LinkedList和ArrayList底层分析)
java·开发语言
吃饭只吃七分饱1 小时前
C++第8章:IO库
开发语言·c++
宴之敖者、2 小时前
数组——初识数据结构
c语言·开发语言·数据结构·算法
青小莫2 小时前
c语言-数据结构-二叉树OJ
c语言·开发语言·数据结构·二叉树·力扣
典学长编程2 小时前
Java从入门到精通!第十一天(Java常见的数据结构)
java·开发语言·数据结构
后端小张2 小时前
智谱AI图生视频:从批处理到多线程优化
开发语言·人工智能·ai·langchain·音视频
m0dw2 小时前
js迭代器
开发语言·前端·javascript
叫我:松哥2 小时前
基于python django深度学习的中文文本检测+识别,可以前端上传图片和后台管理图片
图像处理·人工智能·后端·python·深度学习·数据挖掘·django
Bonnie_12153 小时前
02-netty基础-java四种IO模型
java·开发语言·nio·jetty