libnetwork 网络实现剖析:Docker 网桥模式与 Swarm Overlay 模式的 iptables 流量转发全解 容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载libnetwork 是 Docker 引擎内置的容器网络核心库负责为容器提供网桥bridge与 Swarm 集群叠加overlay两种主要组网能力。本文基于 docker-ce 仓库中 libnetwork 网络文档 展开逐一拆解两种模式下网络命名空间、veth 对、网桥与 iptables 规则如何协作并深入到 service_linux.go 等源码层验证每条规则的真实生成逻辑。读完本文你将能够理解容器出网为何需要 MASQUERADE、端口发布背后的 DNAT/FORWARD 规则、Swarm 服务发布端口时从请求到回包穿越四层命名空间与 ipvs 负载均衡的完整路径以及 DOCKER-USER、DOCKER-ISOLATION 等链的用途。libnetwork 使用 iptables 在 Linux 内核 Netfilter 框架上配置 NAT 与转发规则其默认链PREROUTING、FORWARD、POSTROUTING、OUTPUT和表nat、filter、mangle的机制是理解本文所有内容的基础。Docker 容器网络的核心思想可以概括为用网络命名空间隔离、用 veth 对联通、用网桥汇聚、用 iptables 做地址转换与策略控制。Bridge 模式单机容器网络的默认形态当 docker engine 以 bridge 网络模式实例化一个容器时libnetwork 构建的拓扑如下图所示。三步完成容器网络初始化bridge 模式下 libnetwork 只做三件核心工作创建新的网络命名空间Container NS为容器提供独立的协议栈视图创建 veth 对一端挂接到宿主命名空间Host NS的docker0网桥另一端移入新建的容器命名空间成为容器的eth0在容器 NS 内分配地址与路由从 docker0 子网默认172.17.0.0/16中分配一个 IP并把默认网关设置为 docker0 的 IP。完成这三步容器的 bridge 网络即宣告就绪。容器出向流量依次经过容器 NS 内路由 → veth 对 → docker0 网桥Host NS→ 主机路由 → eth0Host NS离开宿主机入向流量则完全相反。容器 NS 内分配 IP、设置默认网关等沙箱操作由 sandbox.go 中定义的网络沙箱sandbox完成它对应 libnetwork 的 CNMContainer Network Model抽象下文 overlay 模式的ingress_sbox也是同一机制的特例。容器 IP 的“隐身术”POSTROUTING 链的 MASQUERADE 规则容器被分配到的 IP如上图中的172.17.0.2属于 docker0 子网对外部网络不可见。为此docker engine 在初始化时向宿主机 nat 表 POSTROUTING 链加入一条默认的伪装masquerading规则匹配条件流量已通过路由阶段且源 IP 位于 docker0 子网172.17.0.0/16内即判定该流量必然来自 docker 容器动作把源 IP 替换为路由决定的外出接口 IP上图中为 eth0 的172.31.2.1。从原理上讲MASQUERADE 等同于 SNAT只是替换后的源 IP 由路由查表动态决定而非显式指定。在源码 setup_ip_tables.go 的setupIPTablesInternal中可以看到这条规则的确凿实现natArgs : []string{-s, address, !, -o, bridgeIface, -j, MASQUERADE} hpNatArgs : []string{-m, addrtype, --src-type, LOCAL, -o, bridgeIface, -j, MASQUERADE} natRule : iptRule{table: iptables.Nat, chain: POSTROUTING, preArgs: []string{-t, nat}, args: natArgs}即规则形态为-t nat POSTROUTING -s docker0子网 ! -o docker0 -j MASQUERADE其中! -o docker0确保发往 docker0 网桥自身的流量不被改写。从源码结构看若在启动时显式配置了宿主机 IP则会退化为显式源地址的 SNAT 规则--to-source hostAddr。端口发布DOCKER 链中的 DNAT FORWARD 放行如果容器背后承载了服务容器 NS 内监听某个目标端口targetPort则宿主机 NS 内必须有对应的发布端口publishedPort接收请求并转发给容器。libnetwork 为此在宿主机创建两条规则nat 表在 PREROUTING 链中插入DOCKER链并在其中加入一条 DNAT 规则例如DNAT tcp any any dport:45999 to 容器IP——将到达宿主机 eth0、目标为172.17.0.2:80的流量改写目标地址使被 DNAT 的请求可以路由到后端监听 80 端口的容器filter 表在 FORWARD 链中插入DOCKER链并加入一条 ACCEPT 规则如ACCEPT tcp any containerIP dport:targetPort允许被 DNAT 后的请求转发进容器。这两条规则在 iptables.go 的ChainInfo.Forward()方法中一次性编程完成且每次写入前都会用iptable.Exists()检查去重args : []string{-p, proto, -d, daddr, --dport, strconv.Itoa(port), -j, DNAT, --to-destination, net.JoinHostPort(destAddr, strconv.Itoa(destPort))} // ... args []string{!, -i, bridgeName, -o, bridgeName, -p, proto, -d, destAddr, --dport, strconv.Itoa(destPort), -j, ACCEPT}注意 DOCKER 链本身由 setup_ip_tables.go 在 bridge 网络初始化时通过iptable.NewChain(DockerChain, ...)创建PREROUTING 中也会预先加入-i bridgeName -j RETURN的跳转保护避免对已进入网桥的流量重复 DNAT。该逻辑的测试可参考 iptables_test.go。Swarm/Overlay 模式集群级服务网络的构建overlay 模式与 bridge 模式完全不同它使用一组全新的命名空间、网桥和 iptables 规则来承载跨宿主的服务通信。当一台主机加入 Swarm 集群时docker engine 会构建下图所示的拓扑。初始设置创建服务前的基础设施Ingress Sandbox在创建任何服务之前libnetwork 先完成 6 步“地基”工作创建 docker_gwbridge 网桥宿主 NS分配本地子网图中为172.18.0.1/16。该子网仅存在于本机不会泄漏到集群其他节点是服务流量进出宿主机的“网关桥”。对应常量见 default_gateway_linux.go 中的libnGWNetwork docker_gwbridge添加 masquerading 规则在宿主 nat 表图中编号 5POSTROUTING 链为源 IP 位于172.18.0.0/16的流量加伪装规则处理 northbound容器主动出网流量创建 ingress_sbox 命名空间创建两个 veth 对其中 eth1 以固定 IP172.18.0.2连接 docker_gwbridgeeth0 连接 ingress NS 的 br0 网桥并被分配10.255.0.2。ingress_sbox是 Docker 唯一一个被显式命名的沙箱controller.go 中sb.id ingress_sbox其 netns 路径固定为/var/run/docker/netns/ingress_sbox在 ingress_sbox 的 nat 表编号 2PREROUTING 链添加规则对服务请求做 SNAT 并重定向到 ipvs 做负载均衡典型规则为SNAT all -- anywhere 10.255.0.0/16 ipvs to:10.255.0.2在 ingress_sbox 的 nat 表 POSTROUTING 与 OUTPUT 链添加 DNS 规则将 DNS 查询重定向到 docker engine这是 Swarm 服务发现service discovery所必需的创建 ingress 命名空间与 br0 网桥br0 有两个挂接点——一端连接 ingress_sbox 的 eth0另一端连接 vxlan 接口使 br0 通过 VXLAN 隧道跨越整个 Swarm 集群的所有主机。集群内每个 ingress_sbox 的 eth0、每个服务实例容器、每个服务本身都被分配唯一的10.255.xx.xxIP 并挂接在 br0 上从而在逻辑上构成一张覆盖全集群的大二层网络。服务设置发布端口时的 9 条关键规则当创建服务targetPort80, publishedPort30000时上述拓扑被追加如下规则#位置表/链规则实质1容器 NS—两个 veth 对eth1 挂 docker_gwbridgeIP172.18.0.2eth0 挂 ingress 的 br0IP10.255.0.52容器 NSfilter3INPUT/OUTPUT仅放行 targetPort80 的流量3容器 NSnat4PREROUTING将 publishedPort 改写为 targetPortREDIRECT tcp -- anywhere 10.255.0.11 tcp dpt:30000 redir ports 804容器 NSnat4INPUT/OUTPUT将容器内 DNS 查询重定向到 docker engine5宿主 NSfilter5FORWARD插入DOCKER-INGRESS链并放行服务端口及其回包ACCEPT tcp -- anywhere anywhere tcp dpt:30000与ACCEPT tcp -- anywhere anywhere state RELATED,ESTABLISHED tcp spt:300006宿主 NSnat6PREROUTING在另一个DOCKER-INGRESS链中把服务请求 DNAT 到 ingress_sbox 的 eth1DNAT tcp -- anywhere anywhere tcp dpt:30000 to:172.18.0.2:300007ingress_sboxmangle1PREROUTING标记服务请求MARK tcp -- anywhere anywhere tcp dpt:30000 MARK set 0x1008ingress_sboxnat2POSTROUTING将请求源 IP SNAT 为 eth1 IP 并送交 ipvsSNAT all -- 0.0.0.0/0 10.255.0.0/16 ipvs to:10.255.0.29ingress_sboxipvs为被标记流量配置轮询负载均衡策略FWM 256 rr - 10.255.0.5:0 / 10.255.0.7:0 / 10.255.0.8:0均为 Masq 权重 1第 9 步中每个10.255.0.x都代表一个承载该服务的容器实例 IPipvs 会在它们之间做轮询分发。源码印证DOCKER-INGRESS 链的编程逻辑DOCKER-INGRESS链由 service_linux.go 中的常量ingressChain定义其创建与规则编排集中在programIngress()函数service_linux.go。关键行为包括启动时通过ingressOnce.Do清空可能遗留的旧规则保证幂等在 nat 与 filter 两个表都创建DOCKER-INGRESS链并以-j RETURN结尾兜底在 nat 表OUTPUT与PREROUTING链插入-m addrtype --dst-type LOCAL -j DOCKER-INGRESS跳转在 filter 表FORWARD链头部插入跳转对每个发布端口生成 DNAT 规则-t nat DOCKER-INGRESS -p tcp --dport published -j DNAT --to-destination gwIP:published与文档第 6 步描述的to:172.18.0.2:30000一一对应生成两条 filter 规则分别放行入站请求--dport published -j ACCEPT与已建立连接回包-m state --state ESTABLISHED,RELATED --sport published -j ACCEPT对应文档第 5 步通过os.WriteFile(/proc/sys/net/ipv4/conf/gwIf/route_localnet, ...)允许本地回环地址转发并为gwIf追加--src-type LOCAL -o gwIf -j MASQUERADE的 POSTROUTING 规则。辅助排查工具 ssd.pySwarm service diagnostics会用nsenter --net/var/run/docker/netns/ingress_sbox读取 ingress_sbox 内 eth1 的 IP再检查宿主 nat 表DOCKER-INGRESS中对应端口的 DNAT 规则是否存在可用来验证第 6 步的规则是否被正确配置。源码印证fwmark ipvs 负载均衡第 79 步的“标记流量 → ipvs 分发”机制在源码 service_linux.go 的addLBBackend中实现以FWMark: lb.fwMark即0x100256创建 ipvs 服务调度算法显式设置为SchedName: ipvs.RoundRobin通过reexec.Register(fwmarker, fwMarker)注册的 fwmarker 工具同文件第 28 行在 ingress_sbox 的 mangle 表为 vip 目标流量打上防火墙标记每个服务容器实例作为 ipvs 的 Destinationreal server加入权重为 1默认走 Masq 转发模式仅在loadBalancerModeDSR时切换为 DirectRoute。服务流量全流程请求与回包如何穿过 4 层命名空间以publishedPort30000, targetPort80为例一次完整的服务访问会经历下述请求路径与回包路径。请求方向8 个阶段请求到达宿主 eth0dstIP172.31.2.1, dstPort30000, srcIPCLIENT_IP, srcPortCLIENT_PORT。路由前先被服务设置第 6 步的 nat 规则 DNAT 到172.18.0.2路由期间被第 5 步 FORWARD 规则放行随后沿路由转发到 docker_gwbridge请求到达 ingress_sbox 的 eth1dstIP172.18.0.2, dstPort30000, srcIPCLIENT_IP。路由前被第 7 步 mangle 规则打标记路由后被第 8 步 nat 规则把源 IP 改写为10.255.0.2并送入 ipvs第 9 步策略选中某个实例本例10.255.0.5并对其实施 DNAT请求到达 ingress NS 的 br0dstIP10.255.0.5, dstPort30000, srcIP10.255.0.2。本例假设实例在本机因此 br0 直接转发即便实例位于远端由于 br0 经 vxlan 横跨全集群、所有实例都挂接其上路由策略配置无需任何变化请求到达容器 NS 的 eth0dstIP10.255.0.5, dstPort30000。路由前目标端口被第 3 步 nat 规则改写为 80路由后被第 2 步 INPUT 规则放行监听 tcp:80 的进程最终收到dstIP10.255.0.5, dstPort80, srcIP10.255.0.2。回包方向对称解包进程回复dstIP10.255.0.2, dstPortEPHEMERAL_PORT, srcIP10.255.0.5, srcPort80。经第 2 步 OUTPUT 过滤放行路由确定出口为 eth1再经第 3 步 nat 规则将源端口 un-DNAT 回 30000回复到达 br0被转发给 ingress_sbox 的 eth0在 ingress_sbox 中回复先经 ipvs 把源 IP un-DNAT 回172.18.0.2再由第 8 步 nat 规则把目标 IP un-SNAT 回 CLIENT_IP、目标端口还原为 CLIENT_PORT回复抵达宿主 docker_gwbridgedstIPCLIENT_IP, dstPortCLIENT_PORT, srcIP172.18.0.2, srcPort30000经第 6 步 nat 规则 un-SNAT 把源 IP 改写为172.31.2.1从 eth0 出网。从外部视角看请求以dstIP172.31.2.1:30000进入宿主机回包以srcIP172.31.2.1:30000离开宿主机中间的容器 IP、gwbridge IP 与十数条 iptables 规则对客户端完全透明——这正是 Docker 端口发布对外表现“一个 IP、一个端口”的底层原因。其他流量Northbound 与 DNS容器主动出网的 Northbound 流量例如容器内ping外部站点路径与 bridge 模式完全相同只是经宿主 NS 的 docker_gwbridge 出口并由初始设置第 2 步的 masquerading 规则做源地址伪装DNS 流量容器实例的 DNS 查询被重定向到 docker engine用于 Swarm 服务发现。对应实现见 resolver_unix.golibnetwork 以 DNAT 形式将发往内置 resolver IP 的 UDP/TCP 53 端口请求定向到 docker engine 的 resolver 进程容器内与 ingress_sbox 内的 OUTPUT 链均有对应规则。Docker 维护的其他 iptables 链与规则除上述两条核心链路外docker engine / libnetwork 还会创建并管理以下全局链DOCKER-USER用户自定义防火墙的“前置阀门”DOCKER-USER链被插入到宿主 filter 表 FORWARD 链的第一条使用户可以独立管理所有经宿主机转发、与 docker 容器相关或不相关的流量。其实现见 firewall_linux.goDocker 不会删除或修改用户预先写入DOCKER-USER的规则因而用户的防火墙策略可以在 Docker 重启后持久存在即便IPTableForwarding被禁用Docker 也不会移除该链因为它包含超出引擎控制范围的用户规则链尾以-j RETURN收束保证未命中规则的流量回到 FORWARD 链继续处理。典型用法是在该链中添加过滤规则如拒绝特定源 IP 访问容器发布端口从而避免 Docker 每次重启后覆盖用户配置。相关行为有 firewall_linux_test.go 的用例覆盖。DOCKER-ISOLATION-STAGE-1 / 2跨网桥隔离的两级筛网DOCKER-ISOLATION-STAGE-1与DOCKER-ISOLATION-STAGE-2用于阻断不同 bridge 网络之间的流量对应 Docker 的 inter-network communication 开关。这两条链由 setup_ip_tables.go 定义常量并创建其两阶段设计在源码注释中说明得很清楚STAGE-1匹配“源接口是某个 bridge 网络的网桥、而目的接口是其他接口”的报文命中后跳入 STAGE-2未命中则 RETURN 回父链STAGE-2匹配“目的接口是某个 bridge 网络的网桥”命中即判定该报文发自一个 bridge 网络的网桥、要进入另一个 bridge 网络的网桥直接DROP未命中 RETURN。setINC()函数setup_ip_tables.go负责安装/移除具体规则在 STAGE-1 添加-i iface ! -o iface -j DOCKER-ISOLATION-STAGE-2在 STAGE-2 添加-o iface -j DROP。创建新 bridge 网络时即会在 FORWARD 链头部确保-j DOCKER-ISOLATION-STAGE-1跳转同文件第 192 行。此外还保留了旧链名DOCKER-ISOLATION第 377 行的兼容清理逻辑。结语一张流量全路径的排查清单理解 libnetwork 的网络模型后可总结出一套实用的排障思路容器无法出网优先检查宿主 nat 表 POSTROUTING 链的 MASQUERADE 规则与 filter 表 FORWARD 链是否放行发布端口无法访问依序检查 nat 表 PREROUTING 中 DOCKER 链的 DNAT 规则、filter 表 FORWARD 中 DOCKER 链的 ACCEPT 规则Swarm 服务端口异常使用 support.sh 中列出的docker network lsoverlay与ingress_sbox命名空间定位或用 ssd.py 检查DOCKER-INGRESS的 DNAT 规则是否就位跨网桥容器通信被意外阻断检查 filter 表DOCKER-ISOLATION-STAGE-1/2规则自定义防火墙策略总被 Docker 覆盖应写入DOCKER-USER链而非 FORWARD 链。libnetwork 的价值在于把这些复杂的命名空间编排、veth 拓扑、iptables/ipvs 编程抽象成统一的 CNMContainer Network Model编程接口使用方式可参考 libnetwork README 中的 Go 示例而理解本文所述的底层数据路径将帮助你更准确地定位集群网络问题也更清楚每一项 Docker 网络配置背后的真实内核行为。赞分享容器运行时云原生【免费下载链接】docker-ce:warning: This repository is deprecated and will be archived (Docker CE itself is NOT deprecated) see the https://github.com/docker/docker-ce/blob/master/README.md :warning:项目地址https://gitcode.com/gh_mirrors/do/docker-ce点击查看免费下载相关推荐Moby libnetwork 网络模型详解Bridge 与 Swarm/Overlay 两种模式下的 iptables 流量转发原理Moby libnetwork 网络模型详解Bridge 与 Swarm/Overlay 两种模式下的 iptables 流量转发原理 本文基于 Moby 仓云原生容器运行时虚拟化容器编排Docker Compose 中 Swarm 模式下 Overlay 网络问题的分析与解决Docker Compose 中 Swarm 模式下 Overlay 网络问题的分析与解决 在 Docker Swarm 集群环境中用户经常遇到一个典型问题云原生容器编排DevOpsCLIMobyDocker Enginerouted 模式 bridge 网络端口发布iptables 规则全解MobyDocker Enginerouted 模式 bridge 网络端口发布iptables 规则全解 routed路由直连是 Docker En云原生容器运行时虚拟化容器编排上一篇Hunyuan3D-2多视图案例如何用2D图片生成3D模型下一篇SvelteKit 入门指南用 Svelte 构建现代化全栈 Web 应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考