AI 资讯 知识库 后端技术操作系统内功 · 第 29 篇|容器网络:veth、bridge 与跨主机 Overlay 的完整拓扑

操作系统内功 · 第 29 篇|容器网络:veth、bridge 与跨主机 Overlay 的完整拓扑

容器网络的一跳:veth、bridge 与 NAT(手绘信息图)

起点:network namespace 隔离出「独立的网络栈」

第 8 篇讲过 namespace 是容器的底座,其中 network namespace 隔离的是完整的网络栈:独立的网卡、IP、路由表、iptables 规则、conntrack 视图。容器里的 eth0 不是物理网卡,而是一对「虚拟交叉线缆」的一端——veth pair:一对成对出现的虚拟网卡,从一个口塞进去的包会从另一个口出来。

容器网络的全部魔法,就是用 veth 把容器的 namespace 接到宿主机的网络拓扑上,再用 bridge/路由/NAT 决定包往哪走。理解了 veth,后面每一层都是普通的网络问题。

单机拓扑:容器发包出网的完整旅程

以 Docker 默认的 bridge 模式(网段 172.17.0.0/16)为例,容器访问外网 8.8.8.8 的完整路径:

flowchart LR
    subgraph NS["容器 network namespace"]
        C[应用] --> ETH0["eth0(veth 容器端)"]
    end
    ETH0 <-->|"veth pair"| VH["veth0 宿主端"]
    VH --> BR["docker0 bridge<br>(172.17.0.1)"]
    BR -->|"默认路由"| HOST["宿主机协议栈"]
    HOST -->|"iptables SNAT/MASQUERADE"| NIC[物理网卡 eth0]
    NIC --> NET[(外网 8.8.8.8)]

逐跳拆解:

  1. 容器内应用发包 → 容器的 eth0(veth 容器端),按容器内路由表走默认网关 172.17.0.1。
  2. 包从 veth 另一端冒出来——宿主机上的 vethxxxx 设备,它被挂在 docker0 bridge 上。
  3. bridge 相当于二层交换机:宿主机上的多个容器 veth 都挂在它上面,容器间互访走二层直接转发(同网段 ARP 后直达),不经过 NAT。
  4. 目的是外网 → 包交给宿主机三层协议栈,命中 iptables nat 表 POSTROUTING 链的 MASQUERADE 规则:源地址 172.17.0.x 被改写为宿主机出口 IP(第 28 篇的 SNAT 机制,conntrack 记下映射)。
  5. 回包到达宿主机,conntrack 按 NAT 映射把目的地址改回 172.17.0.x,bridge 转发给对应 veth,回到容器。

这张图解释了容器网络最基础的观测现象:容器里看到的源 IP 永远是网段地址;外网服务看到的永远是宿主机 IP(SNAT);容器间互访不走宿主机协议栈的 filter INPUT 链(走的是 bridge 二层转发)。

入站方向:端口映射与 DNAT

外部访问 -p 8080:80 的容器,走的是第 28 篇讲过的 DNAT 路径:外部包到达宿主机 → PREROUTING 链 nat 表把目的地址 宿主机IP:8080 改写为 172.17.0.x:80 → 路由判定(目的地已是容器 IP)→ FORWARD 链过滤 → bridge 转发进容器。

三个由此而来的经典排查结论:容器端口映射不经过宿主机的 INPUT 链(查防火墙白名单时别盯 INPUT);conntrack 表项由 DNAT 生成(第 28 篇的表满风险在容器密度高的节点放大);docker ps 的端口映射本质是 iptables 规则,手工清理 iptables 会让映射静默失效。

Kubernetes 的 CNI 两代路线:Overlay 与路由

Pod 调度到哪台节点不可预知,Pod IP 必须跨主机可达——这是容器编排对网络的核心诉求,CNI(container network interface)插件的两代路线由此分野:

Overlay 隧道路线(Flannel VXLAN、Calico IPIP):Pod IP 装进隧道包里,宿主机之间用普通网络互传。VXLAN 把二层帧封装进 UDP(默认端口 4789),宿主机的 flannel.1/vxlan.calico 虚拟接口负责封拆。优点是对底层网络零要求(三层可达即可,云上跨 VPC 也能跑);代价是封装开销(MTU 被吃掉 50 字节 → 内层 MTU 缩小 → 应用大包效率降)、封拆包的 CPU、排障复杂度(抓包要抓对接口)。

BGP 路由路线(Calico BGP 模式):不做封装,把 Pod 网段当真实路由发布——每台节点跑 BGP 客户端(bird),把自己的 Pod CIDR 通告给集群内其他节点/上层路由器,包按普通三层路由直达对端节点。优点是无封装开销、性能接近裸机、可观测性好(路径就是普通路由);代价是要求底层网络放行 BGP 与大 MTU,跨 VPC/云厂商受限。

维度 Overlay(VXLAN) BGP 路由
底层要求 仅三层可达 需 BGP 互通/路由器配合
封装开销 50+ 字节 + CPU 无
跨云/VPC 可 难
MTU 缩水 完整
排障 需懂隧道 普通 routing

选型共识:条件允许(自建机房/支持 BGP 的网络)选路由方案;公有云托管集群默认 Overlay;性能敏感的大集群在 BGP 可行时优先 BGP。

Service/ClusterIP:iptables/IPVS 在容器网络上的叠加

Kubernetes 的 Service(ClusterIP)不是一台设备,是每台节点上的转发规则:访问 10.96.x.x:80 的包在本节点被 iptables nat 表的 KUBE-SERVICES 链拦截,随机 DNAT 到某个后端 Pod 的 IP:Port(第 28 篇的矩阵模型在集群规模的应用)。规则条数 = Service 数 × 后端数,万级 Service 时线性匹配成为瓶颈,IPVS 模式(内核哈希表替代线性链)与 eBPF 模式(Cilium 直接在内核可编程点做服务负载均衡,绕过整条 iptables)是两个世代的后继方案。

# 观察一个 Pod 的网络全貌
kubectl exec <pod> -- ip addr        # 容器视角的 eth0
ip link | grep veth                  # 节点视角的 veth 家族
bridge link                          # bridge 上的端口
iptables -t nat -L KUBE-SERVICES -n  # Service 的 DNAT 规则
# 抓包的正确接口选择:容器内 eth0 / 节点 veth / vxlan 接口 / 物理网卡
tcpdump -i flannel.1 -nn udp port 8472   # VXLAN 封装流量

排障心法:沿着包的旅程设检查点

容器网络故障的通用动线是沿拓扑逐跳确认,每一跳有明确的观测点:

  1. 容器内:IP/路由对不对(ip addr、ip route)、DNS 解析(/etc/resolv.conf)。
  2. veth 与 bridge:veth pair 是否成对存在、挂在正确的 bridge(bridge link)。
  3. 同节点互通:ARP 能否解析(arping)、bridge 是否被 filter 链误伤。
  4. 出网方向:MASQUERADE 规则命中计数、宿主机转发开关 net.ipv4.ip_forward=1(未开启是「容器能 ping 通网关不能出网」的头号原因)。
  5. 跨主机:Overlay 的隧道接口状态与对端可达性 / BGP 的路由表(ip route 里有没有对端 Pod 网段)。
  6. Service 层:DNAT 规则是否存在与命中(计数器)、endpoints 是否就绪。

这套动线的价值在于把「容器网络不通」这种模糊的恐慌,拆成六道有明确对错答案的检查题。

Pod 出网的 IP 细节:SNAT、固定出口与 EIP

容器与虚拟机在「对外身份」上有一组容易踩坑的 IP 语义。Pod 访问集群外(公网 API、第三方服务)时,流量经节点的 SNAT/MASQUERADE(第 28 篇机制)改写源地址为节点 IP——对端看到的访问来源是节点地址,Pod IP 对外不可见。三个由此而来的工程问题。其一,出口 IP 不固定:多节点集群里同一个 Pod 的不同连接可能从不同节点出去(对端做 IP 白名单就翻车),解法按序:SNAT 策略(iptables 的 same-nat 保证同流同出口)、专网关节点(给节点打标签集中出口)、NAT 网关设备(云厂商的 NAT Gateway 绑固定 EIP)。其二,连接跟踪的容量:出口 SNAT 的映射全记在节点 conntrack(第 28 篇),Pod 密度高的节点做大量外呼(爬虫、消息推送)时表满丢新连接,nf_conntrack_max 与超时参数按外呼量规划。其三,入站方向的对称性:对外提供服务的 Pod 走 LoadBalancer/Ingress 的 EIP 进来,回包按 conntrack 原路返回——但 Pod 主动外呼再被对端「主动回连」(如 FTP 主动模式、某些回调协议)时回包没有映射可查,会被丢——协议评审时把「对端是否会回连」加进检查单。混合云场景还有一层:Pod 网段与本地 IDC 网段的路由通告(BGP 或专线静态路由),网段冲突是史上最常见的翻车点——规划地址池时把「未来三年的节点数乘以每节点 Pod CIDR」算进去,IP 规划错误是迁回不去的错误。

与本系列其他章节的勾连

veth/bridge 是第 8 篇 namespace 隔离在网络侧的连接件;DNAT/SNAT/conntrack 是第 28 篇 netfilter 矩阵的直接应用;eBPF 模式的 Service 网格/替代 kube-proxy 是第 9 篇 eBPF 在网络栈的纵深推进;Overlay 的 MTU 缩水会改变 TCP 的 MSS 协商,与第 7 篇 TCP 深水区的分片话题相接。至此网络栈的主干(收包路径、软中断、skb、netfilter、容器拓扑)已经完整,下一篇转入时间维度:内核的时间子系统与时钟轮——超时、定时、延迟测量背后的共同地基。

容器网络的 MTU 链:一排数字引发的连环故障

容器网络排障里有一类「能连通、能 ping、大包必挂」的事故,根因几乎都是 MTU 链失配。链的形态:物理网卡 1500 或巨型帧 9000 → Overlay 封装吃掉 50 字节(VXLAN 后内层 1450)→ IPIP 吃 20 → 容器 veth 继承 → 应用以为还是 1500。失配的两种形态:其一,PMTU 黑洞——路径上某段 MTU 更小且 ICMP 分片需要被防火墙吞掉(第 28 篇的某条 DROP 规则),发送方收不到「分片需要」的通知,大包静默丢失,症状是「小请求正常、TLS 握手(大证书包)卡死、SSH 能连不能传」——ping -M do -s 1400 二分定位是标准动作。其二,双栈失配——节点 A 的容器 MTU 1500、节点 B 的是 1450(后来装了 Overlay),跨节点互访时 B 发出的 1450 包 A 能收,A 发出的 1500 包被 B 的 veth 丢弃,故障呈现「单向大包丢」的诡异形态。治理原则:MTU 是全网一致性的配置,必须由基础设施层统一声明(K8s 里 CNI 配置是唯一真相源,节点网络参数跟随),应用层禁止自行调整;监控加一条「路径 MTU 探测」(定期从各节点 ping 大包到关键端点),把失配从应用故障提前到基础设施告警。这条链的知识结构也适用于 TCP 的 MSS 协商(第 7 篇):MSS 由两端 MTU 减去头部长度协商而来,MTU 链对齐是 MSS 正确的前提。

从包的旅程看 Service 流量的两跳 DNAT

Kubernetes 里一个请求从 Pod A 访问 Service 再到 Pod B,DNAT 发生两次,看清这两跳能解释一整类排障问题。第一跳:A 所在节点上,访问 ClusterIP 的包被本节点 KUBE-SERVICES 规则 DNAT 成某个后端 Pod B 的 IP(Endpoint 选择在本节点完成,这就是「Service 负载均衡是每个节点独立做的」的含义——各节点看到的 Endpoint 集合同步有先后,滚动更新时的偶发打到的旧 Pod 就是这个窗口)。第二跳:如果 B 在另一台节点,包以「目的=B」跨节点传输(Overlay 封装或路由),到 B 节点后走 FORWARD 进 B;回包沿 conntrack 的映射原路返回 A,全程 A 看到的源地址是 ClusterIP——SNAT 只在「Pod 访问集群外」时发生,集群内回程靠 conntrack 恢复身份。两个由此而来的工程现象:其一,hairpin(Pod 访问自己所在的 Service 且后端含自己)需要特殊的 SNAT 补丁规则(KUBE-HAIRPIN),没配时「自己访问自己」的流量回包丢失——Dubbo/本地开发场景的经典坑;其二,preserve client IP 的问题:经 Service 的流量源地址变成 A 的 Pod IP(不是 ClusterIP),B 看到的客户端是 A——需要真实客户端 IP 的场景要 externalTrafficPolicy: Local(节点本地后端直连不 SNAT)或应用层传 X-Forwarded-For。排障心法:conntrack -L 里找对应流,看两个方向的地址改写记录,两跳 DNAT 的任何一跳断链都会呈现「Service 不通」,而 Endpoint 与规则各自正常——机制图在脑中,故障在图上找位置。

小结

容器网络的本体是一段内核拓扑:network namespace 隔离出独立网络栈,veth pair 当网线,bridge 当交换机,出网靠 SNAT、入站靠 DNAT,全程是第 28 篇 netfilter 矩阵的应用。Kubernetes 把拓扑扩展到跨主机:CNI 的两代路线(VXLAN Overlay 换普适性、BGP 路由换性能)解决 Pod IP 可达性,Service 的 ClusterIP 靠节点上的转发规则实现并沿着 iptables、IPVS、eBPF 三代演进。排障的方法论是沿包的旅程设检查点:容器内、veth/bridge、出网 NAT、跨主机隧道或路由、Service 层,每跳有明确的观测命令与对错判据。

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?