Linux 网络命名空间与虚拟路由深度工程实战

深入理解 Linux 网络栈的隔离机制与虚拟路由原理,从 netns、veth、bridge 到 VRF、策略路由,掌握容器网络、多租户网络隔离的底层实现。

一、为什么需要网络隔离?

现代云计算环境中,一个物理服务器上可能运行数百个容器、数十个租户网络。每个租户都需要独立的路由表、独立的防火墙规则、独立的 IP 地址空间——甚至允许地址重叠(如多个租户都可以使用 192.168.1.0/24)。传统的 IPv4 路由机制无法解决这些需求,网络命名空间(network namespace)正是 Linux 内核给出的答案。

网络命名空间最早由 Linux 2.4.19 引入,经过二十多年的演进,已经成为容器技术(Docker、Kubernetes、Podman 等)的网络基础。

二、网络命名空间核心概念

网络命名空间将网络协议栈从全局共享变为每个命名空间独立拥有。这意味着每个 netns 都拥有:

  • 独立的网络设备(网卡)
  • 独立的 IP 地址配置
  • 独立的路由表
  • 独立的 iptables/nftables 规则
  • 独立的 socket 和连接

创建和查看网络命名空间:


# 创建新的网络命名空间
ip netns add ns-blue
ip netns add ns-red

# 查看命名空间列表
ip netns list
# 输出:
# ns-red
# ns-blue id 0 if 1

# 在指定命名空间中执行命令
ip netns exec ns-blue ip addr show

使用 /var/run/netns/ 目录持久化管理命名空间:


# 创建一个持久化的命名空间(通过挂载)
ip netns add ns-persist
# 命名空间在没有任何引用时会自动删除
# 可以在 /var/run/netns/ 下绑定挂载保持引用
mkdir -p /var/run/netns
touch /var/run/netns/ns-persist
mount --bind /proc/$(pidof process)/ns/net /var/run/netns/ns-persist

三、veth Pair:命名空间之间的以太网隧道

veth(Virtual Ethernet)设备总是成对出现,类似于管道——一端进入的数据从另一端出来。它是连接不同网络命名空间的"虚拟网线"。

3.1 基本拓扑构建


# 创建 veth pair
ip link add veth-blue type veth peer name veth-red

# 分别放入不同命名空间
ip link set veth-blue netns ns-blue
ip link set veth-red netns ns-red

# 配置 IP 地址
ip netns exec ns-blue ip addr add 10.1.1.1/24 dev veth-blue
ip netns exec ns-red ip addr add 10.1.1.2/24 dev veth-red

# 启用设备
ip netns exec ns-blue ip link set veth-blue up
ip netns exec ns-blue ip link set lo up
ip netns exec ns-red ip link set veth-red up
ip netns exec ns-red ip link set lo up

# 验证连通性
ip netns exec ns-blue ping -c 2 10.1.1.2

通过 ethtool -S veth-blue 可以看到 veth 设备的统计信息,包括 tx_queue_len(默认 1000)、对端 ifindex 等。

3.2 veth 的数据包路径

veth pair 的数据包从一端进入后,会经过以下路径:

  1. 从发送端网卡的 TX queue 出队
  2. 通过内部 ring buffer 直接复制到接收端的 RX queue
  3. 触发软中断(NET_RX_SOFTIRQ),由 NAPI 调度处理
  4. 注意:veth 两端的命名空间通信不经过物理网卡,数据在内核空间直接复制。但 veth 的性能受限于:

    • 单队列设计(默认只有一个 TX/RX queue)
    • 默认 MTU 1500(可调整)
    • 跨命名空间的上下文切换开销

    3.3 veth 性能优化

    
    # 增加 TX 队列长度
    ip link set veth-blue txqueuelen 10000
    
    # 启用多队列(Linux 4.x+ 支持)
    ip link set eth0 combined 4
    

    在 Kubernetes 的 Flannel host-gw 模式下,每个 pod 通过 veth pair 连接到宿主机网桥,跨节点通信则走主机路由表,overlay 开销极小。

    四、Linux Bridge:软件交换机

    Linux Bridge 是一个二层虚拟交换机,用于将多个网络接口(包括 veth pair 的末端)桥接在一起。

    4.1 基本操作

    
    # 创建网桥
    ip link add br0 type bridge
    ip link set br0 up
    
    # 添加 veth 末端到网桥
    ip link set veth-blue master br0
    ip link set veth-red master br0
    
    # 查看网桥端口
    bridge link show
    
    # 给网桥配置 IP(使其可被外部路由到达)
    ip addr add 10.1.1.254/24 dev br0
    
    # 查看 MAC 地址表
    bridge fdb show br br0
    

    4.2 网桥与 iptables 的交互(hairpin mode)

    传统上,Linux Bridge 工作在纯二层模式,不经过 iptables。但 Docker/Kubernetes 需要 MASQUERADE 等功能,因此引入 net.bridge.bridge-nf-call-iptables:

    
    # 是否让桥接流量经过 iptables
    sysctl net.bridge.bridge-nf-call-iptables=1
    sysctl net.bridge.bridge-nf-call-ip6tables=1
    

    当此参数为 1 时,docker0 网桥上的流量也会经过宿主机的 INPUT/FORWARD/OUTPUT 链,这使得 iptables 规则可以控制东西向流量。但这也带来了复杂的交互问题,在某些场景下建议设为 0,配合 eBPF 方案处理策略。

    4.3 VLAN 隔离

    Linux Bridge 支持 802.1Q VLAN 过滤:

    
    # 启用 VLAN 过滤
    ip link set br0 type bridge vlan_filtering 1
    
    # 将端口分配到 VLAN 10
    bridge vlan add dev veth-blue vid 10 pvid
    bridge vlan add dev veth-red vid 10
    
    # trunk 端口
    bridge vlan add dev eth0 vid 10
    bridge vlan add dev eth0 vid 20
    

    *注意*:传统的 bridge vlan 命令使用独立的 VLAN 数据库,与 vlan 子接口(eth0.10)不同,需要根据场景选择合适的方案。

    五、虚拟路由与转发(VRF)

    VRF(Virtual Routing and Forwarding)是 Linux 3.x+ 引入的三层隔离机制。与网络命名空间不同,VRF 在同一网络命名空间内隔离路由表,不需要复制整个协议栈。

    5.1 创建 VRF

    
    # 创建 VRF 设备
    ip link add vrf-blue type vrf table 100
    ip link add vrf-red type vrf table 200
    
    ip link set vrf-blue up
    ip link set vrf-red up
    
    # 将网卡绑定到 VRF
    ip link set eth1 master vrf-blue
    ip link set eth2 master vrf-red
    
    # 查看 VRF 路由表
    ip route show table 100
    ip route show table 200
    

    5.2 VRF 路由规则

    VRF 创建时自动添加策略路由规则:

    
    # 查看自动生成的规则
    ip rule show
    # 输出:
    # 1000: from all lookup [vrf_table] unreachable
    # 其含义:来自某 VRF 的流量如果不在本 VRF 路由表中找到,则 unreachable
    # 防止"泄漏"到全局表
    

    手动向 VRF 添加路由:

    
    # 设置 VRF 默认路由
    ip route add default via 10.1.1.1 table 100
    ip route add 10.2.0.0/16 via 10.1.1.2 table 100
    
    # 直接命令在 VRF 内设置
    ip netns exec vrf-blue ip route add default via 10.1.1.1
    # 注意:带 "netns" 关键字是 VRF-aware 的
    

    5.3 VRF vs 网络命名空间

    特性 网络命名空间 VRF
    隔离层级 完整协议栈 仅路由表
    性能开销 较高(套接字隔离) 较低(共享协议栈)
    MAC 隔离 独立 不隔离
    L2 隔离 独立 共享 ARP 表
    典型使用场景 容器网络 多租户 L3VPN

    最佳实践:在 MPLS/VPN 场景中常叠加使用:网络命名空间隔离容器,VRF 实现租户路由,两者结合即为 CNI 网桥方案的核心设计。

    六、策略路由(Policy Routing)

    传统的 Linux 路由只根据目的 IP 查表做出转发决策。策略路由(Policy Routing)允许根据源 IP、DSCP 标记、fwmark 等多种属性选择路由表。

    6.1 基本配置

    
    # 规则:源地址为 10.1.1.0/24 的流量查表 100
    ip rule add from 10.1.1.0/24 table 100 priority 1000
    
    # 规则:fwmark 0x1 的流量查表 101
    ip rule add fwmark 0x1 table 101 priority 1100
    
    # 规则:来自 eth2 的流量查表 102
    ip rule add iif eth2 table 102 priority 1200
    
    # 查看规则
    ip rule show
    # 优先级数值越小优先级越高
    

    规则优先级规则:内核按优先级数值从小到大匹配,第一条匹配的规则生效。特殊表 local(254)、main(253)、default(255)的优先级不可被覆盖。

    6.2 结合 iptables MARK 实现复杂策略

    
    # 标记来自特定容器的流量
    iptables -t mangle -A PREROUTING -s 172.18.0.5 -j MARK --set-mark 0x10
    
    # 标记的流量走特定路由表
    ip rule add fwmark 0x10 table 100
    
    # 在路由表 100 中指定 VPN 网关
    ip route add default via 10.8.0.1 dev tun0 table 100
    

    6.3 源地址验证与反向路径过滤

    启用策略路由后,默认的严格反向路径过滤(rp_filter)可能导致丢弃:

    
    # 推荐设置:宽松模式
    sysctl net.ipv4.conf.all.rp_filter=2
    sysctl net.ipv4.conf.default.rp_filter=2
    

    在 VRF 场景中更需注意:不同 VRF 中的流量回包应走相同 VRF,否则连接会挂起。

    七、容器网络底层实战

    理解上述基础组件后,我们来看看真实容器网络是如何组装的。

    7.1 手动搭建 Docker 式网络

    
    # 创建"容器"网络命名空间(以 PID 方式)
    # 1. 创建 veth pair
    ip link add veth0 type veth peer name veth1
    
    # 2. 创建并配置网桥(宿主机侧)
    ip link add cni0 type bridge
    ip link set cni0 up
    ip addr add 10.244.1.1/24 dev cni0
    
    # 3. 将 veth1 加入网桥
    ip link set veth1 master cni0
    ip link set veth1 up
    
    # 4. 将 veth0 放入"容器"命名空间(模拟)
    PID=$(docker inspect -f '{{.State.Pid}}' mycontainer 2>/dev/null)
    if [ -z "$PID" ]; then
        # 模拟:用 shell 进程
        unshare --net=/var/run/netns/container-ip netns
    fi
    
    # 5. 容器内配置
    ip netns exec container-ip ip link set veth0 name eth0
    ip netns exec container-ip ip addr add 10.244.1.2/24 dev eth0
    ip netns exec container-ip ip link set eth0 up
    ip netns exec container-ip ip route add default via 10.244.1.1
    
    # 6. 宿主机 NAT
    iptables -t nat -A POSTROUTING -s 10.244.1.0/24 ! -o cni0 -j MASQUERADE
    iptables -A FORWARD -i cni0 -j ACCEPT
    iptables -A FORWARD -o cni0 -j ACCEPT
    

    7.2 Kubernetes CNI 数据包路径

    以 Calico(纯三层模式,不使用 overlay)为例:

    节点内跨 Pod 通信:

    
    Pod A (eth0) -> vethXXX -> host ns 路由表 -> vethYYY -> Pod B (eth0)
    

    全程在内核态完成,无需 IPIP/VXLAN 封装。

    跨节点 Pod 通信:

    
    Pod A -> host ns 路由 -> 物理网卡 eth0 -> 物理网络 -> 节点B eth0 -> 路由转发 -> Pod B
    

    Calico 使用 BGP 协议分发 Pod CIDR 路由,每个 Pod 路由的下一跳是 Pod 所在节点 IP。

    7.3 Cilium eBPF 方案

    Cilium 绕过 iptables 内核路径,在 Socket 层做负载均衡:

    
    # 查看 Cilium eBPF 程序加载
    bpftool prog show
    
    # 查看 Cilium 路由映射
    cilium bpf lb list
    
    # 查看 endpoint 映射
    cilium endpoint list
    

    Cilium 的核心优化在于:在容器网卡 TC 或 XDP 钩子直接做 DNAT/SNAT,不需要遍历 iptables 链,大幅降低延迟。

    八、实战故障排查案例

    案例 1:容器无法访问外网

    现象:Kubernetes Pod 内无法 ping 通 8.8.8.8,只能 ping 通同节点 Pod。

    排查过程:

    
    # 检查宿主机 IP 转发是否开启
    sysctl net.ipv4.ip_forward
    # 返回 0 -> echo 1 > /proc/sys/net/ipv4/ip_forward
    
    # 检查 iptables 规则是否正确生成
    iptables -t nat -L POSTROUTING -n | grep MASQUERADE
    # 若无输出 -> kube-proxy 或 CNI 插件异常
    
    # 检查路由表
    ip route show table main | grep default
    
    # 检查网桥状态
    ip -d link show cni0
    # 确认 br0 状态为 UP
    

    案例 2:VRF 内流量不通(回包问题)

    现象:VRF 中的 TCP 握手失败,tcpdump 能看到 SYN 但无 SYN-ACK。

    根因:服务端在全局命名空间监听,客户端从 VRF 发起连接,服务端回包时 RP 查询全局路由表,但下一跳接口在 VRF 中。

    修复:确保回包也走相同 VRF。

    
    # 将服务监听接口绑定到 VRF
    ip link set eth0 master vrf-blue
    
    # 或添加策略规则
    ip rule add iif eth0 lookup 100
    

    案例 3:veth pair 丢包严重

    现象:iperf 测试显示吞吐量远低于预期。

    
    # 检查 ring buffer
    ethtool -g veth-blue
    # veth 默认 ring buffer 仅 1000/1000
    
    # 检查是否丢包
    ethtool -S veth-blue | grep drop
    
    # 调优 TX 队列
    ip link set veth-blue txqueuelen 5000
    
    # 检查 NAPI 调度
    cat /proc/net/softnet_stat
    # 第 2 列表示丢包次数
    

    九、生产级部署 Checklist

    在容器平台生产部署中,建议检查以下网络基础配置:

    
    # === 系统参数 ===
    # IP 转发(必需)
    net.ipv4.ip_forward = 1
    net.ipv6.conf.all.forwarding = 1
    
    # 桥接流量是否过 iptables(按需)
    net.bridge.bridge-nf-call-iptables = 1
    
    # 反向路径过滤(策略路由场景设为 2)
    net.ipv4.conf.all.rp_filter = 2
    
    # conntrack 表大小(大规模环境调大)
    net.netfilter.nf_conntrack_max = 524288
    
    # === 资源限制 ===
    # 每个 Pod 的 veth 占用一对网络资源
    # 建议 max_pod_per_node <= 250(IP 充足时)
    
    # === 监控指标 ===
    # 丢包率 - ethtool 统计
    # veth 带宽 - /proc/<pid>/net/dev
    # conntrack 使用率 - /proc/sys/net/netfilter/nf_conntrack_count vs max
    # 网卡 ERROR - ip -s link show
    
    # === 高可用 ===
    # 多网卡 bonding(mode 802.3ad 或 active-backup)
    # 节点多路由 - BGP/ECMP
    

    十、总结与展望

    Linux 网络命名空间与虚拟路由是现代云原生基础设施的基石。从最基础的手动配置到 CNI 插件的自动化管理,核心始终是:

    • netns 提供隔离边界
    • veth pair 打通命名空间通信
    • Bridge 实现二层交换
    • VRF + 策略路由 提供三层多租户隔离

    随着 eBPF 技术的发展,Cilium 等项目正在重新定义容器网络:绕过 conntrack、集成 XDP、在 Socket 层做负载均衡。未来的趋势是"内核级 SDN"——网络策略直接编译为 eBPF 字节码加载到内核,实现接近物理网卡的转发性能。

    理解这些底层机制,有助于我们在生产环境中更高效地排查网络问题,设计更合理的网络架构,以及在 CNI 选型时做出正确决策。


    *参考资料:iproute2 源码、Linux 内核文档 Documentation/networking/vrf.txt、Calico/Cilium 官方文档*

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部