+
18
-

回答

您可以将这个文件(我们称之为 agent.py)分发到您需要监控的每一台服务器上运行。

这个代理程序将完成两件事:

提供一个独立的网页:在每台服务器上直接访问 http://<服务器IP>:5000,可以实时看到该服务器的性能指标。

提供一个数据接口:它有一个接口,可以被一个“中心服务器”调用,以收集这台服务器的性能数据。

接下来,我将提供两个代码块:

agent.py: 这是您需要部署到每一台被监控服务器上的核心代码。

central_server.py: 这是一个简单的汇总服务器示例,用来向所有 agent.py 拉取数据并统一展示。

第1步:监控代理 agent.py

将以下代码保存为 agent.py 文件,并将其放置在您想要监控的每一台服务器上。

安装依赖:在每台服务器上,您需要先安装几个必要的 Python 库:

pip install Flask psutil

代码 (agent.py):

import psutil
import platform
import socket
from flask import Flask, jsonify, render_template_string

# 创建 Flask 应用
app = Flask(__name__)

# --- 内嵌的 HTML 模板 ---
# 使用 Jinja2 模板语法 {{ variable_name }} 来插入动态数据
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<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">
    <title>服务器状态监控 - {{ hostname }}</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f4f7f9; color: #333; margin: 0; padding: 20px; }
        .container { max-width: 800px; margin: 0 auto; background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 20px; }
        h1, h2 { color: #2c3e50; border-bottom: 2px solid #e0e0e0; padding-bottom: 10px; }
        .metric { margin-bottom: 20px; }
        .metric-label { font-weight: bold; color: #555; display: block; margin-bottom: 5px; }
        .progress-bar { width: 100%; background-color: #e0e0e0; border-radius: 4px; overflow: hidden; }
        .progress { height: 20px; color: #fff; text-align: center; line-height: 20px; font-weight: bold; transition: width 0.5s ease-in-out; }
        .cpu { background-color: #3498db; }
        .memory { background-color: #2ecc71; }
        .disk { background-color: #e74c3c; }
        .info-table { width: 100%; border-collapse: collapse; margin-top: 10px; }
        .info-table td { padding: 8px 12px; border: 1px solid #ddd; }
        .info-table tr:nth-child(even) { background-color: #f9f9f9; }
        .info-table td:first-child { font-weight: bold; width: 30%; }
        footer { text-align: center; margin-top: 20px; color: #888; font-size: 0.9em; }
    </style>
</head>
<body>
    <div class="container">
        <h1>服务器状态监控</h1>

        <div class="metric">
            <span class="metric-label">主机名: {{ hostname }} (IP: {{ ip_address }})</span>
        </div>

        <div class="metric">
            <span class="metric-label">CPU 使用率</span>
            <div class="progress-bar">
                <div class="progress cpu" style="width: {{ cpu_percent }}%;">{{ cpu_percent }}%</div>
            </div>
        </div>

        <div class="metric">
            <span class="metric-label">内存使用率</span>
            <div class="progress-bar">
                <div class="progress memory" style="width: {{ memory.percent }}%;">{{ memory.percent }}%</div>
            </div>
        </div>

        <div class="metric">
            <span class="metric-label">磁盘使用率 ({{ disk.mountpoint }})</span>
            <div class="progress-bar">
                <div class="progress disk" style="width: {{ disk.percent }}%;">{{ disk.percent }}%</div>
            </div>
        </div>

        <h2>详细信息</h2>
        <table class="info-table">
            <tr><td>操作系统</td><td>{{ system_info.system }} {{ system_info.release }}</td></tr>
            <tr><td>CPU 核心数</td><td>物理核心: {{ cpu_cores.physical }}, 总核心: {{ cpu_cores.logical }}</td></tr>
            <tr><td>总内存</td><td>{{ '%.2f'|format(memory.total / (1024*1024*1024)) }} GB</td></tr>
            <tr><td>已用内存</td><td>{{ '%.2f'|format(memory.used / (1024*1024*1024)) }} GB</td></tr>
            <tr><td>总磁盘空间</td><td>{{ '%.2f'|format(disk.total / (1024*1024*1024)) }} GB</td></tr>
            <tr><td>已用磁盘空间</td><td>{{ '%.2f'|format(disk.used / (1024*1024*1024)) }} GB</td></tr>
        </table>
    </div>
    <footer>
        <p>页面将自动刷新。最后更新于 <span id="update-time"></span></p>
    </footer>

    <script>
        // 自动刷新页面以获取最新数据
        setTimeout(() => {
            location.reload();
        }, 15000); // 15秒刷新一次

        // 显示最后更新时间
        document.getElementById('update-time').innerText = new Date().toLocaleTimeString();
    </script>
</body>
</html>
"""

def get_system_stats():
    """获取系统性能指标"""
    # CPU
    cpu_percent = psutil.cpu_percent(interval=1)
    cpu_cores = {
        "physical": psutil.cpu_count(logical=False),
        "logical": psutil.cpu_count(logical=True)
    }

    # 内存
    memory = psutil.virtual_memory()

    # 磁盘 (只获取根目录 '/')
    disk = psutil.disk_usage('/')

    # 系统信息
    system_info = {
        "system": platform.system(),
        "release": platform.release()
    }

    # 主机名和IP
    hostname = socket.gethostname()
    try:
        ip_address = socket.gethostbyname(hostname)
    except socket.gaierror:
        ip_address = "127.0.0.1"


    stats = {
        "hostname": hostname,
        "ip_address": ip_address,
        "cpu_percent": cpu_percent,
        "cpu_cores": cpu_cores,
        "memory": memory,
        "disk": disk,
        "system_info": system_info
    }
    return stats

@app.route('/')
def index():
    """渲染HTML主页"""
    stats = get_system_stats()
    return render_template_string(HTML_TEMPLATE, **stats)

@app.route('/api/stats')
def api_stats():
    """提供JSON格式的性能数据接口"""
    stats = get_system_stats()
    # psutil返回的对象不是直接JSON可序列化的,需要提取关键值
    return jsonify({
        "hostname": stats["hostname"],
        "ip_address": stats["ip_address"],
        "cpu_percent": stats["cpu_percent"],
        "memory_percent": stats["memory"].percent,
        "disk_percent": stats["disk"].percent,
        "memory_total_gb": round(stats["memory"].total / (1024**3), 2),
        "memory_used_gb": round(stats["memory"].used / (1024**3), 2),
        "disk_total_gb": round(stats["disk"].total / (1024**3), 2),
        "disk_used_gb": round(stats["disk"].used / (1024**3), 2)
    })

if __name__ == '__main__':
    # 监听在 0.0.0.0 上,允许局域网内所有IP访问
    app.run(host='0.0.0.0', port=5000)

如何运行 agent.py:

将代码保存为 agent.py。

在服务器的终端中运行它:python agent.py。

为了让它在后台持续运行,建议使用 nohup 或 supervisor 等工具:

nohup python agent.py &

现在,您可以通过浏览器访问 http://<服务器IP>:5000 来查看该服务器的监控页面了。

第2步:中央汇总服务器 central_server.py

这是一个单独的程序,您可以运行在您自己的电脑或任何一台管理服务器上。它会去轮询所有配置好的 agent.py 服务,并把数据汇总到一个页面上。

安装依赖:

pip install Flask requests

代码 (central_server.py):

import requests
import concurrent.futures
from flask import Flask, render_template_string

app = Flask(__name__)

# --- 在这里配置您所有服务器的 agent 地址 ---
# 格式: "http://<服务器IP>:5000"
SERVER_AGENTS = [
    "http://192.168.1.10:5000",
    "http://192.168.1.11:5000",
    "http://10.0.0.5:5000",
    # 如果在本地测试 agent.py,可以添加下面这行
    "http://127.0.0.1:5000", 
]

# --- 内嵌的汇总页面 HTML 模板 ---
CENTRAL_HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多服务器监控仪表盘</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background-color: #f4f7f9; color: #333; margin: 0; padding: 20px; }
        h1 { text-align: center; color: #2c3e50; margin-bottom: 30px; }
        .grid-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; }
        .card { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); padding: 20px; transition: transform 0.2s; }
        .card:hover { transform: translateY(-5px); }
        .card.offline { background-color: #fbecec; border-left: 5px solid #e74c3c; }
        .card.online { border-left: 5px solid #2ecc71; }
        .card-header { margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
        .hostname { font-size: 1.2em; font-weight: bold; }
        .ip-address { font-size: 0.9em; color: #888; }
        .metric { margin-bottom: 12px; }
        .metric-label { font-weight: bold; color: #555; display: inline-block; width: 120px; }
        .progress-bar { display: inline-block; width: calc(100% - 130px); background-color: #e0e0e0; border-radius: 4px; overflow: hidden; height: 18px; vertical-align: middle; }
        .progress { height: 100%; color: #fff; text-align: right; line-height: 18px; font-size: 0.8em; padding-right: 5px; white-space: nowrap; }
        .cpu { background-color: #3498db; }
        .memory { background-color: #2ecc71; }
        .disk { background-color: #e74c3c; }
        footer { text-align: center; margin-top: 30px; color: #888; font-size: 0.9em; }
    </style>
</head>
<body>
    <h1>多服务器监控仪表盘</h1>
    <div class="grid-container">
        {% for server in servers %}
            <div class="card {% if server.status == 'online' %}online{% else %}offline{% endif %}">
                <div class="card-header">
                    <span class="hostname">{{ server.data.hostname or 'N/A' }}</span>
                    <span class="ip-address">{{ server.data.ip_address or server.url }}</span>
                </div>
                {% if server.status == 'online' %}
                    <div class="metric">
                        <span class="metric-label">CPU</span>
                        <div class="progress-bar"><div class="progress cpu" style="width: {{ server.data.cpu_percent }}%;">{{ server.data.cpu_percent }}%</div></div>
                    </div>
                    <div class="metric">
                        <span class="metric-label">内存</span>
                        <div class="progress-bar"><div class="progress memory" style="width: {{ server.data.memory_percent }}%;">{{ '%.1f'|format(server.data.memory_used_gb) }} / {{ '%.1f'|format(server.data.memory_total_gb) }} GB</div></div>
                    </div>
                    <div class="metric">
                        <span class="metric-label">磁盘</span>
                        <div class="progress-bar"><div class="progress disk" style="width: {{ server.data.disk_percent }}%;">{{ server.data.disk_percent }}%</div></div>
                    </div>
                {% else %}
                    <p><strong>状态:</strong> 离线或无法访问</p>
                    <p><strong>错误:</strong> {{ server.error }}</p>
                {% endif %}
            </div>
        {% endfor %}
    </div>
    <footer>
        <p>页面将每15秒自动刷新。最后更新于 <span id="update-time"></span></p>
    </footer>
    <script>
        setTimeout(() => { location.reload(); }, 15000);
        document.getElementById('update-time').innerText = new Date().toLocaleTimeString();
    </script>
</body>
</html>
"""

def fetch_server_data(url):
    """从单个 agent 获取数据"""
    try:
        # 设置一个较短的超时时间,例如3秒
        response = requests.get(f"{url}/api/stats", timeout=3)
        response.raise_for_status()  # 如果请求失败 (如 404, 500), 抛出异常
        return {"status": "online", "url": url, "data": response.json()}
    except requests.exceptions.RequestException as e:
        return {"status": "offline", "url": url, "data": {}, "error": str(type(e).__name__)}

@app.route('/')
def dashboard():
    """渲染汇总仪表盘"""
    all_server_data = []
    # 使用线程池并发获取所有服务器的数据,提高效率
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = executor.map(fetch_server_data, SERVER_AGENTS)
        for result in results:
            all_server_data.append(result)

    return render_template_string(CENTRAL_HTML_TEMPLATE, servers=all_server_data)

if __name__ == '__main__':
    # 运行在本地的 8080 端口
    app.run(host='0.0.0.0', port=8080, debug=True)

如何运行 central_server.py:

修改配置:打开 central_server.py 文件,找到 SERVER_AGENTS 列表,将其中的 IP 地址替换为您所有运行了 agent.py 的服务器的真实 IP 地址。

运行程序:在您的电脑或管理服务器上,运行此脚本: python central_server.py。

访问仪表盘:打开您的浏览器,访问 http://<运行中央服务器的IP>:8080 (如果是本机运行,就是 http://127.0.0.1:8080)。

现在,您应该能看到一个汇总了所有服务器性能数据的漂亮仪表盘了。这个页面同样会自动刷新。

网友回复

我知道答案,我要回答