您可以将这个文件(我们称之为 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)。
现在,您应该能看到一个汇总了所有服务器性能数据的漂亮仪表盘了。这个页面同样会自动刷新。
网友回复


