跳过正文

NUC 9 AIO 再折腾

·2951 字
目录

2024 年 11 月,苹果发布了改款的 M4 Mac Mini,更强的性能、更小的机身让摆在桌上的 M2 Mac mini 不再「mini」,黑神话的发布也使得我的 NUC 11 很快就从 AIO 转为 steam 启动器。因此一年多时间里我的 AIO 实际上是由一台 J4125 小主机装上黑群晖再通过部署几台虚拟机来实现,实际使用我也发现自己在 AIO 上折腾下的功夫远超我日常使用需要,这次索性一步到位配一台「退烧机」,彻底满足我对「小体积、全闪存、配备显卡」的需求,同时保证长期稳定性,因此软硬件部署都做了一些改动。

软硬件选型
#

一直以来对 NUC 9 念念不忘,加之我对 AIO 偏好于精巧稳定,对性能要求不高。因此在闲鱼上又收了一台换新回来的 i5 9300H 机器,配上 16G 的内存(赶上内存涨价,后续再配一根 16G)和 3050 单槽刀卡,余下的一个 PCIE 插上 Intel X550 T2 万兆网卡(HPE T562),加上之前的一块 4T 的致态 7100、2T 的 SC001 和 1T 的 WD 580,这就构成了这台 AIO 的硬件配置。

软件方面也做了精简,PVE 8.4 打底,NAS 系统转为便捷易用的黑群晖、3050 显卡直通给 Ubuntu Server 24.04,再加上 HomeAssistant、飞牛,就已经相当能够满足我折腾小参数模型、Docker、智能家居、下载的需求。Windows 作为桌面系统,更多时候拿来打游戏,桥归桥路归路,就没必要放在 AIO 里徒增功耗了。

架构设计
#

通常认为 「AIO」玩到后面难免「All in Boom」,不如趁早「存算网」分离,因此这台机子的黑群晖使用单独的U盘做引导,独立分配存储盘,Ubuntu 虚拟机尽管为了方便 PVE 拍快照把系统安装在虚拟磁盘,但个人数据放在直通的 SATA 固态,且通过群晖 ABB 文件服务器功能定期备份到 NAS。群晖上的所有数据又通过 Hyper Backup 定期备份到异地的另一台群晖上。最大程度实现数据的 「321」备份。出于安全考虑,关闭了本地所有服务的公网访问,而且通过部署 OpenWRT 跑 Tailscale 也能够很方便地从外面连回来,让 Homelab 彻底 Home 化。

安装过程
#

截止到写这篇文章,这台机器已经不断电稳定运行了一个多月。把安装配置贴出来,以后即使 BOOM 了也好原样配置,以下配置只在我的环境验证过,可以作为参考。

PVE 虚拟化配置
#

# Enable VT-d & NIC SR-IOV, Disable WIFI BT Thunderbolt, Enable iGPU in BIOS

# Update PVE (Update PVE 8.4 kernel to Linux 6.8.12-17-pve)
apt update
apt dist-upgrade

# Enable IOMMU
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt pci=assign-busses pci=realloc pcie_acs_override=downstream,multifunction"

update-grub

# Load VFIO modules
echo vfio >> /etc/modules
echo vfio_iommu_type1 >> /etc/modules
echo vfio_pci >> /etc/modules
echo vfio_virqfd >> /etc/modules

# Add drivers blacklist
## NVIDIA GPU
echo "blacklist nouveau" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist nvidia*" >> /etc/modprobe.d/pve-blacklist.conf

## Intel Chip
echo "blacklist ixgbevf" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist iwlwifi" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist iwlmvm" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist btusb" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist ahci" >> /etc/modprobe.d/pve-blacklist.conf 

# Bind passthrough devices to VFIO (Include GPU Audio IDs)
echo "options vfio-pci ids=xxxx:xxxx,xxxx:xxxx" > /etc/modprobe.d/vfio.conf  

# Update initramfs
update-initramfs -u -k all
reboot

网络配置
#

分配 VF 网卡并持久化保存
#

写入系统服务,在开机时自动为一个端口创建 8个 VF 网卡,另一个端口暂时不创建网卡:

# Enable X550 T2 NIC SR-IOV and making the VF persistent
## Edit sriov-NIC.service in /etc/systemd/system
[Unit]
Description=Script to enable SR-IOV on boot
After=network-pre.target
Wants=network-pre.target
Before=network.target

[Service]
Type=oneshot
RemainAfterExit=yes

# Set PF down
ExecStart=/usr/bin/ip link set enp2s0f0 down
ExecStart=/usr/bin/ip link set enp2s0f1 down

# Initial VF
ExecStart=/usr/bin/bash -c '/usr/bin/echo 0 > /sys/class/net/enp2s0f0/device/sriov_numvfs'

ExecStart=/usr/bin/bash -c '/usr/bin/echo 0 > /sys/class/net/enp2s0f1/device/sriov_numvfs'

ExecStart=/usr/bin/bash -c '/usr/bin/echo 8 > /sys/class/net/enp2s0f0/device/sriov_numvfs'

# Sleep
ExecStart=/usr/bin/sleep 5
  
# Config VF MAC
ExecStart=/usr/bin/ip link set enp2s0f0 vf 0 mac 11:22:33:44:55:01
ExecStart=/usr/bin/ip link set enp2s0f0 vf 1 mac 11:22:33:44:55:02
ExecStart=/usr/bin/ip link set enp2s0f0 vf 2 mac 11:22:33:44:55:03
ExecStart=/usr/bin/ip link set enp2s0f0 vf 3 mac 11:22:33:44:55:04
ExecStart=/usr/bin/ip link set enp2s0f0 vf 4 mac 11:22:33:44:55:05
ExecStart=/usr/bin/ip link set enp2s0f0 vf 5 mac 11:22:33:44:55:06
ExecStart=/usr/bin/ip link set enp2s0f0 vf 6 mac 11:22:33:44:55:07
ExecStart=/usr/bin/ip link set enp2s0f0 vf 7 mac 11:22:33:44:55:08

# Enable VEB by turn off spoofchk & turn on trust
ExecStart=/usr/bin/ip link set enp2s0f0 vf 0 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 1 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 2 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 3 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 4 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 5 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 6 spoofchk off trust on
ExecStart=/usr/bin/ip link set enp2s0f0 vf 7 spoofchk off trust on

# Set PF up
ExecStart=/usr/bin/ip link set enp2s0f0 up
ExecStart=/usr/bin/ip link set enp2s0f1 up

[Install]
WantedBy=multi-user.target

启用系统服务使得 VF 重启后也能生效:

## Enable sriov-NIC service
systemctl daemon-reload
systemctl enable sriov-NIC.service
systemctl start sriov-NIC.service

固定 PVE 管理网口名称
#

如果硬件发生变化,PVE 的网卡名很可能发生变动,进而失去管理口连接。因此根据网卡 mac 地址固定 PVE 管理网口名:

# Bind mac address to vmbr0 (Prevent when network interface name changes cause lost webUI connection)

## Fix network interface name

nano /etc/udev/rules.d/10-net-names.rules
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="aa:bb:cc:dd:ee:ff", NAME="ethpve"

## Edit config in /etc/network/interfaces

auto ethpve
iface ethpve inet manual
auto vmbr0
iface vmbr0 inet static
    address 192.168.x.x/24
    gateway 192.168.x.x
    bridge-ports ethpve

数据备份配置
#

通过群晖 Active Backup for Business 工具,能很方便地备份 Ubuntu 服务器的文件到 NAS,避免意外情况造成数据丢失。为了安全考虑,新创建一个备份账户专门用于群晖连接 Ubuntu,专用账户对需要备份的数据仅有只读权限,而且限制了对 ssh 密钥等敏感文件的读取。

# 创建专用于备份的用户,用户不在root或sudo组
sudo useradd bak

# 设置文件夹权限
## 查看文件夹权限
ls -ld /home /home/xxx
  
## 配置文件夹权限
sudo install acl

## 处理现有的文件(立即让 bak 能够进入 /home/xxx 并读取里面现在已经有的所有文件。)
sudo setfacl -R -m u:bak:rX /home/xxx

## 处理未来的文件(加上 -d 即default ACL, 对/home/xxx创建的新文件,bak自动拥有只读权限。)
sudo setfacl -R -d -m u:bak:rX /home/xxx

## 敏感文件禁止访问
sudo setfacl -m u:bak:--- /home/xxx/.ssh
sudo setfacl -m u:bak:--- /home/xxx/.gnupg

由于待备份文件夹里挂载了 Docker 卷,包含了一些数据库文件,但 ABB 的文件服务器并不支持热备份,只能在启动备份任务前先停止 docker 容器,等待一段时间备份完成后再重新启动 Docker 容器。使用 system services 就可以轻松实现:

编写脚本
#

  1. 创建停止容器脚本 abb-stop-containers.sh
sudo nano /usr/local/bin/abb-stop-containers.sh
#!/bin/bash
set -e

STATEFUL_CONTAINERS=$(/usr/bin/docker ps -q)

if [ -z "$STATEFUL_CONTAINERS" ]; then
    exit 0
fi

for c in $STATEFUL_CONTAINERS; do
    /usr/bin/docker stop "$c"
done
  1. 创建启动容器脚本 abb-start-containers.sh
sudo nano /usr/local/bin/abb-start-containers.sh
#!/bin/bash
set -e

STATEFUL_CONTAINERS=$(/usr/bin/docker ps -a -q)

[ -z "$STATEFUL_CONTAINERS" ] && exit 0

for c in $STATEFUL_CONTAINERS; do
    /usr/bin/docker start "$c" || true
done
  1. 配置脚本权限
sudo chown root:root /usr/local/bin/abb-stop-containers.sh
sudo chmod 750 /usr/local/bin/abb-stop-containers.sh
sudo chown root:root /usr/local/bin/abb-start-containers.sh
sudo chmod 750 /usr/local/bin/abb-start-containers.sh 

创建服务
#

  1. 创建停容器服务 abb-stop-containers.service
sudo nano /etc/systemd/system/abb-stop-containers.service 
[Unit]
Description=ABB - Stop Docker containers

[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/bin/abb-stop-containers.sh
  1. 创建启容器服务 abb-start-containers.service
sudo nano /etc/systemd/system/abb-start-containers.service
[Unit]
Description=ABB - Start previously stopped Docker containers

[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/bin/abb-start-containers.sh

创建触发条件
#

  1. 停容器 timer(UTC时间)
sudo nano /etc/systemd/system/abb-stop-containers.timer
[Unit]
Description=ABB stop containers before backup

[Timer]
OnCalendar=*-*-* 18:55:00
Persistent=true

[Install]
WantedBy=timers.target
  1. 启容器 timer(UTC时间)
sudo nano /etc/systemd/system/abb-start-containers.timer
[Unit]
Description=ABB start containers after backup

[Timer]
OnCalendar=*-*-* 20:10:00
Persistent=true

[Install]
WantedBy=timers.target

启用服务
#

sudo systemctl daemon-reload
sudo systemctl enable --now abb-stop-containers.timer
sudo systemctl enable --now abb-start-containers.timer

注意事项
#

  • 核显直通。无数次踩坑实践证明,核显直通属于亡命之徒行为,非绝对使用必要务必保持远离,直通独显才是正道。否则碰到了 PVE 系统出了问题既登不了 WebUI,也连不上控制台的时候那叫一个头大,若不幸掉坑请在开机时狂按「E」键进入 GRUB 编辑界面,暂时禁用核显直通后排障。
  • PVE 执行重启不能正常启动,只能关机后手动开机。猜测是 BIOS 问题,但在关闭了 BIOS watchDOG 后仍然不行,不太影响日常使用,先放着。
  • 根据矿神的教程,对黑群晖 DSM7 引导,在虚拟机上添加一个串行端口,可以实现 PVE 系统里直接安全关闭群晖虚拟机。

最后
#

折腾 AIO 到最后,发现消费级设备或多或少存在点小毛病。比如在这台 NUC 9 上配置 VF 网卡就碰见了 BIOS 无法分配足够的 PCI 地址内存空间的错误(error: Cannot allocate memory),经过反复尝试只有在 PVE 8.4 并把内核升级到 Linux 6.8.12-17-pve 环境下,添加 ACS 补丁和编辑 GRUB 参数后才终于能够成功创建 VF 网卡。而两年前折腾 NUC 9 的时候还存在无法在 TrueNAS 虚拟机上同时直通三块 NVME 却不能正常使用的问题,到现在也没弄清楚原因。以及之前碰到的多块 4T NVME 的不认盘问题,实在是不一而足。

或许消费级设备的 BIOS 和硬件兼容性天然比不上工作站和服务器,难以支持一些特殊需求。折腾过程中屡屡怀疑到底是参数没写好,还是硬件存在兼容性问题,亦或干脆是机器坏了?心情也随着验证各种猜想时的确定或排除而如同坐过山车般跌宕起伏,不由得想起以前也是乐此不疲地折腾安卓解 BL、刷 RC、改 ROM。正所谓「当其欣于所遇,暂得于己, 快然自足,不知老之将至。及其所之既倦,情随事迁,感慨系之矣」,深以为然。

玩到最后,越来越觉得时间和精力不应该也不需要放在设硬件局限和反复试错上,使用正儿八经的小型服务器或者用厂商配好的养老方案才是归宿,毕竟折腾是过程,用得开心、玩得尽兴,才更能享受科技带来的美好。但无论怎么说,网上的各类资料和教程都给了我莫大的帮助,感谢开源社区和众多博主贴主的倾囊相授,碰到各种问题的时候也不至于孤立无援,也多亏了各类 AI,让我省了许多写脚本、排障的麻烦事。作为一台「退烧机」,这台 NUC9 能到目前这个可用程度已经让我满意。所以把我的折腾过程和脚本贴上来,供后来人参考,给大家省点繁琐事,少走点弯弯绕绕,用技术扩展生活边界,祝各位玩得开心、玩得尽兴!

参考资料
#