Linux network namespace 与容器网络实战

Linux network namespace 与容器网络实战示意

本文教你如何用 Linux network namespace 从零搭建容器网络,覆盖 namespace 创建、veth 对、bridge 互联、NAT 出网与故障排查五类核心场景。读完你会得到三件产出:一份完整可运行的多容器互通实验脚本;与 Docker bridge 网络的对应关系图;针对网络不通、MTU 异常、conntrack 错乱的快速排查清单。

一、network namespace 是什么

Linux 内核提供的命名空间是一套资源隔离机制,网络命名空间把网络协议栈、网卡接口、路由表、防火墙规则与套接字整体隔离开。一个命名空间里看到的本地回环、网卡接口、路由表项都是独立的,与其他命名空间互不可见。Docker 与 Kubernetes 容器组的网络隔离本质就是给每个容器或容器组分配一个网络命名空间,借此实现轻量的多租户隔离。

如何手动查看?ip netns list 列出命名的命名空间,lsns -t net 列出所有包含匿名的命名空间。容器的命名空间一般是匿名的,可以通过 docker inspect 拿到容器主进程的进程号,再用 nsenter 进入它的命名空间查看。云服务器虚拟机内部完全支持命名空间,是搭建轻量隔离测试环境的好选择,相关选型可参考 云服务器选购指南

二、ip netns 创建与基本操作

最直接的方式是 ip netns add ns1 创建命名空间,然后用 ip netns exec ns1 ip link 查看接口、ip netns exec ns1 ip addr add 127.0.0.1/8 dev lo && ip netns exec ns1 ip link set lo up 把 lo 起起来才能 ping 通。

刚建出来的 namespace 只有一个 down 状态的 lo。ip netns exec <ns> <cmd> 是最常用的语法,在指定 namespace 内执行任意命令。namespace 删除用 ip netns delete ns1,里面的所有接口与路由都会被清空。

注意 ip netns add 创建的命名 namespace 文件挂载在 /var/run/netns/,而 Docker 创建的匿名 namespace 不会自动挂载到这里。要让 ip netns 看到 Docker 容器的 namespace,需要手动建符号链接 ln -s /proc/<pid>/ns/net /var/run/netns/<name>,之后就能用 ip netns exec 操作了。Web 部署相关网络配置可参考 美国 VPS 部署教程

三、veth 对:跨 namespace 的网线

veth 是一对虚拟以太网接口,一端发包另一端立即收包。要让两个命名空间互通至少需要一对 veth:用 ip link add 创建接口对,再用 ip link set 把两端分别放入两个命名空间,进入各自命名空间配上同网段 IP 后 set up,最后 ping 验证互通。

这就是最小的两命名空间互通模型。Docker bridge 网络在每个容器内放一根 veth,另一头插在宿主机的 docker0 bridge 上,多容器靠 bridge 转发。理解这套模型后,容器内的 eth0 就是 veth 的一端,宿主机的 vethXXX 是另一端。

四、bridge 互联多 namespace

veth 一对一连接只适合两个 namespace,多个互联要靠 bridge。Linux 的 bridge 类似软件交换机,把多个接口连成一个二层域。核心步骤:用 ip link add br0 type bridge 建桥并 up、给桥配上网关 IP 例如 10.0.0.254/24;为每个 namespace 创建一对 veth,宿主机这端挂到 br0 上,另一端放进对应 namespace 并设好 IP 与默认路由指向网关。

每个 namespace 用一对 veth 接到 bridge 上,bridge 充当默认网关。这正是 Docker 默认 bridge 网络的工作机制,docker0 就是这样一个 bridge,IP 段一般在 172.17.0.0/16。容器化边缘节点的网络模型仍是这一套,只是被 CNI 插件封装了一层,相关性能调优可看 W3 Total Cache 调优实战

五、NAT 出网与端口映射

namespace 内的容器要访问外网,需要在宿主机做 SNAT;外部要访问容器,需要 DNAT。用 iptables 或 nftables 配合:先用 sysctl -w net.ipv4.ip_forward=1 打开 IP 转发,POSTROUTING 链上对源地址 10.0.0.0/24 走 MASQUERADE 自动 SNAT 到出口网卡地址;PREROUTING 链上把宿主机的 8080 端口 DNAT 到容器 10.0.0.1:80。这正是 Docker -p 8080:80 背后的实现。

注意常见陷阱:ip_forward 必须开否则路由不转发;POSTROUTING 的出口网卡要写对,多网卡机器选错会发不出去;连接数大时 conntrack 表满会丢包,调优 nf_conntrack_max。更多服务器维护见 WordPress 分类

六、故障排查路径

容器网络不通时按顺序排查:先用 ip netns exec <ns> ip addr 看接口与 IP,再用 ip route 看默认网关,然后逐步 ping 网关与外网,最后 ss -tlnp 看监听。MTU 异常是另一类隐蔽故障,PMTUD 被中间网络阻断时小包通大包不通,可以临时把容器 MTU 调到 1400 验证。conntrack 错乱多见于 SNAT 后端口耗尽,dmesg 会出现 table full,按前文 sysctl 调优即可。

进阶诊断用 tcpdump -i any -nn host <ip> 在宿主机两侧抓包,看请求是否真的到了对端、回包有没有走对路径,conntrack 表项用 conntrack -L 查具体连接状态。

七、性能与可观测性进阶

network namespace 隔离虽然轻量,但成千上万个 namespace 同时存在仍会带来开销。每个 namespace 都有独立的协议栈状态机与 conntrack 表,内存与 CPU 开销不可忽略。Kubernetes 节点跑几千 Pod 时建议监控 socket 总数(/proc/net/sockstat)、conntrack 表项数(conntrack -C)、每个 veth 的丢包计数(ip -s link)以及硬件级统计(ethtool -S)。

eBPF 是新一代的网络观测工具,可以在内核态低开销地抓取 namespace 间的连接事件。bcc-tools 里的 tcpconnect、tcpaccept 都能按 namespace 过滤,cilium 还把 eBPF 直接做成 CNI 实现,绕过 iptables 走 BPF 程序处理网络,性能比传统模型好不少。

八、Kubernetes Pod 网络模型回顾

Kubernetes Pod 内多容器共享同一个 network namespace 是核心设计。kubelet 启动 Pod 时先建一个 pause 容器持有 namespace,再让业务容器加入,于是 Pod 内所有容器共享 lo、eth0、端口,这就是为什么同一个 Pod 内两个容器可以通过 127.0.0.1 互访。

CNI 插件负责给 Pod namespace 分配 IP、连接到节点上的网桥或路由。常见实现:flannel 走 vxlan overlay,calico 走 BGP 路由,cilium 走 eBPF,底层都是 veth 与 bridge 这套基础积木。排查问题时先用 kubectl exec 看 Pod 内接口,再到节点上找到对应 namespace 用 nsenter 进入抓包,比看 CNI 日志高效得多。

总结与建议

network namespace 是容器网络的内核基石,理解 veth、bridge、NAT 三件套就能彻底看懂 Docker 与 Kubernetes 的网络模型。建议你按本文顺序落地:先用 ip netns 手动搭一个最小实验环境跑通互通与出网,再对照阅读 Docker 网络源码,最后把所学应用到生产排查。如果你需要在生产做深度网络优化,可以考虑用 cilium 这类基于 eBPF 的 CNI 替代传统 iptables 方案。总结一下,network namespace 不复杂但细节多,亲手搭一遍永远比看十篇文章管用,把实验脚本保存下来反复跑就能形成肌肉记忆。

发表评论