PVE备份与美化
PVE备份:在黑群晖使用 Docker 部署 Proxmox Backup Server (hellowood.dev)
Proxmox Backup Server 是 PVE 容器、虚拟机的备份解决方案,支持增量、重复数据消除备份,可以节省存储空间,同时支持加密和完整性校验
Proxmox Backup Server 官方提供了 iso 格式的镜像,同时社区也有开源的 Docker 镜像的部署方式,为了在黑群晖上部署方便,使用 Docker 的方式进行部署,项目地址:https://github.com/ayufan/pve-backup-server-dockerfiles
部署 Proxmox Backup Server
使用 Docker 或者 Docker Compose 方式部署都可以
- docker-compose.yaml
需要注意,要使用 tmpfs 方式挂载 /run 目录,用于容器内部创建临时文件和目录;
/etc/proxmox-backup
: 用于存储 PVE Backup Server 的配置信息 /var/log/proxmox-backup
: 用于存储日志信息 /var/lib/proxmox-backup
:用于存储数据 /backups
:存储容器、虚拟机的备份数据
version: '2.1'
services:
pve-backup-server:
image: ayufan/proxmox-backup-server:latest
ports:
- "8007:8007"
volumes:
- /docker/PVEBackup/etc:/etc/proxmox-backup
- /docker/PVEBackup/log:/var/log/proxmox-backup
- /docker/PVEBackup/lib:/var/lib/proxmox-backup
- /BackupServer:/backups
tmpfs:
- /run
restart: unless-stopped
配置完成后启动容器,访问 https://<ip>:8007/
端口即可进行登录,默认的用户名是 admin
,密码是 pbspbs
,选择 Proxmox Backup authentication server
领域进行登录
配置存储
- 配置存储路径
在 Proxmox Backup Server 的数据存储中添加数据存储,将刚才映射的 /backups
目录作为存储路径
- 为用户添加权限
给用于备份的 root 和 admin 用户添加备份路径的访问权限
配置
- 获取 Proxmox Backup Server 的指纹
指纹用于在 PVE 中添加备份时进行认证
- 添加存储
在 PVE 的数据中心-存储中选择添加 Proxmox Backup Server,输入认证信息和指纹;Datastore 为 Proxmox Backup Server 的数据存储的名称,如 Backup
- 添加备份作业
在 PVE 的数据中心-备份中添加备份计划,按需添加,添加完成后选择现在运行即可开始备份
PVE美化:【Proxmox VE】看不懂正则没关系,DIY PVE 首页显示 CPU、主板、硬盘 温度等信息-恩山无线论坛 (right.com.cn)
网上各种版本的 PVE 温度 DIY 脚本,但萝卜青菜各有所爱,总是难得遇到钟情的那一款
自行 DIY 的话,正则 + js 入门确实需要点门槛
所以这里提供另一种实现方式 json,清晰明了,便于阅读,修改简单
无论是 温度、风扇转速、硬盘温度、硬盘信息都可以轻松搞定
-----------------------------------------------------------------------------------------------------------------------------
因为重做了空值及报错处理
没人回复怪可怜的,还是回复可见吧
-----------------------------------------------------------------------------------------------------------------------------
- 自行修改代码时,出错原因及处理方式
1. 首页转圈,只显示一部分数据
一般都是由于参数值读取错误造成的
假设硬盘 smartctl 返回值中没有温度值,而 var temperature = value['temperature']['current'].toFixed(1); 这一行命令中读取了温度值,就会造成参数读取错误,首页转圈
解决方法:
逐个参数查看是否输入错误(数据不存在或键值输入有误)
2. 首页转圈,所有自定义数据都不能显示
检查 CPU 温度代码段,是否有代码错误(参照第一条)
如修改了 Nodes.pm 文件,需要使用 systemctl restart pveproxy 命令重载 PVE 界面
仅修改 js 文件的情况下,无需重载 PVE 界面,浏览器强制刷新就能看到修改后的结果
3. 首页白屏,什么都不显示
一般都是代码有误引起的,请还原文件重新操作,或检查代码是否缺少相对应的 {} , () 等符号
4. 中文乱码
请不要将文件拖到本地使用 记事本 操作,直接在 WinSCP 中操作,或在本地使用 Notepad 等软件进行编辑
如操作无误依然乱码,请检查终端软件及 WinSCP 的编码设置,并在 PVE 终端中输入
- export LC_ALL=en_US.UTF-8
复制代码
5. 无报错,但显示不全
参考文末,修改显示范围
-----------------------------------------------------------------------------------------------------------------------------
- 前置命令
- # 更新软件包列表:
- apt-get update
- # 安装 lm-sensors:
- apt-get install lm-sensors patch
- # 初始化 sensors(一路yes,回车):
- sensors-detect
- # 给予 smartctl 权限(如不需要硬盘信息可以忽略):
- chmod +s /usr/sbin/smartctl
- # 设置 PVE 编码为 UTF-8(如 PVE 安装时正确选择了 china 地区可以忽略):
- export LC_ALL=en_US.UTF-8
- # 获取温度信息,查看可以设置的数据:
- sensors
- # 这个时候是没有风扇等信息的,需要重启,被动散热式的主机无视这条:
- reboot
复制代码
- 备份原文件
如命令未错误输入,文件备份在原文件相同目录下
如 : /usr/share/perl5/PVE/API2/Nodes.pm 备份为 /usr/share/perl5/PVE/API2/Nodes.pmbak
- proxmoxlib_js="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
- Nodes_pm="/usr/share/perl5/PVE/API2/Nodes.pm"
- pvemanagerlib_js="/usr/share/pve-manager/js/pvemanagerlib.js"
- cp ${proxmoxlib_js} ${proxmoxlib_js}bak
- cp ${Nodes_pm} ${Nodes_pm}bak
- cp ${pvemanagerlib_js} ${pvemanagerlib_js}bak
复制代码
-----------------------------------------------------------------------------------------------------------------------------
- 懒人补丁法(若未修改过原文件,已包含高度修改、去除订阅提示,宽度未更改)
将补丁文件放到 /tmp/ 文件夹
大版本升级可能依然需要手动修改部分代码,补丁失败的文件及代码行数会在 patch 返回值中显示,请留意
如遇页面显示错误,参照上文
不保证可用
PVE_7.2_temperatures.zip (3.77 KB, 下载次数: 769)
PVE_8.0_temperatures.zip (3.54 KB, 下载次数: 704)
- # 应用补丁(请确认已经备份原文件)
- patch ${proxmoxlib_js} < /tmp/proxmoxlib_js.patch
- patch ${Nodes_pm} < /tmp/Nodes_pm.patch
- patch ${pvemanagerlib_js} < /tmp/pvemanagerlib_js.patch
- # 重载 PVE 界面
- systemctl restart pveproxy
复制代码
- 自行制作补丁
可以在重装或升级后快速修改文件(大版本升级可能需要手动修改部分代码)
生成的补丁文件在 /tmp/ 文件夹:
- proxmoxlib_js="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
- Nodes_pm="/usr/share/perl5/PVE/API2/Nodes.pm"
- pvemanagerlib_js="/usr/share/pve-manager/js/pvemanagerlib.js"
- diff -uN ${proxmoxlib_js}bak ${proxmoxlib_js} > /tmp/proxmoxlib_js.patch
- diff -uN ${Nodes_pm}bak ${Nodes_pm} > /tmp/Nodes_pm.patch
- diff -uN ${pvemanagerlib_js}bak ${pvemanagerlib_js} > /tmp/pvemanagerlib_js.patch
复制代码
-----------------------------------------------------------------------------------------------------------------------------
- 自行修改文件
修改 Nodes.pm 文件加入以下代码
/usr/share/perl5/PVE/API2/Nodes.pm
搜索:$res->{pveversion} = PVE::pvecfg::package()
下方添加
- $res->{sensors_json} = `sensors -j`; # 获取 CPU 、主板温度及风扇转速
- $res->{smartctl_nvme_json} = `smartctl -a -j /dev/nvme?`; # 读取 nvme 硬盘 S.M.A.R.T. 值,获取硬盘寿命、容量、温度等,如果存在设备号错误无显示,自行修改 nvme? ,帖子内容只做了一个硬盘显示,需要多个硬盘的话还需要修改下方代码块
- $res->{smartctl_sda_json} = `smartctl -i -n standby /dev/sda|grep "STANDBY" || smartctl -i -n standby /dev/sda|grep "No such device" || smartctl -a -j /dev/sda`; #先检测硬盘是否为休眠状态,若否,则检查磁盘是否存在,若否,则返回 S.M.A.R.T. 值
- $res->{smartctl_sdb_json} = `smartctl -i -n standby /dev/sdb|grep "STANDBY" || smartctl -i -n standby /dev/sdb|grep "No such device" || smartctl -a -j /dev/sdb`; #先检测硬盘是否为休眠状态,若否,则检查磁盘是否存在,若否,则返回 S.M.A.R.T. 值
- $res->{cpusensors} = `lscpu | grep MHz`; # 读取 CPU 频率
复制代码
- 关于获取硬盘名称
- lsblk | awk '$NF=="disk" {print $1}' | sort -u
复制代码
-
- 终端输入
sensors -j
-
- 命令,我们可以得到类似下面的结果(以下为 CPU 温度值截取)
-
- 先眼熟一下 JSON 格式的返回值,等下会用到
- "coretemp-isa-0000":{
- "Adapter": "ISA adapter",
- "Package id 0":{
- "temp1_input": 38.000,
- "temp1_max": 84.000,
- "temp1_crit": 100.000,
- "temp1_crit_alarm": 0.000
- },
- "Core 0":{
- "temp2_input": 34.000,
- "temp2_max": 84.000,
- "temp2_crit": 100.000,
- "temp2_crit_alarm": 0.000
- },
- "Core 1":{
- "temp3_input": 38.000,
- "temp3_max": 84.000,
- "temp3_crit": 100.000,
- "temp3_crit_alarm": 0.000
- }
复制代码
修改 pvemanagerlib.js 文件
-
- /usr/share/pve-manager/js/pvemanagerlib.js
-
- 搜索 PVE Manager Version
-
- 下方添加
-
-
- 原始版本及注释
-
- {
- itemId: 'thermal', // thermal 代表了这一段代码的 id ,随便改一下不要重复就行了
- colspan: 2,
- printBar: false,
- title: gettext('温度'), // 这里表示你想在页面中显示的左侧标题名
- textField: 'sensors_json', // 这里需要填写 Nodes.pm 文件中对应的命令,也就是从哪一个返回值中获取数据
- renderer: function(value) {
- value = JSON.parse(value); // 使用 JavaScript 内置函数 JSON.parse() 将字符串转换为 JavaScript 对象
- const cpu0 = value['coretemp-isa-0000']['Package id 0']['temp1_input'].toFixed(1); // 这里表示读取 CPU 温度,对应 sensors -j 输出的 JSON 格式数据,toFixed(1) 表示将数字转换为字符,只保留 (1) 位小数
- const PECI0 = value['nct6798-isa-0290']['PECI Agent 0']['temp7_input'].toFixed(1); // 同上,自行修改
- const pch = value['pch_cometlake-virtual-0']['temp1']['temp1_input'].toFixed(1); // 同上,自行修改
- return `CPU: ${cpu0}°C || 南桥: ${pch} ℃ | 网卡: ${PECI0} ℃`; // return 表示输出值,也就是最后显示在 WEB 页面中的值,{}中填入上几行中定义的变量,格式自行调整
- }
- },
复制代码
-
-
- 加一点细节
-
- {
- itemId: 'thermal',
- colspan: 2,
- printBar: false,
- title: gettext('温度'),
- textField: 'sensors_json',
- renderer: function(value) {
- value = value.replace(/temp([0-9]{1,})_input/g,'input');
- // Intel
- if (value.indexOf("coretemp-isa") != -1 ) {
- value = value.replace(/coretemp-isa-(.{4})/g,'coretemp-isa');
- value = value.replace(/nct6798-isa-(.{4})/g,'nct6798-isa');
- value = JSON.parse(value);
- try {var cpu_Intel = 'CPU: ' + value['coretemp-isa']['Package id 0']['input'].toFixed(1) + '°C';} catch(e) {var cpu_Intel = '';}
- try {var acpi = ' || 主板: ' + value['acpitz-acpi-0']['temp1']['input'].toFixed(1) + '°C';} catch(e) {var acpi = '';}
- try {var pch = ' || 南桥: ' + value['pch_cometlake-virtual-0']['temp1']['input'].toFixed(1) + '°C';} catch(e) {var pch = '';}
- try {var pci0 = ' || 网卡: ' + value['nct6798-isa']['PECI Agent 0']['input'].toFixed(1) + '°C';} catch(e) {var pci0 = '';}
- // 如果存在主板、PCI网卡温度,优先显示
- if (cpu_Intel.length > 0 && pch.length + acpi.length + pci0.length > 0) {
- return `${cpu_Intel}${acpi}${pch}${pci0}`;
- // 如果不存在,显示 CPU 全核温度,最高支持 8 核心
- } else if (cpu_Intel.length > 0) {
- try {var cpu0 = ' || 核心 0 : ' + value['coretemp-isa']['Core 0']['input'].toFixed(1) + '°C';} catch(e) {var cpu0 = '';}
- try {var cpu1 = ' | 核心 1 : ' + value['coretemp-isa']['Core 1']['input'].toFixed(1) + '°C';} catch(e) {var cpu1 = '';}
- try {var cpu2 = ' | 核心 2 : ' + value['coretemp-isa']['Core 2']['input'].toFixed(1) + '°C';} catch(e) {var cpu2 = '';}
- try {var cpu3 = ' | 核心 3 : ' + value['coretemp-isa']['Core 3']['input'].toFixed(1) + '°C';} catch(e) {var cpu3 = '';}
- try {var cpu4 = ' | 核心 4 : ' + value['coretemp-isa']['Core 4']['input'].toFixed(1) + '°C';} catch(e) {var cpu4 = '';}
- try {var cpu5 = ' | 核心 5 : ' + value['coretemp-isa']['Core 5']['input'].toFixed(1) + '°C';} catch(e) {var cpu5 = '';}
- try {var cpu6 = ' | 核心 6 : ' + value['coretemp-isa']['Core 6']['input'].toFixed(1) + '°C';} catch(e) {var cpu6 = '';}
- try {var cpu7 = ' | 核心 7 : ' + value['coretemp-isa']['Core 7']['input'].toFixed(1) + '°C';} catch(e) {var cpu7 = '';}
- return `${cpu_Intel}${cpu0}${cpu1}${cpu2}${cpu3}${cpu4}${cpu5}${cpu6}${cpu7}`;
- }
- // AMD
- } else if (value.indexOf("amdgpu-pci") != -1 ) {
- value = value.replace(/k10temp-pci-(.{4})/g,'k10temp-pci');
- value = value.replace(/zenpower-pci-(.{4})/g,'zenpower-pci');
- value = value.replace(/amdgpu-pci-(.{4})/g,'amdgpu-pci');
- value = JSON.parse(value);
- try {var cpu_amd_k10 = 'CPU: ' + value['k10temp-pci']['Tctl']['input'].toFixed(1) + '°C';} catch(e) {var cpu_amd_k10 = '';}
- try {var cpu_amd_zen = 'CPU: ' + value['zenpower-pci']['Tctl']['input'].toFixed(1) + '°C';} catch(e) {var cpu_amd_zen = '';}
- try {var amdgpu = ' | GPU: ' + value['amdgpu-pci']['edge']['input'].toFixed(1) + '°C';} catch(e) {var amdgpu = '';}
- return `${cpu_amd_k10}${cpu_amd_zen}${amdgpu}`;
- } else {
- return `提示: CPU 及 主板 温度读取异常`;
- }
- }
- },
- {
- itemId: 'nvme_ssd',
- colspan: 2,
- printBar: false,
- title: gettext('NVME'),
- textField: 'smartctl_nvme_json',
- renderer: function(value) {
- value = JSON.parse(value);
- if (value['model_name']) {
- try {var model_name = value['model_name'];} catch(e) {var model_name = '';}
- try {var percentage_used = ' | 使用寿命: ' + value['nvme_smart_health_information_log']['percentage_used'].toFixed(0) + '% ';} catch(e) {var percentage_used = '';}
- try {var data_units_read = value['nvme_smart_health_information_log']['data_units_read']*512/1024/1024/1024;var data_units_read = '(读: ' + data_units_read.toFixed(2) + 'TB, ';} catch(e) {var data_units_read = '';}
- try {var data_units_written = value['nvme_smart_health_information_log']['data_units_written']*512/1024/1024/1024;var data_units_written = '写: ' + data_units_written.toFixed(2) + 'TB)';} catch(e) {var data_units_written = '';}
- try {var power_on_time = ' | 通电: ' + value['power_on_time']['hours'].toFixed(0) + '小时';} catch(e) {var power_on_time = '';}
- try {var temperature = ' | 温度: ' + value['temperature']['current'].toFixed(1) + '°C';} catch(e) {var temperature = '';}
- return `${model_name}${percentage_used}${data_units_read}${data_units_written}${power_on_time}${temperature}`;
- } else {
- return `提示: 未安装硬盘或已直通硬盘控制器`;
- }
- }
- },
- {
- itemId: 'SATA_sda',
- colspan: 2,
- printBar: false,
- title: gettext('SATA_sda'),
- textField: 'smartctl_sda_json',
- renderer: function(value) {
- if (value.indexOf("Device is in STANDBY mode") != -1 ) {
- return `提示: 磁盘休眠中`;
- } else if (value.indexOf("No such device") != -1 ) {
- return `提示: 未安装硬盘或已直通硬盘控制器`;
- } else {
- value = JSON.parse(value);
- try {var model_name = value['model_name'];} catch(e) {var model_name = '';}
- try {var user_capacity = value['user_capacity']['bytes']/1024/1024/1024;var user_capacity = ' | 容量: ' + user_capacity.toFixed(2) + ' GB';} catch(e) {var user_capacity = '';}
- try {var power_on_time = ' | 已通电: ' + value['power_on_time']['hours'].toFixed(0) + ' 小时';} catch(e) {var power_on_time = '';}
- try {var error_count = value['ata_smart_error_log']['summary']['count'].toFixed(0);if (error_count != 0){error_count = ' | 磁盘错误: ' + error_count;} else {var error_count = '';} } catch(e) {var error_count = '';}
- try {var self_count = value['ata_smart_self_test_log']['standard']['error_count_total'].toFixed(0);if (self_count != 0){self_count = ' | 自检错误: ' + self_count;} else {var self_count = '';} } catch(e) {var self_count = '';}
- try {var temperature = ' | 温度: ' + value['temperature']['current'].toFixed(1) + '°C';} catch(e) {var temperature = '';}
- return `${model_name}${user_capacity}${power_on_time}${error_count}${self_count}${temperature}`;
- }
- }
- },
- {
- itemId: 'SATA_sdb',
- colspan: 2,
- printBar: false,
- title: gettext('SATA_sdb'),
- textField: 'smartctl_sdb_json',
- renderer: function(value) {
- if (value.indexOf("Device is in STANDBY mode") != -1 ) {
- return `提示: 磁盘休眠中`;
- } else if (value.indexOf("No such device") != -1 ) {
- return `提示: 未安装硬盘或已直通硬盘控制器`;
- } else {
- value = JSON.parse(value);
- try {var model_name = value['model_name'];} catch(e) {var model_name = '';}
- try {var user_capacity = value['user_capacity']['bytes']/1024/1024/1024;var user_capacity = ' | 容量: ' + user_capacity.toFixed(2) + ' GB';} catch(e) {var user_capacity = '';}
- try {var power_on_time = ' | 已通电: ' + value['power_on_time']['hours'].toFixed(0) + ' 小时';} catch(e) {var power_on_time = '';}
- try {var error_count = value['ata_smart_error_log']['summary']['count'].toFixed(0);if (error_count != 0){error_count = ' | 磁盘错误: ' + error_count;} else {var error_count = '';} } catch(e) {var error_count = '';}
- try {var self_count = value['ata_smart_self_test_log']['standard']['error_count_total'].toFixed(0);if (self_count != 0){self_count = ' | 自检错误: ' + self_count;} else {var self_count = '';} } catch(e) {var self_count = '';}
- try {var temperature = ' | 温度: ' + value['temperature']['current'].toFixed(1) + '°C';} catch(e) {var temperature = '';}
- return `${model_name}${user_capacity}${power_on_time}${error_count}${self_count}${temperature}`;
- }
- }
- },
- {
- itemId: 'MHz',
- colspan: 2,
- printBar: false,
- title: gettext('CPU频率'),
- textField: 'cpusensors',
- renderer:function(value){
- var f0 = value.match(/CPU MHz.*?([\d]+)/)[1];
- var f1 = value.match(/CPU min MHz.*?([\d]+)/)[1];
- var f2 = value.match(/CPU max MHz.*?([\d]+)/)[1];
- return `实时: ${f0} MHz || 最小: ${f1} MHz | 最大: ${f2} MHz `
- }
- },
复制代码
-
- 如果是 PVE 8.0,CPU 不再返回实时频率,需要修改为下列代码
- {
- itemId: 'MHz',
- colspan: 2,
- printBar: false,
- title: gettext('CPU频率'),
- textField: 'cpusensors',
- renderer:function(value){
- var f1 = value.match(/CPU min MHz.*?([\d]+)/)[1];
- var f2 = value.match(/CPU max MHz.*?([\d]+)/)[1];
- var f0 = value.match(/CPU.*scaling MHz.*?([\d]+)/)[1];
- var f0 = f0*f2/100;
- return `实时: ${f0} MHz || 最小: ${f1} MHz | 最大: ${f2} MHz `
- }
- },
复制代码
-
-
- 修改显示范围
-
-
- 依然是 pvemanagerlib.js 文件
-
- 搜索 widget.pveNodeStatus
-
- 将 height: 300 (默认值) 改大为 420,或者更大,然后保存(每多一行大概增大 20~25)
-
-
- 修改完成后重载 PVE 界面
- 如无把握,建议不要一次加入太多代码,修改一段就重载一次
-
- systemctl restart pveproxy
复制代码
-
-
- 关于虚拟机标签显示
-
版权声明:
作者:Jays
链接:https://ijays.com/2024/03/pve-backup-airbrush.html
来源:颓废的美
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论