适用版本: Proxmox VE 8 / 9 · Intel & AMD 均适用
难度: 初级 · 耗时: ~10 分钟
Proxmox VE 9 默认不在 Web UI 中展示硬件温度,对 Homelab 玩家来说是个小遗憾。本文提供经过验证的完整流程——安装传感器、修改 Dashboard、取消订阅弹窗,以及更现代的外部监控方案。
一、安装温度传感器
SSH 登录 PVE 节点,依次执行:
与人玫瑰,手留余香
适用版本: Proxmox VE 8 / 9 · Intel & AMD 均适用
难度: 初级 · 耗时: ~10 分钟
Proxmox VE 9 默认不在 Web UI 中展示硬件温度,对 Homelab 玩家来说是个小遗憾。本文提供经过验证的完整流程——安装传感器、修改 Dashboard、取消订阅弹窗,以及更现代的外部监控方案。
SSH 登录 PVE 节点,依次执行:
本文记录一次真实的 PVE 8.4.19 → PVE 9.2.2 原地升级过程,包含踩坑、源配置细节,适合国内用户参考。
| 项目 | 值 |
|---|---|
| 升级前版本 | pve-manager/8.4.19 |
| 升级后版本 | pve-manager/9.2.3 |
| 内核 | 6.8.12-25-pve → 6.14.x-pve |
| Debian | Bookworm → Trixie |
| 镜像源 | USTC(中科大) |
| Ceph | 未使用 |
| 容器 | 无 |
原版的openwrt磁盘容量太小,安装了两个插件, 磁盘容量就报不足了, 需要扩容磁盘
旧的博客PVE之openwrt的ext4格式扩容为2025年的博客,当前环境进行升级,核心步骤未变
PVE服务器版本
# root@:~# pveversion -v
proxmox-ve: 8.4.0 (running kernel: 6.8.12-15-pve)
pve-manager: 8.4.14 (running version: 8.4.14/b502d23c55afcba1)
cat /etc/config/network
注意:eth0为内网网卡,虚拟的局域网网卡,eth1为外网网卡可以上网的网卡
对应net0为内网网卡,net1为外网网卡
下面配置本版本只修改了option ipaddr '172.16.1.1'这一项
config interface 'loopback'
option device 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config globals 'globals'
option ula_prefix 'fd69:b7e9:1122::/48'
config device
option name 'br-lan'
option type 'bridge'
list ports 'eth0'
config interface 'lan'
option device 'br-lan'
option proto 'static'
option ipaddr '172.16.1.1'
option netmask '255.255.255.0'
option ip6assign '60'
config interface 'wan'
option device 'eth1'
option proto 'dhcp'
config interface 'wan6'
option device 'eth1'
option proto 'dhcpv6'
背景:我32G的内存,才开了3个虚拟机,内存竟然接近91%,需要找到到底谁在吃内存。
PVE版本信息
root@:~# pveversion -v
proxmox-ve: 8.4.0 (running kernel: 6.8.12-15-pve)
pve-manager: 8.4.14 (running version: 8.4.14/b502d23c55afcba1)
proxmox-kernel-helper: 8.1.4
proxmox-kernel-6.8: 6.8.12-15
root@:~# zfs --version
zfs-2.2.8-pve1
zfs-kmod-2.2.8-pve1
背景:我要调试最小化调试apk应用
JDK** 推荐
JDK 17(LTS)版本**
https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html#license-lightboxPVE版本信息
root@:~# pveversion -v
proxmox-ve: 8.4.0 (running kernel: 6.8.12-15-pve)
pve-manager: 8.4.14 (running version: 8.4.14/b502d23c55afcba1)
proxmox-kernel-helper: 8.1.4
proxmox-kernel-6.8: 6.8.12-15
我的PVE主机换了硅脂,拆了风扇,内存条被插拔了,然后ZFS每次重启需要导入
PVE版本信息
root@:~# pveversion -v
proxmox-ve: 8.4.0 (running kernel: 6.8.12-15-pve)
pve-manager: 8.4.14 (running version: 8.4.14/b502d23c55afcba1)
proxmox-kernel-helper: 8.1.4
proxmox-kernel-6.8: 6.8.12-15
PVE经过一系列的优化后,CPU 温度降到了 55 °C ~ 75 °C。但是我我发现我 32G的内存只有16G,另外一个内存好久之前就无法识别了。
PVE版本信息
root@:~# pveversion -v
proxmox-ve: 8.4.0 (running kernel: 6.8.12-15-pve)
pve-manager: 8.4.14 (running version: 8.4.14/b502d23c55afcba1)
proxmox-kernel-helper: 8.1.4
proxmox-kernel-6.8: 6.8.12-15
想了解浏览器可以捕获到客户端的那些行为用来做更深入的分析
index.html保存桌面浏览器运行
<!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: 'Segoe UI', Tahoma, sans-serif;
padding: 20px;
background: #f9f9f9;
max-width: 1000px;
margin: 0 auto;
}
.section {
margin-top: 5px;
border-top: 2px solid #eee;
}
.risk-high { color: #d32f2f; font-weight: bold; }
.risk-medium { color: #f57c00; }
.risk-low { color: #388e3c; }
.info {
background: #f0f7ff;
padding: 10px;
margin: 6px 0;
border-left: 4px solid #2196f3;
border-radius: 4px;
}
#log {
width: 100%;
height: 250px;
background: white;
border: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
margin-top: 5px;
font-size: 13px;
white-space: pre-wrap;
}
.counter { font-weight: bold; color: #c62828; }
/* ===== 行为状态横向布局 ===== */
.behavior-status {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 10px;
font-size: 14px;
}
.behavior-status > span {
white-space: nowrap;
}
button {
margin-top: 10px;
padding: 8px 16px;
background: #1976d2;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover { background: #1565c0; }
</style>
</head>
<body>
<p><strong>🔍 客户端行为检测:</strong>虚拟化环境 + 切屏/失焦行为</p>
<!-- 行为状态面板 -->
<div class="section">
<h3>👁️ 实时行为状态</h3>
<div class="behavior-status">
<span>页面可见性:<strong id="visibilityState">visible</strong></span>
<span>窗口聚焦:<strong id="focusState">true</strong></span>
<span>鼠标在页内:<strong id="mouseInState">未知</strong></span>
<span>失焦次数:<strong class="counter" id="blurCount">0</strong></span>
<span>隐藏次数:<strong class="counter" id="hiddenCount">0</strong></span>
<span>最长离开:<strong id="maxHiddenTime">0 秒</strong></span>
</div>
</div>
<!-- 环境风险 -->
<div class="section">
<h3>⚠️ 高风险虚拟化特征</h3>
<div id="highRisk"></div>
</div>
<button onclick="clearLog()">清空日志</button>
<div id="log"></div>
<!-- 详细信息 -->
<div class="section">
<h3>ℹ️ 详细环境指纹</h3>
<div id="details"></div>
</div>
<script>
// ========== 全局变量 ==========
let blurCount = 0;
let hiddenCount = 0;
let maxLeaveDuration = 0;
let leaveStartTime = null; // 统一记录离开开始时间
const logEl = document.getElementById('log');
// ========== 日志函数 ==========
function log(msg) {
const time = new Date().toLocaleTimeString();
logEl.innerHTML += `[${time}] ${msg}\n`;
logEl.scrollTop = logEl.scrollHeight;
}
function updateBehaviorStatus() {
document.getElementById('visibilityState').textContent = document.visibilityState;
const hasFocus = document.hasFocus();
document.getElementById('focusState').textContent = String(hasFocus);
}
// 判断是否处于“离开”状态:页面隐藏 或 窗口失焦
function isUserAway() {
return document.visibilityState === 'hidden' || !document.hasFocus();
}
// 开始离开计时(防重复)
function startLeaveTimer() {
if (leaveStartTime === null && isUserAway()) {
leaveStartTime = Date.now();
log('⏳ 用户离开页面(隐藏或失焦),开始计时...');
}
}
// 结束离开计时
function endLeaveTimer() {
if (leaveStartTime !== null && !isUserAway()) {
const durationSec = Math.round((Date.now() - leaveStartTime) / 1000);
if (durationSec > maxLeaveDuration) {
maxLeaveDuration = durationSec;
document.getElementById('maxHiddenTime').textContent = `${durationSec} 秒`;
}
log(`✅ 用户返回,本次离开时长:${durationSec} 秒`);
leaveStartTime = null;
}
}
// ========== 行为监听 ==========
// 页面可见性变化
document.addEventListener('visibilitychange', () => {
updateBehaviorStatus();
if (document.visibilityState === 'hidden') {
hiddenCount++;
document.getElementById('hiddenCount').textContent = hiddenCount;
log('⚠️ 页面变为 HIDDEN(可能被切屏/最小化)');
}
// 触发离开/返回判断
if (isUserAway()) {
startLeaveTimer();
} else {
endLeaveTimer();
}
});
// 窗口失焦
window.addEventListener('blur', () => {
blurCount++;
document.getElementById('blurCount').textContent = blurCount;
log('⚠️ WINDOW 失去焦点(blur)— 可能因点击本地程序、切换窗口等');
updateBehaviorStatus();
startLeaveTimer(); // 可能进入离开状态
});
// 窗口聚焦
window.addEventListener('focus', () => {
log('✅ WINDOW 重新获得焦点(focus)');
updateBehaviorStatus();
endLeaveTimer(); // 尝试结束离开
});
// 鼠标进出
document.addEventListener('mouseenter', () => {
document.getElementById('mouseInState').textContent = '是';
log('🖱️ 鼠标进入页面区域');
});
document.addEventListener('mouseleave', () => {
document.getElementById('mouseInState').textContent = '否';
log('🖱️ 鼠标离开页面区域(注意:仅移出不等于失焦)');
});
// ========== 环境检测函数(保持不变)==========
function detectWebGL() {
let info = [], risks = [];
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (gl) {
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'unknown';
const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || 'unknown';
info.push(`WebGL Renderer: ${renderer}`);
info.push(`WebGL Vendor: ${vendor}`);
const lowerRenderer = renderer.toLowerCase();
const lowerVendor = vendor.toLowerCase();
if (lowerRenderer.includes('vmware') || lowerRenderer.includes('svga')) {
risks.push('检测到 VMware 虚拟 GPU (SVGA3D)');
} else if (lowerRenderer.includes('virtualbox') || lowerVendor.includes('chromium')) {
risks.push('检测到 VirtualBox 虚拟 GPU (Chromium)');
} else if (lowerVendor.includes('microsoft basic') || lowerRenderer.includes('basic render')) {
risks.push('检测到 Microsoft Basic Render Driver(Hyper-V/远程桌面)');
} else if (lowerRenderer.includes('llvmpipe') || lowerRenderer.includes('software')) {
risks.push('使用软件渲染(常见于无 GPU 的虚拟机)');
}
}
} else {
info.push('WebGL 不可用');
risks.push('WebGL 不可用(真实设备通常支持)');
}
} catch (e) {
info.push('WebGL 检测出错: ' + e.message);
}
return { info, risks };
}
function detectScreen() {
const info = [];
const risks = [];
const cd = screen.colorDepth;
const w = screen.width;
const h = screen.height;
info.push(`分辨率: ${w}x${h}, 色深: ${cd} bit`);
if (cd <= 24) {
risks.push('屏幕色深 ≤24 bit(典型远程桌面/RDP 特征)');
}
return { info, risks };
}
function detectSystem() {
const info = [];
const risks = [];
const ua = navigator.userAgent;
const cores = navigator.hardwareConcurrency || 'unknown';
const memory = navigator.deviceMemory || 'unknown';
info.push(`UserAgent: ${ua}`);
info.push(`CPU 核心数: ${cores}`);
info.push(`估算内存 (GB): ${memory}`);
if (ua.includes('HeadlessChrome')) risks.push('检测到 Headless Chrome');
if (navigator.webdriver) risks.push('navigator.webdriver=true(自动化工具)');
if (typeof cores === 'number' && cores <= 2) risks.push('CPU 核心数 ≤2(轻量虚拟机配置)');
return { info, risks };
}
function detectNetwork() {
const info = [];
const risks = [];
const ips = new Set();
try {
const pc = new (window.RTCPeerConnection || window.webkitRTCPeerConnection)();
pc.createDataChannel('');
pc.onicecandidate = (e) => {
if (e.candidate) {
const match = e.candidate.candidate.match(/(\d+\.\d+\.\d+\.\d+)/);
if (match) {
const ip = match[1];
if (!ips.has(ip)) {
ips.add(ip);
info.push(`本地 IP: ${ip}`);
if (ip.startsWith('192.168.56.')) risks.push('IP 属于 VirtualBox Host-Only 网段');
else if (ip.startsWith('192.168.220.')) risks.push('IP 属于 VMware NAT 网段');
else if (ip.startsWith('10.0.2.')) risks.push('IP 属于 VirtualBox NAT 网段');
}
}
}
};
pc.createOffer().then(offer => pc.setLocalDescription(offer));
setTimeout(() => pc.close(), 2000);
} catch (e) {
info.push('WebRTC 不可用');
}
if (ips.size === 0) info.push('未获取到本地 IP(WebRTC 可能被禁用)');
return { info, risks };
}
function detectSensors() {
const info = [];
const risks = [];
if (!('Accelerometer' in window)) {
risks.push('缺少设备传感器(常见于虚拟机)');
}
info.push(`Battery API 支持: ${'getBattery' in navigator ? '是' : '否'}`);
return { info, risks };
}
// ========== 主检测逻辑 ==========
function runEnvironmentDetection() {
const allInfo = [];
const highRisks = [];
const tests = [detectSystem, detectScreen, detectWebGL, detectNetwork, detectSensors];
tests.forEach(test => {
const res = test();
allInfo.push(...res.info);
highRisks.push(...res.risks);
});
const riskEls = highRisks.map(r => `<div class="info risk-high">• ${r}</div>`).join('');
document.getElementById('highRisk').innerHTML =
highRisks.length > 0 ? riskEls : '<div class="info">✅ 未发现高风险虚拟化特征</div>';
document.getElementById('details').innerHTML =
allInfo.map(i => `<div class="info">${i}</div>`).join('');
}
// ========== 初始化 ==========
updateBehaviorStatus();
log('监听页面行为(切屏/失焦/鼠标) + 检测虚拟化环境特征...');
runEnvironmentDetection();
function clearLog() {
logEl.innerHTML = '';
blurCount = 0;
hiddenCount = 0;
maxLeaveDuration = 0;
leaveStartTime = null;
document.getElementById('blurCount').textContent = '0';
document.getElementById('hiddenCount').textContent = '0';
document.getElementById('maxHiddenTime').textContent = '0 秒';
}
</script>
</body>
</html>