手头有一台闲置的 Dell Precision 3240 Compact,i7-10700、64GB 内存,还有一张 6GB 的 Quadro RTX 3000。拿来跑 Docker 开发环境挺合适,问题是机器里没有硬盘,现在硬盘贵的要死。
刚好 ESXi 上还有空间,也有一台已经在运行的 Ubuntu VM,于是试了下:给 VM 加一块 100GiB 虚拟磁盘,再让 3240 从网络启动,系统盘通过 iSCSI 接过去。这样可以把开发环境和容器搬到 3240 上,给主力机省点内存。
最后跑 Docker 的是 3240 实体机,CPU、内存和 GPU 都是它自己的;ESXi 上的 Ubuntu VM 负责提供引导文件和系统盘。重启之后,文件和 Docker volume 里的数据都还在。下面把实际搭建过程和踩的坑记一下。
整体架构

- RouterOS:负责 DHCP,告诉 3240 到哪台服务器、取哪个 UEFI 启动文件。
- Ubuntu 引导/存储 VM:TFTP 提供 iPXE;Nginx 提供内核和 initramfs;Linux LIO 通过 iSCSI 导出整块 100GiB 虚拟磁盘。
- Dell 3240:UEFI 网络引导进入 Ubuntu,根目录挂在 iSCSI 盘上,随后运行 Docker、Compose 和 NVIDIA 驱动。
- 交换机:宿主机继续使用原来的 untagged 管理网;需要独立 Lab 地址的容器使用 tagged VLAN 110。
这套方案里,ESXi 只承载虚拟磁盘和存储 VM,对外提供 iSCSI 的是 Ubuntu VM。VM 自己原有的系统盘不能拿来格式化;我另外加了一块专门给 Dell 使用的 VMDK。

环境和示例地址
本次使用 ESXi 8.0 U3、Ubuntu 24.04 LTS、Mikrotik RouterOS,以及一台 WNDR3800 做 VLAN 交换。交换机型号不是必需条件,关键是能让 Dell 的端口同时保留 untagged 管理网和 tagged VLAN 110。
下面统一使用示例地址和示例 MAC,不能直接当成自己的配置。如果你的管理网不是 /24,要一起修改网关、掩码、DHCP 和访问控制。
| 用途 | 示例值 |
|---|---|
| 管理网 | 192.168.10.0/24 |
| RouterOS 管理网网关 / DNS | 192.168.10.1 |
| Dell 管理地址 | 192.168.10.60 |
| Dell MAC | 02:11:22:33:44:55 |
| Lab VLAN | 110,192.168.110.0/24 |
| Lab 网关 | 192.168.110.1 |
| Ubuntu 引导/存储 VM | 192.168.110.10 |
| Docker Lab 地址池 | 192.168.110.224/27,即 .224–.254 |
| Lab DHCP 地址池 | 示例 .50–.199,与 Docker 地址池分开 |
宿主机和存储 VM 可以位于不同子网,但路由必须能通。至少要检查 TFTP 的请求及返回流量、HTTP 8088 和 iSCSI TCP 3260;防火墙只放行需要访问的客户端。
第一步:给 Ubuntu VM 加一块专用磁盘
在 ESXi 里给现有 Ubuntu VM 加一块 100GiB 虚拟磁盘。我的做法是单独放到一个目录,便于以后识别和备份。进入 VM 后,先确认新磁盘的大小、SCSI 位置和稳定设备路径:
1 2 3 | lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS ls -l /dev/disk/by-path/ sudo wipefs -n /dev/disk/by-path/你的新磁盘路径 |
分区和 mkfs 会清掉所选磁盘的数据。先确认它是新加的空盘,没有挂载,也没有其他用途;不要照着别人文章里的 /dev/sdb 直接执行。这里建议始终使用确认过的 by-path/by-id 路径。
我把盘分为一个 512MiB 的 FAT32 ESP 和剩余空间的 ext4 根分区。ESP 是为以后迁移或调整引导方式预留的;当前直接由 PXE 加载 Linux,不依赖磁盘里的 GRUB。
1 2 3 4 5 6 7 8 9 10 11 12 13 | sudo apt update sudo apt install debootstrap parted dosfstools targetcli-fb tftpd-hpa nginx-light # 先核对,再把下面两个变量改成新盘的真实分区路径。 ROOT_PART=/dev/disk/by-path/你的新磁盘路径-part2 EFI_PART=/dev/disk/by-path/你的新磁盘路径-part1 # 分区并格式化完成后再挂载。 sudo mkdir -p /mnt/dell-root sudo mount "$ROOT_PART" /mnt/dell-root sudo mkdir -p /mnt/dell-root/boot/efi sudo mount "$EFI_PART" /mnt/dell-root/boot/efi sudo blkid "$ROOT_PART" "$EFI_PART" |
第二步:在新盘上准备 Ubuntu 根文件系统
我用 debootstrap 构建 Noble 根文件系统,没有让安装器去改 ESXi VM 的引导盘或 EFI 启动项。以下命令在存储 VM 上执行,目标目录都是新磁盘:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | sudo debootstrap --arch=amd64 --include=ca-certificates noble /mnt/dell-root https://archive.ubuntu.com/ubuntu # 进入 chroot 前挂载基本运行环境,避免挂载传播回宿主机。 sudo mount --rbind /dev /mnt/dell-root/dev sudo mount --make-rslave /mnt/dell-root/dev sudo mount -t proc proc /mnt/dell-root/proc sudo mount --rbind /sys /mnt/dell-root/sys sudo mount --make-rslave /mnt/dell-root/sys # 这些文件都写在新系统目录里。DNS 地址按实际环境修改。 printf "nameserver 192.168.10.1\n" | sudo tee /mnt/dell-root/etc/resolv.conf printf "#!/bin/sh\nexit 101\n" | sudo tee /mnt/dell-root/usr/sbin/policy-rc.d sudo chmod 755 /mnt/dell-root/usr/sbin/policy-rc.d sudo chroot /mnt/dell-root /bin/bash |
软件源需要按实际使用的 Ubuntu 镜像配置好,包含 noble、noble-updates 和 noble-security;如果后面装 NVIDIA 包,还要启用 restricted / universe / multiverse。chroot 里主要装这些包:
1 2 3 4 5 6 7 | apt update apt install linux-generic linux-firmware intel-microcode \ initramfs-tools open-iscsi busybox-initramfs finalrd \ systemd-sysv systemd-resolved netplan.io openssh-server \ sudo ca-certificates curl gnupg echo dell3240-dev > /etc/hostname |
另外创建自己的登录用户、安装 SSH 公钥,并启用 ssh、systemd-networkd、systemd-resolved 等服务。下面仍在 chroot 里执行;authorized_keys 的内容换成你自己的公钥。不要把私钥拷到服务器上。
1 2 3 4 5 6 7 8 9 | useradd -m -s /bin/bash -G sudo ubuntu passwd ubuntu install -d -m 700 -o ubuntu -g ubuntu /home/ubuntu/.ssh # 将自己的 SSH 公钥写入 /home/ubuntu/.ssh/authorized_keys chown ubuntu:ubuntu /home/ubuntu/.ssh/authorized_keys chmod 600 /home/ubuntu/.ssh/authorized_keys ssh-keygen -A systemctl enable ssh systemd-networkd systemd-resolved systemd-timesyncd iscsid.socket systemctl set-default multi-user.target |
本地密码由自己设置,SSH 可以另外在 sshd_config.d 中明确关闭 PasswordAuthentication、KbdInteractiveAuthentication 和 root 登录。临时的 policy-rc.d 要在制作完成后移除,否则以后装服务时会被它拦住。
我按网卡 MAC 写 Netplan,保持宿主机走原来的管理网。示例 /etc/netplan/10-dell-netboot.yaml:
1 2 3 4 5 6 7 8 9 10 11 12 | network: version: 2 renderer: networkd ethernets: dell-wired: match: macaddress: "02:11:22:33:44:55" dhcp4: true dhcp6: false dhcp-identifier: mac critical: true optional: false |
根目录的 fstab 用 blkid 查到的 UUID,不依赖下一次启动时磁盘叫 sda 还是 sdb:
1 2 | UUID=你的根分区UUID / ext4 defaults,_netdev 0 1 UUID=你的ESP分区UUID /boot/efi vfat umask=0077,nofail,_netdev 0 2 |
第三步:让 initramfs 在挂根目录前登录 iSCSI
这是无盘系统能不能起来的关键。普通安装里,网络和 iSCSI 服务可能在挂好根目录以后才启动;这里根目录本身就在网络上,所以这些步骤必须提前放进 initramfs。
设置唯一的 initiator IQN,例如:
1 | InitiatorName=iqn.2026-10.example.lab:dell3240 |
将它写入 /etc/iscsi/initiatorname.iscsi,并在 /etc/iscsi/iscsi.initramfs 配置目标:
1 2 3 4 5 6 7 | ISCSI_INITIATOR=iqn.2026-10.example.lab:dell3240 ISCSI_TARGET_NAME=iqn.2026-10.example.lab:storage.dell3240 ISCSI_TARGET_IP=192.168.110.10 ISCSI_TARGET_PORT=3260 ISCSI_TARGET_GROUP=1 ISCSI_USERNAME=dell3240 ISCSI_PASSWORD=替换为你自己生成的CHAP密码 |
这里的密码要和服务端 ACL 的 CHAP 配置一致。认证配置文件应只有 root 可读。再把网卡和存储模块加入 /etc/initramfs-tools/modules;这台 3240 的有线网卡使用 e1000e:
1 2 3 | e1000e iscsi_tcp ext4 |
1 2 3 4 5 6 | chmod 600 /etc/iscsi/iscsi.initramfs update-initramfs -u -k all # 选定实际准备发布的内核版本,再检查生成物。 ls /boot/vmlinuz-* /boot/initrd.img-* lsinitramfs /boot/initrd.img-你的内核版本 | grep -E "e1000e|iscsi_tcp|iscsistart|iscsi.initramfs" |
系统用户、SSH host key、Netplan、fstab 和服务启用状态也要在这一步核对。制作完成后移除 policy-rc.d,退出 chroot。先把准备引导的内核和 initramfs 复制到存储 VM 的私有暂存目录,再卸载构建挂载:
1 2 3 4 5 6 7 8 9 10 | # 在 chroot 内完成清理后退出 rm /usr/sbin/policy-rc.d exit # 以下回到存储 VM;KVER 必须改成新系统实际安装的版本。 KVER=6.8.0-147-generic sudo install -d -m 700 /root/dell-boot-staging sudo install -m 600 /mnt/dell-root/boot/vmlinuz-${KVER} /root/dell-boot-staging/vmlinuz sudo install -m 600 /mnt/dell-root/boot/initrd.img-${KVER} /root/dell-boot-staging/initrd.img sudo umount -R /mnt/dell-root |
第四步:用 LIO 导出整块新磁盘
在存储 VM 上用 targetcli/LIO 把专用 VMDK 导出为 iSCSI LUN 0。Dell 连接后看到的是整块磁盘,自己识别其中的 GPT、ESP 和 ext4 根分区。
下面的命令全部在存储 VM 的普通 Bash 终端执行,已经退出第三步的 chroot;每条 sudo targetcli ... 都是一次性命令,不需要先进入 targetcli 交互界面。按小节逐步执行,并留在同一个 SSH 会话中,后面的命令会用到前面定义的变量。
4.1 确认磁盘已经卸载,准备变量和配置备份
这是首次搭建的流程,此时 Dell 还没有使用这块盘。先安装工具、查看现有 target,再定义本节的示例值。两个 IQN、存储 VM 地址和 CHAP 用户名必须与第三步一致;磁盘路径则换成第一步确认过的那块 100GiB 新 VMDK 的整盘路径,不能带 -part1 或 -part2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | sudo apt install targetcli-fb sudo targetcli ls DELL_DISK=/dev/disk/by-path/替换为新VMDK的整盘路径 DELL_TARGET=iqn.2026-10.example.lab:storage.dell3240 DELL_INITIATOR=iqn.2026-10.example.lab:dell3240 DELL_PORTAL=192.168.110.10 DELL_TPG=/iscsi/${DELL_TARGET}/tpg1 DELL_ACL=${DELL_TPG}/acls/${DELL_INITIATOR} DELL_SAVE=/etc/rtslib-fb-target/saveconfig.json readlink -f "$DELL_DISK" lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "$DELL_DISK" findmnt -R /mnt/dell-root |
确认容量、分区与前面的安装结果一致,磁盘及其子分区的 MOUNTPOINTS 都为空,findmnt 也不再列出构建目录的挂载。如果仍然挂载着,先回第三步完成卸载。这一步只做导出,不再分区或格式化。
如果 targetcli ls 里已经有同名 backstore 或同一个 Target IQN,先核对现有配置,不要重复创建,也不要用 clearconfig 清掉其他 target。修改前可以备份已有配置:
1 2 3 4 | sudo install -d -m 700 /root/target-backups if sudo test -f "$DELL_SAVE"; then sudo cp -a "$DELL_SAVE" "/root/target-backups/saveconfig-$(date +%Y%m%d-%H%M%S).json" fi |
本文 Ubuntu 24.04 安装包的默认保存位置是 /etc/rtslib-fb-target/saveconfig.json;有些发行版使用 /etc/target/saveconfig.json。以本机 saveconfig 的提示和恢复服务实际使用的路径为准,不要把两者混用。配置文件及备份可能包含 CHAP 密码,需要保持私有。
同一块 ext4 文件系统不能同时由存储 VM 和 Dell 作为两个独立系统读写挂载。制作系统时挂在 VM 上;正式运行时由 Dell 使用。以后维护也要先把 Dell 干净关机、停用对应 LUN,再考虑在 VM 上挂载检查。
4.2 创建 block backstore 和独立 Target
把刚才确认的整盘注册为名为 dell3240 的 block backstore,再建立 Target。targetcli 会同时创建 tpg1;先将这个新 TPG 禁用,等 Portal、ACL 和密码都配置好再开放。
1 2 3 4 | sudo targetcli /backstores/block create name=dell3240 dev="$DELL_DISK" readonly=false sudo targetcli /iscsi create "$DELL_TARGET" sudo targetcli "$DELL_TPG" disable sudo targetcli "$DELL_TPG" set attribute generate_node_acls=0 authentication=1 |
generate_node_acls=0 表示只接受手工创建的 ACL;authentication=1 要求登录认证。不要把这里改成允许任意 initiator 的演示模式。
4.3 移除默认全地址 Portal,只监听存储地址
先列出这个新 TPG 的 Portal:
1 | sudo targetcli "$DELL_TPG/portals" ls |
如果看到默认的 0.0.0.0:3260,删除它:
1 | sudo targetcli "$DELL_TPG/portals" delete 0.0.0.0 3260 |
有些版本显示的是 IPv6 通配地址 [::0]:3260,此时改用下面这条。如果显示 [::]:3260,参数对应改为 ::。只删除列表中实际存在的通配 Portal;如果列表本来就是空的,跳过删除。
1 | sudo targetcli "$DELL_TPG/portals" delete ::0 3260 |
再创建明确的 LAN 监听地址。该地址必须已经配置在存储 VM 的网卡上:
1 2 3 | ip -4 -br address sudo targetcli "$DELL_TPG/portals" create "$DELL_PORTAL" 3260 sudo targetcli "$DELL_TPG/portals" ls |
这个 Target 的 Portal 列表里应只剩 192.168.110.10:3260。这里的 delete 只移除监听端点,不是删除磁盘;路径始终限定在刚创建的 Dell Target 下。
4.4 创建 LUN 0、Dell 的 ACL 和显式映射
先把 backstore 作为 TPG 的 LUN 0 导出,再创建与 Dell initiator IQN 对应的 ACL。两处都明确关闭自动映射,然后手工把客户端看到的 LUN 0 映射到 TPG 的 LUN 0:
1 2 3 4 | sudo targetcli "$DELL_TPG/luns" create /backstores/block/dell3240 lun=0 add_mapped_luns=false sudo targetcli "$DELL_TPG/acls" create "$DELL_INITIATOR" add_mapped_luns=false sudo targetcli "$DELL_ACL" create 0 0 write_protect=0 sudo targetcli "$DELL_ACL" ls |
最后一条 create 里的第一个 0 是客户端的 LUN 编号,第二个 0 是 TPG 的 LUN 编号;write_protect=0 允许读写。ACL 下应看到 mapped_lun0 指向 lun0,并标记 rw。只创建 ACL、没有这个映射时,即使认证成功,客户端也可能看不到系统盘。命令参数可对照 targetcli 的 LUN/ACL 实现。
4.5 为这个 ACL 设置 CHAP
用户名继续用第三步的 dell3240。密码也必须与第三步 initramfs 中的 ISCSI_PASSWORD 完全一致,建议使用 16 位随机 ASCII 字母和数字,避免空格、引号带来的解析差异。通过 read 私下输入,不把密码字面量写进命令历史:
1 2 3 4 | read -r -s -p '输入第三步使用的 CHAP 密码: ' DELL_CHAP printf '\n' sudo targetcli "$DELL_ACL" set auth userid=dell3240 password="$DELL_CHAP" > /dev/null unset DELL_CHAP |
确认设置命令没有报错后再继续。这里只给 Dell 的 ACL 设置单向 CHAP,不配置 mutual CHAP。如果密码后来改了,要同步修改 Dell 的 initramfs 配置并重新生成、发布引导文件;只改服务端会导致下一次启动无法登录系统盘。
4.6 启用 Target,保存并启用开机恢复
配置齐了再启用 TPG,明确保存一次,并限制配置文件权限:
1 2 3 4 5 | sudo targetcli "$DELL_TPG" enable sudo targetcli / saveconfig sudo chmod 600 "$DELL_SAVE" sudo systemctl enable rtslib-fb-targetctl.service systemctl is-enabled rtslib-fb-targetctl.service |
saveconfig 的输出应指向前面设置的 DELL_SAVE,最后一条命令应显示 enabled。targetcli 修改会立即作用于内核;保存文件负责持久化,systemd 服务负责下次开机恢复,这是两件事。可参考 Ubuntu 24.04 的 targetcli 手册。
为避免服务在存储 VM 的网络地址尚未上线时恢复失败,我还为它加了启动顺序与失败重试配置。执行下面的编辑命令,在 drop-in 中填入紧随其后的内容:
1 | sudo systemctl edit rtslib-fb-targetctl.service |
1 2 3 4 5 6 7 | [Unit] Wants=network-online.target After=network-online.target [Service] Restart=on-failure RestartSec=5s |
1 2 | sudo systemctl daemon-reload systemctl cat rtslib-fb-targetctl.service |
确认能够看到刚才的 drop-in。network-online 的实际等待还依赖 VM 所用网络管理器的 wait-online 服务,不能只凭这一行就认定网络已经就绪。
Dell 运行时不要随手 stop 或 restart 这个恢复服务。本机服务的 ExecStop 会调用 targetctl clear,会移除内核中当前的 target 配置,影响正在使用的 iSCSI 磁盘。这里不需要靠 restart 让 targetcli 的修改生效;要做重启恢复验收,先让 Dell 正常关机,再安排存储 VM 的维护窗口。
4.7 检查监听、ACL 和网络放行
1 2 3 4 | sudo targetcli "$DELL_TPG" ls sudo targetcli "$DELL_TPG" get attribute sudo ss -lnt 'sport = :3260' sudo stat -c '%a %U:%G %n' "$DELL_SAVE" |
逐项核对:TPG 为 enabled;authentication 为 1、generate_node_acls 为 0;只有指定 Portal;LUN 0 指向 dell3240 backstore;Dell ACL 下的 mapped_lun0 为 rw;保存文件只有 root 可读写。TCP 3260 在监听只能证明端口已打开,不能代替 ACL 和 CHAP 的检查。
管理网与 Lab 网之间还要有双向路由,并允许 Dell 到存储 VM 的 TCP 3260。若 VM 已启用 UFW,可按本节示例地址添加精确规则;使用其他防火墙时配置等效规则:
1 2 3 | sudo ufw status verbose # 仅在已经使用 UFW 管理防火墙时添加;地址按实际环境替换。 sudo ufw allow proto tcp from 192.168.10.60 to 192.168.110.10 port 3260 |
无需为了本教程远程启用一套原本没使用的防火墙。这里的 CHAP 用于登录认证,并不加密 iSCSI 数据;端口保持在可信内网中。
4.8 验收存储 VM 重启后的恢复
在 Dell 尚未启动、并确认存储 VM 上其他工作负载允许维护时,可以重启一次存储 VM;重新连接后检查:
1 2 3 4 | systemctl status --no-pager rtslib-fb-targetctl.service sudo journalctl -b -u rtslib-fb-targetctl.service --no-pager -n 40 sudo targetcli /iscsi ls sudo ss -lnt 'sport = :3260' |
这种 oneshot 服务显示 active (exited) 是正常的;关键是日志没有恢复失败,Target、Portal、LUN 和 ACL 都还在。若失败,先检查 VMDK 是否仍已连接、稳定设备路径是否存在,以及绑定的 IP 是否已上线。不要在存储 VM 上为了测试而登录并挂载自己导出的 ext4 盘。
存储端准备好之后,再继续第五、六步的 PXE 设置。Dell 真正启动成功后,在 Dell 上用 sudo iscsiadm -m session 和 findmnt / 核对 iSCSI 会话与根目录,然后按第七步做文件和 Docker volume 的重启持久化验收。
第五步:准备 TFTP、HTTP 和 iPXE
UEFI 先取一个小加载器,体积较大的内核和 initramfs 再走 HTTP。iPXE 使用项目官方提供的签名发布包,取其中 x86_64-sb 目录的 snponly-shim.efi 和 snponly.efi,放在同一个 TFTP 目录,autoexec.ipxe 也放在那里。
1 2 3 4 5 | curl -fL https://github.com/ipxe/ipxe/releases/latest/download/ipxeboot.tar.gz -o ipxeboot.tar.gz tar -xzf ipxeboot.tar.gz sudo mkdir -p /srv/dell-netboot/tftp /srv/dell-netboot/http sudo cp ipxeboot/x86_64-sb/snponly-shim.efi /srv/dell-netboot/tftp/ sudo cp ipxeboot/x86_64-sb/snponly.efi /srv/dell-netboot/tftp/ |
tftpd-hpa 的关键设置:
1 2 3 4 | TFTP_USERNAME="tftp" TFTP_DIRECTORY="/srv/dell-netboot/tftp" TFTP_ADDRESS="192.168.110.10:69" TFTP_OPTIONS="--secure --verbose --blocksize 1468" |
Nginx 单独监听 8088,限制只有 Dell 和存储 VM 自己能取文件:
1 2 3 4 5 6 7 8 | server { listen 192.168.110.10:8088; root /srv/dell-netboot/http; allow 192.168.10.60; allow 192.168.110.10; deny all; location / { try_files $uri =404; } } |
把刚才暂存在 /root/dell-boot-staging 的匹配版本 vmlinuz 和 initrd.img 发布到 HTTP 目录,保证 Nginx 用户能读取,但不要开放目录浏览。shimx64.efi 使用 Ubuntu 签名 shim;该文件来自存储 VM 上的 shim-signed 包,具体路径先用 dpkg -L shim-signed 查。autoexec.ipxe 的关键内容如下:
1 2 3 4 5 6 7 8 9 10 11 | #!ipxe dhcp || goto failed iseq ${net0/mac} 02:11:22:33:44:55 || goto failed set base http://192.168.110.10:8088 kernel ${base}/vmlinuz initrd=initrd.img root=UUID=你的根分区UUID ro ip=dhcp BOOTIF=01-${net0/mac:hexhyp} rootwait console=tty0 || goto failed initrd ${base}/initrd.img || goto failed shim ${base}/shimx64.efi || goto failed boot || goto failed :failed echo Network boot failed shell |
iPXE 的 shim 命令在 Secure Boot 关闭时会直接跳过。
initramfs 里包含 iSCSI 认证信息。文件目录和权限、Nginx 访问限制都要保留,不要把它当成公开下载文件,也不要上传到文章附件里。HTTP 本身没有加密,这套配置放在可信内网使用。
服务设为随网络上线后启动,并做启动顺序检查。特别是 tftpd-hpa:我这次遇到过 enable 之后服务仍没真正运行的情况,要用 is-active 确认,再从客户端实际下载验证。
1 2 3 4 | sudo nginx -t sudo systemctl enable nginx tftpd-hpa rtslib-fb-targetctl sudo systemctl start nginx tftpd-hpa rtslib-fb-targetctl systemctl is-active nginx tftpd-hpa rtslib-fb-targetctl |
第六步:RouterOS 只给这台 Dell 发 PXE 参数
不要把整个局域网的 DHCP 都改成网络启动。我给 Dell 做了固定租约,再增加一条只匹配该地址的 DHCP network 配置。示例管理网是 /24,所以虽然匹配规则写 /32,发给客户端的 netmask 仍然是 24。RouterOS 的 netmask 和 next-server 字段说明可以对照着看。
| DHCP network 字段 | 示例值 |
|---|---|
| Address | 192.168.10.60/32 |
| Netmask | 24(按真实管理网填写) |
| Gateway / DNS | 192.168.10.1 |
| Next Server | 192.168.110.10 |
| Boot File Name | snponly-shim.efi |
固定租约按 Dell 的 MAC 匹配,Client ID 留空。这里踩了个比较绕的坑:UEFI PXE、initramfs 和正式系统发出的 DHCP Client ID 可以不一样。最初固定租约还限制着 Client ID,initramfs 就拿到了另一个动态地址。下一次 PXE 又复用了那个旧动态租约,而那个地址没有引导参数,于是卡在 Start PXE over IPv4,再回退到 IPv6。
处理方法是核对 MAC,把租约改成按 MAC 匹配,清掉确认属于这台 Dell 的旧动态租约,再重新 PXE。不要顺手清其他机器的租约,也别把另一个设备正在用的旧地址重新启用。
之后 Dell 开机按 F12,选择 UEFI Network / Onboard NIC IPv4。成功进入 Ubuntu 后检查:
1 2 3 4 5 6 | hostname uname -r findmnt / sudo iscsiadm -m session ip -4 -br address systemctl --failed |
第七步:装 Docker,先做持久化验收
Docker 使用官方 Ubuntu APT 仓库。配置好仓库后安装 Engine、Buildx 和 Compose 插件:
1 2 3 4 5 | sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo usermod -aG docker ubuntu # 重新登录,让组成员身份生效。 docker run --rm hello-world docker compose version |
Docker 组可以控制宿主机,添加成员时按管理员权限看待。开发数据放在明确的 bind mount 或 named volume,临时容器删除之后还能保留。
我做了两种重启验收:普通文件写入系统盘,另外用临时容器往 named volume 写一个标记;正常重启后重新读取。两者都保留下来,确认它确实是可持久化的系统,而不是一次性的 Live 环境。
如果只是给自己访问开发服务,仍然可以用普通 Docker bridge,把端口绑定到宿主 loopback,再用 SSH 转发:
1 | ssh -N -L 3000:127.0.0.1:3000 dell3240 |
第八步:宿主 IP 保留,让容器进 Lab VLAN
后面我又把 Lab VLAN 接给 Docker。Dell 所在交换机端口原有管理网保持 untagged,另外把 VLAN 110 设置成 tagged;通向路由器和 ESXi 的上联端口也要已经放行这条 VLAN。不要把宿主机管理 IP 直接搬进 Lab,否则还可能影响 iSCSI 根盘的路由。
创建一个 macvlan 网络,父接口带 .110,Docker 会创建并管理对应的 802.1Q 子接口。地址池单独划在 DHCP 范围之外:
1 2 3 4 5 6 7 8 | docker network create --driver macvlan \ --subnet 192.168.110.0/24 \ --gateway 192.168.110.1 \ --ip-range 192.168.110.224/27 \ --aux-address boot-server=192.168.110.10 \ --opt parent=eno2.110 \ --opt macvlan_mode=bridge \ lab110 |
eno2 是这台机器实际的有线网卡名,别的机器先用 ip -br link 确认。宿主机不需要给 eno2.110 再配置一个 Lab 地址或默认路由。Docker 的 macvlan 文档也说明了这种带 VLAN 子接口的用法和主机通信限制。
Compose 里把它作为已存在的共享网络引用:
1 2 3 4 5 6 7 8 | services: web: image: nginx:stable-alpine networks: [lab110] networks: lab110: external: true |
这样容器有自己的 Lab IP,服务直接监听容器里的端口即可,不必再映射宿主端口。只有加入 lab110 的容器会拿 Lab 地址;Docker 默认 bridge 网络仍然是另一回事。固定 IP 要和其他 VM、设备统一协调,Docker 不会替你发现其他机器手工配置的地址。
还要注意 macvlan 默认的宿主隔离:宿主不能直接通过同一个父接口与自己的 macvlan 容器通信。本次管理网与 Lab 分属不同 VLAN,宿主管理地址的连通性走了路由路径;如果需要宿主直接访问 Lab 容器,要单独设计 shim 接口或路由,并预留地址。
这次从容器访问 Lab 网关、存储 VM、宿主机、DNS 和 HTTPS 都通过了;还起了一个临时 HTTP 服务,从主力机和存储 VM 分别访问它的 Lab IP,验证双向通信后删除了测试容器。
可选:把 RTX 3000 交给 Docker 用
Intel 网卡、核显、Wi-Fi 等驱动已经由 Ubuntu 内核提供。RTX 3000 则安装 Ubuntu 推荐的 NVIDIA server 驱动和对应内核的预编译模块,再按NVIDIA 官方说明安装 Container Toolkit。
本机实测的是 595.91.07 server open 驱动和 Container Toolkit 1.20.1。驱动分支应先用 ubuntu-drivers list --gpgpu 检查,不要把这个版本号直接套到所有显卡上。修改 Docker 配置前备份 daemon.json,nvidia-ctk runtime configure --runtime=docker 会合并 NVIDIA runtime;重启 Docker 要先确认没有正在使用的容器。
1 2 | nvidia-smi docker run --rm --gpus all ubuntu:24.04 nvidia-smi |
宿主机和容器都识别出了 Quadro RTX 3000 的显存。这个检查证明 GPU 对容器可见;具体 CUDA 程序还需要镜像内配套的用户态库。
后续维护里最容易忘的几件事
- 开关机顺序:ESXi 和存储 VM 先启动,再开 Dell;关闭存储 VM 或 ESXi 前,先把 Dell 正常关机。
- 升级内核要同步 PXE 文件:Dell 上 /boot 里的文件和 Nginx 发布目录里的副本是两份。update-initramfs 跑完,还要把匹配的内核与 initramfs 发布过去,校验哈希,并保留旧版本。
- 保留当前引导内核的模块:尤其是 NVIDIA 预编译模块,别让 apt autoremove 删掉仍在被 PXE 使用的版本。
- 网络就是系统盘链路:远程改 Netplan、VLAN、路由之前,要把 iSCSI 根盘也算进去。
- 有持久化不等于有备份:VMDK、应用数据、配置都需要另外安排备份;不要在共享开发机上随手
docker system prune --volumes。
另外记两个小插曲。GPU 的一个未使用 USB-C 控制功能会报 I2C/UCSI 超时;确认接口用途后,我只禁用了 i2c_nvidia_gpu 的自动探测,并重建、发布 initramfs。重启后该报错消失,GPU 和普通 USB 仍能用。这是针对本机未使用功能的绕过方案,不是通用固件修复,更不适合需要 GPU USB-C 的机器。
Intel AMT 带外管理
无盘机器还有一个实际问题:改错网络,SSH 连不上了怎么办。3240 这类工作站平台有 Intel vPro AMT,但要先确认自己的 CPU、主板、ME 固件和 BIOS 配置确实支持并启用了它。

SSH 是进入 Ubuntu 的日常通道;AMT 运行在 Intel ME 固件侧,配置和网络条件满足时,可以在操作系统未正常运行时提供管理入口。它的用途包括查询电源状态、远程电源控制和 Serial over LAN。AMT 管理地址可能和 Ubuntu 共用,也可能另行配置,连接时按 MEBx/AMT 的实际网络参数核对。
先看 BIOS 的 Manageability 设置。Dell 的 3240 文档列有 Intel AMT Capability 和 MEBx Hotkey。按自己的管理需求启用后进入 MEBx,设置独立的管理凭据、网络参数,并启用需要的 SOL 等功能。MEBx 的进入方式取决于 BIOS 配置,不能保证每台机器开机按 Ctrl+P 都直接出现。
管理连接优先配置 TLS 和证书校验,并限制在可信管理网或 VPN 后面;不要把 AMT 管理端口直接映射到公网。常见 TLS 管理端口是 16993,重定向 TLS 端口是 16995;具体是否开放由固件版本和配置决定。Intel 也明确建议迁移到 TLS 管理连接。
本次 AMT 客户端已经能够连接机器,接下来补的是 Ubuntu 的 SOL 登录端。先用 lspci、dmesg 和 sysfs 确认 AMT 的串口映射;这台 Comet Lake KT 重定向设备 00:16.3 对应 ttyS4,ttyS0 是另一个传统串口:
1 2 3 | lspci -nnk | grep -A 3 -E "Serial controller|Communication controller" sudo dmesg | grep ttyS readlink -f /sys/class/tty/ttyS4/device |
为实际对应的串口启用 serial-getty。这里固定 115200 8N1,不依赖客户端的载波检测;下面是 [email protected] 的 drop-in,注意不要照搬 ttyS4 到不同硬件上:
1 | sudo systemctl edit serial-getty@ttyS4.service |
1 2 3 | [Service] ExecStart= ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --local-line --8bits 115200 - vt100 |
1 2 3 4 | sudo systemctl daemon-reload sudo systemctl enable --now serial-getty@ttyS4.service systemctl status serial-getty@ttyS4.service sudo stty -F /dev/ttyS4 speed |
同时可以在前面 iPXE 的 kernel 命令行里追加 console=ttyS4,115200n8,保留 console=tty0。这样下一次使用这些参数启动时,Linux 的文本输出也会走串口;仅仅启用 getty 就能立即出现登录提示,不需要为了登录提示本身重启机器。
AMT/MEBx 登录和 Ubuntu 登录是两道认证。进入 SOL 后仍然需要 Linux 用户名和本地密码;最初我只配了 SSH 公钥,ubuntu 的本地密码是锁定的,需要自己通过 SSH 运行 sudo passwd ubuntu 设置。密码只在终端里输入,SSH 的密码登录仍可以保持关闭,也不需要开 root 自动登录。
客户端与 Linux 的终端尺寸也要一致。Intel Manageability Commander 2.2.0 默认是 80 列、25 行,而串口不会像 SSH 一样自动协商窗口大小,必要时在 SOL shell 内用 stty rows 25 cols 80 匹配。我的机器确认能通过 SOL 登录到 Ubuntu,但这版 Commander 对 htop/top 的全屏渲染仍有问题,调整尺寸和终端能力后也没完全解决,暂时留着换支持更好的客户端,日常监控走 SSH。
总结
无盘启动、多次重启、系统文件与 Docker volume 持久化、Lab 网络双向通信,以及 Docker 里的 GPU 识别都验证过了。开发代码和容器可以搬到 3240 上跑,主力机通过 SSH / Remote SSH 连接。
代价也很明确:存储 VM 和网络成了必需依赖,系统盘 I/O 要过千兆网口。千兆链路理论上约 125MB/s,实际还要扣掉协议和存储开销;这次没有做存储吞吐基准测试。已经有 ESXi 空间、交换机和闲置小工作站的话,这样借一块网络系统盘,确实能让机器重新干活。
0 评论