目标:网卡设备名与 MAC↔VMnet 网段的实际对应关系严格一致(eth0=NAT、eth1=VMnet1 / eth2=VMnet2 / eth3=VMnet3 / eth4=VMnet4),保留 eth 命名,且重启后不漂移。
环境:Oracle Linux Server release 8.10(UEK 5.15),VMware Workstation,五块 vmxnet3 网卡(eth0=NAT 管理 + eth1
eth4=Host-only VMnet14),BIOS 引导。
一、为什么普通 .link 不够用(根因)
在 OL8(systemd 239)下,仅靠 /etc/systemd/network/*.link 按 MAC 改名时,如果目标是 4 元循环改名(如 eth1→eth3→eth4→eth2→eth1),引导阶段的 udev 无法完成循环交换,导致 .link 全部静默失效,重启后名字仍乱。
验证方法:
udevadm test-builtin net_setup_link /sys/class/net/eth1
它会显示“本应改成 ethX”,但实际引导时改不了。
结论:.link 保留作为兜底,真正的可靠性保证是一个开机自启的“两段改名”服务(先用临时名断开循环,再落到最终名),在 NetworkManager 接管之前把**全部 5 块卡(含 eth0/NAT)**按 MAC 钉死。脚本按 MAC 判断、幂等(名字已对则不动),天然免疫循环改名问题。
二、权威对应表(最终基线,勿再重排 Workstation 适配器)
⚠️ 不要重排 Workstation 的网络适配器顺序——每次重排都会导致 MAC 整体更换,所有基于 MAC 的配置(.link、脚本 TARGET、NM 绑定)全部失效。
ebsdb1
| 设备名 | MAC | 连接 | IP/24 | 网关 |
|---|---|---|---|---|
| eth0 | 00:0C:29:33:85:D2 | NAT(管理) | 10.0.0.61 | — |
| eth1 | 00:50:56:2F:EA:A0 | Host-only VMnet1 | 10.0.10.61 | 10.0.10.1 |
| eth2 | 00:50:56:34:C3:58 | Host-only VMnet2 | 10.0.11.61 | 10.0.11.1 |
| eth3 | 00:50:56:3F:70:92 | Host-only VMnet3 | 10.0.12.61 | 10.0.12.1 |
| eth4 | 00:50:56:2A:AF:E8 | Host-only VMnet4 | 10.0.13.61 | 10.0.13.1 |
ebsdb2
| 设备名 | MAC | 连接 | IP/24 | 网关 |
|---|---|---|---|---|
| eth0 | 00:50:56:3E:B1:D9 | NAT(管理) | 10.0.0.62 | — |
| eth1 | 00:50:56:22:BC:C0 | Host-only VMnet1 | 10.0.10.62 | 10.0.10.1 |
| eth2 | 00:50:56:24:0A:A4 | Host-only VMnet2 | 10.0.11.62 | 10.0.11.1 |
| eth3 | 00:50:56:35:CB:14 | Host-only VMnet3 | 10.0.12.62 | 10.0.12.1 |
| eth4 | 00:50:56:26:91:13 | Host-only VMnet4 | 10.0.13.62 | 10.0.13.1 |
三、前置检查(每个节点先确认)
# 1) 确认当前系统真实 MAC(以此为准,不是 Workstation 列表顺序)
ip -br link
# 2) 确认内核启动参数(应无 net.ifnames=0;biosdevname=0 可保留)
cat /proc/cmdline
# 3) 确认 vmxnet3 驱动识别
dmesg | grep -i vmxnet3
若 /proc/cmdline 中仍有 net.ifnames=0,先执行“步骤 1”移除。
四、实施步骤(每个节点分别执行)
步骤 1:移除 net.ifnames=0 内核参数
# 编辑 /etc/default/grub,从 GRUB_CMDLINE_LINUX 中删掉 net.ifnames=0(保留 biosdevname=0)
vi /etc/default/grub
# 重新生成引导配置(BIOS 引导用这个路径)
grub2-mkconfig -o /boot/grub2/grub.cfg
# 确认已移除
cat /proc/cmdline # 需重启后生效,先继续下一步再一起重启
步骤 2:创建 .link 文件(兜底 eth0 与正式命名机制,可选但推荐)
ebsdb1:
cat > /etc/systemd/network/10-eth0.link <<'EOF'
[Match]
MACAddress=00:0C:29:33:85:D2
[Link]
Name=eth0
EOF
cat > /etc/systemd/network/10-eth1.link <<'EOF'
[Match]
MACAddress=00:50:56:2F:EA:A0
[Link]
Name=eth1
EOF
cat > /etc/systemd/network/10-eth2.link <<'EOF'
[Match]
MACAddress=00:50:56:34:C3:58
[Link]
Name=eth2
EOF
cat > /etc/systemd/network/10-eth3.link <<'EOF'
[Match]
MACAddress=00:50:56:3F:70:92
[Link]
Name=eth3
EOF
cat > /etc/systemd/network/10-eth4.link <<'EOF'
[Match]
MACAddress=00:50:56:2A:AF:E8
[Link]
Name=eth4
EOF
ebsdb2:
cat > /etc/systemd/network/10-eth0.link <<'EOF'
[Match]
MACAddress=00:50:56:3E:B1:D9
[Link]
Name=eth0
EOF
cat > /etc/systemd/network/10-eth1.link <<'EOF'
[Match]
MACAddress=00:50:56:22:BC:C0
[Link]
Name=eth1
EOF
cat > /etc/systemd/network/10-eth2.link <<'EOF'
[Match]
MACAddress=00:50:56:24:0A:A4
[Link]
Name=eth2
EOF
cat > /etc/systemd/network/10-eth3.link <<'EOF'
[Match]
MACAddress=00:50:56:35:CB:14
[Link]
Name=eth3
EOF
cat > /etc/systemd/network/10-eth4.link <<'EOF'
[Match]
MACAddress=00:50:56:26:91:13
[Link]
Name=eth4
EOF
注:
.link的 MAC 匹配大小写不敏感,大写亦可。
步骤 3:创建钉名脚本 fix-eth-names.sh
核心机制:遍历全部 5 块 vmxnet3 网卡(含 eth0/NAT,不再按名字跳过 eth0)→ 按 MAC 查目标名 → 若名字错位,先用短临时名断开循环(阶段一),再落到最终名(阶段二);已正确则跳过(幂等)。只要某块卡的 MAC 是 NAT,就把它钉到 eth0。
⚠️ 在线执行注意:若当前 eth0 就是活动的管理口(SSH 走它),且它正好需要改名,脚本会先
down它再改,可能断掉 SSH。此时不要在线跑,直接重启,由开机服务在 NM 接管前完成改名(无会话、安全)。
ebsdb1:
cat > /usr/local/sbin/fix-eth-names.sh <<'EOF'
#!/bin/bash
# ebsdb1:按 MAC→ethX 映射钉名,两段改名破循环,幂等
set -u
declare -A TARGET=(
["00:0c:29:33:85:d2"]=eth0 # NAT 管理
["00:50:56:2f:ea:a0"]=eth1 # VMnet1
["00:50:56:34:c3:58"]=eth2 # VMnet2
["00:50:56:3f:70:92"]=eth3 # VMnet3
["00:50:56:2a:af:e8"]=eth4 # VMnet4
)
for i in $(seq 1 30); do
cnt=0
for d in /sys/class/net/*; do
drv=$(readlink "$d/device/driver" 2>/dev/null) || continue
case "$drv" in *vmxnet3*) cnt=$((cnt+1));; esac
done
[ "$cnt" -ge 4 ] && break
sleep 1
done
declare -A TMP
idx=0
for d in /sys/class/net/*; do
drv=$(readlink "$d/device/driver" 2>/dev/null) || continue
case "$drv" in *vmxnet3*) ;; *) continue;; esac
n=${d##*/}
mac=$(cat "$d/address")
target="${TARGET[$mac]:-}"
[ -z "$target" ] && continue
if [ "$n" != "$target" ]; then
idx=$((idx+1)); tmp="t$idx"
ip link set "$n" down 2>/dev/null
ip link set "$n" name "$tmp"
TMP["$tmp"]="$target"
fi
done
for tmp in "${!TMP[@]}"; do
ip link set "$tmp" name "${TMP[$tmp]}"
done
exit 0
EOF
chmod +x /usr/local/sbin/fix-eth-names.sh
ebsdb2: 同上,仅替换 TARGET 表:
cat > /usr/local/sbin/fix-eth-names.sh <<'EOF'
#!/bin/bash
# ebsdb1:按 MAC→ethX 映射钉名,两段改名破循环,幂等
set -u
declare -A TARGET=(
["00:50:56:3e:b1:d9"]=eth0 # NAT 管理
["00:50:56:22:bc:c0"]=eth1 # VMnet1
["00:50:56:24:0a:a4"]=eth2 # VMnet2
["00:50:56:35:cb:14"]=eth3 # VMnet3
["00:50:56:26:91:13"]=eth4 # VMnet4
)
for i in $(seq 1 30); do
cnt=0
for d in /sys/class/net/*; do
drv=$(readlink "$d/device/driver" 2>/dev/null) || continue
case "$drv" in *vmxnet3*) cnt=$((cnt+1));; esac
done
[ "$cnt" -ge 4 ] && break
sleep 1
done
declare -A TMP
idx=0
for d in /sys/class/net/*; do
drv=$(readlink "$d/device/driver" 2>/dev/null) || continue
case "$drv" in *vmxnet3*) ;; *) continue;; esac
n=${d##*/}
mac=$(cat "$d/address")
target="${TARGET[$mac]:-}"
[ -z "$target" ] && continue
if [ "$n" != "$target" ]; then
idx=$((idx+1)); tmp="t$idx"
ip link set "$n" down 2>/dev/null
ip link set "$n" name "$tmp"
TMP["$tmp"]="$target"
fi
done
for tmp in "${!TMP[@]}"; do
ip link set "$tmp" name "${TMP[$tmp]}"
done
exit 0
EOF
chmod +x /usr/local/sbin/fix-eth-names.sh
步骤 4:创建开机服务(NetworkManager 之前运行)
两个节点完全一致:
cat > /etc/systemd/system/pin-eth-names.service <<'EOF'
[Unit]
Description=Pin eth interface names per authoritative MAC mapping
After=systemd-udevd.service
Before=NetworkManager.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/fix-eth-names.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable pin-eth-names.service
systemctl is-enabled pin-eth-names.service # 应输出 enabled
步骤 5:重建 NetworkManager 连接(按 ifname 绑定 + 设计 IP)
ebsdb1(.61):
nmcli con delete eth1 eth2 eth3 eth4 2>/dev/null
nmcli con add type ethernet con-name eth1 ifname eth1 ipv4.method manual ipv4.addresses 10.0.10.61/24 ipv4.gateway 10.0.10.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth2 ifname eth2 ipv4.method manual ipv4.addresses 10.0.11.61/24 ipv4.gateway 10.0.11.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth3 ifname eth3 ipv4.method manual ipv4.addresses 10.0.12.61/24 ipv4.gateway 10.0.12.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth4 ifname eth4 ipv4.method manual ipv4.addresses 10.0.13.61/24 ipv4.gateway 10.0.13.1 connection.autoconnect yes
ebsdb2(.62):
nmcli con delete eth1 eth2 eth3 eth4 2>/dev/null
nmcli con add type ethernet con-name eth1 ifname eth1 ipv4.method manual ipv4.addresses 10.0.10.62/24 ipv4.gateway 10.0.10.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth2 ifname eth2 ipv4.method manual ipv4.addresses 10.0.11.62/24 ipv4.gateway 10.0.11.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth3 ifname eth3 ipv4.method manual ipv4.addresses 10.0.12.62/24 ipv4.gateway 10.0.12.1 connection.autoconnect yes
nmcli con add type ethernet con-name eth4 ifname eth4 ipv4.method manual ipv4.addresses 10.0.13.62/24 ipv4.gateway 10.0.13.1 connection.autoconnect yes
步骤 6:重新生成 initramfs 并立即生效验证
# 把 .link 与新的内核参数打入 initramfs
dracut -f
# 立即执行一次钉名(当前若已正确则是无操作)
/usr/local/sbin/fix-eth-names.sh
# 逐个拉起连接(注意:nmcli con up 一次只能带一个连接名)
nmcli con up eth1; nmcli con up eth2; nmcli con up eth3; nmcli con up eth4
步骤 7:重启并做最终验收
reboot
五、验收标准
重启后依次执行:
systemctl status pin-eth-names.service --no-pager # active(exited),无 Error
ip -br link
ip -br addr
ping 10.0.13.1 # eth4/VMnet4
ping 10.0.10.1 # eth1/VMnet1
ebsdb1 预期:
eth0 UP 00:0c:29:33:85:d2 10.0.0.61/24
eth1 UP 00:50:56:2f:ea:a0 10.0.10.61/24
eth2 UP 00:50:56:34:c3:58 10.0.11.61/24
eth3 UP 00:50:56:3f:70:92 10.0.12.61/24
eth4 UP 00:50:56:2a:af:e8 10.0.13.61/24
ebsdb2 预期:
eth0 UP 00:50:56:3e:b1:d9 10.0.0.62/24
eth1 UP 00:50:56:22:bc:c0 10.0.10.62/24
eth2 UP 00:50:56:24:0a:a4 10.0.11.62/24
eth3 UP 00:50:56:35:cb:14 10.0.12.62/24
eth4 UP 00:50:56:26:91:13 10.0.13.62/24
判定通过 = 名字/MAC/网段/IP 全对齐 + 两条 ping 通 + 重启后保持不变。
六、排错要点
| 现象 | 原因 | 处理 |
|---|---|---|
ip link set ... name ... 报 Device or resource busy | 网卡被 NetworkManager 管理/处于 UP | 先 nmcli device set ethX managed no,再 ip link set ethX down,改完置回 managed yes |
改名报 argument "...": not a valid ifname | 临时名超过 15 字符(IFNAMSIZ 上限) | 临时名用 t1/t2/t3/t4 等短名,勿用 tmp-<MAC>(16 字符) |
引导时 .link 被 udev 识别但名字不变 | 四元循环改名,引导期无法应用 | 依赖本方案的开机服务 pin-eth-names.service 兜底 |
| MAC 与设计不符 | Workstation 重排过适配器导致 MAC 整体更换 | 以 ip -br link 当前真实 MAC 为准重建对应表,勿再重排 |
nmcli con up eth1 eth2 ... 报 invalid extra argument | nmcli up 一次只接受一个连接 | 逐个 nmcli con up ethX |
七、关键文件清单(每节点)
| 文件 | 作用 |
|---|---|
/etc/systemd/network/10-eth0.link ~ 10-eth4.link | 按 MAC 正式钉名(配合开机服务兜底循环场景) |
/usr/local/sbin/fix-eth-names.sh | 两段改名脚本(核心可靠性保证,幂等) |
/etc/systemd/system/pin-eth-names.service | 开机在 NetworkManager 前执行钉名脚本 |
/etc/default/grub | 已移除 net.ifnames=0 |
| NM 连接 eth1~eth4 | ifname 绑定 + 设计 IP/网关,autoconnect yes |
排查 udev 判定:
udevadm test-builtin net_setup_link /sys/class/net/eth1
若输出“应改名 ethX 但实际没变”,即为循环改名,交给开机服务处理即可。

徐万新之路

