引言
一台物理机要跑几百个「看似独立」的网络环境,靠的是网络虚拟化:network namespace 隔离各自的协议栈,veth pair 充当虚拟网线,Linux bridge / OVS 当虚拟交换机,iptables/nftables 负责转发与 NAT。Docker、Kubernetes 的容器网络正是这几样东西的组合。本文从最小单元讲起,用 ip netns/ip link/bridge 手动搭一个「两个命名空间互通」的实验,再拆解 Docker 网络模式与 VRF 多租户,最后给出排错命令集。
前置:/linux-network-commands/(网络命令与排错基础)。容器隔离原理见 /linux-containers-isolation/,虚拟化整体视图见 /linux-virtualization-guide/。
目录
- 1. network namespace:隔离的协议栈
- 2. veth pair:一根虚拟网线
- 3. Linux bridge:虚拟交换机
- 4. OVS:可编程虚拟交换机
- 5. iptables/nftables 与转发路径
- 6. Docker 网络模式:bridge、host、overlay
- 7. VRF 与多租户网络
- 8. 实战:手动搭建 netns 互通
- 9. 排错:ip、netns、bridge、tcpdump
- 10. 速查表
- 延伸阅读
1. network namespace:隔离的协议栈
network namespace 让一组进程拥有「自己的」网卡、路由表、iptables 规则——互不可见,如同各自插在同一台物理机上的独立主机:
# 创建/删除/查看命名空间
ip netns add red
ip netns add blue
ip netns list
# 在命名空间里执行命令
ip netns exec red ip addr
被隔离的网络资源:
| 资源 | 是否隔离 |
|---|---|
| 网卡(lo、eth0…) | 是(各自独立) |
| 路由表 | 是 |
| iptables/nftables 规则 | 是(较新内核) |
| 邻居表(ARP) | 是 |
| 端口号空间 | 是(各自有自己的端口) |
| /proc/net 视图 | 是 |
# 未特权创建自己的命名空间
unshare --net bash # 新 bash 在隔离的网络栈里
# 命名空间里的进程
ip netns exec red sleep 1000 &
心智:namespace 是「容器网络的最小单位」——Docker 每个容器默认一个独立 netns。命名空间之间天生不通,要互通必须搭「桥」——这正是 veth + bridge 的用途。
2. veth pair:一根虚拟网线
veth 永远是成对出现的虚拟网线:一端发数据,另一端立刻收到——把两个 netns「物理」拉通:
# 创建一对 veth(veth0 ⇄ veth1)
ip link add veth0 type veth peer name veth1
# 把一端放进 red 命名空间
ip link set veth1 netns red
# 两端分别配 IP 并启用
ip addr add 10.0.0.1/24 dev veth0 && ip link set veth0 up
ip netns exec red ip addr add 10.0.0.2/24 dev veth1
ip netns exec red ip link set veth1 up
ip netns exec red ping 10.0.0.1
red netns 宿主 blue netns
veth1 ⇄======= veth0 ⇄======= veth2
记忆:veth 是「线」,bridge 是「交换机」,组合起来就是一套可扩展的虚拟局域网。veth 本身只解决「两个点直连」,三五个命名空间就得靠 bridge 挂多根线。
3. Linux bridge:虚拟交换机
Linux bridge 是内核里的二层交换机:多个 veth 一端挂进来,数据按 MAC 地址转发:
# 建桥 + 挂接
ip link add br0 type bridge
ip link set veth0 master br0
ip link set veth2 master br0
ip link set br0 up
# 查看桥与端口
bridge link show
# 查看 MAC 地址表(谁在哪个口)
bridge fdb show
| 能力 | 说明 |
|---|---|
| MAC 学习 | 自动记录 MAC 从哪个口来 |
| 泛洪 | 未知单播/广播向所有端口转发 |
| STP | 可选生成树,防环路 |
| VLAN | 内核桥支持 802.1Q 过滤 |
# 老工具 brctl 的现代替代就是 ip/bridge 命令
# 桥本身也可配 IP,作为网关与外部通信
ip addr add 10.0.0.254/24 dev br0
心法:Docker 默认桥接网络 = veth + bridge + iptables NAT 的组合。排查容器互访问题时,
bridge fdb show能直接告诉你「这个 MAC 到底从哪个口进来」。
4. OVS:可编程虚拟交换机
Linux bridge 是「硬件交换机的软件版」,OVS 则是「可编程 SDN 交换机」——支持 OpenFlow,规则可由控制器下发:
# 安装并启动(Debian 系)
sudo apt install openvswitch-switch
sudo systemctl enable --now openvswitch-switch
# 建桥与端口
ovs-vsctl add-br ovs0
ovs-vsctl add-port ovs0 veth0
ovs-vsctl show
# 下发一条流规则(OpenFlow)
ovs-ofctl add-flow ovs0 'priority=10,in_port=1,actions=output:2'
| 对比 | Linux bridge | OVS |
|---|---|---|
| 数据面 | 内核 MAC 学习 | 内核+用户态流表 |
| 控制面 | 简单二层 | OpenFlow/控制器可编程 |
| 适用 | 容器互通、小规模 | 虚拟化集群、SDN、OpenStack |
记忆:需要「下发规则控制走向」时选 OVS,只要「二层互通」Linux bridge 就够。生产上 OVS 常见于 OpenStack/K8s 的底层网络插件,复杂度换来的是集中管控能力。
5. iptables/nftables 与转发路径
虚拟网络通了二层,三层转发与 NAT 还要靠 netfilter 钩子——iptables/nftables 在其中扮演网关角色:
# 开启 IP 转发(容器/虚拟机网关的前提)
sysctl -w net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
# 桥接流量的 NAT(容器访问外网)
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 ! -o br0 -j MASQUERADE
netfilter 的五个钩子贯穿收发路径:PREROUTING → INPUT / FORWARD → POSTROUTING / OUTPUT。nftables 是新一代替代:
# nftables 等价规则
nft add table ip nat
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 \
oifname != "br0" masquerade
铁律:转发流量走 FORWARD 链,不是 INPUT 链。容器/虚拟机互访不通时先
iptables -L FORWARD看默认策略,-P FORWARD DROP的策略会把所有转发流量静默丢弃。
6. Docker 网络模式:bridge、host、overlay
Docker 把上面所有组件封装成几种开箱即用的网络模式:
| 模式 | 原理 | 场景 |
|---|---|---|
| bridge(默认) | veth + 自定义桥 + NAT | 单机容器互通、容器出外网 |
| host | 直接用宿主网络栈 | 性能敏感、端口直达 |
| none | 无网络 | 离线任务/手动配置 |
| overlay | VXLAN 跨主机隧道 | Swarm/K8s 跨节点 |
| macvlan | 容器挂到物理网段 | 容器直接拥有物理 IP |
# 自定义桥接网络
docker network create -d bridge --subnet=172.20.0.0/24 mynet
docker run -d --network mynet nginx
# 查看网络细节
docker network inspect mynet
# host 模式
docker run --rm --network host alpine ip addr
心法:单机隔离用 bridge、跨主机要互通用 overlay、追求性能且不怕端口占用用 host。K8s 的 Pod 网络则是 CNI 插件(calico/flannel)在底层做同样的 namespace+veth+bridge 组合,只是更自动化。
7. VRF 与多租户网络
VRF(虚拟路由转发)在单台设备上维护多张独立路由表——让一台 Linux 同时充当多个「互不干扰的路由器」:
# 启用 VRF 支持后创建
ip link add vrf-red type vrf table 100
ip link set vrf-red up
ip link set eth1 master vrf-red
# 策略路由:按源/目的选表
ip rule add from 10.10.0.0/16 lookup 100
ip route show table 100
租户 A 的流量 → table 100(只含租户 A 的路由)
租户 B 的流量 → table 200(只含租户 B 的路由)
同一物理接口、同一设备,两套完全隔离的转发视图
记忆:VRF = 「一台设备拆成多台路由器」,比「每个租户一台虚拟机」省得多。排查多租户问题先确认
ip rule的选表策略,再看对应表里有没有路由。
8. 实战:手动搭建 netns 互通
用一小时搭一个小实验:两个命名空间经 Linux 桥互通,并验证与外网的路径:
# 1. 两个命名空间
ip netns add red && ip netns add blue
# 2. 一对 veth,一端进 red
ip link add veth-r type veth peer name veth-r-br
ip link set veth-r netns red
# 3. 另一对 veth,一端进 blue
ip link add veth-b type veth peer name veth-b-br
ip link set veth-b netns blue
# 4. 建桥并挂接两个「桥端」
ip link add br0 type bridge
ip link set veth-r-br master br0
ip link set veth-b-br master br0
ip link set veth-r-br up && ip link set veth-b-br up && ip link set br0 up
# 5. 两端配 IP 并启用
ip netns exec red ip addr add 10.0.0.1/24 dev veth-r
ip netns exec red ip link set veth-r up
ip netns exec blue ip addr add 10.0.0.2/24 dev veth-b
ip netns exec blue ip link set veth-b up
# 6. 验证互通
ip netns exec red ping 10.0.0.2
# 7. 给桥配 IP,验证网关语义
ip addr add 10.0.0.254/24 dev br0
ip netns exec red ping 10.0.0.254
心法:这套手工流程就是 Docker 桥接网络的「裸版本」。亲手敲一遍,以后再遇到容器网络问题,心里就有完整的二层/三层画面。
9. 排错:ip、netns、bridge、tcpdump
虚拟网络排错有一套固定顺序:先看拓扑、再看状态、最后抓包:
ip netns list # 命名空间都在吗
ip addr # 宿主网卡/桥的地址与 up 状态
ip netns exec red ip addr # 容器侧网卡有没有 up、IP 配没配
bridge link show # 端口挂对没有
bridge fdb show # MAC 从哪个口进来的
ip route # 路由表有没有默认路由
# 抓包定位「包到底到没到」
ip netns exec red tcpdump -n -i veth-r icmp
# 同时看宿主桥侧
tcpdump -n -i br0 icmp
# 常见坑位
# 网卡 DOWN(忘了 up)→ 报文丢在本地
# FORWARD DROP → 跨桥转发被策略挡
# 没开 ip_forward → 三层转发直接失败
铁律:排错从「包在哪一层消失」入手:二层看
bridge fdb、三层看ip route+ip_forward、策略看iptables -L FORWARD、最后才是tcpdump抓包确认。
10. 速查表
| 需求 | 命令 |
|---|---|
| 建命名空间 | ip netns add <name> |
| 在命名空间执行 | ip netns exec <name> <cmd> |
| 建 veth 对 | ip link add a type veth peer name b |
| 把设备放进 ns | ip link set <dev> netns <name> |
| 建桥 | ip link add br0 type bridge |
| 挂端口进桥 | ip link set <dev> master br0 |
| 看桥端口 | bridge link show |
| 看 MAC 表 | bridge fdb show |
| 开启转发 | sysctl -w net.ipv4.ip_forward=1 |
| NAT 出网 | iptables -t nat -A POSTROUTING ... -j MASQUERADE |
| 建 OVS 桥 | ovs-vsctl add-br ovs0 |
| 建 VRF | ip link add vrf-red type vrf table 100 |
| 抓包 | tcpdump -n -i <if> icmp |
一句话记忆:namespace 隔离协议栈、veth 拉虚拟网线、bridge/OVS 当虚拟交换机、iptables/nftables 管转发 NAT;Docker 桥接 = 这套组合的封装,排错按「二层 fdb → 三层路由 → 策略链 → 抓包」逐层推进。
延伸阅读
- /linux-network-commands/ — 网络命令与基础排错
- /linux-containers-isolation/ — 容器隔离技术全景
- /linux-virtualization-guide/ — 虚拟化方案对比
- /linux-security-hardening/ — 网络隔离与安全加固
- /linux-kernel-tuning/ — 网络栈内核参数调优
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。