双网卡双网关服务器策略路由配置与持久化完全指南

双网卡双网关服务器策略路由配置与持久化完全指南

彻底解决非对称路由问题,实现网络高可用与流量分离

在企业级服务器网络架构中,双网卡双网关 配置是实现网络高可用、业务流量隔离和负载分担的核心方案。然而,这种配置极易引发非对称路由 问题,导致外部访问超时、丢包甚至完全不通。本文将从原理到实践,使用真实可直接参考的私网IP示例,全面讲解如何通过Linux策略路由彻底解决这一问题,并提供生产级的持久化配置方案和故障排查手册。

文章目录

  • 双网卡双网关服务器策略路由配置与持久化完全指南
    • 彻底解决非对称路由问题,实现网络高可用与流量分离
    • 一、非对称路由问题深度解析
      • [1.1 典型问题现象](#1.1 典型问题现象)
      • [1.2 根本原因与原理](#1.2 根本原因与原理)
    • [二、解决方案:Linux策略路由(Policy Routing)](#二、解决方案:Linux策略路由(Policy Routing))
      • [2.1 策略路由核心原理](#2.1 策略路由核心原理)
      • [2.2 整体设计方案](#2.2 整体设计方案)
      • [2.3 设计原则](#2.3 设计原则)
    • 三、生产级详细配置步骤
      • [3.1 环境准备与规划](#3.1 环境准备与规划)
      • [3.2 创建自定义路由表](#3.2 创建自定义路由表)
      • [3.3 配置策略路由规则](#3.3 配置策略路由规则)
      • [3.4 配置自定义路由表内容](#3.4 配置自定义路由表内容)
      • [3.5 配置主路由表](#3.5 配置主路由表)
    • 四、持久化配置实现
      • [4.1 两种持久化方案对比](#4.1 两种持久化方案对比)
      • [4.2 方案一:通过rc.local实现持久化](#4.2 方案一:通过rc.local实现持久化)
      • [4.3 方案二:通过NetworkManager dispatcher脚本实现(生产级推荐)](#4.3 方案二:通过NetworkManager dispatcher脚本实现(生产级推荐))
      • [4.4 配置反向路径过滤(通用)](#4.4 配置反向路径过滤(通用))
    • 五、全面验证与测试
      • [5.1 一键验证脚本](#5.1 一键验证脚本)
      • [5.2 关键验证点](#5.2 关键验证点)
      • [5.3 重启验证](#5.3 重启验证)
    • 六、配置优化与最佳实践
      • [6.1 路由表设计优化](#6.1 路由表设计优化)
      • [6.2 脚本优化建议](#6.2 脚本优化建议)
      • [6.3 安全加固](#6.3 安全加固)
    • 七、常见故障排查指南
      • [7.1 通用故障排查流程](#7.1 通用故障排查流程)
      • [7.2 持久化方案专属问题](#7.2 持久化方案专属问题)
      • [7.3 常见问题与解决方案](#7.3 常见问题与解决方案)
      • [7.4 常用排查命令](#7.4 常用排查命令)
    • 八、总结与扩展
      • [8.1 本文方案优势](#8.1 本文方案优势)
      • [8.2 扩展应用场景](#8.2 扩展应用场景)
      • [8.3 注意事项](#8.3 注意事项)

一、非对称路由问题深度解析

1.1 典型问题现象

  • 服务器双网卡分别接入不同网段,配置两个默认网关
  • 从外网或其他网段访问服务器时,出现间歇性连接超时
  • 同一网段内访问正常,跨网段访问异常
  • 使用ping测试时,部分数据包丢失,telnet端口测试成功率低
  • 从服务器主动向外连接正常,但外部主动连接服务器失败

1.2 根本原因与原理

非对称路由 是指数据包的请求路径响应路径不一致。这是Linux内核默认路由机制的必然结果:

复制代码
客户端 → 网关1(192.168.1.1) → 服务器ens18网卡(192.168.1.100) → 服务器处理请求
                                                          ↓
客户端 ← 网关2(10.0.0.1) ← 服务器ens19网卡(10.0.0.100) ← 服务器生成响应

当服务器收到来自ens18网卡的请求时,内核会根据主路由表 的默认网关优先级选择响应出口。如果ens19的默认网关优先级更高,响应包就会从ens19网卡发出。

此时,中间的防火墙或网关设备会检测到:一个来自网关1的请求,却收到了来自网关2的响应,出于安全考虑会直接丢弃该数据包,导致连接失败。

二、解决方案:Linux策略路由(Policy Routing)

2.1 策略路由核心原理

传统路由仅根据目标IP地址 选择下一跳,而策略路由允许根据源IP地址、目标IP地址、入站接口、服务端口等多种条件,选择不同的路由表进行转发。

对于双网卡双网关场景,核心解决思路是:

谁接收的请求,就从谁那里返回响应

2.2 整体设计方案

我们将采用"一网卡一路由表"的设计模式:

  1. 为每个网卡创建独立的自定义路由表
  2. 配置策略规则:根据源IP地址选择对应的路由表
  3. 每个自定义路由表只包含该网卡的网段路由和默认网关
  4. 保留主路由表用于服务器主动发起的连接

2.3 设计原则

  • 路径对称:确保同一连接的进出流量通过同一网卡
  • 职责分离:每个路由表只管理一个网卡的流量
  • 优先级明确:目标地址规则优先级高于源地址规则
  • 持久可靠:配置在系统重启和网络服务重启后自动恢复
  • 向后兼容:不影响服务器原有网络功能

三、生产级详细配置步骤

3.1 环境准备与规划

本文以CentOS/RHEL 7/8/9系统为例,使用以下真实私网IP配置作为示例:

网卡名称 IP地址/掩码 网关地址 所属网段 用途
ens18 192.168.1.100/24 192.168.1.1 192.168.1.0/24 业务主网(对外提供服务)
ens19 10.0.0.100/24 10.0.0.1 10.0.0.0/24 管理网/专用网
- - - 172.16.0.0/16 需要通过ens19访问的内部专用网段

注意:请将以下配置中的IP地址替换为您的实际环境地址。

3.2 创建自定义路由表

Linux内核支持最多255个自定义路由表,我们需要在/etc/iproute2/rt_tables文件中定义:

bash 复制代码
# 备份原始配置文件(生产环境必做)
cp /etc/iproute2/rt_tables /etc/iproute2/rt_tables.bak

# 添加自定义路由表(ID建议从100开始,避免与系统保留表冲突)
cat >> /etc/iproute2/rt_tables << 'EOF'
# 自定义策略路由表
100    ens18_table  # 对应ens18网卡(192.168.1.100)的路由表
101    ens19_table  # 对应ens19网卡(10.0.0.100)的路由表
EOF

3.3 配置策略路由规则

策略规则决定了什么样的数据包使用哪个路由表,规则按优先级从小到大依次匹配:

bash 复制代码
# 清除可能存在的旧规则(避免重复配置导致冲突)
ip rule del from 192.168.1.100 lookup ens18_table 2>/dev/null || true
ip rule del from 10.0.0.100 lookup ens19_table 2>/dev/null || true
ip rule del to 172.16.0.0/16 lookup ens19_table 2>/dev/null || true
ip rule del pref 10000 2>/dev/null || true

# 添加核心策略规则
# 规则1:源IP是192.168.1.100的数据包,使用ens18_table路由表
ip rule add from 192.168.1.100 lookup ens18_table

# 规则2:源IP是10.0.0.100的数据包,使用ens19_table路由表
ip rule add from 10.0.0.100 lookup ens19_table

# 规则3:目标是172.16.0.0/16专用网段的数据包,强制使用ens19_table
# 优先级10000,高于默认规则(32766)
ip rule add to 172.16.0.0/16 lookup ens19_table pref 10000

3.4 配置自定义路由表内容

为每个自定义路由表配置独立的路由信息,实现流量完全分离:

bash 复制代码
# ==================== 配置ens18_table路由表 ====================
# 默认网关:所有未知目标通过192.168.1.1转发
ip route replace default via 192.168.1.1 dev ens18 table ens18_table

# 本地网段路由:直接通过ens18网卡访问192.168.1.0/24
ip route replace 192.168.1.0/24 dev ens18 scope link table ens18_table

# ==================== 配置ens19_table路由表 ====================
# 默认网关:所有未知目标通过10.0.0.1转发
ip route replace default via 10.0.0.1 dev ens19 table ens19_table

# 本地网段路由:直接通过ens19网卡访问10.0.0.0/24
ip route replace 10.0.0.0/24 dev ens19 scope link table ens19_table

# 专用网段路由:通过10.0.0.1访问172.16.0.0/16
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19 table ens19_table

3.5 配置主路由表

主路由表用于服务器主动发起的连接,通过metric值控制默认网关优先级:

bash 复制代码
# 清除原有默认网关(避免冲突)
ip route del default 2>/dev/null || true

# 添加两个默认网关,metric值越小优先级越高
# 这里设置业务主网(ens18)优先级更高
ip route add default via 192.168.1.1 dev ens18 metric 100
ip route add default via 10.0.0.1 dev ens19 metric 101

# 添加专用网段路由(主路由表也需要,用于服务器主动访问)
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19

四、持久化配置实现

4.1 两种持久化方案对比

本文提供两种主流的持久化方案,您可以根据自己的系统环境和需求选择:

方案 优点 缺点 适用场景
rc.local 简单易上手,兼容所有Linux系统 执行时机不确定,网络服务重启后配置丢失,错误处理能力弱 测试环境、不使用NetworkManager的老旧系统
NetworkManager dispatcher 执行时机准确,网络状态变化自动触发,支持完善的日志和错误处理 仅适用于使用NetworkManager的系统(CentOS7+/Ubuntu18.04+) 生产环境、需要高可靠性的场景

4.2 方案一:通过rc.local实现持久化

这是最传统、最简单的持久化方式,适合快速部署和测试环境:

bash 复制代码
# 编辑rc.local文件
cat >> /etc/rc.d/rc.local << 'EOF'
#!/bin/bash
# 双网卡策略路由持久化配置
# 等待网络接口就绪(根据实际情况调整等待时间)
sleep 3

# 1. 确保自定义路由表定义存在
grep -q "^100[[:space:]]" /etc/iproute2/rt_tables || echo "100    ens18_table" >> /etc/iproute2/rt_tables
grep -q "^101[[:space:]]" /etc/iproute2/rt_tables || echo "101    ens19_table" >> /etc/iproute2/rt_tables

# 2. 清除可能存在的旧规则
ip rule del from 192.168.1.100 lookup ens18_table 2>/dev/null || true
ip rule del from 10.0.0.100 lookup ens19_table 2>/dev/null || true
ip rule del to 172.16.0.0/16 lookup ens19_table 2>/dev/null || true
ip rule del pref 10000 2>/dev/null || true

# 3. 添加策略路由规则
ip rule add from 192.168.1.100 lookup ens18_table
ip rule add from 10.0.0.100 lookup ens19_table
ip rule add to 172.16.0.0/16 lookup ens19_table pref 10000

# 4. 配置自定义路由表路由
ip route replace default via 192.168.1.1 dev ens18 table ens18_table
ip route replace 192.168.1.0/24 dev ens18 scope link table ens18_table
ip route replace default via 10.0.0.1 dev ens19 table ens19_table
ip route replace 10.0.0.0/24 dev ens19 scope link table ens19_table
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19 table ens19_table

# 5. 保持主路由表的关键路由
ip route replace default via 192.168.1.1 dev ens18 metric 100
ip route replace default via 10.0.0.1 dev ens19 metric 101
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19

# 6. 记录执行日志
echo "[$(date)] 策略路由配置已通过rc.local加载" >> /var/log/rc-local-route.log
EOF

# 设置执行权限
chmod +x /etc/rc.d/rc.local

# 启用并启动rc-local服务
systemctl enable rc-local --now

4.3 方案二:通过NetworkManager dispatcher脚本实现(生产级推荐)

这是目前最可靠的持久化方式,脚本会在网络接口状态变化(up/down)、NetworkManager服务重启时自动执行,彻底解决rc.local的缺陷:

bash 复制代码
# 创建NetworkManager dispatcher脚本
cat > /etc/NetworkManager/dispatcher.d/99-policy-routing << 'EOF'
#!/bin/bash
# 双网卡策略路由自动配置脚本
# 触发时机:网络接口up/down、NetworkManager服务重启

INTERFACE="$1"
ACTION="$2"
LOG_FILE="/var/log/policy-routing.log"

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
}

# 只在接口up时执行配置
if [ "$ACTION" != "up" ]; then
    exit 0
fi

log "网络接口 $INTERFACE 已启动,开始配置策略路由"

# 1. 确保自定义路由表存在
if ! grep -q "^100[[:space:]]*ens18_table" /etc/iproute2/rt_tables; then
    echo "100    ens18_table" >> /etc/iproute2/rt_tables
    log "添加自定义路由表:ens18_table"
fi

if ! grep -q "^101[[:space:]]*ens19_table" /etc/iproute2/rt_tables; then
    echo "101    ens19_table" >> /etc/iproute2/rt_tables
    log "添加自定义路由表:ens19_table"
fi

# 2. 清除旧的策略规则
ip rule del from 192.168.1.100 lookup ens18_table 2>/dev/null
ip rule del from 10.0.0.100 lookup ens19_table 2>/dev/null
ip rule del to 172.16.0.0/16 lookup ens19_table 2>/dev/null
log "清除旧策略规则完成"

# 3. 添加新的策略规则
ip rule add from 192.168.1.100 lookup ens18_table
ip rule add from 10.0.0.100 lookup ens19_table
ip rule add to 172.16.0.0/16 lookup ens19_table pref 10000
log "添加新策略规则完成"

# 4. 配置自定义路由表
# ens18_table
ip route replace default via 192.168.1.1 dev ens18 table ens18_table
ip route replace 192.168.1.0/24 dev ens18 scope link table ens18_table

# ens19_table
ip route replace default via 10.0.0.1 dev ens19 table ens19_table
ip route replace 10.0.0.0/24 dev ens19 scope link table ens19_table
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19 table ens19_table
log "配置自定义路由表完成"

# 5. 配置主路由表
ip route replace default via 192.168.1.1 dev ens18 metric 100
ip route replace default via 10.0.0.1 dev ens19 metric 101
ip route replace 172.16.0.0/16 via 10.0.0.1 dev ens19
log "配置主路由表完成"

log "策略路由配置全部执行成功"
EOF

# 设置脚本执行权限(必须为755,否则NetworkManager不会执行)
chmod 755 /etc/NetworkManager/dispatcher.d/99-policy-routing

# 创建日志文件并设置权限
touch /var/log/policy-routing.log
chmod 644 /var/log/policy-routing.log

# 重启NetworkManager服务,立即触发脚本执行
systemctl restart NetworkManager

# 查看日志确认配置成功
tail -f /var/log/policy-routing.log

4.4 配置反向路径过滤(通用)

反向路径过滤(rp_filter)是Linux内核的安全机制,在策略路由环境下必须设置为宽松模式,否则会丢弃非对称路由的数据包:

bash 复制代码
# 创建sysctl配置文件
cat > /etc/sysctl.d/99-policy-routing.conf << 'EOF'
# 策略路由反向路径过滤配置
# 0:关闭反向路径过滤
# 1:严格模式(默认,会导致非对称路由数据包被丢弃)
# 2:宽松模式(推荐,只检查源地址是否可达)
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
net.ipv4.conf.ens18.rp_filter=2
net.ipv4.conf.ens19.rp_filter=2

# 禁用ICMP重定向,防止路由表被意外修改
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.ens18.accept_redirects=0
net.ipv4.conf.ens19.accept_redirects=0
EOF

# 应用配置
sysctl -p /etc/sysctl.d/99-policy-routing.conf

五、全面验证与测试

5.1 一键验证脚本

创建一个验证脚本,快速检查所有配置是否正确:

bash 复制代码
cat > /usr/local/bin/verify-policy-routing << 'EOF'
#!/bin/bash
echo "====================================="
echo "      双网卡策略路由配置验证"
echo "====================================="
echo ""

echo "1. 自定义路由表定义:"
grep -E "(100|101)" /etc/iproute2/rt_tables
echo ""

echo "2. 策略路由规则:"
ip rule show | grep -E "(ens18|ens19)"
echo ""

echo "3. ens18_table路由表内容:"
ip route show table ens18_table
echo ""

echo "4. ens19_table路由表内容:"
ip route show table ens19_table
echo ""

echo "5. 主路由表内容:"
ip route show | grep -E "(default|172.16)"
echo ""

echo "6. 路由路径测试:"
echo "→ 测试从ens18(192.168.1.100)返回的路径:"
ip route get 8.8.8.8 from 192.168.1.100
echo ""
echo "→ 测试从ens19(10.0.0.100)返回的路径:"
ip route get 8.8.8.8 from 10.0.0.100
echo ""
echo "→ 测试访问专用网段(172.16.1.1)的路径:"
ip route get 172.16.1.1
echo ""

echo "7. 连通性测试:"
echo "→ 测试通过ens18访问外网:"
ping -c 2 -I 192.168.1.100 8.8.8.8
echo ""
echo "→ 测试通过ens19访问外网:"
ping -c 2 -I 10.0.0.100 8.8.8.8
echo ""
echo "→ 测试访问专用网段:"
ping -c 2 172.16.1.1
echo ""

echo "8. 持久化配置验证:"
if [ -f "/etc/NetworkManager/dispatcher.d/99-policy-routing" ]; then
    echo "→ 已配置NetworkManager dispatcher持久化方案"
    echo "→ 最近一次执行日志:"
    tail -n 5 /var/log/policy-routing.log
elif grep -q "策略路由配置已通过rc.local加载" /var/log/rc-local-route.log; then
    echo "→ 已配置rc.local持久化方案"
    echo "→ 最近一次执行日志:"
    tail -n 5 /var/log/rc-local-route.log
else
    echo "→ 未检测到持久化配置"
fi

echo ""
echo "====================================="
echo "验证完成,请检查以上输出是否正确"
echo "====================================="
EOF

# 设置执行权限
chmod +x /usr/local/bin/verify-policy-routing

# 运行验证脚本
verify-policy-routing

5.2 关键验证点

  1. 策略规则:必须存在两条源IP规则和一条目标IP规则
  2. 路由表:每个自定义路由表必须包含默认网关和本地网段路由
  3. 路径测试ip route get命令必须显示正确的出口网卡
  4. 连通性测试:指定源IP的ping测试必须全部成功
  5. 持久化验证:重启网络服务和系统后,配置依然存在

5.3 重启验证

bash 复制代码
# 重启系统验证持久化
reboot

# 重启后再次运行验证脚本
verify-policy-routing

六、配置优化与最佳实践

6.1 路由表设计优化

  • ID规划:自定义路由表ID建议从100开始,避免与系统保留表(0-255中的低ID)冲突
  • 命名规范:路由表名称与网卡名称保持一致,便于维护
  • 规则优先级:目标地址规则优先级应高于源地址规则,源地址规则高于默认规则

6.2 脚本优化建议

  • 变量化配置:将IP地址、网卡名称等配置提取为变量,便于修改
  • 错误处理:添加命令执行结果检查,失败时记录错误日志
  • 幂等性设计 :使用ip route replace替代ip route add,避免重复添加错误
  • 日志轮转:配置logrotate管理策略路由日志,防止日志文件过大

6.3 安全加固

  • 禁用ICMP重定向,防止路由表被恶意修改
  • 配置防火墙规则,限制不必要的网络访问
  • 定期检查策略路由配置,防止被意外修改

七、常见故障排查指南

7.1 通用故障排查流程

  1. 检查网络接口状态ip addr show
  2. 检查策略路由规则ip rule show
  3. 检查自定义路由表ip route show table <表名>
  4. 检查主路由表ip route show
  5. 测试路由路径ip route get <目标IP> from <源IP>
  6. 抓包分析tcpdump -i <网卡名> host <目标IP>
  7. 检查反向路径过滤sysctl -a | grep rp_filter

7.2 持久化方案专属问题

rc.local方案常见问题
  • 配置不生效:检查rc.local文件是否有执行权限,systemd是否启用了rc-local服务
  • 网络服务重启后丢失:这是rc.local的固有缺陷,建议改用NetworkManager dispatcher方案
  • 执行时机过早 :适当增加sleep时间,等待网络接口完全就绪
NetworkManager dispatcher方案常见问题
  • 脚本不执行 :检查脚本权限是否为755,脚本是否在/etc/NetworkManager/dispatcher.d/目录下
  • 配置错误 :查看/var/log/policy-routing.log日志文件,定位错误原因
  • 部分接口不触发:确认脚本中没有限制特定接口,NetworkManager管理了所有网卡

7.3 常见问题与解决方案

问题现象 可能原因 解决方案
外部访问192.168.1.100超时 反向路径过滤设置为严格模式 将rp_filter设置为2
策略规则不生效 规则优先级太低 调整pref值,确保优先级高于默认规则
服务器主动访问172.16.0.0/16失败 主路由表没有配置专用网段路由 在主路由表添加172.16.0.0/16路由
出现"RTNETLINK answers: File exists"错误 路由或规则已存在 使用replace替代add命令
部分客户端能访问,部分不能 防火墙配置问题 检查192.168.1.1和10.0.0.1两个网关的防火墙规则

7.4 常用排查命令

bash 复制代码
# 查看所有策略规则
ip rule show

# 查看指定路由表
ip route show table ens18_table
ip route show table ens19_table

# 测试路由路径(最关键的命令)
ip route get 8.8.8.8 from 192.168.1.100
ip route get 8.8.8.8 from 10.0.0.100

# 实时抓包分析
tcpdump -i ens18 host 192.168.1.100 and icmp
tcpdump -i ens19 host 10.0.0.100 and icmp

# 查看内核网络日志
dmesg | grep -i net

# 查看持久化脚本日志
# rc.local方案
tail -f /var/log/rc-local-route.log
# NetworkManager dispatcher方案
tail -f /var/log/policy-routing.log

八、总结与扩展

8.1 本文方案优势

  • 彻底解决非对称路由导致的网络连通性问题
  • 生产级可靠:支持系统重启和网络服务重启
  • 易于维护:配置清晰,命名规范,日志完善
  • 灵活扩展:可轻松添加更多网卡和路由表

8.2 扩展应用场景

  • 多出口负载均衡:结合iptables实现基于源IP的负载均衡
  • VPN分流:将特定流量引导至VPN接口
  • 端口级路由:使用iptables标记数据包,实现基于端口的策略路由
  • IPv6支持 :本文方案同样适用于IPv6,只需将ip命令替换为ip -6

8.3 注意事项

  • 本文方案适用于CentOS/RHEL 7/8/9、Ubuntu 18.04+等使用NetworkManager的系统
  • 对于使用传统network服务的系统,可将dispatcher脚本改为rc.local脚本
  • 配置前请确保您有服务器的物理访问权限或远程控制台访问权限,避免网络中断导致无法连接
  • 建议在测试环境验证通过后再应用到生产环境

通过本文介绍的策略路由配置方案,您可以构建一个稳定、可靠、高性能的双网卡双网关服务器网络环境,为您的业务系统提供坚实的网络基础。


如果本文对您有帮助,欢迎点赞、收藏和关注!如有任何问题,欢迎在评论区留言交流。

相关推荐
最后一个bug3 小时前
ubuntu24.04在docker下迁移gitlab16
linux·运维·docker
yyyyy_abc3 小时前
负载均衡与高可用
运维·负载均衡
遇印记3 小时前
软考知识点(windows系统管理与命令)
运维·服务器·网络·windows·ddos
风曦Kisaki3 小时前
# Linux运维Day03:Nginx 反向代理(服务集群)、负载均衡、四层调度与优化
linux·运维·nginx
木雷坞3 小时前
csdn-enterpriseGitLab Runner docker pull 慢:并行流水线镜像拉取排查
运维·docker·容器·gitlab
十子木3 小时前
git 如何恢复特定版本的内容
linux·git
雪度娃娃3 小时前
Asio异步读写——简单服务器和客户端异步通信
运维·服务器·网络·c++·php
2601_953660373 小时前
File类
linux·开发语言·python
梦仔生信进阶3 小时前
【linux使用技巧】复制粘贴快捷键
linux