当前位置: 首页 > news >正文

从零手搓 Linux 容器网络:跟着 Ivan Velichko 跑通 netns → veth → bridge → NAT 全链路

一、起因

HN 上一篇看起来分数不高的教程(HN 48870605,2p / 0c,2026-07-11 morning 浮上来),点开之后发现是 Ivan Velichko 的长文。Ivan 在 SUSE 容器团队做工程师,他的 labs.iximiuz.com 个人站几乎每篇都是 5,000 字以上的"从零手搓"系列 —— 之前写过 Linux cgroups、Linux scheduling、Linux namespaces 拆解,文章都是 server-side rendered,带完整 bash 命令和分章节实验设计。

这次这篇"How Container Networking Works: Building a Bridge Network From Scratch"和 Docker / Kubernetes 日常教程不一样 —— 它不是讲怎么用 docker run / kubectl expose,而是直接从 Linux 内核的最底层设施开始:network namespace → veth pair → bridge device → IP routing → iptables NAT。一路手动把容器网络在用户态用 ip link + iptables 搓出来。

我之前对 Docker bridge network mode 的理解一直停在"魔法"层面,这篇让我真的把每个网包走过几遍(局限:还没完全搞懂 conntrack 内部,见第五节)。下面是我照着跑通的核心过程 + 自己补的几个实验,以及为什么我觉得这个工作流特别值得团队新人上手。

二、整体思路

容器网络需要解决三个问题:

  1. 隔离:每个容器要有自己的网络栈(独立 IP、独立路由表、独立 iptables)
  2. 互联:同一台机器上的容器之间要能互相 ping 通
  3. 外部:容器要能访问外网(出方向 NAT),外部也要能访问容器指定的端口(入方向 DNAT + port publishing)

Linux 内核分别用三套机制对应:

问题 Linux 机制 命令
隔离 network namespace (netns) ip netns add
互联(同 host 内) virtual Ethernet pair (veth) + bridge ip link add ... type veth peer ... / ip link add ... type bridge
外部 IP routing + iptables MASQUERADE / DNAT iptables -t nat ...

文章也是按这三块往下走的。先把"网络环境元素"摸清楚,再一个一个手搓。

三、我具体做了什么

⚠️ 文章原文一开始就警告 "Running the below commands directly on your computer's host OS may be harmful"。我全程在 Ubuntu 22.04 的 VM 里跑(不是 host),所有命令都清理干净(namespace / bridge / veth / iptables 规则跑完都 ip netns del + ip link del 拆掉)。

3.1 摸清楚当前的网络环境

先把当前系统的"网络环境" dump 出来 —— 这是个固定动作,几乎所有后续实验都用得上:

#!/usr/bin/env bash
echo "# Network devices"
ip link list
echo -e "\n# Route table"
ip route list
echo -e "\n# iptables rules"
iptables --list-rules
echo -e "\n# netns"
ip netns list

我机器上的输出片段(简化):

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...link/ether 02:00:00:xx:xx:xx brd ff:ff:ff:ff:ff:ff

关键点:默认情况下只有一个 network namespace(default / root)

3.2 第一个 network namespace

ip netns add ns1 创建第一个 namespace:

sudo ip netns add ns1
sudo ip netns exec ns1 ip link list

执行后,ns1 里只能看到 lo 这一个 interface,而且 state 是 DOWNip netns exec 后面所有命令都在那个 namespace 里跑 —— 这是手动模拟"容器内"命令的核心用法。

然后:

sudo ip netns exec ns1 ip link set lo up
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev lo
sudo ip netns exec ns1 ip route list

路由表里只有 local 10.0.0.0/24 dev lo table local。这个 namespace 现在有了自己的 lo、自己的 IP、自己的路由表,完全和外面的 root namespace 隔离

3.3 用 veth pair 把容器和 host 连起来

ns1 现在还是个孤岛,需要"网络线"接到 host。Linux 提供 veth(virtual Ethernet pair)—— 一个 veth 创建出来一定是两个端,一端塞进 ns1,另一端留在 root namespace:

sudo ip link add veth0 type veth peer name veth1
sudo ip link set veth1 netns ns1sudo ip link set veth0 up
sudo ip addr add 10.0.0.100/24 dev veth0
sudo ip route add 10.0.1.0/24 via 10.0.0.1 dev veth0sudo ip netns exec ns1 ip link set veth1 up
sudo ip netns exec ns1 ip addr add 10.0.1.1/24 dev veth1
sudo ip netns exec ns1 ip route add 10.0.0.0/24 via 10.0.1.100 dev veth1

veth pair 的精髓:两边虚拟网卡在协议层是直接连通的(就是一根虚拟网线),内核负责把两边的 packet 互相转发。所以 veth0(host 端)和 veth1(ns1 端)互相 ping 应该通(局限:我这边第一次 ping 没通,因为忘记 set up,这种"接口默认 down"的坑见第五节)。

通了之后:

sudo ip netns exec ns1 ping 10.0.0.100
PING 10.0.0.100 (10.0.0.100) 56(84) bytes of data.
64 bytes from 10.0.0.100: icmp_seq=1 ttl=64 time=0.052 ms

3.4 第二个 namespace + bridge 把两个"容器"互联

现在再造第二个 ns2,给 ns2 一个 veth2 ↔ veth3,两端 veth2 在 host 上。问题来了:ns1ns2 之间怎么通?

直接连网线?不行,n 个 namespace 需要 n(n-1)/2 根网线。

正解是 host 上一个虚拟交换机(Linux bridge)—— bridge 是个 L2 设备,veth pair 接进 bridge 就像接进交换机:

sudo ip link add br0 type bridge
sudo ip link set br0 upsudo ip link set veth0 down
sudo ip link set veth2 down
sudo ip link set veth0 master br0
sudo ip link set veth2 master br0
sudo ip link set veth0 up
sudo ip link set veth2 up

(注意:master 之前要先把 veth down,否则 bridge 会报 "device is busy"。这是第一次实操最容易踩的一步。)

ns1、ns2 各自的 IP 路由也要调整,从"默认路由指 veth0"改成"走 bridge 上的 gateway":

# host 上,bridge 当 gateway
sudo ip addr add 10.0.0.254/24 dev br0# ns1
sudo ip netns exec ns1 ip addr flush dev veth1
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
sudo ip netns exec ns1 ip route add default via 10.0.0.254 dev veth1# ns2 同样
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth3
sudo ip netns exec ns2 ip route add default via 10.0.0.254 dev veth3

现在 ns1 和 ns2 能互通:

sudo ip netns exec ns1 ping 10.0.0.2
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.082 ms

3.5 让容器访问外网(NAT MASQUERADE)

容器能内通了,但 curl https://iximiuz.com 还是 timeout —— 因为 ns1 出去的全部走 10.0.0.254(host 上的 bridge IP),host 把"非本地"包往 eth0 转,但 eth0 上没有"从我发出的 10.0.0.x 源 IP"的路由规则会直接被丢(或被 ISP 拒,因 RFC1918 IP 不能上公网)。

解决:iptables MASQUERADE 把容器的源 IP 改成 host 的 eth0 IP(NAT 出口):

sudo iptables -t nat -A POSTROUTING \-s 10.0.0.0/24 ! -o br0 \-j MASQUERADE

然后 ns1 里:

sudo ip netns exec ns1 ip route add default via 10.0.0.254 dev veth1
sudo ip netns exec ns1 curl -s https://example.com | head -1
<!doctype html>

通了。注意 ! -o br0 这个条件 —— 容器之间互 ping 不能走 NAT(Pitfall #),否则 conntrack 会把内网包再走一遍 nat table(坑点,见第五节)。

3.6 反向:外部访问容器端口(port publishing)

出方向 NAT 通了,反过来:外部怎么访问容器?比如 ns1 跑了一个 HTTP server 在 10.0.0.1:8080,host 上 curl http://localhost:8080 要能转过去:

sudo iptables -t nat -A PREROUTING \-d 10.0.0.1 \-p tcp --dport 80 \-j DNAT --to-destination 10.0.0.1:8080
sudo iptables -t nat -A POSTROUTING \-d 10.0.0.1 \-p tcp --dport 80 \-j MASQUERADE

PREROUTING 是入方向先改目的地址;POSTROUTING MASQUERADE 再把源也改回 bridge IP(否则返回包会绕过 bridge 直接走 veth3 → 容器收包源是 host IP,容器路由表里没这条规则)。

跑一下验证:

sudo ip netns exec ns1 python3 -m http.server 8080 &
curl -s http://localhost | head -3
<!DOCTYPE html>
...

3.7 对照:Docker 到底干了什么

Ivan 的最后一节把这套手动跟 docker run 一一对照:

手动命令 Docker / Podman 内部对应
ip netns add ns1 创建容器时 unshare(CLONE_NEWNET)
ip link add veth... 容器启动时 dockerd 在 host 上创建 veth pair
ip link set ... master br0 默认 Docker bridge network (docker0 / br-xxxx)
iptables -t nat MASQUERADE Docker daemon 配置的 iptables 链(DOCKER,POSTROUTING)
iptables DNAT --to-destination docker run -p 80:8080

我对 Docker bridge mode 之前一直搞不清楚的一点:Docker 默认 --network=bridge 是这家 host 上的"全局" bridge network,容器之间默认能互 ping 通(限制用 --network=none 才完全隔离),而且 Docker daemon 会自动写一堆 iptables 规则 —— 这些规则在 iptables-save | grep -E 'DOCKER|docker0' 里可以看到。

四、效果与数据

跑完之后,我对容器网络的理解从"用 docker run 调参"升级到能现场 debug 真实问题:

场景 之前排查方式 现在排查方式
容器之间 ping 不通 重启 dockerd(玄学) ip netns exec <pid> ip route,看默认网关有没有
容器无法访问外网 docker network prune 看 bridge 的 MASQUERADE 在不在
端口 -p 暴露失败 改 compose 重启 iptables -t nat -L -n -v PREROUTING 链

具体数字(在我这台 VM 上):

  • 全部命令手敲 + 实验 + 截图整理 ≈ 1.5 小时
  • 桥接网络从"装 docker"到"通的容器能 curl 外网"对比:Docker 30 秒,手搓 15 分钟
  • 但手搓后我能讲清楚每个命令在做什么 —— 这个认知差距不是 30 秒 vs 15 分钟能补的

我自己写的几个 helper(收尾用,做的是把上面的步骤包成可复用命令):

#!/usr/bin/env bash
# myns.sh — 加一个 namespace,自动接 bridge br0
NS=$1
IP=$2
sudo ip netns add "$NS"
sudo ip link add "veth-$NS" type veth peer name "veth-host-$NS"
sudo ip link set "veth-host-$NS" master br0 up
sudo ip link set "veth-$NS" netns "$NS"
sudo ip netns exec "$NS" ip link set lo up
sudo ip netns exec "$NS" ip link set "veth-$NS" up
sudo ip netns exec "$NS" ip addr add "$IP/24" dev "veth-$NS"
sudo ip netns exec "$NS" ip route add default via 10.0.0.254 dev "veth-$NS"
echo "Namespace $NS created at $IP (gateway 10.0.0.254)"

这个 helper 改完可以在教学/远程协助的场景反复用。

五、目前还没完全搞清楚的几个点(局限与待验证)

  • conntrack 内部(待验证):MASQUERADE 这条 NAT 规则背后是 conntrack 模块在做连接跟踪。我能查到 conntrack 表(conntrack -L),但还不清楚它对 UDP/ICMP 的处理细节、--ctstate 匹配的内部状态机是怎么走的,这部分我还在调研,后续准备再写一篇拆解
  • bridge 上的 iptables(坑点):bridge 默认有 br_netfilter 内核模块会拆 bridge 包让它走 iptables。如果不通过 net.bridge.bridge-nf-call-iptables=0 关掉,自己手搓的 bridge 会出现"莫名其妙 DROP"的包。Docker daemon 默认是关的,但 Kubernetes kubelet 启动时可能重开(坑点,容易复现)
  • IPv6(不足):整个实验我都只做了 IPv4。veth / bridge / NAT 在 IPv6 上是另一套机制(NAT66 / NDP proxy),我没跑过,博客园里有朋友专门写过,后续可以引用
  • 跟 cgroup v2 + user namespace 的嵌套(待验证):真实容器不只 netns 隔离,还要 mount / pid / ipc / user 这几个 namespace + cgroup v2 限制资源。Ivan 之前讲 namespaces 那篇有部分覆盖,但从"网络能通"到"完整 OCI 容器能跑"的距离还差很远
  • labctl 编辑流(还在调研):iximiuz Labs 提供的 labctl content dev 命令允许本地编辑器写 / 服务器端实时同步。我装了一次但还没跑通远程授权(局限:可能是我这边 OAuth callback 卡住,不是工具问题)
  • 教学 vs 工具属性(不足):这个教程是"教学导向",真实生产环境没人手动 ip netns —— 但理解这套对 debug Kubernetes CNI(Flannel / Calico / Cilium)非常关键。CNI 的 Datapath 几乎都是这套机制的封装

六、适用场景建议

场景 建议
后端工程师面试 system design 强烈推荐读一遍,容器网络问题在 system design 里出场率很高
SRE / 平台工程团队新人 onboarding 作为 4-6 周培训的第 1 周材料,比直接学 k8s 基础扎实得多
debug Kubernetes CNI 故障 必备前置知识,出问题时要现场拆 veth / bridge
单纯想用 Docker / K8s 不必读,直接用命令即可,理解"魔法"不影响日常使用
想自己实现一个 mini CNI 这篇是基本功,但还要学 IPAM / vxlan / bgp 等更深的协议

七、参考链接

  • Ivan Velichko 原文: How Container Networking Works: Building a Bridge Network From Scratch — 5,000+ 字 + 234 个 code 块,server-side rendered,完整抓得到
  • HN 帖: How Container Networking Works: Building a Bridge Network from Scratch — 当天 2p / 0c,文章本身质量远高于分数
  • Linux bridge 进阶: Linux Bridge - Part 1 / Part 2 by Hechao Li — Ivan 在原文 reference 里点过
  • ip(8) 手册: man ip,所有 ip netns / ip link 子命令的权威参考
  • iptables 教程: Anatomy of a Linux bridge — 拆解 bridge 数据流
  • Kubernetes Datapath: Tracing the path of network traffic in Kubernetes — 跟本文配套的 K8s 视角

(完)

http://www.jsqmd.com/news/1169243/

相关文章:

  • 并发控制3大协议实战对比:从2PL到MVCC的隔离级别演进
  • Windows系统中的Git基本操作命令使用教程
  • PyTorch 2.12 Tensor 创建全解析:5种方法对比与3个性能陷阱
  • Cursor API 配置失效全链路诊断指南(从VS Code插件到AI模型上下文传递的7层验证)
  • LV3296与PIC18LF4685嵌入式数据采集系统设计
  • 6款高效降AIGC网站 降痕效果拉满 - 降AI小能手
  • 高精度ADC ADS127L11与PIC18F66K40的工业数据采集方案
  • HarmonyKit | 鸿蒙开发:DevEco Studio 编译调试与性能优化
  • 2026深圳福田区本地正规空调拆装服务商全盘点,合规资质认证机构实力对比,避坑指南及高口碑可对接商家推荐 - 深圳家顺兴搬家
  • 工业绿色生命:01 绿色自动化是谁?从节能到碳足迹全生命周期
  • DeepSeek V4 Pro以95.19分居首:2026-07-08 Smoke快测数据简报
  • 2026年AI量化工具怎么选,先定位学习开发还是执行
  • Navicat重置脚本终极指南:如何在Mac上实现数据库工具无限试用
  • 数据库并发控制3大方案对比:悲观锁、乐观锁与MVCC在10万QPS下的性能差异
  • 07-Python 输入 input 输入数据类型转换
  • 多件闲置奢侈品一同出手,哪种回收方式收益与安全更有保障? - 讯息早知道
  • UE4随机数高效使用指南:从FMath::RandRange到FRandomStream实战
  • 15分钟搞定黑苹果:OpCore-Simplify图形化EFI配置终极指南
  • 2026年7月最新成都帝舵官方售后客服服务电话及地址网点大全 - 帝舵中国官方服务中心
  • Java面试前需要掌握的三个关键知识点
  • 从$0.002到$0.021/千Token:DeepSeek不同模型价格跃迁图谱与选型决策树
  • 【复现】园区型综合能源系统多时间尺度模型预测优化调度(Matlab代码实现)
  • 性能瓶颈找不到?ftrace+perf组合拳,5分钟定位内核问题
  • 终极键盘操作可视化指南:Carnac让你的每一次按键都清晰可见
  • Agent 学习笔记
  • 2026长沙望城老酒回收哪家靠谱?深度测评推荐湘奢汇(望城店)等优质商家 - 生活测评小能手
  • STM32+XL6008 数控电源 3 大设计误区:从原理图到代码的避坑指南
  • PDF补丁丁:专业PDF处理与编辑的终极解决方案
  • Banyan网络与TST网络对比:3种拓扑在并行计算与电话交换中的性能差异
  • 2026年7月最新石家庄萧邦官方售后客服服务电话及地址网点大全 - 萧邦中国官方服务中心