☰
Linux双网卡路由冲突:systemd-networkd配置Metric解决工业设备网络延迟
2026/11/2 10:34:56 网站建设 项目流程

先说说我为什么想写这篇东西。最近给一套产线设备换了 Linux 工业计算机,Ubuntu 22.04 装好之后,设备同时插着有线网口和无线网卡。结果只要无线网络一连上,工业控制器那边的数据就开始抽风,延迟从 1 毫秒级别直接飙到 200 毫秒往上,现场调试的人心态直接崩了。最后定位下来,问题出在 systemd-networkd 管理的双网卡默认路由优先级冲突上,核心解法就是 Metric 配置。

这篇文章我会把这套场景从头拆一遍:路由优先级是怎么冲突的、systemd-networkd 里 Metric 参数到底怎么配、我踩过的坑有哪些。适合在工业现场做嵌入式 Linux 的工程师、搞物联网网关和边缘计算的朋友,也包括家里 Linux 主机插了双网卡但总被“莫名断网”困扰的同学。内容不绕弯子,直接讲能落地的方案。

1. 双网卡路由冲突是怎么发生的:从内核路由表说起

1.1 默认路由的“谁先抢到”机制

先明确一个基础概念。一台 Linux 机器上,每块网卡只要有 IP 地址、有网关,内核就会给它生成一条路由记录。常见场景是两条默认路由同时存在,例如这样:

default via 192.168.10.1 dev eno1 proto dhcp metric 100 default via 192.168.20.1 dev wlan0 proto dhcp metric 600

这里eno1是工控机自带的千兆有线网口,wlan0是 USB 无线网卡。工业和办公网络各自有独立的网关,设备本身并不缺网络,缺的是“数据到底该走哪条路”。

内核选路的规则很多人理解成“按顺序找”,实际不是。Linux 路由查找会先比前缀长度,0.0.0.0/0的默认路由永远是最后兜底;当两条默认路由的前缀长度一样,就得比 metric。metric 数值越小,优先级越高。所以上面这个例子里,哪怕有线路由是后加的,只要 metric 是 100,它依然会被选中作为默认出口。

这个“小数值优先”逻辑,生活里其实很像快递公司:你有两个站点都说能送全网,但派送系统只认距离编号,编号越小越先派件。metric 就是内核给路由派件用的那个编号。

1.2 为什么插着有线还是突然走无线

问题就出在 metric 值上。很多发行版默认给 DHCP 获取的默认路由分配 100、200、1024 之类的数值,Windows 和各类嵌入式系统也各有默认策略,但这些默认值往往不会考虑你实际设备上“哪块网卡才是主通道”。

比如上面例子里的无线 metric 是 600,有线是 100,看着没问题。但如果某些设备驱动、NetworkManager 或者 netplan 接管网卡时,把无线路由的 metric 改成了 50,有线反而成了 100,那数据就全部涌向无线。工业协议里大量小包、周期包对延迟和抖动极其敏感,走无线之后表现就是:PLC 里频繁报通信超时,上位机画面上数据刷新一顿一顿的,但 ping 网关又偶尔能通,极具迷惑性。

更隐蔽的是热插拔引起的“瞬时黑洞”。网线一旦拔出,有线网卡 carrier 状态改变,systemd-networkd 会重新应用配置,把有线路由删掉。如果这时另一个网卡的默认路由还没来得及接管,或者接管之后又因为 metric 竞争反复跳变,TCP 长连接直接断掉。工业现场很多设备跑的是 OPC UA、Modbus TCP、WebSocket 这类长连接,一旦断开会话,重连要经历漫长的超时和握手过程。

1.3 工业场景为什么格外致命

我之前处理过的典型现场,是一台连接机器人控制柜的上位机。上位机通过有线网口接控制柜交换机,无线网卡用来接办公网,方便远程调试。表面看都通了,但业务上用 Ethernet/IP 或 Profinet 这类对实时性有硬性要求的协议时,哪怕只有几十毫秒的跳变,都会让控制器进入故障保护,产线直接停机。

这种环境里你不能指望系统自己“聪明地切换”,它只会按规则走。大家真正需要的是:有线网卡在的时候,默认路由必须走有线;有线拔掉后,无线自动顶上来;顶上来之后如果又插回有线,必须马上切回有线。这不是个玄学问题,就是 metric 规划问题。

另外还有一个常被忽略的点:很多工业计算机装的是精简版 Linux,出来的系统自带了 systemd-networkd,却没有禁用 NetworkManager 或其他网络管理工具。两个服务同时在管网卡,路由表就被反复拉扯,谁后启动谁覆盖。这类问题我会在前面诊断步骤里着重讲一下,因为绝大多数人在这里就先被带偏了。

2. systemd-networkd 配置的完整骨架:从零到手搓

2.1 先摸清现在到底是哪块网卡在干活

我处理这类问题从来不急着写配置文件。先执行一套标准检查流程,把系统当前状态摸清楚:

networkctl list ip -br link ip route show ip route show protocol dhcp

networkctl list能看到 systemd-networkd 管理的网卡列表,如果某块网卡显示“unmanaged”,那说明它被 NetworkManager 或其他服务在管,你写给 systemd-networkd 的配置对它没用。

ip route show看的是 main 路由表。重点看默认路由有几条、每条的 dev 和 metric 是多少。举个例子:

default via 192.168.10.1 dev eno1 proto dhcp metric 100 default via 192.168.20.1 dev wlan0 proto dhcp metric 600

看到这样的输出,说明配置层面并不缺默认路由,缺的是策略。下一步就要检查 systemd-networkd 的配置目录/etc/systemd/network/和/run/systemd/network/,看看是否有历史遗留的 .network 文件,尤其是 netplan 生成的那套。

2.2 两块网卡的配置单元文件怎么写

systemd-networkd 的配置思路是“一个接口对应一个 .network 文件”,文件放在/etc/systemd/network/下,文件名原则上带数字前缀,便于排序和管理。我给一个非常典型、可直接抄作业的配置。

有线网卡eno1,假设静态 IP:

# /etc/systemd/network/20-wired.network [Match] Name=eno1 [Network] DHCP=no Address=192.168.10.100/24 [Route] Destination=0.0.0.0/0 Gateway=192.168.10.1 Metric=100

无线网卡wlan0,假设用 DHCP 拿地址:

# /etc/systemd/network/25-wireless.network [Match] Name=wlan0 [Network] DHCP=yes [DHCP] UseRoutes=true RouteMetric=200

这里的关键是[DHCP]段里的RouteMetric。很多教程只在[Route]里写 metric,这对于 DHCP 接口来说往往不生效。因为 DHCP 获取默认路由时,默认路由的 metric 是由 DHCP 协议栈自己决定的,你需要通过RouteMetric=明确覆盖。如果接口是静态 IP,那就在[Route]段里写 Metric,效果一样。

2.3 配置文件生效的完整步骤

配置写完不是改完就完,还要让它真正应用。我推荐的顺序是:

sudo networkctl reload sudo networkctl up eno1 sudo networkctl up wlan0 ip route show

networkctl reload让 systemd-networkd 重新读取配置文件,但有时候网卡状态没变化,路由不会立即重建。这时候networkctl up一下对应接口,能强制应用最新的 .network 配置。

如果这样操作还是不行,就直接重启服务:

sudo systemctl restart systemd-networkd

重启之后优先看journactl输出的错误:

journalctl -u systemd-networkd -n 50

实际经验里,重启 systemd-networkd 比单纯 reload 更彻底,而且对工业设备来说,短暂重启网络服务通常可以接受。

2.4 静态 IP 与 DHCP 的细节差异

很多工业现场为了管理方便,会把工控机设为静态 IP。静态 IP 场景下,[Network]段里的Gateway=和[Route]段的Gateway=不要同时写,否则可能生成重复路由。我更建议统一用[Route]段去描述默认路由,这样 metric 逻辑集中在同一个位置。

DHCP 场景下还需要注意一个问题:如果 DHCP 服务器下发的路由信息里包含“classless static routes”,而 systemd-networkd 默认UseRoutes=true,它可能额外插入一些非默认路由。如果你希望完全掌控路由,可以把UseRoutes=false,然后自己写[Route]。但RouteMetric依然要写,因为 DHCP 自身生成的默认路由还要用它来定 metric。

一个很常见的报错是Gateway is on a different subnet。这是因为你在[Route]里写的网关地址和[Network]里的 Address 不在同一网段。systemd-networkd 校验很严格,遇到这种情况会直接拒绝接受该路由。检查网关和 IP 是否匹配,是排查配置无效时第一件要做的事。

3. Metric 参数:路由优先级的前世今生与选值心法

3.1 Metric 数值的选值逻辑

Metric 不是“越大越稳”也不是“越小越好”,它只有相对意义。最大原则是:主用网卡数值要明显小于备用网卡,差值要拉大到让系统不需要思考。

我一般用 100 和 500 搭配。有线主用给 100,无线备用给 500,差值 400,足够大。为什么不直接用 0?因为很多 routing 工具、路由协议和策略规则里,0 有特殊含义,比如某些场景把它当成“不参与竞争”或者“立即失效”的标记。为了降低踩坑概率,从 100 起步更安全。

那 100 会不会和系统里其他路由冲突?不会。内核的 metric 允许重复,两条不同网卡的路由即使 metric 一样,也会因其他字段不同而共存。但相同网卡、相同网关、相同前缀的路由如果 metric 相同,新增时可能会因为重复而失败。

3.2 反向场景:想让无线做主用、有线做保底

如果你的业务恰相反,比如无线是接办公网用于日常通信,有线只是本地调试口,那就把无线 metric 设小,有线设大。配置文件倒过来即可:

# 无线网卡 [Network] DHCP=yes [DHCP] RouteMetric=100
# 有线网卡 [Network] Address=192.168.10.100/24 [Route] Destination=0.0.0.0/0 Gateway=192.168.10.1 Metric=300

这种场景下,有线网卡如果只是为了登录设备、做本地维护,可以干脆不配 Gateway。systemd-networkd 允许只有 Address、没有 Gateway 的网卡,它不会生成默认路由,也就不会影响整体选路。这也是很多工程师容易忽略的另一种“干净”做法。

3.3 配套参数:IgnoreCarrierLoss 与 UseGateway

有个参数叫IgnoreCarrierLoss=,它决定 systemd-networkd 是否在检测到物理链路断开时立即清空该网卡的地址和路由。工业场景里,如果一台设备就是双网卡互为备份,但又不希望网线拔出瞬间把地址和路由全清掉(因为某些服务还在依赖这块网卡的 IP 做监听),可以把该参数设为 yes:

[Network] IgnoreCarrierLoss=yes

这样链路恢复后,地址和路由可以快速返回。不过这个参数要看场景,如果设备上的应用会自己处理 IP 变化,那默认的no反而更合适。

另一个是 DHCP 场景下的UseGateway=no。如果你的无线 DHCP 服务器经常下发网关,但你根本不想让无线参与默认路由竞争,用它就能把 DHCP 下发的默认路由挡在外面,省得之后反复被覆盖。

3.4 动态切换的底层注意点

最后说一个概念:metric 控制的是“默认路由选哪条”,但它不控制“数据包源地址选哪个”。当有线和无线处于不同网段时,数据从无线出去,源地址往往是无线网卡的 IP,对端回包也会回到无线。即使你通过路由优先级强行让所有 default 流量走有线,某些服务还是可能因为源地址问题暴露错误。

真实业务里,如果对端设备做了 IP 白名单或者基于来源 IP 的访问控制,你会遇到一种诡异现象:网关通了,外网通了,但特定服务就是连不上。这就要结合ip route get <目标IP>和ip -br addr一起看,确认实际出口是不是你想走的那块网卡。

4. 实操过程实录:一块带双网卡的工业主板排查全流程

4.1 现象与初步排查

这台工业计算机配置是:板载千兆网口eno1,USB 无线网卡wlan0,系统为 Ubuntu 22.04。业务上需要 eno1 连 PLC 控制网段 192.168.10.0/24,wlan0 连办公网 192.168.20.0/24。

现场首次故障表现是:控制软件无法稳定连接 PLC。于是我做了三步检查:

ping -c 4 192.168.10.10 ip route show default ip route get 192.168.10.10

ping有响应但延迟很高。ip route show default看到了两条默认路由,其中走wlan0的 metric 竟然比走eno1还小,因为无线网卡驱动里自动配置了非常激进的 metric。ip route get 192.168.10.10就更直接了,返回结果明确显示dev wlan0,也就是说访问 PLC 的数据包实际是从无线网卡出去的。

4.2 临时调整法:不重启、先救急

现场第一优先级是让产线恢复通信,先别急着写文件。我用了临时路由替换:

sudo ip route replace default via 192.168.10.1 dev eno1 metric 100 sudo ip route del default via 192.168.20.1 dev wlan0

执行后再看ip route get 192.168.10.10,出口已经变成eno1。这种临时方法能马上生效,但只在当前内核路由表层面有效,重启后就会还原,适合现场救急和验证判断。

4.3 永久配置:写文件并验证

现场验证临时方案有效后,再写 systemd-networkd 配置。我给有线网卡配置如下:

# /etc/systemd/network/20-wired.network [Match] Name=eno1 [Network] DHCP=no Address=192.168.10.100/24 [Route] Destination=0.0.0.0/0 Gateway=192.168.10.1 Metric=100

无线网卡:

# /etc/systemd/network/25-wireless.network [Match] Name=wlan0 [Network] DHCP=yes [DHCP] UseRoutes=true RouteMetric=500

执行:

sudo networkctl reload sudo networkctl up eno1 sudo networkctl up wlan0 ip route show default ip route get 192.168.10.10

这次输出里,默认路由是两条,但eno1的 metric 是 100,wlan0是 500。ip route get 192.168.10.10也指向dev eno1,问题解决。

4.4 热插拔场景复测

配置永久化之后,我特意做了复测:拔掉 eno1 的网线,观察ip route show default,发现无线路由自动变成唯一默认路由;插回网线后,有线路由重新出现,且依然因为 metric 更小而接管默认流量。这个切换过程虽然不至于毫秒级,但系统最终会回到正确状态,对工业设备来说是足够的。

如果业务对切换时间有更高要求,比如要求 1 秒内完成主备切换,那 metric 方案就不够,要往 bond 或网络冗余方案上走。这部分我在后面专门说。

4.5 关键命令清单

我把现场用得最多的命令整理成一份清单,方便运维同事直接抄:

networkctl list networkctl status eno1 ip -br addr ip route show ip route show default ip route get 192.168.10.10 ip route replace default via 192.168.10.1 dev eno1 metric 100 journalctl -u systemd-networkd -f

这套命令可以覆盖从观察、验证到临时调整的绝大多数流程。真正需要动手改配置时,再打开/etc/systemd/network/去编辑文件。

5. 常见问题与排查技巧实录

5.1 DHCP 下发的路由总是把 Metric 覆盖掉

这是最普遍的问题。你在[Route]里写了 Metric,但系统实际生效的还是 DHCP server 返回的 metric,因为 DHCP 客户端在收到 lease 后会自己建立路由,[Route]里写的静态路由优先级不一定能和它竞争。

解法就是前面提到的[DHCP] RouteMetric=。注意写法是放在[DHCP]段,不是[Network]段:

[DHCP] RouteMetric=200

如果 DHCP server 还会下发 classless static routes,且你希望完全忽略,加上UseRoutes=false。

5.2 同一张网卡被两个 .network 文件同时匹配

systemd-networkd 的规则是,如果多个 .network 文件匹配同一张网卡,它会选用一定的优先级顺序。但这不该成为你的设计方式。实际运维中最难搞的并不是文件太多,而是 netplan 生成的配置位于/run/systemd/network/,优先级比/etc/systemd/network/下自己写的文件还高,导致你改了/etc/下文件却毫无效果。

碰到这种情况,先执行:

networkctl status eno1

看输出里有没有提示“Configuration: /run/systemd/network/10-netplan-eno1.network”之类的路径。如果有,说明 netplan 或 cloud-init 生成的配置正在接管网卡。要么修改配套的 netplan YAML,要么把/run下对应文件移除,但要注意系统重启后会不会重新生成。

5.3 networkctl reload 之后路由没变化

这个坑我也经常遇到。networkctl reload只是让 systemd-networkd 重新读取配置,如果网卡的地址、链路状态都没有触发重新配置,它就“懒”得动。最直接的办法是把对应网卡 down 再 up:

sudo networkctl down eno1 sudo networkctl up eno1

或者用传统 ip 命令:

sudo ip link set eno1 down sudo ip link set eno1 up

这样能确保地址、路由全部按新配置重建。对于生产环境,这个操作会导致短暂断网,要挑维护窗口做。

5.4 NetworkManager 和 systemd-networkd 同时抢管

桌面版 Ubuntu 和某些带图形界面的工控 Linux 系统会同时存在 NetworkManager 和 systemd-networkd。两个服务都去配置同一块网卡时,路由表一会儿被 NM 改,一会儿被 networkd 改,metric 规则全乱。

检查方法:

systemctl status NetworkManager systemctl status systemd-networkd

如果 NetworkManager 正在运行,而且你希望统一由 systemd-networkd 管理,就禁用 NM:

sudo systemctl disable --now NetworkManager

之后再看networkctl list,原来 unmanaged 的网卡应该会进入 managed 状态。如果不想禁用,也可以在 NetworkManager 里把网卡设为 unmanaged,但那样维护两条链路很痛苦,我建议直接禁用其中一个。

5.5 系统启动后服务起得太快导致网卡没就绪

工业设备上经常有开机自启动的控制服务,它如果依赖有线网卡已经在线,那么 systemd-networkd 配置正确但服务启动顺序不对,照样会出现“路由已经对了,业务还是连不上”。

systemd 里有一个systemd-networkd-wait-online.service,它专门等待网络就绪。默认情况下,它会等待所有RequiredForOnline=yes的网卡在线。如果你的网络配置里网卡设置了RequiredForOnline=no,那 wait-online 可能提前退出,业务服务跟着启动,但网卡实际还没拿到 IP。

常规做法是设置:

[Network] RequiredForOnline=yes

在配置里明确哪块网卡是“必须在线”的主网卡。同时对自启动服务增加After=systemd-networkd-wait-online.service和Wants=systemd-networkd-wait-online.service依赖。这个细节平时没人提,但很多“重启后过几分钟才能连”的怪问题就是它引起的。

5.6 排查问题时的现场速查表

我把常见现象、原因和处理方式整理成一个表,方便大家直接对照:

现象可能原因处理方式
默认路由永远走无线网卡DHCP 下发的 RouteMetric 比有线小在无线配置里用[DHCP] RouteMetric=500覆盖
拔掉主网线后业务恢复很慢系统没有立即切换备份路由给备份网卡设置合理 metric,必要时用脚本监视网关
配置文件改了但路由不变netplan 的/run配置抢占检查 networkctl status 中显示的配置文件路径
一插网线就断无线两块网卡 metric 相同且互相竞争拉开 metric 差值,如 100 vs 500
网关地址提示不在同一子网配置里的 IP 与 Gateway 网段不匹配修正 Address 或 Gateway 字段

这个表基本覆盖了我在现场遇到过的 80% 双网卡路由问题。剩下 20% 大多是硬件驱动和 DHCP server 行为怪异导致的,只能靠抓日志逐个看。

6. 工业场景里的几个进阶建议

6.1 用 bond 还是用 metric 双网卡?

不少工程师会问:既然 metric 这么麻烦,为什么不直接做 bond?这个思路没错,但 bond 主要用于两块网卡连接到同一个二三层网络中,做链路聚合或者主备冗余。工业现场如果两块网卡分别连接不同的网段、不同的网关,bond 并不合适,因为 bond 会把两个网卡抽象成同一个接口,而你的业务本来就需要分别访问两个网络。

更合理的做法是“业务分层”:关键控制网络走有线,普通办公/外网走无线,两者用 metric 区分默认路由,同时靠静态路由让特定业务网段强制走特定网卡。比如:

ip route add 10.0.0.0/8 via 192.168.10.1 dev eno1

这种静态路由的优先级天然高于默认路由,可以保证 PLC 网段永远从有线走,不受 metric 影响。默认路由只负责“剩下所有流量”的出口选择。

6.2 网关健康检查的小脚本

光配置 metric 还不够,如果主网关的链路“假通”——物理连着但对端设备死机,内核路由表还是会走主网关,业务照样断。工业上比较通用的做法是写一个网关健康检查脚本,定时探测主网关,不通就临时把默认路由切到备用网卡。

我常用的一个思路如下:

#!/bin/bash MAIN_GW=192.168.10.1 BACKUP_GW=192.168.20.1 if ! ping -c 2 -W 1 $MAIN_GW > /dev/null 2>&1; then ip route replace default via $BACKUP_GW dev wlan0 metric 200 else ip route replace default via $MAIN_GW dev eno1 metric 100 fi

用 systemd timer 每 10 秒跑一次,比 cron 更可控。这个脚本能解决“链路在但网关死”的场景,但也要注意:如果两个网关都在,它会在 10 秒内反复切换,所以脚本里建议加一个状态记忆,避免抖动。

6.3 日志与调试的硬核武器

配置实在查不出来时,开 systemd-networkd 的调试日志很有效。临时开启:

SYSTEMD_LOG_LEVEL=debug systemd-networkd

或者直接看运行日志,过滤关键字段:

journalctl -u systemd-networkd -f -o short-iso

重点看两条关键信息:Configuring route和Could not set route。前者说明 systemd 确实尝试添加那条路由;后者则说明添加失败,失败原因多半是网关网段不匹配、metric 参数被忽略或路由冲突。

6.4 最后再分享一个小习惯

我现在每次给工业 Linux 设备做完双网卡配置,都会强制自己执行一遍“拔线测试”:把主网线拔掉等 3 秒,再插回去,看路由是否按预期切换。这个动作在文档里没人要求,但现场救过我好几次。很多配置在静态环境下看起来完美,一旦热插拔就原形毕露。如果连拔线测试都能稳定通过,这套 metric 方案基本就算彻底落地了,剩下的交给运维手册就行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询