网卡位的 SATA——踩坑 mini PCIe 硬盘拓展卡

网卡位的 SATA——踩坑 mini PCIe 硬盘拓展卡

如果使用笔记本或一体机等 SATA 接口较少的设备玩家里云,通过无线网卡的 mini PCIe 插槽拓展多块硬盘,是一个既能保障硬盘高速平稳运行,又能顺便让 SATA 控制器直通虚拟机的不二之选。

不过,最常用到 mini PCIe 转 SATA 拓展卡的 OEM 平台通常高度定制,可能产生非预期表现,博主也因此踩了一点小坑。好在博主最终解决了这些问题,获得了较为良好的体验。

改造前
改造后

前情提要

(跳过此部分)

可用性暴降

虽然原方案的低性能是早有预期,可用性也早已初见端倪,但此时的状况已经十分严峻。

2025 年 6 月更换的新转接线

如本站该说说所述,博主在 2024 年 10 月搭建的基于多个 USB2.0 转 SATA 转接线的硬盘阵列柜,在 2025 年 6 月因稳定性下降替换了所有转接线后,在 2026 年 6 月底又到了崩溃周期,平均无故障时间不足一星期。故障要通过反复拔插 USB 线缆直到所有硬盘上线解决,繁琐、玄学且不能远程处理。

此时已经下掉掉线的硬盘降级运行,但又有一块硬盘没有识别,超出 RAID Z1 容错能力

崩溃时,磁盘从 PVE 硬盘列表临时性或永久性消失,TrueNAS 虚拟机因非预期硬件更改而退出,顺便崩掉依赖其 NFS 共享的 Nextcloud 容器。即使这块磁盘仍然保持链接,TrueNAS 也可能因为该阵列 IO 超时而崩溃;或者因 IO 错误,某块硬盘在重启后无法读出 ZFS 信息,导致阵列降级运行或无法初始化。

因 IO 超时导致的 TrueNAS 卡死,需要手动重启
因掉盘导致的 TrueNAS 停机,需删除离线硬盘或重新连接后开机

经过两年半鸡飞狗跳的运维体验,USB2.0 转 SATA 方案,因芯片组的弱鸡 USB 控制器、USB 接口的弱鸡可靠性、USB 协议的逆天开销、转接卡芯片的弱鸡体质,不推荐任何有数据读写需求的人使用。

存储成本上涨

增加的存储需求和节节攀升的存储价格,也是促成本次改造的推手。改用 mini PCIe 转 SATA 的连接方式,能够通过提升可用性和稳定性,盘活存量可用空间,增加好存储1容量供给,疏导 SSD 非热点数据需求,提升磁盘预期寿命,降低存储阵列维护和折旧成本。

用量挺大

说白了,由于阵列不可靠,博主不敢把大量独一无二的垃圾/获取麻烦的收藏放进去保存。把该阵列硬盘的连接方式正常化,就能在不更换大容量硬盘或者更换平台的前提下,增加实用的存储空间,变相扩容。

  1. 好存储:符合 高度可用、充分利用、容量充足、速度合理、一定冗余 要求的存储空间 ↩︎

物料与支出

本次改造成本约 160 CNY,用于购买以下物料:

  • mini PCIe 转 SATA 拓展卡(Linkreal) ——115 CNY
    • Marvell 9215 主控(官方规格书
    • SFF8087 插头转 4 个 SATA 3.0 插头连接线
    • 全高 mini PCIe
  • mini PCIe 延长线 ——19 CNY
    • 带有屏蔽层的 40pin FPC 软排线,触点位于同侧
    • 全高/半高 mini PCIe 支持
  • 12V 降压 DC-DC 模块 ——6.5 CNY
    • 标称最大 8A 电流输出
    • XL4016E1 芯片(官方规格书
    • 调整为 5V 输出
  • SATA 供电线 ——7 CNY
    • 一个 Molex(大4D)转 4 个 SATA 供电
      • 大4D不重要,焊接会剪掉
  • 硬盘支架 ——11 CNY
    • 两个上漆铁板的开放式支架,设计简约
    • 没错,旧的阵列柜没有使用螺丝固定硬盘
  • (家中常备的烙铁胶带电钻螺丝刀等工具和物料不计入)
  • (导热硅脂等其它顺带操作的物料不计入)

由于是从旧硬盘柜升级,硬盘、12V 电源以及散热风扇使用现有配置。

硬件安装

请至少读完一次再下单和操作

以下步骤没有埋雷和反转,可直接参考

修改供电

旧硬盘柜 5V 由转接线从 USB 取电,新硬盘柜使用独立 SATA 供电线,无既有 5V 电源可用。考虑到电源质量对硬盘寿命的影响,使用 XL4016E1 降压模块作为 5V 电源,弃用 USB 取电。

交流电源和降压 DC-DC 模块均能通过电位器调节输出电压,需要在连接前调整

制作 SATA 供电线

确定 mini PCIe 拓展卡位置

请结合设备的实际情况,进行充分的测量,充分考虑拓展卡厚度、线缆空间、稳固性和走线可行性等因素。

例如,请考虑外壳到屏蔽罩的距离

为减少实际与设计偏差产生的后果,减少计算和返工次数,博主建议使用容错更高的设计和改造路线,例如留出延长线盘绕空间,允许安装位置变化,改造工作从内到外、从 mini PCIe 插槽到硬盘连接处进行。

例如基于测量,拓展卡可在博主的机器内安装于红框范围,且空间允许更长的排线迂回

机内放置拓展卡需要给 SATA 线开洞,机外放置拓展卡需要给 FPC 排线开洞。建议拓展卡位置确定后,再给外壳开洞,减少不可逆误操作。

安装 mini PCIe 延长线和拓展卡

博主的一体机使用半高 mini PCIe 槽位,且该拓展卡接口朝向外侧,拓展卡必须要使用延长线才能安装。

红框为半高 mini PCIe 插槽,且后方就是散热器,锯不了

请根据插槽附近的情况,提前确定好拓展卡的位置后,设计合适的 mini PCIe 延长线走线。博主选择剪掉部分屏蔽罩,穿过 FPC 排线,将 mini PCIe 延长线按下图方式布置。当然,你也可以把拓展卡也放在主机外面。

mini PCIe 延长线的抗干扰能力比意料中强,连博主的走线方式都可以正常工作,故布线时关注线缆受力即可。

请不要过度、反复弯折线缆,注意切口处防割。与金属接触的部分,建议进行绝缘处理。走线后做好固定。
拓展卡在机内的最终安装效果

安装之后,请确认其是否工作,并连接硬盘测试。

机身开孔,合盖收尾

确认硬盘可以识别后(相信能识别就是能工作),硬件安装进入了最终的收尾工作。

埋下伏笔:展示 AMI BIOS 前,会先进入这个界面

根据设计的走线方式,给机身开孔,插好需要提前插好的线缆:

p.s. 图中 FPC 排线插反了

放回原位后连接所有线缆:

你在开头见过这张图了

嗯,现在博主得到了一台硬件上正常,但软件无法正常工作的机器,进入故障排除环节

虚拟机连接

该 mini PCIe 硬盘拓展卡在 PVE 中列出

调试完成、进入 PVE 系统后,博主在 Web UI 直接给该 mini PCIe 拓展卡设置了 PCIe 直通,也就是控制器直通。设置控制器直通后,该控制器连接的硬盘就不会被 PVE 识别,但 TrueNAS 虚拟机就可以直接与该控制器通信,直接获取详细信息等。而原有机内硬盘仍然使用硬盘直通模式,SSD 虚拟硬盘也仍继续使用。

红框中的 4 块硬盘正确识别了型号

不过,目前博主也只能确认型号、硬盘转速、硬盘温度可以被识别,SMART 详情还没有打出来过。

异常调试

正如开头所述,OEM 平台通常高度定制,可能产生非预期表现。这些问题应该不会在通用平台上出现,而 OEM 平台因为定制的外围设备和非标准的实现,可能在安装 mini PCIe 拓展卡后会产生一些问题,导致无法正常引导或登录管理后台。

BIOS 无法引导内置硬盘

图中“启动设备选择”均为拓展卡连接的硬盘,无法从 PVE 所在的内置硬盘引导

我也不知道为什么,可能是早期 Asus 的石山代码发力了,总之这确确实实发生了。其实,该机此前的 BIOS 引导就极其诡异,例如插可引导 U 盘来引导 PVE,开关快速启动来引导 PVE 之类的(也许和 PVE 在光驱位硬盘内有一定关系?),这次只是病情加重罢了。

解决方法

借助 Gemini 解决,经过博主验证

博主要修复 PVE(Debian)引导,其它基于 grub 引导的操作系统应该也相似。由于该故障不影响 U 盘引导,故博主选择将 grub 安装到一直插着的 U 盘中,以规避该问题。

第一步,通过 grub 命令行进入 PVE

一般而言,使用 UEFI 启动的 Linux 发行版,其可启动 U 盘(Live CD)都使用 grub 引导,能在启动选项菜单按键调用 grub 命令行。

Ubuntu Live CD 提示按下“C”键进入 grub 命令行

进入 U 盘的 grub 命令行后,加载文件系统模块,确认能否读到引导分区

insmod lvm
insmod fat
insmod ext2

ls

如果有 EFI 分区,尝试使用该分区内的引导:

search --no-floppy --file --set=root /EFI/proxmox/grub.cfg
configfile ($root)/EFI/proxmox/grub.cfg

或者,尝试使用 PVE 的 /boot 内的引导:

search --no-floppy --file --set=root /boot/grub/grub.cfg
configfile ($root)/boot/grub/grub.cfg

此时,应该引导到 PVE 了

第二步,在外置 U 盘安装 grub

以下步骤会抹掉这个 U 盘的所有数据!其它的分区操作也需要谨慎进行!

先创建一个 EFI 分区。插上一个能一直插着的U盘(假设为 /dev/sdX),通过 fdisk 编辑:

fdisk /dev/sdX

在 fdisk 中,按“G”创建 GUID 分区表,按“N”新建分区(根据需要设置分区细节),按“T”后按“1”将分区类型改为 EFI,按“W”应用更改。

接着,格式化新创建的 EFI 分区(假设为 /dev/sdX1):

mkfs.vfat -F 32 /dev/sdX1

挂载该 EFI 分区:

mkdir -p /mnt/usb-efi
mount /dev/sdX1 /mnt/usb-efi

确保安装 EFI 支持包:

apt install -y grub-efi-amd64-bin

安装 grub 到 U 盘:

grub-install --target=x86_64-efi --efi-directory=/mnt/usb-efi --bootloader-id=PVE --removable --recheck

update-grub

到这一步,引导已经安装成功了,可以执行 umount /mnt/usb-efi 卸载这个 EFI 分区。

最后重启,在 BIOS 修改启动选项,即可实现 PVE 引导。

附加步骤,自动将其挂载到 /boot/efi

你还可以让该 EFI 分区自动挂载到 /boot/efi。只需通过 blkid /dev/sdX1 查询分区 UUID,并在 /etc/fstab 追加:

UUID=【 该U盘EFI分区的UUID 】 /boot/efi vfat defaults,nofail 0 0

这样,它基本与一般 EFI 分区无异了。

PCIe 加载顺序变化,导致网卡异常

借助 Gemini 解决,经过博主验证

和 SCSI 枚举顺序影响磁盘 sda sdb 名称一样,机内无线网卡的 PCIe 枚举顺序也会影响机内有线网卡名称。

由于 PVE 使用网桥(形如 vmbr0),多出的高优先级 PCIe 设备(本例为 mini PCIe 硬盘拓展卡)会影响枚举顺序而改变网卡名称(形如 enp2s0),使网桥的配置文件出错,导致系统无法接入网络。

解决方法

除了修正配置文件中的网卡名称,Gemini 推荐通过 systemd.link 绑定 MAC 地址与网卡名称(假设为 lan0),步骤如下:

进入 PVE 命令行,通过 ip l 获取网卡 MAC 地址(假设为 aa:bb:cc:dd:ee:ff),并创建一个名为 10-onboard-lan.link 的配置文件(开头数字小则优先加载;onboard-lan 也可换名):

nano /etc/systemd/network/10-onboard-lan.link

粘贴以下内容:

[Match]
MACAddress=aa:bb:cc:dd:ee:ff

[Link]
Name=lan0

保存,然后编辑 /etc/network/interfaces,找到物理网卡配置和 vmbr0(PVE 虚拟网桥)的配置,修改 bridge-ports

iface lan0 inet manual

auto vmbr0
iface vmbr0 inet static
        address 192.168.1.100/24
        gateway 192.168.1.1
        bridge-ports lan0         # 关键点:这里绑定 lan0
        bridge-stp off
        bridge-fd 0

保存退出编辑器,“保险起见”执行 update-initramfs -u -k all 刷新 initramfs,然后重启,网络配置就应该正常了。

修改后,网络配置应该类似于这样

性能表现

解决完前面一堆故障后,终于能看看这个拓展卡是什么表现了!

PVE 仪表板数据

从 IO 延迟的表现上看,mini PCIe 拓展卡表现没有问题,远好于 4 个 USB2.0 转接线组成的硬盘阵列(这个阵列哪怕不载入也占用 IO)

近一年服务器平均 IO 延迟
2025年4月跳水是由于阵列崩溃下线,当年9月 TrueNAS 基本重新上线
2026年4月跳水也是阵列崩溃下线,6月二次跳水则是因为本文的更改
大高台(25%)是活跃使用USB阵列,小高台是插着不用,底是没插或本文更改后

从平均负载来看,mini PCIe 运行平稳,把负载压回了正常水平。

近一年服务器平均负载
负载尖峰大多是USB阵列卡死TrueNAS产生的

FIO 测速

在 TrueNAS 运行 FIO 测速,大文件读写稳定在 160MB/s 附近,达到普通机械硬盘水准。PCIe2.0x1 有 500MB/s 左右的理论速率,所以性能属硬盘自身限制。

详细输出:

写入测试如下:

[/mnt/External/fio_test]# fio --name=write_test --ioengine=posixaio --rw=write --bs=1M --size=10G --numjobs=1 --direct=1 --group_reporting
write_test: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=posixaio, iodepth=1
fio-3.33
Starting 1 process
write_test: Laying out IO file (1 file / 10240MiB)
Jobs: 1 (f=1): [W(1)][100.0%][w=156MiB/s][w=156 IOPS][eta 00m:00s]
write_test: (groupid=0, jobs=1): err= 0: pid=648699: Tue Aug 18 22:57:49 2026
  write: IOPS=155, BW=156MiB/s (163MB/s)(10.0GiB/65842msec); 0 zone resets
    slat (usec): min=10, max=61222, avg=72.21, stdev=726.73
    clat (usec): min=3, max=222693, avg=6349.12, stdev=4804.75
     lat (usec): min=246, max=222757, avg=6421.33, stdev=4850.35
    clat percentiles (usec):
     |  1.00th=[   343],  5.00th=[  1532], 10.00th=[  4817], 20.00th=[  5342],
     | 30.00th=[  5538], 40.00th=[  5735], 50.00th=[  5932], 60.00th=[  6128],
     | 70.00th=[  6390], 80.00th=[  6718], 90.00th=[  7701], 95.00th=[  9503],
     | 99.00th=[ 20055], 99.50th=[ 30802], 99.90th=[ 69731], 99.95th=[101188],
     | 99.99th=[110625]
   bw (  KiB/s): min=16384, max=1157120, per=100.00%, avg=159446.90, stdev=93070.53, samples=131
   iops        : min=   16, max= 1130, avg=155.64, stdev=90.91, samples=131
  lat (usec)   : 4=0.02%, 100=0.06%, 250=0.32%, 500=2.21%, 750=0.88%
  lat (usec)   : 1000=0.71%
  lat (msec)   : 2=1.17%, 4=0.98%, 10=89.36%, 20=3.30%, 50=0.81%
  lat (msec)   : 100=0.12%, 250=0.07%
  cpu          : usr=0.95%, sys=0.35%, ctx=13974, majf=0, minf=27
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,10240,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=156MiB/s (163MB/s), 156MiB/s-156MiB/s (163MB/s-163MB/s), io=10.0GiB (10.7GB), run=65842-65842msec

读取测试如下:

[/mnt/External/fio_test]# fio --name=read_test --ioengine=posixaio --rw=read --bs=1M --size=10G --numjobs=1 --direct=1 --group_reporting
read_test: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=posixaio, iodepth=1
fio-3.33
Starting 1 process
read_test: Laying out IO file (1 file / 10240MiB)
Jobs: 1 (f=1): [R(1)][100.0%][r=160MiB/s][r=160 IOPS][eta 00m:00s]
read_test: (groupid=0, jobs=1): err= 0: pid=649233: Tue Aug 18 23:00:19 2026
  read: IOPS=151, BW=152MiB/s (159MB/s)(10.0GiB/67408msec)
    slat (usec): min=2, max=4812, avg=18.60, stdev=87.09
    clat (usec): min=2, max=137576, avg=6554.28, stdev=8698.31
     lat (usec): min=308, max=137591, avg=6572.88, stdev=8696.10
    clat percentiles (usec):
     |  1.00th=[   388],  5.00th=[   453], 10.00th=[   494], 20.00th=[   537],
     | 30.00th=[   586], 40.00th=[   660], 50.00th=[   938], 60.00th=[  8094],
     | 70.00th=[  9241], 80.00th=[ 15270], 90.00th=[ 16909], 95.00th=[ 17695],
     | 99.00th=[ 21103], 99.50th=[ 48497], 99.90th=[ 98042], 99.95th=[116917],
     | 99.99th=[125305]
   bw (  KiB/s): min=114003, max=178533, per=100.00%, avg=155766.28, stdev=14289.74, samples=134
   iops        : min=  111, max=  174, avg=152.04, stdev=13.95, samples=134
  lat (usec)   : 4=0.11%, 10=0.18%, 100=0.02%, 500=10.71%, 750=34.38%
  lat (usec)   : 1000=5.11%
  lat (msec)   : 2=2.07%, 4=0.70%, 10=21.63%, 20=23.99%, 50=0.62%
  lat (msec)   : 100=0.39%, 250=0.08%
  cpu          : usr=0.39%, sys=0.48%, ctx=10694, majf=0, minf=27
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=10240,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=152MiB/s (159MB/s), 152MiB/s-152MiB/s (159MB/s-159MB/s), io=10.0GiB (10.7GB), run=67408-67408msec

SMB 测速

结合 FIO 测速和 iperf3 拉流,可以确定这个速度其实是受 Wi-Fi 限制。可能由于局域网设备和环境原因,传输有时会断流卡死。

(瓶颈发现,新坑+1)

从服务器下载
上传到服务器

主观表现

应用 mini PCIe 硬盘拓展卡以后,即使给这个阵列加上高负载,TrueNAS 也没有再自爆过,WebUI 体验也好了一个档次。局域网播放存储的高码率视频,也终于不会再卡顿了。可以说,博主的 TrueNAS 已经现阶段毕业(内存所限也折腾不了了),以后的方向就是异地备份等额外需求了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇