mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1820 字
5 分钟
容器网络
2026-05-30

当你执行 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
Note

如果你需要补课 Linux 网络基础,推荐阅读姊妹系列「从零剖析 Linux 操作系统」中的网络相关章节。

一、容器网络基础#

1.1 Network Namespace 回顾#

每个容器有独立的 Network Namespace,拥有自己的网络栈:

# 创建 Network Namespace
sudo 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 与外部通信?

graph LR subgraph 容器NS["容器 Network Namespace"] ETH0["eth0<br/>172.17.0.2"] end subgraph 宿主机["宿主机"] BRIDGE["docker0 bridge<br/>172.17.0.1"] VETH_HOST["veth123"] ETH_HOST["eth0<br/>192.168.1.100"] end subgraph 外部["外部网络"] CLIENT["客户端<br/>192.168.1.200"] end ETH0 <-->|"veth pair"| VETH_HOST VETH_HOST <--> BRIDGE BRIDGE <--> ETH_HOST ETH_HOST <--> CLIENT style 容器NS fill:#e0f2f1,stroke:#00695c style 宿主机 fill:#e8eaf6,stroke:#283593 style 外部 fill:#fff3e0,stroke:#e65100

二、veth pair#

2.1 veth pair 原理#

veth pair 是一对虚拟网卡,像一根”虚拟网线”——从一端发送的数据包会从另一端接收:

# 创建 veth pair
sudo ip link add veth-host type veth peer name veth-container
# 将一端放入容器的 Network Namespace
sudo ip link set veth-container netns ns1
# 在容器内配置 IP
sudo ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth-container
sudo ip netns exec ns1 ip link set veth-container up
sudo ip netns exec ns1 ip link set lo up
# 在宿主机配置 IP
sudo ip addr add 10.0.0.1/24 dev veth-host
sudo ip link set veth-host up
# 测试连通性
sudo ip netns exec ns1 ping -c 3 10.0.0.1

2.2 veth pair 的特性#

特性说明
成对出现创建时必须指定两端
跨 Namespace一端在宿主,一端在容器
MAC 地址每端有独立的 MAC 地址
MTU默认 1500 字节
性能数据包在内核空间传递,延迟极低

2.3 veth pair 在容器中的使用#

CNI 插件网络模式适用场景特点
bridge桥接单机测试简单,veth+bridge,不支持跨节点
flannelOverlay/VXLANKubernetes 集群简单易用,etcd 管理
calicoBGP/路由生产环境高性能,NetworkPolicy 支持
ciliumeBPF高性能场景内核级转发,可观测性强
# 查看 Docker 容器的 veth pair
docker 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 地址转发数据帧:

# 创建 bridge
sudo ip link add br0 type bridge
sudo ip link set br0 up
# 将 veth pair 的宿主端连接到 bridge
sudo ip link set veth-host master br0
# 配置 bridge 的 IP 地址(作为容器的网关)
sudo ip addr add 172.17.0.1/16 dev br0

3.2 Docker 的 bridge 网络模型#

graph TB subgraph 宿主机["宿主机"] DOCKER0["docker0 bridge<br/>172.17.0.1/16"] VETH1["veth-容器A"] VETH2["veth-容器B"] ETH0["eth0<br/>192.168.1.100"] IPTABLES["iptables NAT"] end subgraph 容器A["容器 A (Network NS)"] CA_ETH0["eth0<br/>172.17.0.2"] CA_APP["nginx"] end subgraph 容器B["容器 B (Network NS)"] CB_ETH0["eth0<br/>172.17.0.3"] CB_APP["redis"] end CA_ETH0 <-->|"veth pair"| VETH1 CB_ETH0 <-->|"veth pair"| VETH2 VETH1 <--> DOCKER0 VETH2 <--> DOCKER0 DOCKER0 <--> IPTABLES IPTABLES <--> ETH0 style 宿主机 fill:#e8eaf6,stroke:#283593 style 容器A fill:#e0f2f1,stroke:#00695c style 容器B fill:#fff3e0,stroke:#e65100

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 完整的数据包路径#

sequenceDiagram participant CLIENT as 客户端 participant ETH0 as 宿主机 eth0 participant IPT as iptables NAT participant BRIDGE as docker0 bridge participant VETH as veth pair participant CONTAINER as 容器 eth0 Note over CLIENT,CONTAINER: 入站:客户端 → 容器 CLIENT->>ETH0: SYN → 192.168.1.100:8080 ETH0->>IPT: DNAT: 192.168.1.100:8080 → 172.17.0.2:80 IPT->>BRIDGE: 转发到 172.17.0.2 BRIDGE->>VETH: MAC 查找 → veth123 VETH->>CONTAINER: SYN → 172.17.0.2:80 Note over CLIENT,CONTAINER: 出站:容器 → 客户端 CONTAINER->>VETH: SYN-ACK ← 172.17.0.2:80 VETH->>BRIDGE: 转发到网关 BRIDGE->>IPT: SNAT: 172.17.0.2 → 192.168.1.100 IPT->>ETH0: SYN-ACK ← 192.168.1.100:8080 ETH0->>CLIENT: SYN-ACK ← 192.168.1.100:8080

五、Docker 网络模式#

5.1 四种网络模式#

模式Namespace 策略特点性能
bridge独立 NS + veth + bridge默认模式,有 NAT中等
host共享宿主 NS无隔离,性能最好最好
none独立 NS,仅 loopback无网络
container共享另一容器 NSPod 内共享中等
# 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 myapp

5.2 自定义 bridge 网络#

# 创建自定义 bridge 网络
docker network create --driver bridge --subnet 10.0.0.0/24 mynet
# 在自定义网络中运行容器
docker run --network=mynet -d --name app1 nginx
docker 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 插件的工作流程#

sequenceDiagram participant KUBELET as Kubelet participant CRI as CRI Runtime participant CNI as CNI Plugin KUBELET->>CRI: 创建 Pod (pause 容器) CRI->>CNI: ADD 网络配置 Note over CNI: 1. 创建 veth pair Note over CNI: 2. 连接到 bridge Note over CNI: 3. 分配 IP 地址 Note over CNI: 4. 配置路由 Note over CNI: 5. 设置 iptables 规则 CNI-->>CRI: 返回 IP 地址和路由 KUBELET->>CRI: 启动应用容器 Note over CRI: 应用容器共享 pause 容器的 Network NS

6.3 常见 CNI 插件#

CNI 插件类型特点
BridgeL2 bridge最简单的 CNI 插件
FlannelOverlay (VXLAN)简单易用,跨主机通信
CalicoBGP + eBPF高性能,网络策略
CiliumeBPF高性能,可观测性
WeaveOverlay (加密)简单,加密通信

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 Namespace
sudo ip netns add container1
# 2. 创建 veth pair
sudo ip link add veth-host1 type veth peer name veth-container1
# 3. 将一端放入容器 NS
sudo ip link set veth-container1 netns container1
# 4. 创建 bridge
sudo ip link add br-container type bridge
sudo ip link set br-container up
sudo ip addr add 10.0.0.1/24 dev br-container
# 5. 连接 veth 到 bridge
sudo ip link set veth-host1 master br-container
sudo ip link set veth-host1 up
# 6. 配置容器内的网络
sudo ip netns exec container1 ip addr add 10.0.0.2/24 dev veth-container1
sudo ip netns exec container1 ip link set veth-container1 up
sudo ip netns exec container1 ip link set lo up
sudo ip netns exec container1 ip route add default via 10.0.0.1
# 7. 配置 NAT
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE
sudo 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 container1
sudo ip link delete br-container
sudo iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -j MASQUERADE

7.2 网络诊断脚本#

#!/bin/bash
# 容器网络诊断脚本
CONTAINER=$1
PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER 2>/dev/null)
if [ -z "$PID" ]; then
echo "Container not found"
exit 1
fi
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. 绕过部分内核协议栈路径
# 安装 Cilium
helm install cilium cilium/cilium \
--namespace kube-system \
--set kubeProxyReplacement=strict

附、实践:手工搭建容器网络#

本节用 ip 命令手工搭建一个最小容器网络,理解 veth pair、Network Namespace、IP 配置的完整链路。所有命令需要 root 权限。

附.1 创建 Network Namespace#

ip netns add ns1
ip netns add ns2
ip netns list
ns2
ns1

Network Namespace 是容器网络的隔离基础——每个容器拥有独立的网络栈(接口、路由表、iptables 规则)。

附.2 创建 veth pair#

veth pair 是一对虚拟网卡,像一根网线的两端——从一端发送的数据包会从另一端收到:

ip link add veth1 type veth peer name veth2
ip link show veth1
27: 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 show
1: 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:00
26: 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 0

veth2 的 link-netnsid 0 表示它连接到对端(veth1)所在的 Network Namespace。

附.4 配置 IP 地址并启用接口#

# 宿主端
ip addr add 10.0.0.1/24 dev veth1
ip link set veth1 up
# ns1 端
ip netns exec ns1 ip addr add 10.0.0.2/24 dev veth2
ip netns exec ns1 ip link set veth2 up
ip 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 1001ms
rtt 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网络配置接口插件层
Tip

容器网络问题排查时,先确认数据包走到了哪一步:tcpdump 抓宿主机接口确认 DNAT 是否命中,nsenter -n 进入容器 NS 确认 veth 对端是否收到,iptables -t nat -L -n -v 查看规则匹配计数。90% 的容器网络问题都是 iptables 规则缺失或 veth 未正确连接 bridge。

Note

容器网络的核心是 Network Namespace + veth pair + bridge + iptables 的组合。CNI 插件将这些组件的配置自动化,让你不需要手动创建 veth pair 和配置路由。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

容器网络
https://blog.souloss.com/posts/container-runtime/container-networking/
作者
Souloss
发布于
2026-05-30
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
1
容器存储
容器运行时 容器的可写层随容器删除而丢失,数据持久化需要 Volume。一网打尽容器存储的完整方案——Volume(绑定挂载/命名卷/tmpfs)、存储驱动(overlay2/devicemapper/btrfs)、CSI(容器存储接口)插件机制,以及 Kubernetes 的 PV/PVC/StorageClass 体系——从「docker run -v」到「理解容器存储的每一条挂载规则」。
2
容器在 Kubernetes 中
容器运行时 深入容器在 Kubernetes 中的运行机制——CRI 接口、Pod Sandbox 创建、多容器模式、Init Container、Sidecar 注入,以及 kubelet 如何通过 CRI 与容器运行时交互。
3
Wasm 容器:WasmEdge/WASI
容器运行时 WebAssembly(Wasm)正在从浏览器走向服务器——WASI(WebAssembly System Interface)定义了 Wasm 访问操作系统的标准接口,WasmEdge/runwasi 等运行时让 Wasm 模块可以作为容器运行。详细解读 Wasm 容器的架构、WASI 接口、与 OCI 的集成、与 Linux 容器的对比——从「Wasm 只能在浏览器运行」到「Wasm 是下一代容器运行时」。
4
OverlayFS:容器文件系统
容器运行时 OverlayFS 是容器分层文件系统的核心。全面剖析 OverlayFS 的 lowerdir/upperdir/workdir 三层结构、whiteout 标记文件机制、copy-up 写时复制语义、多层叠加规则,以及容器运行时如何用 OverlayFS 实现镜像层复用和容器可写层——理解「为什么容器启动这么快」和「镜像层是怎么共享的」。
5
容器安全:seccomp/AppArmor/Capabilities
容器运行时 容器的安全边界在哪里?Namespace 提供视图隔离,但不阻止特权操作。从零讲透 Linux 安全模块在容器中的应用——Capabilities(权限细分)、seccomp(系统调用过滤)、AppArmor(文件访问控制),以及 rootless 容器的实现,让你理解容器的安全边界和加固方法。