从零构建容器网络:Linux 虚拟化技术实现桥接方案
容器技术的核心隔离机制依赖 Linux Namespace,而网络隔离后的通信问题则需要借助虚拟网络设备解决。本文通过 veth 设备对、网桥、iptables 等底层技术,完整复现容器桥接网络的构建过程,深入理解数据包在容器与宿主机之间的流转路径。
核心组件解析
veth 设备对
veth(Virtual Ethernet)是成对创建的虚拟网卡,具有双向管道特性:向一端写入的数据包会立即出现在另一端。利用这一特性,可将 veth 的一端置于隔离的网络命名空间(模拟容器环境),另一端保留在宿主机,从而打通跨命名空间的通信通道。
Linux Bridge
Linux Bridge 是内核实现的二层虚拟交换机,负责连接多个网络接口并实现同网段内的数据帧转发。与物理交换机类似,它维护 MAC 地址表,根据目标 MAC 将流量定向到正确端口。Docker 的 bridge 网络驱动本质上就是对此机制的封装。
NAT 地址转换
容器使用私有网段地址,与外部网络通信时必须经过地址转换:
- SNAT(源地址转换):容器发出的请求经过宿主机出口时,将源 IP 替换为宿主机 IP,确保响应能正确返回
- DNAT(目的地址转换):外部访问容器服务时,将宿主机 IP 映射到容器内部 IP,实现流量定向
实验环境搭建
实验基于 Ubuntu 20.04,通过 ip netns 创建隔离环境模拟容器。
创建网络命名空间
sudo ip netns add c1
sudo ip netns add c2
构建 veth 设备对
sudo ip link add tap-c1 type veth peer name br-c1
sudo ip link add tap-c2 type veth peer name br-c2
将一端移入命名空间:
sudo ip link set tap-c1 netns c1
sudo ip link set tap-c2 netns c2
配置网桥
sudo ip link add dev br0 type bridge
sudo ip link set br-c1 master br0
sudo ip link set br-c2 master br0
分配地址并激活
网桥地址:
sudo ip addr add 10.0.5.1/24 dev br0
sudo ip link set br0 up
容器内网卡:
sudo ip netns exec c1 ip addr add 10.0.5.10/24 dev tap-c1
sudo ip netns exec c1 ip link set tap-c1 up
sudo ip netns exec c1 ip link set lo up
sudo ip netns exec c2 ip addr add 10.0.5.11/24 dev tap-c2
sudo ip netns exec c2 ip link set tap-c2 up
sudo ip netns exec c2 ip link set lo up
宿主机侧网卡:
sudo ip link set br-c1 up
sudo ip link set br-c2 up
连通性验证
容器间通信
在 c1 中测试到 c2 的可达性:
sudo ip netns exec c1 ping -c 3 10.0.5.11
抓包观察网桥上的 ARP 学习过程:
sudo tcpdump -i br0 -n -e
输出显示 ARP 请求广播后,网桥学习到 MAC 地址映射,后续 ICMP 包直接单播转发,验证了二层交换机制。
宿主机直接访问容器
在 c1 启动监听服务:
sudo ip netns exec c1 python3 -m http.server 8080
宿主机直接通过容器 IP 访问:
curl http://10.0.5.10:8080/
外部网络互通配置
容器访问外网(SNAT)
配置容器默认路由指向网桥:
sudo ip netns exec c1 ip route add default via 10.0.5.1
sudo ip netns exec c2 ip route add default via 10.0.5.1
开启宿主机 IP 转发:
sudo sysctl -w net.ipv4.ip_forward=1
添加 MASQUERADE 规则,自动替换源地址:
sudo iptables -t nat -A POSTROUTING -s 10.0.5.0/24 ! -o br0 -j MASQUERADE
验证外网连通:
sudo ip netns exec c1 ping -c 3 223.5.5.5
外部访问容器(DNAT)
将宿主机 8080 端口映射到 c1 的 80 端口:
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.5.10:80
处理宿主机本机发起的访问(OUTPUT 链):
sudo iptables -t nat -A OUTPUT -p tcp --dport 8080 -j DNAT --to-destination 10.0.5.10:80
数据包完整路径分析
以容器 c1 访问公网为例,数据包经历以下处理:
- 容器内路由决策,默认网关指向 10.0.5.1
- ARP 解析网关 MAC,封装二层帧经 tap-c1 发出
- veth 对端 br-c1 接收,网桥查询 MAC 表后从 br0 转发
- 宿主机内核识别目的地址非本地,触发 IP 转发
- POSTROUTING 钩子执行 SNAT,源地址替换为宿主机公网 IP
- 数据包经物理网卡 eth0 进入公网
响应包返回时路径相反,conntrack 模块自动还原地址,确保回到正确容器。
环境清理
sudo ip netns del c1
sudo ip netns del c2
sudo ip link del br0
sudo iptables -t nat -F
手动构建过程揭示了 Docker 网络的本质:Namespace 提供隔离,veth 打通通道,Bridge 汇聚连接,iptables 实现内外转换。理解这些底层机制,有助于排查容器网络故障、设计自定义网络方案。