当你执行 docker run -p 8080:80 nginx 后,从浏览器访问 http://localhost:8080 就能看到 Nginx 欢迎页。但数据包是怎么从浏览器到达容器内的 Nginx 的?
它穿越了 iptables DNAT 规则(8080→172.17.0.2:80),经过 docker0 bridge,通过 veth pair 进入容器的 Network Namespace,最终到达 Nginx 进程的 socket。这条链路涉及 Network Namespace、veth pair、bridge、iptables 四个核心组件。
本章将深入容器网络的每一个环节,理解数据包从宿主机到容器的完整路径。
容器网络的演进是一部从”简单连通”到”安全策略”的历史。Docker 早期用简单的 bridge + NAT 方案——所有容器挂在 docker0 网桥上,通过 iptables MASQUERADE 访问外网,通过 DNAT 暴露端口。这个方案简单粗暴,但只解决了单机通信问题。多主机容器通信需要覆盖网络(Overlay Network),CoreOS 于 2016 年提出 CNI(Container Network Interface)标准,将网络配置从运行时中解耦——flannel、Calico、Cilium 等 CNI 插件各自实现不同的网络方案。2019 年,基于 eBPF 的 Cilium 异军突起,它用 eBPF 程序在内核态实现网络策略和可观测性,性能远超 iptables。容器网络从”让容器能 ping 通”进化到”让容器安全、可观测、高性能地通信”,每一步演进都由实际需求驱动。
前置知识
- Ch02 Linux Namespace 深入:Network Namespace 是容器网络的基础——每个容器拥有独立的网络栈
- Linux 网络基础:veth pair、bridge、iptables/NAT、路由表
- 网络协议基础:TCP/IP、DNS、ARP
如果你需要补课 Linux 网络基础,推荐阅读姊妹系列「从零剖析 Linux 操作系统」中的网络相关章节。
一、容器网络基础
1.1 Network Namespace 回顾
每个容器有独立的 Network Namespace,拥有自己的网络栈:
# 创建 Network Namespacesudo ip netns add ns1
# 查看 Namespace 中的网络设备sudo ip netns exec ns1 ip link# 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT# 只有 loopback,且处于 DOWN 状态
# 容器的 Network Namespace 也是如此PID=$(docker inspect -f '{{.State.Pid}}' mynginx)sudo nsenter -t $PID -n ip link# 1: lo: <LOOPBACK,UP,LOWER_UP># 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>1.2 容器网络的核心问题
Network Namespace 隔离了网络栈,但也带来了核心问题:如何让隔离的 Namespace 与外部通信?
二、veth pair
2.1 veth pair 原理
veth pair 是一对虚拟网卡,像一根”虚拟网线”——从一端发送的数据包会从另一端接收:
# 创建 veth pairsudo ip link add veth-host type veth peer name veth-container
# 将一端放入容器的 Network Namespacesudo ip link set veth-container netns ns1
# 在容器内配置 IPsudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth-containersudo ip netns exec ns1 ip link set veth-container upsudo ip netns exec ns1 ip link set lo up
# 在宿主机配置 IPsudo ip addr add 10.0.0.1/24 dev veth-hostsudo ip link set veth-host up
# 测试连通性sudo ip netns exec ns1 ping -c 3 10.0.0.12.2 veth pair 的特性
| 特性 | 说明 |
|---|---|
| 成对出现 | 创建时必须指定两端 |
| 跨 Namespace | 一端在宿主,一端在容器 |
| MAC 地址 | 每端有独立的 MAC 地址 |
| MTU | 默认 1500 字节 |
| 性能 | 数据包在内核空间传递,延迟极低 |
2.3 veth pair 在容器中的使用
| CNI 插件 | 网络模式 | 适用场景 | 特点 |
|---|---|---|---|
| bridge | 桥接 | 单机测试 | 简单,veth+bridge,不支持跨节点 |
| flannel | Overlay/VXLAN | Kubernetes 集群 | 简单易用,etcd 管理 |
| calico | BGP/路由 | 生产环境 | 高性能,NetworkPolicy 支持 |
| cilium | eBPF | 高性能场景 | 内核级转发,可观测性强 |
# 查看 Docker 容器的 veth pairdocker exec mynginx ip link show eth0# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500# link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
# 在宿主机上找到对应的另一端ip link | grep -A1 "veth"# 5: veth123@if2: <BROADCAST,MULTICAST,UP,LOWER_UP># link/ether 3a:2b:1c:4d:5e:6f brd ff:ff:ff:ff:ff:ff
# 注意:veth123 的 @if2 表示它的对端是 interface index 2# 容器内 eth0 的 index 正是 2三、Bridge
3.1 Bridge 原理
Bridge(网桥)是二层数据链路层设备,类似于物理交换机——它连接多个网络接口,根据 MAC 地址转发数据帧:
# 创建 bridgesudo ip link add br0 type bridgesudo ip link set br0 up
# 将 veth pair 的宿主端连接到 bridgesudo ip link set veth-host master br0
# 配置 bridge 的 IP 地址(作为容器的网关)sudo ip addr add 172.17.0.1/16 dev br03.2 Docker 的 bridge 网络模型
3.3 Bridge 的 MAC 学习
Bridge 维护一张 MAC 地址转发表,记录每个端口对应的 MAC 地址:
# 查看 bridge 的 MAC 转发表bridge fdb show br docker0
# 输出示例:# 02:42:ac:11:00:02 dev veth123 master docker0# 02:42:ac:11:00:03 dev veth456 master docker0# 33:33:00:00:00:01 dev docker0 self permanent四、iptables/NAT
4.1 容器的出站 NAT
容器访问外网时,数据包的源地址需要从容器 IP(172.17.0.2)转换为宿主机 IP(192.168.1.100):
# Docker 创建的 NAT 规则sudo iptables -t nat -L POSTROUTING -n -v
# Chain POSTROUTING (policy ACCEPT)# MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0# ↑ 容器网段的所有出站流量做 MASQUERADE(动态 SNAT)4.2 容器的入站 DNAT
外部访问容器的端口时,数据包需要做 DNAT(目标地址转换):
# docker run -p 8080:80 nginx 创建的 DNAT 规则sudo iptables -t nat -L DOCKER -n -v
# Chain DOCKER (2 references)# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80# ↑ 访问宿主机 8080 端口的流量转发到容器 80 端口4.3 完整的数据包路径
五、Docker 网络模式
5.1 四种网络模式
| 模式 | Namespace 策略 | 特点 | 性能 |
|---|---|---|---|
| bridge | 独立 NS + veth + bridge | 默认模式,有 NAT | 中等 |
| host | 共享宿主 NS | 无隔离,性能最好 | 最好 |
| none | 独立 NS,仅 loopback | 无网络 | — |
| container | 共享另一容器 NS | Pod 内共享 | 中等 |
# bridge 模式(默认)docker run -d nginx
# host 模式docker run --network=host -d nginx
# none 模式docker run --network=none -d nginx
# container 模式(共享另一个容器的网络栈)docker run --network=container:nginx_id -d myapp5.2 自定义 bridge 网络
# 创建自定义 bridge 网络docker network create --driver bridge --subnet 10.0.0.0/24 mynet
# 在自定义网络中运行容器docker run --network=mynet -d --name app1 nginxdocker run --network=mynet -d --name app2 redis
# 容器间可以通过名称通信(Docker DNS)docker exec app1 ping -c 3 app2# PING app2 (10.0.0.3): 56 data bytes六、CNI:容器网络接口
6.1 CNI 原理
CNI(Container Network Interface)是 Kubernetes 的网络插件标准,定义了容器网络配置的通用接口:
{ "cniVersion": "0.4.0", "name": "bridge-network", "type": "bridge", "bridge": "cni0", "ipam": { "type": "host-local", "subnet": "10.244.0.0/24", "routes": [{"dst": "0.0.0.0/0", "gw": "10.244.0.1"}] }, "dns": {"nameservers": ["8.8.8.8"]}}6.2 CNI 插件的工作流程
6.3 常见 CNI 插件
| CNI 插件 | 类型 | 特点 |
|---|---|---|
| Bridge | L2 bridge | 最简单的 CNI 插件 |
| Flannel | Overlay (VXLAN) | 简单易用,跨主机通信 |
| Calico | BGP + eBPF | 高性能,网络策略 |
| Cilium | eBPF | 高性能,可观测性 |
| Weave | Overlay (加密) | 简单,加密通信 |
6.4 Pod 网络模型
Kubernetes 的 Pod 网络模型:同一 Pod 内的所有容器共享 Network Namespace:
# Pod 内的 pause 容器创建 Network Namespace# 其他容器加入 pause 的 Network Namespace
# 效果:# 1. 所有容器共享同一个 IP 地址# 2. 所有容器共享同一个端口空间# 3. 容器间可以通过 localhost 通信# 4. 容器间可以通过 IPC 通信七、动手实践
7.1 手动创建容器网络
#!/bin/bash# 手动创建容器网络(模拟 Docker bridge 模式)
# 1. 创建 Network Namespacesudo ip netns add container1
# 2. 创建 veth pairsudo ip link add veth-host1 type veth peer name veth-container1
# 3. 将一端放入容器 NSsudo ip link set veth-container1 netns container1
# 4. 创建 bridgesudo ip link add br-container type bridgesudo ip link set br-container upsudo ip addr add 10.0.0.1/24 dev br-container
# 5. 连接 veth 到 bridgesudo ip link set veth-host1 master br-containersudo ip link set veth-host1 up
# 6. 配置容器内的网络sudo ip netns exec container1 ip addr add 10.0.0.2/24 dev veth-container1sudo ip netns exec container1 ip link set veth-container1 upsudo ip netns exec container1 ip link set lo upsudo ip netns exec container1 ip route add default via 10.0.0.1
# 7. 配置 NATsudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADEsudo sysctl -w net.ipv4.ip_forward=1
# 8. 测试连通性sudo ip netns exec container1 ping -c 3 8.8.8.8
# 9. 清理sudo ip netns delete container1sudo ip link delete br-containersudo iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -j MASQUERADE7.2 网络诊断脚本
#!/bin/bash# 容器网络诊断脚本
CONTAINER=$1PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER 2>/dev/null)
if [ -z "$PID" ]; then echo "Container not found" exit 1fi
echo "=== 容器网络配置 ==="echo "Container: $CONTAINER (PID: $PID)"echo ""
echo "1. 网络接口"sudo nsenter -t $PID -n ip addr
echo ""echo "2. 路由表"sudo nsenter -t $PID -n ip route
echo ""echo "3. DNS 配置"sudo nsenter -t $PID -n cat /etc/resolv.conf
echo ""echo "4. 连通性测试"sudo nsenter -t $PID -n ping -c 2 -W 2 8.8.8.8 2>&1 | tail -3
echo ""echo "5. iptables NAT 规则"sudo iptables -t nat -L DOCKER -n -v 2>/dev/null | grep "$CONTAINER"
echo ""echo "6. veth pair 对应"sudo nsenter -t $PID -n ip link show eth0 2>/dev/null | grep "link/ether"八、容器网络性能优化
8.1 网络性能瓶颈
容器网络的性能瓶颈主要在三个地方:
| 瓶颈 | 原因 | 优化方案 |
|---|---|---|
| veth pair 开销 | 每个包经过两次内核协议栈 | 使用 host 网络或 SR-IOV |
| bridge 转发 | 软件二层转发 | 使用 OVS 硬件卸载 |
| iptables NAT | 规则匹配开销 | 使用 IPVS 或 eBPF 替代 |
8.2 网络模式性能对比
# 性能基准测试# 1. bridge 模式(默认)docker run --network=bridge --rm alpine iperf3 -c 192.168.1.1# 吞吐量: ~8 Gbps
# 2. host 模式docker run --network=host --rm alpine iperf3 -c 192.168.1.1# 吞吐量: ~10 Gbps (接近原生)
# 3. 性能差距来源# bridge 模式: veth pair + bridge + iptables NAT# host 模式: 直接使用宿主网络栈8.3 Cilium eBPF 网络优化
Cilium 使用 eBPF 程序替代 iptables,显著提升网络性能:
# Cilium 的 eBPF 优化点# 1. 用 eBPF 替代 iptables 做服务负载均衡# 2. 用 eBPF 做 Network Policy 执行# 3. 用 eBPF 做连接跟踪# 4. 绕过部分内核协议栈路径
# 安装 Ciliumhelm install cilium cilium/cilium \ --namespace kube-system \ --set kubeProxyReplacement=strict附、实践:手工搭建容器网络
本节用
ip命令手工搭建一个最小容器网络,理解 veth pair、Network Namespace、IP 配置的完整链路。所有命令需要 root 权限。
附.1 创建 Network Namespace
ip netns add ns1ip netns add ns2ip netns listns2ns1Network Namespace 是容器网络的隔离基础——每个容器拥有独立的网络栈(接口、路由表、iptables 规则)。
附.2 创建 veth pair
veth pair 是一对虚拟网卡,像一根网线的两端——从一端发送的数据包会从另一端收到:
ip link add veth1 type veth peer name veth2ip link show veth127: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 12:30:f5:ac:41:b2 brd ff:ff:ff:ff:ff:ff附.3 将 veth2 移入 ns1
ip link set veth2 netns ns1
# 确认 veth2 已在 ns1 中ip netns exec ns1 ip link show1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:0026: veth2@if27: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 2e:a3:f7:94:33:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0veth2 的 link-netnsid 0 表示它连接到对端(veth1)所在的 Network Namespace。
附.4 配置 IP 地址并启用接口
# 宿主端ip addr add 10.0.0.1/24 dev veth1ip link set veth1 up
# ns1 端ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth2ip netns exec ns1 ip link set veth2 upip netns exec ns1 ip link set lo up附.5 测试连通性
ping -c 2 10.0.0.2--- 10.0.0.2 ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1001msrtt min/avg/max/mdev = 0.064/0.633/1.203/0.569 ms宿主机通过 veth1 → veth2 与 ns1 中的网络栈通信——这就是容器网络的最小模型。Docker 的 bridge 网络模式在此基础上增加了 bridge(连接多个容器)和 iptables NAT(访问外网)。
注意:实验结束后清理:
ip link del veth1; ip netns del ns1; ip netns del ns2
九、本章小结
上一章建立了沙箱运行时 gVisor/Kata的认知框架。
| 组件 | 功能 | 所在层 |
|---|---|---|
| Network Namespace | 网络栈隔离 | 内核 |
| veth pair | 跨 Namespace 通信 | 数据链路层 |
| bridge | 二层转发 | 数据链路层 |
| iptables/NAT | 地址转换 | 网络层 |
| CNI | 网络配置接口 | 插件层 |
容器网络问题排查时,先确认数据包走到了哪一步:tcpdump 抓宿主机接口确认 DNAT 是否命中,nsenter -n 进入容器 NS 确认 veth 对端是否收到,iptables -t nat -L -n -v 查看规则匹配计数。90% 的容器网络问题都是 iptables 规则缺失或 veth 未正确连接 bridge。
容器网络的核心是 Network Namespace + veth pair + bridge + iptables 的组合。CNI 插件将这些组件的配置自动化,让你不需要手动创建 veth pair 和配置路由。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






