暗色模式

Linux 网络命名空间:从 ip netns 到 veth 虚拟网卡互联

技术教程
2026-09-04
7
0
本文要点
  • 网络命名空间(network namespace)给进程一套完全独立的网络栈:自己的网卡、IP、路由、防火墙,互不可见——Docker 容器隔离的基石
  • ip netns add 名字 创建命名空间,ip netns list 查看,ip netns exec 名字 命令 进入它的视角看网络
  • 新建的命名空间里只有一张 DOWN 状态的 lo,与宿主机网络完全隔离,什么外网都访问不到
  • veth 是一对"虚拟网线":ip link add veth1 type veth peer name veth1p 一次建两根,两端可分别放进不同命名空间,实现跨命名空间通信
  • veth 一端被 ip link set ... netns 某空间 分配后,宿主机侧就再也看不到它(ip link show 报 Device does not exist),只能到目标命名空间里查看
  • 给两端配好同网段 IP 即可 ping 通,实测延迟约 0.03ms(纯内存虚拟设备,接近零开销)
  • 多个命名空间可用 Linux 网桥ip link add br0 type bridge)互联——这正是 Docker 默认 bridge 网络的工作原理
  • 全部命令在 Ubuntu 24.04(内核 6.8,iproute2 6.1)上真实执行验证,截图即真实输出

网络命名空间:进程眼中的"独立网络世界"

Linux 的 namespace(命名空间)机制用来隔离资源视图:PID 命名空间让进程以为自己是 1 号进程,mount 命名空间让进程有一套独立的挂载表,而 network namespace(网络命名空间) 则给进程一套完全独立的网络栈——自己的网卡、IP 地址、路由表、防火墙规则,和宿主机以及其它命名空间互不可见、互不影响。

想象一台物理服务器上装了 N 个独立的"小服务器",每个小服务器有自己的网线、自己的 IP,彼此通过虚拟线路连接——这就是网络命名空间带来的效果。Docker 的每个容器默认都有自己独立的网络命名空间,所以容器里的 ifconfig 看不到宿主机的网卡、监听端口也不会冲突。理解了网络命名空间,Docker 的网络原理就揭开了大半。

管理网络命名空间的工具是 iproute2 的 ip netns 子命令(Ubuntu 24.04 上为 iproute2 6.1.0,内核 6.8 的 veth/bridge 模块均已加载)。下面的演示都在一台 Ubuntu 云服务器上真实执行。

创建与查看命名空间

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

创建两个网络命名空间

ip netns add 会在 /var/run/netns/ 下创建一个绑定文件,命名空间即刻生效;ip netns list 列出当前存在的全部命名空间(输出里 ns2 排在 ns1 前面,顺序与创建顺序无关)。这台机器上本来没有任何命名空间,执行后出现了 ns1ns2 两个。

命名空间里有什么:一间"毛坯房"

ip netns exec 在指定命名空间里执行命令——ip netns exec ns1 ip addr 就等于"走进 ns1 里看一眼它的网卡":

ip netns exec ns1 ip addr

输出只有一行:

1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

注意两个细节:一是只有 lo,宿主机上的 eth0 之类的网卡完全看不见;二是 lo 还是 DOWN 状态(回环也没启用)。所以现在 ns1 是一个"断网"的独立世界:没有对外网卡、没有 IP、路由表是空的,什么都访问不了。Docker 容器刚启动、还没配网络时,里面就是这副光景。

veth:连接两个命名空间的"虚拟网线"

要让两个命名空间互通,得给它们之间拉一根"网线"。Linux 的 veth(virtual ethernet) 设备就是干这个的:它永远成对出现,一对 veth 就像一根网线的两端——数据从这一端进去,立刻从另一端出来。创建一对:

ip link add veth1 type veth peer name veth1p
ip link set veth1 netns ns1
ip link set veth1p netns ns2

ip link add ... type veth peer name ... 一次生成两根网线头:veth1veth1pip link set ... netns 名字 把各自的一端分配进对应的命名空间——"把网线的一头插进 ns1,另一头插进 ns2"。

分配之后有个值得注意的现象:在宿主机上执行 ip link show veth1p,会报错 Device "veth1p" does not exist.——设备已经不在宿主机的网络栈里了。veth 的两端被分走之后,宿主机的 ip link 列表里就再也看不到它们。这是排查网络问题时容易懵的点:网卡"不见了",先想想是不是被分配进了某个命名空间。

配置 IP:让两端"说上话"

veth 两端现在都是"没插好"的状态(NO-CARRIER,链路层没起来),要给它们配上 IP 并 up。先配置 ns1 这一端:

ip netns exec ns1 ip link set veth1 up && ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1 && ip netns exec ns1 ip addr show

配置 ns1 端 veth 并查看

输出里能看到完整状态:

  • lo 依然 DOWN(没启用,不影响 veth 通信);
  • veth1@if57: <BROADCAST,MULTICAST,UP> ... link-netns ns2——@if57 表示对端设备在另一个命名空间的编号是 57,link-netns ns2 直接标出了对端在哪个命名空间;此时状态还是 LOWERLAYERDOWN(对端没 up,链路不通);
  • inet 10.0.0.1/24 scope global veth1:IP 配好了。

同样配置 ns2 那一端:

ip netns exec ns2 ip link set veth1p up && ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth1p

两端都 up 之后链路就通了。在 ns1 里 ping ns2 的地址:

ip netns exec ns1 ping -c 3 10.0.0.2

跨命名空间 ping 通

3 个包全部收到、0% 丢包,延迟 0.03ms 左右——veth 是纯内存设备,数据不经过任何物理网卡,所以延迟接近零。至此两个完全隔离的"小服务器"通过一根虚拟网线连通了。注意 ping 时必须用 ip netns exec ns1 ping ...,直接在宿主机上 ping 10.0.0.2 会失败——宿主机网络栈里根本没有 10.0.0.0/24 这个网段的路由。

用网桥把多个命名空间连起来

两个命名空间用 veth 直连还行,三个以上呢?每对都要建 veth 就乱套了。真实做法是用 Linux 网桥(bridge):像现实中的交换机一样,把多个命名空间的 veth 一端都"插"到桥上,大家就同在一个二层网络里。这也是 Docker 默认 bridge 网络的原理——Docker 创建了一个叫 docker0 的网桥,每个容器的 veth 都挂在上面。

建一个网桥 br0,再建一对 veth,一端挂到桥上、另一端插进 ns2:

ip link add br0 type bridge
ip link set br0 up
ip link add veth2 type veth peer name veth2p
ip link set veth2 master br0
ip link set veth2 up
ip link set veth2p netns ns2
ip netns exec ns2 ip link set veth2p up
ip netns exec ns2 ip addr add 10.0.1.2/24 dev veth2p
ip addr add 10.0.1.1/24 dev br0

ip link set veth2 master br0 把 veth2 挂进网桥(相当于网线插进交换机),veth2p 进 ns2 配 10.0.1.2/24;最后给网桥自己在宿主机侧配 10.0.1.1/24。现在从 ns2 里 ping 宿主机侧:

ip netns exec ns2 ping -c 2 10.0.1.1

2 个包全通、0% 丢包。ns2 通过 veth2p → veth2 → br0 这条路和宿主机通了——一条网线插进交换机,就能和交换机上所有设备通信。往网桥上加新的 veth,就能不断挂进新的命名空间,这就是容器网络"随时拉一个新容器进来"的实现基础。

清理:删除命名空间

演示做完要还原环境。删除命名空间后,分配进去的 veth 会自动销毁(不用手动删),但挂在宿主机侧的网桥要手动删:

ip netns del ns1
ip netns del ns2
ip link del br0
ip netns list

ip netns list 输出为空,宿主机网络恢复原样。

小结

网络命名空间这套机制可以概括为:

  1. 隔离ip netns add 名字 创建独立网络栈(自己的网卡/IP/路由/防火墙),ip netns exec 名字 命令 进入它的视角。
  2. 连线ip link add vethA type veth peer name vethB 生成一对虚拟网线,ip link set 端 netns 名字 把两端分别放进不同命名空间。
  3. 组网:两端同网段配 IP 即通;多设备用 ip link add br0 type bridge + ip link set 端 master br0 挂桥互联。

理解这套机制后,再看《Docker 网络模式详解》里的 bridge 模式就一目了然——容器网络、Kubernetes 的 pod 内多容器共享网络、OpenVPN 的 tap 设备,底层全是 veth + 命名空间 + 网桥的组合。配合《Linux 网络配置》里宿主机视角的 ip 命令,以及《Linux 网络排查》的排查思路,服务器上绝大多数的网络问题都能定位。

参考资料

发表评论

暂无评论,快来抢沙发吧!