要搞清楚这个问题我们首先需要搞清楚,在集群中的容器和非集群的容器,数据流向到底是怎样的
数据包流转:
下面我假设一个数据包从客户端发起到应用,当容器处于不同的网络中时:
docker容器(bridge网络)
ipv4
外部请求进入宿主机
-
路径:外部用户访问主机
-
流量首先进入宿主机的
iptables
的PREROUTING
链。 -
通过PREROUTING 的DNAT规则转发到docker0网卡(即把目标ip修改为容器IP)
-
目标容器接收请求
-
路径: 数据进入容器
-
通过veth,veth连接了主机网络命名空间和容器命名空间,数据发送到容器内eth0
-
应用监听的端口取得数据,响应数据按反向路径返回
-
ipv6
外部请求进入宿主机
-
路径:外部用户访问主机
-
流量首先进入宿主机的
ip6tables
的PREROUTING
链。 -
通过PREROUTING 的DNAT规则转发到docker0网卡(即把目标ip修改为容器IP)
-
目标容器接收请求
-
路径: 数据进入容器
-
通过veth,veth连接了主机网络命名空间和容器命名空间,数据发送到容器内eth0
-
应用监听的端口取得数据,响应数据按反向路径返回
-
可以看到基本是一致的,只是ipv6使用了ip6tables,而ipv4使用的iptables。
docker swarm中的容器(overlay网络)
ipv4
外部请求进入宿主机
- 路径:外部用户访问任意Swarm节点
- 关键动作:
- 流量首先进入宿主机的
iptables
的PREROUTING
链。 - 通过
DOCKER-INGRESS
自定义链的DNAT规则,将目标端口的流量转发至ingress-sbox
网络命名空间的IP。 ingress-sbox
是一个特殊的网络命名空间,连接了docker_gwbridge
(172.18.0.0/16)和ingress
Overlay网络。
- 流量首先进入宿主机的
Ingress网络处理负载均衡
- 路径:
ingress-sbox
命名空间 → IPVS负载均衡 - 关键动作:
iptables
对访问流量打上标记,触发IPVS负载均衡机制。- IPVS根据标记查询路由表,通过轮询(rr)策略将流量分发到实际服务容器的虚拟IP(如10.0.0.5和10.0.0.6)。
- 此阶段实现了Swarm的Routing Mesh特性,确保请求可被集群中任意节点的服务实例处理。
Overlay网络跨节点传输
- 路径:源节点 → 目标节点(容器所在节点)
- 关键动作:
- 若目标容器在其他节点,数据包通过VXLAN隧道封装,在
ingress
Overlay网络(10.0.0.0/24)中跨主机传输。 docker_gwbridge
负责宿主机与Overlay网络的桥接,确保跨节点通信的隔离性和安全性。
- 若目标容器在其他节点,数据包通过VXLAN隧道封装,在
目标容器接收请求
- 路径:目标节点 → 服务容器
- 关键动作:
- 数据包通过目标节点的
docker_gwbridge
解封装,转发至对应容器的虚拟网络接口。 - 容器通过自定义网络(如Overlay网络)直接接收请求,响应数据按反向路径返回
- 数据包通过目标节点的
ipv6
理论上同上(只是NAT由ip6talbes完成),但是我发现根本就docker engine 根本就不写ipv6这个栈的规则。具体看下面
二、docker swarm到底对ipv6支持如何?
个人实践
网络上说可以支持的
以如下这篇文章:配置docker swarm网络支持访问ipv6-天翼云开发者社区 - 天翼云 最为典型,整个中文互联网其他的说支持的,基本也是抄的这个文章。
实际情况
主要有两个大问题:
-
docker engine压根不写ipv6的规则,即使手动加上也无济于事(加上规则也依旧不能访问应该是vxlan的实现有问题) 这是同一个服务的ipv4规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
[root@geosmarter01 ~]# iptables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination DOCKER-INGRESS all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match src-type LOCAL MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match src-type LOCAL MASQUERADE all -- 162.168.1.0/24 0.0.0.0/0 MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match src-type LOCAL Chain OUTPUT (policy ACCEPT) target prot opt source destination DOCKER-INGRESS all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL Chain DOCKER-INGRESS (2 references) target prot opt source destination DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:30000 to:172.17.0.2:30000 RETURN all -- 0.0.0.0/0 0.0.0.0/0 Chain DOCKER (2 references) target prot opt source destination
这是同一个服务的ipv6规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
[root@geosmarter01 ~]# ip6tables -t nat -nL Chain PREROUTING (policy ACCEPT) target prot opt source destination DOCKER all ::/0 ::/0 ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all ::/0 ::/0 ADDRTYPE match src-type LOCAL MASQUERADE all 2001:db8:1::/64 ::/0 Chain OUTPUT (policy ACCEPT) target prot opt source destination DOCKER all ::/0 ::/0 ADDRTYPE match dst-type LOCAL Chain DOCKER (2 references) target prot opt source destination
-
分配ipv6地址报错(我试了几十个保留地址段都是如此):
1
2月 12 17:32:57 geosmarter01 dockerd[1205697]: time="2025-02-12T17:32:57.608232468+08:00" level=error msg="Failed allocation for network vweecm9pd8vn3mfogpaxv2kti" error="failed allocating pools and gateway IP for network vweecm9pd8vn3mfogpaxv2kti: cannot find address pool for poolID:2001:db8:9::/64/invalid Prefix" module=node node.id=lwae3e2a1pqf7bvxlqwjqdor4
github和开源社区反馈
- 结论是最后这个人使用了socat另外开辟了端口转发到ipv4端口
因此,从我发现的情况来看,当您使用 docker swarm 时,覆盖网络驱动程序对 IPv4 和 IPv6 的工作方式似乎不同。
对于 IPv4,它会自动将机器 IP 后面的每个暴露端口进行 NAT。
对于 IPv6,不会执行 NAT。真正痛苦的是,尽管 docker 只对 IPv4 执行 NAT,但它却同时监听 IPv4 和 IPv6 套接字。
- 这个问题在docker官方论坛无结论
Support for ipv6 in docker swarm mode - General - Docker Community Forums
我们能够在 ipv4 中创建带有覆盖网络的 docker swarm,但是在创建覆盖网络期间启用 ipv6 支持时,将覆盖网络连接到容器时遇到问题。
- 这个问题在docker官方论坛根本没有人理
https://forums.docker.com/t/cant-attach-container-to-overlay-network-with-ipv6-enabled/141593
我在 Debian 上使用 Docker 版本 26.1.2。我一直在尝试在 Swarm 覆盖网络上运行的某些容器内启用 IPv6。我能够让它在默认桥接驱动程序上工作,但当我使用覆盖驱动程序(这是我需要的)时,它要么失败,要么…
- 这是docker-ce开源项目的issue中,有人遇到相同问题,结论就是目前docker 支持有问题
https://github.com/moby/moby/issues/43615
为什么这个问题仍然存在。
我正在测试一个全新的 docker 安装 27.4.1,但我仍然无法创建一个有效的启用 IPV6 的覆盖网络。
启动连接到默认网桥的容器时,容器会获得正确的 IPv6 地址。
创建启用 IPv6 的覆盖网络时,网络本身会被创建,但如果连接到此覆盖网络,容器将无法启动。Docker 对 IPv6 功能不感兴趣。它“意外地”在版本 25 中运行,但不受支持。Docker 和 Docker Swarm 永远不应该在生产环境中使用;它们不是生产工具。Docker 适用于容器化开发(VSCode 等),但绝对不适用于需要可靠且一致地工作的系统。
- 这个人的博客提供了一些办法,但是我试了其实也是行不通的,他自己在文章末尾也吐槽
Docker Networks Part 2: Customize the network for Docker Swarms
但即便如此,它仍然不起作用!启用 IPv6 后(
--ipv6
标志),不会为群集内的节点分配任何 IP 地址。然后出现了一个令人沮丧的认识:docker swarm 集群尚不支持 IPv6
替代方案
k8s或者轻量化k8s(k3s)
可以使用成熟的k8s体系,能够k8s支持很完整的CNI生态,可以支持ipv6。具体部署方案,可以跟我商量,原理上我已经走通。
使用socat转发流量
因为ipv4和ipv6是完全独立的两个网络栈,数据包发到ipv4的话是没办法流转到ipv6体系中的,Socat 是 Linux 下的一个多功能的网络工具,可以监听一个ipv6的端口,把数据转发到ipv4的某个端口上。
如果使用socat的话,docker swarm 各个节点中间配置ipv4通信即可,数据流转就是: 客户端 → 主机socat端口(ipv6) → 主机中服务的端口(ipv4) → 以ipv4在服务中流转数据 → 主机socat端口(ipv6) → 客户端
建议使用第一种,第二种socat的规则维护需要每个机器都维护,并且需要我们自己代码实现来维护它的规则,还需要多开一倍的端口。