
双网卡配置优先级这六个字我琢磨了好多年。早年帮人调机器十次里有八次碰上同一类问题服务器插了两块网卡之后要么内网通外网不通要么外网通内网又不通要么网络时好时坏像抽风。根因基本都指向同一个地方——Linux 内核收到报文之后并不知道你的流量该走哪块网卡出去而我们又没明确告诉系统什么流量优先走哪个口。这篇文章就围绕 CentOS 环境下的双网卡优先级问题把路由选路规则、临时调整命令、永久配置方式、策略路由用法以及那些常出现的怪毛病一次讲透。适合正在被多网卡路由问题折磨的运维、测试以及刚接触 Linux 网络想系统搞懂路由表机制的开发者。1. 双网卡场景里绕不开的默认路由争夺战1.1 三种典型双网卡拓扑分别要解决什么问题我在实际工作中遇到的双网卡需求基本可以归成下面三类。不同的业务预期决定了你后续要用完全不同的配置思路。场景类型网卡A网卡B期望的效果内外网隔离内网段如 10.0.0.0/24互联网出口如 192.168.1.1默认流量走互联网业务内网段走内网口双线上网/冗余线路1网关线路2网关平时以线路1为主线路2只做备用虚拟化调试NAT网络口Host-Only 网络口默认走 NAT 上网调试专用网段走 Host-Only第一类最常见我在某跨平台系统的测试环境里就搭过类似的组网。一块网卡接办公网用来访问互联网、收邮件、拉更新包另一块网卡接内部测试网用来连数据库、调用内部接口。表面上看只要两块网卡都配上 IP 就能正常工作但实际一开机就会发现不对劲要不就是默认走了内网网关导致互联网访问超时要不就是内网业务全部绕到办公网网关被防火墙拦了个干净。第二类双线冗余对优先级更敏感。两个网关都想当默认路由如果 metric 配得不好就会出现今天走的 A 线明天重启变成 B 线监控图上忽高忽低查了半天才发现是路由表里同时存在两条 default其中一条是 DHCP 动态下发后盖住了原来手工配置的条目。第三类虚拟化调试场景里NAT 口和 Host-Only 口其实可以共存但新手很容易把两块网卡都设成自动获取 IP自动默认网关结果默认路由被最后激活的网卡抢走。我见过不少同学在虚拟机上装完系统后 ping 外网 ping 不通一查route -ndefault 指到了 Host-Only 那个根本没有外部链路的网卡上。1.2 内核选路的底层逻辑最长匹配、metric、路由表三件事想理解双网卡优先级不能只背命令。Linux 内核在决定一个数据包往哪个网卡送时走的是这样一套逻辑第一在路由表里查找最精确的条目。路由条目的本质是目的网段 下一跳 出接口。系统会先把目标 IP 和所有路由条目做匹配前缀越长也就是掩码位数越多的条目优先级越高。比如访问 192.168.56.10 时路由表中如果有192.168.56.0/24这条它肯定比0.0.0.0/0这条默认路由更优先哪怕默认路由写了什么特殊 flag 都不管用。所以特定网段走特定网卡这件事靠更精确的路由条目就能做到不一定非要动策略路由。第二当多条路由的前缀长度完全一样时比较 metric也叫 metric 值老文档里常写作 cost。metric数值越小路由优先级越高。比如两条0.0.0.0/0默认路由同时存在系统就会选择 metric 比较小的那个网关。这就是双网卡默认路由优先级的核心战场——两个网关都在抢这条default谁能当上真正的话事人完全看谁的 metric 小。第三如果 metric 也一样系统再按路由的插入顺序或者其他内部策略做选择。这个状态很不稳定重启一次、网卡 re-up 一次顺序就可能变所以我不建议让生产环境出现两条默认路由完全等价的情况。用生活里的例子类比你打开地图软件查回家路线系统给出三条方案它先找终点精确匹配的路线如果都被选中就按预计耗时排序耗时短的最优如果耗时也一样就看你上一次选了哪条。Linux 的 metric 就相当于这个预计耗时是我们手里最直接、最常用的优先级调节旋钮。2. 先用命令把默认路由切到目标网卡临时方案2.1 两个查看路由的工具route 和 ip route在系统层面动手之前得先看清当前路由长什么样。CentOS 上常见的查看命令有两个route -n输出里有一列Flags如果条目中有UG标志U表示这条路由可用G表示要经过网关转发。看到UG的条目基本就是默认路由或者到某个网段的网关路由。另一个命令是ip route这是 iproute2 软件包提供的输出更详细metric 直接能看到ip route show对比一下route -n也能显示 metric其实是能的老版本route -n输出末尾就有Metric列只是很多朋友没注意。平时调试我更推荐ip route show因为它还能显示路由来源比如proto dhcp或者proto static这信息在排查配置被谁覆盖时特别有用。2.2 临时切换默认网关的操作流程假设当前机器有两块网卡eth0走了内网网关 10.0.0.1eth1走的是互联网网关 192.168.1.1而我们希望默认路由切到eth1。临时调整分三步# 第一步查看当前默认路由 ip route show # 输出可能长这样 # default via 10.0.0.1 dev eth0 proto dhcp metric 100 # 192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.100 metric 200 # 第二步把默认路由切到目标网关 sudo ip route replace default via 192.168.1.1 dev eth1 metric 100 # 第三步确认结果 ip route show # 应该看到 # default via 192.168.1.1 dev eth1 proto static metric 100这里我用的是ip route replace不是先ip route del default再加新路由。为什么因为直接删掉那条 default在那一瞬间路由表里就没有默认路由了如果你正在通过这台机器远程维护ssh 连接走默认路由出去很可能会在删掉路由的同时断开连接等新路由加进去你的会话已经回不来了。replace是原子操作直接把旧条目替换成新的风险小得多。如果你确实想保留两条默认路由做备份也可以同时存在只要把它们设定成不同的 metric 值就行# 主用 sudo ip route add default via 192.168.1.1 dev eth1 metric 100 # 备用 sudo ip route add default via 10.0.0.1 dev eth0 metric 200这样正常情况下所有未知流量都走 192.168.1.1只有当 eth1 链路挂掉、192.168.1.1 不可达时内核才会尝试走 10.0.0.1 这条备用路由。这是做双线上网冗余最简单粗暴但也最有效的配置方法。2.3 验证是否生效的三板斧改完路由不能光看ip route show就说完事儿了还得验证实际流量走的路径。我常用的验证命令有三个第一板斧看某个目标 IP 实际走哪个接口ip route get 223.5.5.5 # 输出里能看到 dev eth1 以及 src 源地址这个命令会把从这台机器到 223.5.5.5 的真实出口网卡和源地址显示出来是诊断路由配了没生效的最快方式强烈建议养成习惯。第二板斧强制指定网卡去 pingping -I eth1 -c 4 223.5.5.5-I参数指定出接口可以验证某块网卡到某目标的连通性绕开路由表的干扰。如果你不确定是不是路由表问题用这个命令分别从 eth0 和 eth1 各自 ping 一下就能知道问题在物理链路还是路由决策。第三板斧traceroute 看第一跳traceroute -i eth1 -n 223.5.5.5第一跳应该是 192.168.1.1。如果第一跳不是期望的网关说明数据包的源地址或者路由表还有没处理干净的地方。这类临时命令适合应急和验证思路改完当下就生效不写文件。但你也应该明白只要网络服务重启、NetworkManager 重载、或者系统重启这些改动就会还原。所以接下来要讲永久化。3. 让优先级写入配置文件ifcfg 和 NetworkManager 的持久化手段3.1 为什么重启后配置总被覆盖很多朋友遇到过这种情况头天晚上用ip route把优先级调好了第二天早上开机发现又回到了原来那个错误状态甚至更乱。原因在于CentOS 7 以后默认由 NetworkManager 接管网络配置它在启动网卡时会按照网卡配置文件或者 DHCP 获取到的信息重新生成路由表。你用命令行敲进去的临时路由只存在于内核的路由表里面属于内存态数据。系统一路由NetworkManager 就按配置文件里的内容重新计算一遍路由之前的手工路由自然灰飞烟灭。所以持久化双网卡优先级本质上是搞清楚如何在网卡配置层面表达优先级意图让 NetworkManager 每次重算路由时都能算出我们希望的结果。3.2 ifcfg 文件里的三员大将GATEWAY、DEFROUTE、METRICCentOS 的网卡配置文件默认在/etc/sysconfig/network-scripts/ifcfg-ethX。里面跟默认路由优先级相关的主要是三个字段GATEWAY配置该网卡的网关地址NetworkManager 看到后会为这张网卡生成一条默认路由。DEFROUTE控制这张网卡是否生成默认路由。DEFROUTEno的意思就是你只管自己直连网段别去抢 default。METRIC指定这条默认路由的 metric 值值越小优先级越高。举个例子。假设 eth0 接内网eth1 接互联网我希望默认路由只归 eth1同时内网直连路由照常走 eth0那么可以这样写# /etc/sysconfig/network-scripts/ifcfg-eth0 BOOTPROTOstatic IPADDR10.0.0.100 NETMASK255.255.255.0 GATEWAY10.0.0.1 DEFROUTEno# /etc/sysconfig/network-scripts/ifcfg-eth1 BOOTPROTOstatic IPADDR192.168.1.100 NETMASK255.255.255.0 GATEWAY192.168.1.1 DEFROUTEyes METRIC100这里的关键点是全局默认路由只让一个网卡去当主角另一个网卡的DEFROUTE直接设为no避免它再往路由表里硬塞一条 default。如果两块网卡都需要上网双线冗余那你可以在保留两个 GATEWAY 的同时为其中一个显式设置更大的METRIC200让默认路由始终优先走 metric 小的那张卡。另外一个老坑是/etc/sysconfig/network文件里也有一个GATEWAY字段这个文件是全局网络配置老版本 CentOS 下它可以直接指定默认网关。在 CentOS 7/8 上它仍然会被读取但很容易和 ifcfg 里的 GATEWAY 打架。我的习惯是尽量不用/etc/sysconfig/network里的 GATEWAY让它保持默认内容网关全部在 ifcfg 里管避免来源太多导致路由生成顺序难以预测。3.3 用 nmcli 在 CentOS 7/8 上快速调整在没有条件改文件、或者懒得重启网络服务的机器上我更推荐用 nmcli 来调整。先查看现有连接nmcli connection show找到对应网卡的连接名称比如eth0和eth1然后直接修改路由 metricsudo nmcli connection modify eth1 ipv4.route-metric 100 sudo nmcli connection modify eth0 ipv4.route-metric 200 sudo nmcli connection up eth0 sudo nmcli connection up eth1这两条命令的效果等同于在 ifcfg 文件里分别指定METRIC100和METRIC200重启之后依然保留。如果你希望 eth0 完全不参与默认路由争夺还可以把它设为不自动获取默认路由sudo nmcli connection modify eth0 ipv4.never-default yesnever-default对应 ifcfg 里的DEFROUTEno在纯命令行环境下操作节约很多时间。改完记得nmcli connection up让配置生效。我特别提醒一句用了 nmcli 和 NetworkManager 的机器别再回头手动编辑 ifcfg除非你把NM_CONTROLLEDno写进文件里否则两边不同步会造成各种莫名其妙的我明明改了却不生效。4. 更精细的流量分流策略路由ip rule与独立路由表4.1 为什么光调默认路由还不够默认路由只能解决未知流量走哪个网关的问题。但不少双网卡场景要求的是特定网段必须走特定网卡哪怕这个网段在公网上或者这个网段只有通过特定网关才能访问。比如 test 环境的 192.168.56.0/24 网段只挂在 eth0 的内网链路上而 223.5.5.5 这样的公网 IP 要绕 eth1 出去。如果只设置默认路由走 eth1、内网直连走 eth0那访问 192.168.56.0/24 确实没问题——因为直连路由最精确。但如果内网网关后面还挂着别的私网段比如 172.16.0.0/16那就麻烦了。默认路由会把它丢给 eth1 的互联网网关互联网网关根本不知道 172.16.0.0/16 往哪转数据包就石沉大海了。这时候就需要策略路由让内核在做路由决策之前先按源地址是谁、目标是谁、从哪张网卡进来这些附加条件选一个专门的路由表来查而不是永远只查那张 main 路由表。4.2 ip rule 与独立路由表给特定流量单独画一条道Linux 支持多张路由表默认有main、local、default等。ip rule就是策略路由规则它决定数据包去查哪张表。操作流程我拆成四步。第一步编辑/etc/iproute2/rt_tables给自定义路由表起个名字比如叫dmz_route# /etc/iproute2/rt_tables 100 dmz_route表号不要和系统已有的冲突100 到 200 之间一般都能用。第二步添加一条策略规则源地址来自 192.168.56.0/24 的流量都去查dmz_route这张表sudo ip rule add from 192.168.56.0/24 table dmz_route priority 100priority数值越小越先匹配。之所以显式写 priority是避免后面加其他规则时出现两条规则优先级相同导致规则覆盖的尴尬。第三步往dmz_route表里写具体路由条目。这张表里我想让 192.168.56.0/24 直连 eth0其余流量全部丢给内网网关 192.168.56.1sudo ip route add 192.168.56.0/24 dev eth0 src 192.168.56.100 table dmz_route sudo ip route add default via 192.168.56.1 dev eth0 table dmz_route第四步验证ip rule show ip route show table dmz_route ip route get 172.16.0.1 from 192.168.56.100最后一条命令的from参数很关键它模拟源地址为 192.168.56.100 的流量要访问 172.16.0.1时内核会匹配到dmz_route表里的默认路由出接口还是 eth0。这样就把特定源网段走特定口这件事落到了实处。4.3 让策略路由开机自动恢复ip rule和ip route ... table同样是内存态命令必须想办法在开机后自动执行。我习惯用的持久化方案是 NetworkManager 的 dispatcher 脚本。在/etc/NetworkManager/dispatcher.d/下放一个脚本当 eth0 被激活时自动添加规则和路由#!/bin/bash if [ $2 up ] [ $1 eth0 ]; then ip rule add from 192.168.56.0/24 table dmz_route priority 100 2/dev/null || true ip route add 192.168.56.0/24 dev eth0 src 192.168.56.100 table dmz_route 2/dev/null || true ip route add default via 192.168.56.1 dev eth0 table dmz_route 2/dev/null || true fi注意脚本要有可执行权限而且 dispatcher 脚本默认不允许往标准输出打印东西所以调试信息最好写到日志文件。很多朋友在服务器上配置了策略路由但不生效十有八九是忘了chmod x。如果你不想依赖 NetworkManager更老派的做法是放到/etc/rc.d/rc.local里。只是 CentOS 7/8 的 rc.local 默认没有执行权限需要手动加一下sudo chmod x /etc/rc.d/rc.local然后在文件尾部追加那几条ip rule和ip route命令。这个方案简单、直观、不依赖 NetworkManager 版本适合只想开机执行一次的静态场景。缺点是没有网卡 down 了再 up的重置能力链路抖动时规则不会自动重灌但多数生产环境也用不上这种动态能力。5. 双网卡踩坑实录从路由覆盖到 DNS 错乱的完整排查链路5.1 路由被覆盖一次配好又失效的完整排查有位同事在调试某套测试平台时早上配好的双网卡路由下午一刷新就失效了。他找我帮忙我先让他把现场状态拉全。排查链路是这样走的先看当前路由表确认默认路由到底指向谁ip route show输出显示default via 10.0.0.1 dev eth0这就不对了按预期应该走 eth1 的 192.168.1.1。再查 NetworkManager 认为的合成配置是什么nmcli device show eth0 nmcli device show eth1重点看IP4.ROUTE那一项能看到 NM 自己生成的路由条目和 metric。这里 eth0 的IP4.ROUTE里居然有0.0.0.0/0。为什么 NM 会给 eth0 生成默认路由再翻 ifcfg 文件发现 eth0 的 ifcfg 里写着GATEWAY10.0.0.1而 eth1 的 ifcfg 里也有GATEWAY192.168.1.1。两张卡都在抢默认路由的编制最终 NetworkManager 按网卡激活顺序把默认路由分配给了先激活的那张。修复方式很清晰把 eth0 的GATEWAY10.0.0.1那条删掉或者加上DEFROUTEno让 eth0 只保留直连路由。然后sudo systemctl restart network重启之后再看ip route show默认路由只剩一条指向 eth1。这个案例里最能说明问题的命令其实是nmcli device show eth0——很多人排查时只盯着内核路由表却忽略了 NetworkManager 自己那套合成配置也会参与路由计算。5.2 DNS 解析的隐藏优先级能 ping 通却解析不了另一个高频坑是网络通、路由也对但域名解析一卡就卡半天或者干脆解析失败。双网卡环境下DNS 配置经常和路由优先级各管各的我见过太多人把时间耗在路由上最后才发现是 DNS 在作妖。排查思路是先确认 DNS 服务器从哪来cat /etc/resolv.conf nmcli device show eth0 | grep DNS nmcli device show eth1 | grep DNS如果 resolv.conf 里第一个 nameserver 是内网 DNS而内网 DNS 又没法解析外网域名那不管路由表多完美域名解析都会失败。这里涉及 DNS 的优先级问题resolv.conf自上而下依次尝试 nameserver第一个失败才会试第二个而不同网卡的 DNS 由 NetworkManager 按自己的规则排序写入。解决方式有几个。最简单的是在两块网卡的 ifcfg 里都指定 DNS并把公网 DNS 写在DNS1、内网 DNS 写在DNS2例如 eth0内网口写DNS1223.5.5.5 DNS210.0.0.53这是让公网 DNS 优先的写法前提是这台机器确实需要解析互联网域名。反过来如果机器是纯内网业务机那应该把内网 DNS 放在 DNS1并配置好DOMAINcorp.local之类的搜索域否则内部短域名根本解析不了。如果 NetworkManager 版本比较新还可以直接设置连接的dns-priority属性数值越小优先级越高控制 resolv.conf 里 nameserver 的排列顺序。较低版本的 NM 上这个属性可能不生效稳妥起见还是靠 DNS1/DNS2 显式排列这个办法在绝大多数 CentOS 机器上都能用。5.3 同网段双网关的迷惑行为到底谁在响应还有一种更隐蔽的情况两块网卡配了不同 IP但网关指向同一个网段甚至两个网关 IP 本身同网段。比如 eth0 的网关是 192.168.1.1eth1 的网关也是 192.168.1.1 或者 192.168.1.2两个网关都在 192.168.1.0/24 里。这种情况下即使你在路由表里硬性指定了某条流量走 eth1数据包出门时还要过 ARP 这一关。内核要确定目标网关的 MAC 地址应该从哪个接口学习。如果两张网卡挂在同一个二层网络里就可能出现路由说走 eth1但 ARP 应答从 eth0 回来的错乱。表现就是ip route get看着正确ping 却不通或者特别慢用 tcpdump 抓包又能看到请求发出来了但 MAC 层地址不对。处理这种场景我一般直接建议改网络规划双网卡就别让它们挂在同一个广播域里至少错开 VLAN或者把其中一块网卡的网关设到另一个网段。如果你必须在同一二层网络里用两个网关那得配合 ip rule 里的from规则加上 arp_filter 之类的内核参数复杂度高不少生产环境尽量少碰。6. 把这些经验固化成一套可复用的日常操作习惯双网卡优先级不是配一次就完事的东西它经常在系统升级、网卡卸载重装、DHCP 租约续期后悄悄发生变化。我在实际运维中体会最深的一点是不要等出了问题再临时敲命令而是要把正确状态固化在配置里并且固化得可查、可对比。我现在的日常习惯大致是这几条。第一配置层面明确一个默认路由主人全机器只让一块网卡启动默认路由其他网卡一律DEFROUTEno或never-default如果确实需要冗余那就显式给不同网卡设不同 metric并写清楚主备关系。第二把检查命令做成一个别名或小脚本。比如每次登录双网卡机器先跑ip route show加ip rule show再看一眼/etc/resolv.conf三个命令加起来十秒钟却能把 90% 的路由问题提前暴露。第三凡是远程调整路由我一定会先留好带外通道最不济也要确认当前 ssh 会话不依赖即将被替换的默认路由再用ip route replace而不是ip route deladd。第四策略路由的规则要写到 dispatcher 脚本里但脚本里每条命令都加上2/dev/null || true避免规则已经存在时重复执行产生报错导致 NetworkManager 误判脚本执行失败。如果你也跟着动手把双网卡调通了你会慢慢发现所谓优先级本质上就两件事一是让系统明白哪条路是主要的二是让系统知道什么流量必须走哪条路。搞清这两件事CentOS 双网卡的绝大多数疑难杂症都能迎刃而解。最后分享一个小技巧调试时多用ip route get它比 ping 更能说明内核的真实意图——因为 ping 成功了只能证明物理链路通而ip route get能直接告诉你数据包打算怎么走。