标签: pve

  • PVE合并local、local-lvm分区!DeepSeek牛逼!

    PVE合并local、local-lvm分区!DeepSeek牛逼!

    PVE年后回来突然发现默认分区也就是local已经占用到96%了,正好在B站刷到个视频,合并local、local-lvm提高利用率。

    其实之前刚安装PVE是就看到,各种教程里都有这个操作,当时想着用不到就偷懒了一下,没有去做。以为128G的系统盘应该够折腾了

    然后视频作者提供了个思路,情景二:local-lvm分区存储有虚拟机磁盘 ,但是我现有的local分区已经不能满足迁移所需空间

    所有就问了下ChatGPT,还可以迁移到已挂载的其他硬盘,所以有了此文

    整体都是参照情景二,只是增加了一个步骤

    1、创建一个新的 storage 目录并添加到 PVE

    PS:我的另外一块硬盘是挂载在/mnt目录

    mkdir -p /mnt/pve-storage
    chown -R www-data:www-data /mnt/pve-storage
    chmod 755 /mnt/pve-storage

    2、在 PVE Web 界面添加存储

    • 进入 Datacenter -> Storage -> Add -> Directory
    • ID: pve-storage
    • Directory: /mnt/pve-storage
    • Content: Disk images, Container
    • 保存

    完成以上就可以按照情景二 ,先把lxc容器、虚拟机磁盘迁移到新创建的pve-storage

    删除local-lvm合并磁盘空间至local后,再迁移到local即可完成了全部过程。

    本来前几个一切顺利,但是有一个lxc容器移动存储时提示:

    you can't move a volume with snapshots and delete the source (500)

    使用以下办法,又无法删除:

    root@PVE:/mnt# pct listsnapshot 203
    `-> vzdump                      2024-12-15 10:06:41     vzdump backup snapshot
    `-> current                                             You are here!
    root@PVE:/mnt# pct delsnapshot 203 vzdump
    lvremove snapshot 'pve/snap_vm-203-disk-0_vzdump' error:   Failed to find logical volume "pve/snap_vm-203-disk-0_vzdump"

    结果问了GPT半天也没解决,最后用了下deepseek 试试,结果一次性解决

    原来不知道为啥,PVE在CT 203配置文件里增加“snapshots ”备份,类似下图

    删除 [vzdump] 这行及它以下的内容,即可正常移动存储了。

     

    [c-alert type=”success”]参考原视频地址:https://www.bilibili.com/video/av113977493293708[/c-alert]

    [c-alert type=”success”]参考原文地址:PVE合并local、local-lvm分区,迁移虚拟机磁盘到其他分区 – 知乎[/c-alert]

    PS:由于知乎不登录没办法看全文,所以建议去知乎看全文

    前言

    PVE默认会有两个分区,这两个分区可以理解为PVE对不同类型的文件进行分类管理。对于我目前的使用情况来说,local分区也就用来存储一些ISO镜像,local-lvm分区则是用来存放虚拟机磁盘,所以其实就没有必要划分为两个分区了。

    那么我把分区的合并,分为了两种情况:

    情景一:刚安装完PVE

    大家刚安装完PVE,两个分区都没存有数据,可以直接通过下面的操作进行合并。

    # 删除local-lvm分区
    lvremove /dev/pve/data -y
    
    # 将剩余的空间扩展至local分区
    lvextend -rl +100%FREE /dev/pve/root

    输入完上面的内容,再在“数据中心”,“存储”里面,把local-lvm分区给移除掉。

    最后再编辑local分区的“内容”,把所有的东西都选上,合并就完成了。

    情景二:local-lvm分区存储有虚拟机磁盘

    若已经开设有虚拟机,则需要先将虚拟机的硬盘进行迁移,把磁盘都迁移到local分区,迁移完成再进行情景一上面的操作。

    当然了,如果这些虚拟机可以删除的话,移除后,直接通过情景一的操作进行合并也是可以的。

  • PVE备份与美化

    PVE备份:在黑群晖使用 Docker 部署 Proxmox Backup Server (hellowood.dev)

    [zd-plane title=”PVE备份”]

    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 领域进行登录

    homelab-pve-backup-server-login-page.png

    配置存储

    • 配置存储路径

    在 Proxmox Backup Server 的数据存储中添加数据存储,将刚才映射的 /backups 目录作为存储路径

    homelab-pve-backup-server-add-backup-storage.png

    • 为用户添加权限

    给用于备份的 root 和 admin 用户添加备份路径的访问权限

    homelab-pve-backup-server-add-backup-permission.png

    配置

    • 获取 Proxmox Backup Server 的指纹

    指纹用于在 PVE 中添加备份时进行认证

    homelab-pve-backup-server-show-printfinger.png

    • 添加存储

    在 PVE 的数据中心-存储中选择添加 Proxmox Backup Server,输入认证信息和指纹;Datastore 为 Proxmox Backup Server 的数据存储的名称,如 Backup

    homelab-pve-backup-server-add-data-storage.png

    • 添加备份作业

    在 PVE 的数据中心-备份中添加备份计划,按需添加,添加完成后选择现在运行即可开始备份

    homelab-pve-backup-server-add-backup-task.png

    [/zd-plane]

    PVE美化:【Proxmox VE】看不懂正则没关系,DIY PVE 首页显示 CPU、主板、硬盘 温度等信息-恩山无线论坛 (right.com.cn)

    [zd-plane title=”PV美化”]

    网上各种版本的 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 终端中输入

    1. export LC_ALL=en_US.UTF-8

    复制代码

    5. 无报错,但显示不全
    参考文末,修改显示范围
    —————————————————————————————————————————–

    • 前置命令
    1. # 更新软件包列表:
    2. apt-get update
    3. # 安装 lm-sensors:
    4. apt-get install lm-sensors patch
    5. # 初始化 sensors(一路yes,回车):
    6. sensors-detect
    7. # 给予 smartctl 权限(如不需要硬盘信息可以忽略):
    8. chmod +s /usr/sbin/smartctl
    9. # 设置 PVE 编码为 UTF-8(如 PVE 安装时正确选择了 china 地区可以忽略):
    10. export LC_ALL=en_US.UTF-8
    11. # 获取温度信息,查看可以设置的数据:
    12. sensors
    13. # 这个时候是没有风扇等信息的,需要重启,被动散热式的主机无视这条:
    14. reboot

    复制代码

     

    • 备份原文件

    如命令未错误输入,文件备份在原文件相同目录下
    如 : /usr/share/perl5/PVE/API2/Nodes.pm 备份为 /usr/share/perl5/PVE/API2/Nodes.pmbak

    1. proxmoxlib_js=”/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js”
    2. Nodes_pm=”/usr/share/perl5/PVE/API2/Nodes.pm”
    3. pvemanagerlib_js=”/usr/share/pve-manager/js/pvemanagerlib.js”
    4. cp ${proxmoxlib_js} ${proxmoxlib_js}bak
    5. cp ${Nodes_pm} ${Nodes_pm}bak
    6. 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)

     

    1. # 应用补丁(请确认已经备份原文件)
    2. patch ${proxmoxlib_js} < /tmp/proxmoxlib_js.patch
    3. patch ${Nodes_pm} < /tmp/Nodes_pm.patch
    4. patch ${pvemanagerlib_js} < /tmp/pvemanagerlib_js.patch
    5. # 重载 PVE 界面
    6. systemctl restart pveproxy

    复制代码

     

    • 自行制作补丁

    可以在重装或升级后快速修改文件(大版本升级可能需要手动修改部分代码)
    生成的补丁文件在 /tmp/ 文件夹:

    1. proxmoxlib_js=”/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js”
    2. Nodes_pm=”/usr/share/perl5/PVE/API2/Nodes.pm”
    3. pvemanagerlib_js=”/usr/share/pve-manager/js/pvemanagerlib.js”
    4. diff -uN ${proxmoxlib_js}bak ${proxmoxlib_js}  > /tmp/proxmoxlib_js.patch
    5. diff -uN ${Nodes_pm}bak ${Nodes_pm}  > /tmp/Nodes_pm.patch
    6. 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()
    下方添加

    1. $res->{sensors_json} = `sensors -j`; # 获取 CPU 、主板温度及风扇转速
    2. $res->{smartctl_nvme_json} = `smartctl -a -j /dev/nvme?`; # 读取 nvme 硬盘 S.M.A.R.T. 值,获取硬盘寿命、容量、温度等,如果存在设备号错误无显示,自行修改 nvme? ,帖子内容只做了一个硬盘显示,需要多个硬盘的话还需要修改下方代码块
    3. $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. 值
    4. $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. 值
    5. $res->{cpusensors} = `lscpu | grep MHz`; # 读取 CPU 频率

    复制代码

     

    • 关于获取硬盘名称
    1. lsblk | awk ‘$NF==”disk” {print $1}’ | sort -u

    复制代码

     

      • 终端输入

     sensors -j 

      • 命令,我们可以得到类似下面的结果(以下为 CPU 温度值截取)

     

      • 先眼熟一下 JSON 格式的返回值,等下会用到

     

    1. “coretemp-isa-0000”:{
    2.       “Adapter”: “ISA adapter”,
    3.       “Package id 0”:{
    4.          “temp1_input”: 38.000,
    5.          “temp1_max”: 84.000,
    6.          “temp1_crit”: 100.000,
    7.          “temp1_crit_alarm”: 0.000
    8.       },
    9.       “Core 0”:{
    10.          “temp2_input”: 34.000,
    11.          “temp2_max”: 84.000,
    12.          “temp2_crit”: 100.000,
    13.          “temp2_crit_alarm”: 0.000
    14.       },
    15.       “Core 1”:{
    16.          “temp3_input”: 38.000,
    17.          “temp3_max”: 84.000,
    18.          “temp3_crit”: 100.000,
    19.          “temp3_crit_alarm”: 0.000
    20.       }

    复制代码

    修改 pvemanagerlib.js 文件

      • /usr/share/pve-manager/js/pvemanagerlib.js

     

      • 搜索 PVE Manager Version

     

      • 下方添加

     

     

        • 原始版本及注释
    1.         {
    2.             itemId: ‘thermal’,  // thermal 代表了这一段代码的 id ,随便改一下不要重复就行了
    3.             colspan: 2,
    4.             printBar: false,
    5.                 title: gettext(‘温度’),  // 这里表示你想在页面中显示的左侧标题名
    6.                 textField: ‘sensors_json’,  // 这里需要填写 Nodes.pm 文件中对应的命令,也就是从哪一个返回值中获取数据
    7.                 renderer: function(value) {
    8.                         value = JSON.parse(value);  // 使用 JavaScript 内置函数 JSON.parse() 将字符串转换为 JavaScript 对象
    9.                         const cpu0 = value[‘coretemp-isa-0000’][‘Package id 0’][‘temp1_input’].toFixed(1);  // 这里表示读取 CPU 温度,对应 sensors -j 输出的 JSON 格式数据,toFixed(1) 表示将数字转换为字符,只保留 (1) 位小数
    10.                         const PECI0 = value[‘nct6798-isa-0290’][‘PECI Agent 0’][‘temp7_input’].toFixed(1); // 同上,自行修改
    11.                         const pch = value[‘pch_cometlake-virtual-0’][‘temp1’][‘temp1_input’].toFixed(1); // 同上,自行修改
    12.                         return `CPU: ${cpu0}°C || 南桥: ${pch} ℃ | 网卡: ${PECI0} ℃`; // return 表示输出值,也就是最后显示在 WEB 页面中的值,{}中填入上几行中定义的变量,格式自行调整
    13.                 }
    14.         },

    复制代码

     

        • 加一点细节
    1.         {
    2.                 itemId: ‘thermal’,
    3.                 colspan: 2,
    4.                 printBar: false,
    5.                 title: gettext(‘温度’),
    6.                 textField: ‘sensors_json’,
    7.                 renderer: function(value) {
    8.                         value = value.replace(/temp([0-9]{1,})_input/g,’input’);
    9.                         // Intel
    10.                         if (value.indexOf(“coretemp-isa”) != -1 ) {
    11.                                 value = value.replace(/coretemp-isa-(.{4})/g,’coretemp-isa’);
    12.                                 value = value.replace(/nct6798-isa-(.{4})/g,’nct6798-isa’);
    13.                                 value = JSON.parse(value);
    14.                                 try {var cpu_Intel = ‘CPU: ‘ + value[‘coretemp-isa’][‘Package id 0’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu_Intel = ”;}
    15.                                 try {var acpi = ‘ || 主板:  ‘ + value[‘acpitz-acpi-0’][‘temp1’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var acpi = ”;}
    16.                                 try {var pch = ‘ || 南桥:  ‘ + value[‘pch_cometlake-virtual-0’][‘temp1’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var pch = ”;}
    17.                                 try {var pci0 = ‘ || 网卡:  ‘ + value[‘nct6798-isa’][‘PECI Agent 0’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var pci0 = ”;}
    18.                                 // 如果存在主板、PCI网卡温度,优先显示
    19.                                 if (cpu_Intel.length > 0 && pch.length + acpi.length + pci0.length > 0) {
    20.                                         return `${cpu_Intel}${acpi}${pch}${pci0}`;
    21.                                 // 如果不存在,显示 CPU 全核温度,最高支持 8 核心
    22.                                 } else if (cpu_Intel.length > 0) {
    23.                                         try {var cpu0 = ‘ || 核心 0 : ‘ + value[‘coretemp-isa’][‘Core 0’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu0 = ”;}
    24.                                         try {var cpu1 = ‘ | 核心 1 : ‘ + value[‘coretemp-isa’][‘Core 1’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu1 = ”;}
    25.                                         try {var cpu2 = ‘ | 核心 2 : ‘ + value[‘coretemp-isa’][‘Core 2’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu2 = ”;}
    26.                                         try {var cpu3 = ‘ | 核心 3 : ‘ + value[‘coretemp-isa’][‘Core 3’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu3 = ”;}
    27.                                         try {var cpu4 = ‘ | 核心 4 : ‘ + value[‘coretemp-isa’][‘Core 4’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu4 = ”;}
    28.                                         try {var cpu5 = ‘ | 核心 5 : ‘ + value[‘coretemp-isa’][‘Core 5’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu5 = ”;}
    29.                                         try {var cpu6 = ‘ | 核心 6 : ‘ + value[‘coretemp-isa’][‘Core 6’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu6 = ”;}
    30.                                         try {var cpu7 = ‘ | 核心 7 : ‘ + value[‘coretemp-isa’][‘Core 7’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu7 = ”;}
    31.                                         return `${cpu_Intel}${cpu0}${cpu1}${cpu2}${cpu3}${cpu4}${cpu5}${cpu6}${cpu7}`;
    32.                                 }
    33.                         // AMD
    34.                         } else if (value.indexOf(“amdgpu-pci”) != -1 ) {
    35.                                 value = value.replace(/k10temp-pci-(.{4})/g,’k10temp-pci’);
    36.                                 value = value.replace(/zenpower-pci-(.{4})/g,’zenpower-pci’);
    37.                                 value = value.replace(/amdgpu-pci-(.{4})/g,’amdgpu-pci’);
    38.                                 value = JSON.parse(value);
    39.                                 try {var cpu_amd_k10 = ‘CPU: ‘ + value[‘k10temp-pci’][‘Tctl’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu_amd_k10 = ”;}
    40.                                 try {var cpu_amd_zen = ‘CPU: ‘ + value[‘zenpower-pci’][‘Tctl’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var cpu_amd_zen = ”;}
    41.                                 try {var amdgpu = ‘ | GPU:  ‘ + value[‘amdgpu-pci’][‘edge’][‘input’].toFixed(1) + ‘°C’;} catch(e) {var amdgpu = ”;}
    42.                                 return `${cpu_amd_k10}${cpu_amd_zen}${amdgpu}`;
    43.                         } else {
    44.                                 return `提示: CPU 及 主板 温度读取异常`;
    45.                         }
    46.                 }
    47.         },
    48.         {
    49.                 itemId: ‘nvme_ssd’,
    50.                 colspan: 2,
    51.                 printBar: false,
    52.                 title: gettext(‘NVME’),
    53.                 textField: ‘smartctl_nvme_json’,
    54.                 renderer: function(value) {
    55.                         value = JSON.parse(value);
    56.                         if (value[‘model_name’]) {
    57.                                 try {var model_name = value[‘model_name’];} catch(e) {var model_name = ”;}
    58.                                 try {var percentage_used = ‘ | 使用寿命: ‘ + value[‘nvme_smart_health_information_log’][‘percentage_used’].toFixed(0) + ‘% ‘;} catch(e) {var percentage_used = ”;}
    59.                                 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 = ”;}
    60.                                 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 = ”;}
    61.                                 try {var power_on_time = ‘ | 通电: ‘ + value[‘power_on_time’][‘hours’].toFixed(0) + ‘小时’;} catch(e) {var power_on_time = ”;}
    62.                                 try {var temperature = ‘ | 温度: ‘ + value[‘temperature’][‘current’].toFixed(1) + ‘°C’;} catch(e) {var temperature = ”;}
    63.                                 return `${model_name}${percentage_used}${data_units_read}${data_units_written}${power_on_time}${temperature}`;
    64.                         } else {
    65.                                 return `提示: 未安装硬盘或已直通硬盘控制器`;
    66.                         }
    67.                 }
    68.         },
    69.         {
    70.                 itemId: ‘SATA_sda’,
    71.                 colspan: 2,
    72.                 printBar: false,
    73.                 title: gettext(‘SATA_sda’),
    74.                 textField: ‘smartctl_sda_json’,
    75.                 renderer: function(value) {
    76.                         if (value.indexOf(“Device is in STANDBY mode”) != -1 ) {
    77.                                 return `提示: 磁盘休眠中`;
    78.                         } else if (value.indexOf(“No such device”) != -1 ) {
    79.                                 return `提示: 未安装硬盘或已直通硬盘控制器`;
    80.                         } else {
    81.                         value = JSON.parse(value);
    82.                                 try {var model_name = value[‘model_name’];} catch(e) {var model_name = ”;}
    83.                                 try {var user_capacity = value[‘user_capacity’][‘bytes’]/1024/1024/1024;var user_capacity = ‘ | 容量: ‘ + user_capacity.toFixed(2) + ‘ GB’;} catch(e) {var user_capacity = ”;}
    84.                                 try {var power_on_time = ‘ | 已通电: ‘ + value[‘power_on_time’][‘hours’].toFixed(0) + ‘ 小时’;} catch(e) {var power_on_time = ”;}
    85.                                 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 = ”;}
    86.                                 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 = ”;}
    87.                                 try {var temperature = ‘ | 温度: ‘ + value[‘temperature’][‘current’].toFixed(1) + ‘°C’;} catch(e) {var temperature = ”;}
    88.                                 return `${model_name}${user_capacity}${power_on_time}${error_count}${self_count}${temperature}`;
    89.                         }
    90.                 }
    91.         },
    92.         {
    93.                 itemId: ‘SATA_sdb’,
    94.                 colspan: 2,
    95.                 printBar: false,
    96.                 title: gettext(‘SATA_sdb’),
    97.                 textField: ‘smartctl_sdb_json’,
    98.                 renderer: function(value) {
    99.                         if (value.indexOf(“Device is in STANDBY mode”) != -1 ) {
    100.                                 return `提示: 磁盘休眠中`;
    101.                         } else if (value.indexOf(“No such device”) != -1 ) {
    102.                                 return `提示: 未安装硬盘或已直通硬盘控制器`;
    103.                         } else {
    104.                         value = JSON.parse(value);
    105.                                 try {var model_name = value[‘model_name’];} catch(e) {var model_name = ”;}
    106.                                 try {var user_capacity = value[‘user_capacity’][‘bytes’]/1024/1024/1024;var user_capacity = ‘ | 容量: ‘ + user_capacity.toFixed(2) + ‘ GB’;} catch(e) {var user_capacity = ”;}
    107.                                 try {var power_on_time = ‘ | 已通电: ‘ + value[‘power_on_time’][‘hours’].toFixed(0) + ‘ 小时’;} catch(e) {var power_on_time = ”;}
    108.                                 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 = ”;}
    109.                                 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 = ”;}
    110.                                 try {var temperature = ‘ | 温度: ‘ + value[‘temperature’][‘current’].toFixed(1) + ‘°C’;} catch(e) {var temperature = ”;}
    111.                                 return `${model_name}${user_capacity}${power_on_time}${error_count}${self_count}${temperature}`;
    112.                         }
    113.                 }
    114.         },
    115.         {
    116.                 itemId: ‘MHz’,
    117.                 colspan: 2,
    118.                 printBar: false,
    119.                 title: gettext(‘CPU频率’),
    120.                 textField: ‘cpusensors’,
    121.                 renderer:function(value){
    122.                         var f0 = value.match(/CPU MHz.*?([\d]+)/)[1];
    123.                         var f1 = value.match(/CPU min MHz.*?([\d]+)/)[1];
    124.                         var f2 = value.match(/CPU max MHz.*?([\d]+)/)[1];
    125.                         return `实时: ${f0} MHz || 最小: ${f1} MHz | 最大: ${f2} MHz `
    126.                 }
    127.         },

    复制代码

     

      • 如果是 PVE 8.0,CPU 不再返回实时频率,需要修改为下列代码

     

    1. {
    2.         itemId: ‘MHz’,
    3.         colspan: 2,
    4.         printBar: false,
    5.         title: gettext(‘CPU频率’),
    6.         textField: ‘cpusensors’,
    7.         renderer:function(value){
    8.                 var f1 = value.match(/CPU min MHz.*?([\d]+)/)[1];
    9.                 var f2 = value.match(/CPU max MHz.*?([\d]+)/)[1];
    10.                 var f0 = value.match(/CPU.*scaling MHz.*?([\d]+)/)[1];
    11.                 var f0 = f0*f2/100;
    12.                 return `实时: ${f0} MHz || 最小: ${f1} MHz | 最大: ${f2} MHz `
    13.         }
    14.         },

    复制代码

     

        • 修改显示范围

     

      • 依然是 pvemanagerlib.js 文件

     

      • 搜索 widget.pveNodeStatus

     

      • 将 height: 300 (默认值) 改大为 420,或者更大,然后保存(每多一行大概增大 20~25)

     

     

        • 修改完成后重载 PVE 界面
        • 如无把握,建议不要一次加入太多代码,修改一段就重载一次

     

    1. systemctl restart pveproxy

    复制代码

     

        • 关于虚拟机标签显示

     

       

    [/zd-plane]

  • Panther x2 (黑豹 x2) 的PVE之路-卒-Reborn

    Panther x2 (黑豹 x2) 的PVE之路-卒-Reborn

    JP-BOX正常服役,跑了很多Docker服务,懒得去动他,N1又感觉性能比较弱,没有太大的意义。又买了个Panther X2 做测试

    Panther X2我这台的体制太差了吧,服役杠2个多月。卒

    刷机Armbian

     

    安装PVE

    我使用的是自动安装,手动安装请查看:转载:Arm版本的Proxmox VE8.0安装教程 – 颓废的美 (ijays.com)

    [c-alert type=”warning”]前提:0、armbian使用的是ophub 大佬编译的。1、主机设置为固定IP翻边查找。2、网络必须为已科学。第2尤为主要,否则容易失联[/c-alert]

    armbian-software

    选择308  – 输入主机名 X2 #这里可以自定义,我填写的X2

    选择 No configuration

    中间会提示一些错误,貌似不影响安装,忽略即可!

    dpkg: error processing package pve-manager (--configure):
    installed pve-manager package post-installation script subprocess returned error exit status 1
    dpkg: dependency problems prevent configuration of proxmox-ve:
    proxmox-ve depends on pve-manager (>= 8.0.4); however:
    Package pve-manager is not configured yet.
    
    dpkg: error processing package proxmox-ve (--configure):
    dependency problems - leaving unconfigured
    Errors were encountered while processing:
    pve-manager
    proxmox-ve

    出现以下,标识安装成功了。

    浏览器打开:https://192.168.1.80:8006  即可访问PVE的管理界面

    查看一下宿主机的网络变换,nano /etc/network/interfaces ,出现vmbr0即代表PVE网络也已配置成功 – 非必须!

    先装个LXC Debian练练手 – 看图即可

    登录PVE后依次选择 主机(X2)-local – CT模板 – 模板 – 搜索 debian – 选择要下载的系统 – 点击下载出现 task ok即下载完成

    我这里选择的是arm64版的debian-bookworm,也就是debian12。
    1、

    2、

    3、

    4、

    5、

    6、

    7、

    8、

    9、

    10、

    11、

    [c-alert type=”warning”]12、没有直通需求,进设置网络即可[/c-alert]

    nano /etc/pve/lxc/100.conf #宿主机操作

    #Network config 
    lxc.net.0.flags: up
    lxc.net.0.type: veth
    lxc.net.0.link: vmbr0
    lxc.net.0.name: eth0
    
    # TUN  - By cooip
    lxc.cgroup2.devices.allow = c 10:200 rwm
    lxc.mount.entry = /dev/net/tun dev/net/tun none bind,create=file
    
    # 直通GPU - By cooip
    # lxc gpu、声卡 直通
    # 需要赋权600或者666...
    # 不限设备架构,纯LXC 自行按照格式修改:为=
    lxc.cgroup2.devices.allow: c 226:* rwm
    lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,create=file
    lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,create=file
    lxc.cgroup2.devices.allow: c 116:* rwm
    lxc.mount.entry: /dev/snd dev/snd none bind,create=dir

    13、

    14、编辑LXC创建的debian网络

    # 通过宿主机进入LXC debian的ssh,也可以通过PVE的控制台操作。三种方式随意

    lxc-attach 100 

    #编辑网络

    14.1 启动网络服务:systemctl start systemd-networkd

    14.2 设置开机启动网络服务:systemctl enable systemd-networkd

    vi /etc/systemd/network/eth0.network

    注释掉原有信息,替换为以下。XX替换为你的IP、网关、DNS

    [Match]
    Name=eth0
    [Network]
    Address=192.168.1.XX/24
    DNS=192.168.1.XX
    IPForward=yes
    IPv6AcceptRA=true
    [Route]
    Gateway=192.168.1.XX

    # 替换源

    # 安装必要软件

    # 更新安装软件
    apt update
    apt upgrade
    apt install -y  openssh-server nano curl wget git apt-transport-https ca-certificates bash

    # 设置ssh

    nano /etc/ssh/sshd_config

    #PermitRootLogin prohibit-password  修改为 PermitRootLogin yes 后重启ssh服务

    可以这样:20241130

    echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
    /etc/init.d/ssh restart

    重启后就可以直接通过 SSH 客户端软件连接LXC debian 了

    # 可以愉快的折腾了

    openwrt和mihomo adg 都是差不多的过程,包括抛弃PVE纯LXC容器部署

    可以去看:第二步‐电视盒子‐LXC · cooip-jm/About-openwrt Wiki (github.com)

     

    [c-alert type=”success”]参考与感谢各位大佬的教程与开源:[/c-alert]

    一、安装篇:手把手教你PVE系统下用LXC方式安装OpenWrt_爱田小牛资源分享 (aitxn.com)

    roacn/pve: PVE脚本,一键换源、去订阅弹窗等;LXC容器OpenWrt自动安装、更新 (github.com)

    xYx-c/build-openwrt: 自用openwrt功能非常精简,完美支持ipv6,适用pve lxc容器 (github.com)

    PVE主体安装 | 一键虚拟化项目 (spiritlhl.net)

    Panther X2折腾记录 | 纷纷 (ffoo.cc)

    百元矿渣Panther X2(黑豹 X2):小白折腾日记1-论坛-深度科技 (deepin.org)

    Index of /lxc-images/images/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

    Proxmox VE(N1)网络配置、内存优化、LXC安装OpenWrt、AdGuardHome、Docker – YsxlionBlog (ysx88.top)

    Arm版本的Proxmox VE8.0安装教程(N1) – YsxlionBlog (ysx88.top)

    Panther X2 rk3566安装jellyfin开启硬件解码 | HiAsia的小站 (gitee.io)

    软路由虚拟机PVE使用LXC安装Debian教程 – 南山如雪 (ckajx.com)

    如何在 Ubuntu/Debian 中安装、创建和管理 LXC (linux-console.net)

    PiMox7 – RPi4 – arm64 CT Basic Configuration – LXD – Linux Containers Forum

    Looking for rootfs.tar.xz | Proxmox Support Forum

    第二步‐电视盒子‐LXC · cooip-jm/About-openwrt Wiki (github.com)

    Linux 安装 Clash 内核并开启透明代理 | ZhiChao’s Blog

    mihomo-config with emoji | Notion Blog (ag.cn.eu.org)

    修改 Proxmox 虚拟机 ID | 呉真的博客 (kuretru.com)

    Arm Proxmox VE8.0 安装教程

  • 转载:Proxmox VE网络配置、内存优化、LXC安装OpenWrt、AdGuardHome、Docker

    [c-alert type=”success”]原文地址:Proxmox VE网络配置、内存优化、LXC安装OpenWrt、AdGuardHome、Docker – (⓿_⓿) (zhou.pp.ua)[/c-alert]

    一、配置网络

    1、安装ifupdown2

    apt install ifupdown2

    2、配置网桥

    #清空原配置,勿点击应用配置

    #点击创建—Linux Bridge,按需配置后应用配置,标记2要对应网口名称

    二、内存优化

    1、禁用非必要的服务

    systemctl stop pve-ha-lrm.service
    systemctl disable pve-ha-lrm.service
    systemctl stop pve-ha-crm.service
    systemctl disable pve-ha-crm.service
    systemctl stop pve-firewall.service
    systemctl disable pve-firewall.service
    systemctl stop pvescheduler.service
    systemctl disable pvescheduler.service
    systemctl stop spiceproxy.service
    systemctl disable spiceproxy.service
    systemctl disable postfix.service

    重启设备后有些服务会自动启动,写个脚本延迟启动执行来关闭服务

    nano /usr/sbin/abc.sh

    #!/bin/sh
    systemctl stop pve-ha-lrm.service
    systemctl stop pve-ha-crm.service
    systemctl stop pve-firewall.service
    systemctl stop pvefw-logger.service
    systemctl stop pvescheduler.service
    systemctl stop spiceproxy.service
    chmod +x /usr/sbin/abc.sh

    添加到exit 0上一行 系统启动后300秒会自动执行

    nano /etc/rc.local

    sleep 300 && /usr/sbin/abc.sh

    2、减少worker数量
    修改目录/usr/share/perl5/PVE/Service下三个文件,将max_workers后面3改为1
    pvedaemon.pm、 pveproxy.pm、spiceproxy.pm

    上面两步操作完重启设备,六分钟后内存占用会降至约550M

    三、LXC安装OpenWrt

    1、下载适合的CT模板
    例1:https://github.com/breakings/OpenWrt/releases/download/ARMv8Docker/openwrt-armvirt-64-default-rootfs.tar.gz(暴躁老哥模板)
    例2:https://downloads.openwrt.org/releases/23.05.2/targets/armsr/armv8/openwrt-23.05.2-armsr-armv8-rootfs.tar.gz(官方最新模板)
    例3:https://github.com/Zane-E/ROOTFS/releases/download/ROOTFS.bak/N1-rootfs-wifi-new.tar.gz(自用N1精简模板——开启wifi见PS)
    例4:https://github.com/Zane-E/ROOTFS/releases/download/ROOTFS.bak/openwrt-s905d-n1-R23.09.29-flippy-rootfs.tar.gz(基于F大N1固件转换模板——开启wifi见PS)
    例5:https://github.com/Zane-E/ROOTFS/releases/download/ROOTFS.bak/jp-tvbox-rootfs.tar.gz(自用荐片盒子精简模板——开启wifi见PS)

    2、上传模板

    3、创建OpenWrt

    pct create 117 \ #序号不能与现有重复
    local:vztmpl/openwrt-armvirt-64-generic-rootfs.tar.gz \ #对应上图1及文件名
    --rootfs local:0.5 \ #分配硬盘空间 单位G
    --ostype unmanaged \
    --hostname OpenWrt \ #名称
    --arch arm64 \ #架构
    --cores 4 \ #核心数
    --memory 256 \ #分配内存 单位M
    --swap 0 \-net0 bridge=vmbr0,name=eth0

    4、勾选嵌套并启动OpenWrt

    5、修改IP

    lxc-attach 117 #在步骤3 shell 输入
    vi /etc/config/network

     

    /etc/init.d/network restart

    浏览器输入IP进入OpenWrt(默认账户root密码password)。之后和设置旁路网关(旁路由)一样步骤,不赘述。

    PS:

    1、编辑配置文件

    nano /etc/pve/lxc/117.conf #117换成创建例3的CT ID

     

    2、末尾添加后重启

    lxc.net.1.type: phys
    lxc.net.1.link: wlan0
    lxc.net.1.flags: up
    lxc.net.1.name: wlan0

    四、LXC安装AdGuardHome(Alpine)

    1、下载并上传模板
    https://mirrors.tuna.tsinghua.edu.cn/lxc-images/images/alpine/3.19/arm64/default/
    https://mirrors.bfsu.edu.cn/lxc-images/images/alpine/3.19/arm64/default/
    ps:alpine比较节省硬盘空间,亦可通过Debian等创建

    2、创建CT

    3、安装AdGuardHome

    换源#非必须

    sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
    apk update
    apk add curl
    curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

     

    按引导进行设置

    五、LXC安装Docker(Alpine)

    部署lxc Alpine(安装AdGuardHome步骤的1、2)

    nano /etc/pve/lxc/118.conf #118换成创建例CT ID

    末尾处添加后重启CT

    lxc.apparmor.profile: unconfined
    lxc.cgroup.devices.allow: a
    lxc.cap.drop:

    lxc.apparmor.profile: unconfined:该配置指定了 AppArmor(应用程序安全性配置框架)的配置文件名称,这里设置为 “unconfined”,用于允许容器内的进程具有更高的系统权限。

    lxc.cgroup.devices.allow: a: 允许容器内的进程访问所有的 cgroup 设备。

    lxc.cap.drop: 此配置项为空,容器内的进程将继承主机系统的默认能力设置。

    换源#非必须

    sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
    apk update
    apk add docker
    rc-update add docker boot
    service docker start