使用不同的物理网络接口和默认网关从Docker容器进行路由


13

背景资料

我有一台带有两个运行Docker的网络接口的服务器。Docker和某些虚拟化工具一样,创建了一个名为的Linux桥接接口docker0。默认情况下,此接口的IP地址为172.17.42.1,所有Docker容器均以此接口作为网关与该接口通信,并在同一/16范围内分配IP地址。据我所知,所有的网络流量/从容器中经过一个NAT,因此出站似乎来自172.17.42.1,和入它被送到172.17.42.1

我的安装程序如下所示:

                                          +------------+        /
                                          |            |       |
                            +-------------+ Gateway 1  +-------
                            |             | 10.1.1.1   |     /
                     +------+-------+     +------------+    |
                     |     eth0     |                      /
                     |   10.1.1.2   |                      |
                     |              |                      |
                     | DOCKER HOST  |                      |
                     |              |                      | Internet
                     |   docker0    |                      |
                     |   (bridge)   |                      |
                     |  172.17.42.1 |                      |
                     |              |                      |
                     |     eth1     |                      |
                     |  192.168.1.2 |                      \
                     +------+-------+     +------------+    |
                            |             |            |     \
                            +-------------+ Gateway 2  +-------
                                          | 192.168.1.1|       |
                                          +------------+            

问题

我想从路由/任何Docker容器列全国第二的所有通信eth1 192.168.1.2接口的默认网关192.168.1.1,同时具有自/至主机的所有流量走出eth0 10.1.1.2接口的默认网关10.1.1.1。到目前为止,我已经尝试了各种各样的方法,但是我认为最接近正确的一件事是使用iproute2,如下所示:

# Create a new routing table just for docker
echo "1 docker" >> /etc/iproute2/rt_tables

# Add a rule stating any traffic from the docker0 bridge interface should use 
# the newly added docker routing table
ip rule add from 172.17.42.1 table docker

# Add a route to the newly added docker routing table that dictates all traffic
# go out the 192.168.1.2 interface on eth1
ip route add default via 192.168.1.2 dev eth1 table docker

# Flush the route cache
ip route flush cache

# Restart the Docker daemon so it uses the correct network settings
# Note, I do this as I found Docker containers often won't be able
# to connect out if any changes to the network are made while it's     
# running
/etc/init.d/docker restart

提起容器后,这样做后我根本无法从中取出容器。我不确定桥接口的处理方式是否与物理接口用于此类路由的处理方式相同,并且只需要进行完整性检查以及有关如何完成此看似简单任务的任何提示。


您对NAT的评论不正确。设置为伪装在输出接口不是docker0的172.17.0.0/16源地址上。即-A POSTROUTING -s 172.17.0.0/16!-o docker0 -j伪装。这意味着如果来自docker容器的数据包通过eth0或eth1退出,它将采用该接口的IP地址,而不是172.17.xx IP。
马特

谢谢你的评论。这样会改变我从特定接口进行路由的方法,对吗?你知道我该怎么做吗?从目前的情况来看,Docker似乎只是使用主机系统的默认网关,而这并不是理想的行为。我希望Docker将所有内容路由到特定接口之外。

我不确定为什么我的建议不起作用。但是另一种选择可能是使用管道。 github.com/jpetazzo/pipework 泊坞窗帮助的高级网络部分也可能对阅读很有帮助docs.docker.com/articles/networking-
马特

@Matt感谢您对此的建议。据我了解,启动容器后,管道工作需要其他命令才能使其访问正确的网关,这似乎是一个安全漏洞。它最初是为了将多个主机之间的容器连接在一起而创建的,这实际上不是我的目标。至于Docker高级网络文档,我将再次看一下,但是到目前为止,在这种情况下它并没有帮助(我在此处发布之前已经阅读过)。我将在Docker Github Issues页面上发布此帖子的链接,并查看它的运行方式。

这是回到Github问题的链接:github.com/docker/docker/issues/13762

Answers:


1

您可能还必须在iptables设置上查找更多内容。Docker将来自容器子网的所有流量(例如172.17.0.0/16)伪装成0.0.0.0。如果您运行iptables -L -n -t nat,您可以在nat表下看到POSTROUTING链,该链将执行以下操作-

连锁投篮(接受政策)
目标prot opt源目的地
化妆舞会全部-172.17.0.0/16 0.0.0.0/0

现在,您可以删除此规则,并将其替换为伪装从容器的子网到第二个接口的IP-192.168.1.2的所有流量的规则,这就是您想要的。假设删除规则是POSTROUTING链下的第一条规则-

iptables -t nat -D POSTROUTING 1

然后添加此自定义规则-

iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT-源192.168.1.2

实际上,它会伪装到输出接口。该目标部分只是说,将从源子网到任何目标的任何流量都将被伪装。
马特

不幸的是,这没有用。我的完整流程是运行:ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache iptables -t nat -D POSTROUTING 1 iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 192.168.1.2 /etc/init.d/docker restart。然后,我尝试从容器运行ping和traceroute,但无法到达任何东西。

我有一个类似的要求,主机具有辅助IP设置以访问公共IP。我希望docker容器也遵循同样的方法。在这种情况下有效:“ iptables -t nat -I POSTROUTING 1 -s 172.17.0.0/16 -d PUBIP / 32 -j SNAT --to-source SECONDARYIP”
Ram

1

我和一个朋友碰到了这个确切的问题,我们想让docker支持服务请求的多个网络接口。我们专门与AWS EC2服务一起使用,在该服务中,我们还附加/配置/创建了其他接口。在这个项目中,除了您需要的以外,因此我将尝试仅在此处包括您需要的内容。

首先,我们要做的是为以下内容创建一个单独的路由表eth1

ip route add default via 192.168.1.2 dev eth1 table 1001

接下来,我们配置了mangle表以设置一些来自的连接标记eth1

iptables -t mangle -A PREROUTING -i eth1 -j MARK --set-xmark 0x1001/0xffffffff
iptables -t mangle -A PREROUTING -i eth1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff

最后,我们为所有fwmarks 添加此规则以使用我们创建的新表。

ip rule add from all fwmark 0x1001 lookup 1001

下面的iptables命令将还原连接标记,然后允许路由规则使用正确的路由表。

iptables -w -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff

我相信这是我们更复杂的示例所需要的,在这个示例中(如我所说),我们的项目是eth1在启动时附加/配置/ 启动接口。

现在,此示例不会停止eth0从服务请求到的连接,docker0但我相信您可以添加路由规则来防止这种情况。


@stuntmachine只是好奇您是否有机会尝试一下?如果您已经自己找到了解决方案,可以共享您的解决方案吗?
williamsbdev

@williamsbdev,您好-旧帖子和很远的看法,但是您认为此解决方案也可以解决我的问题吗? stackoverflow.com/questions/51312310/...
海盗旗

2
乔利·罗杰(Jolly Roger)-我相信这会解决您的问题。最近,我有另一个团队向我询问此博客文章(与此处的解决方案基本相同),他们说效果很好。williamsbdev.com/posts/docker-connection-marking
williamsbdev18年

0

化妆舞会不是来自172.17.42.1,而是

 -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

这意味着该规则行不通。

ip rule add from 172.17.42.1 table docker

试试看

ip rule add from 172.17.0.0/16 table docker

不幸的是,这没有用。我的容器仍在路由到与主机相同的默认网关。这是我运行的内容: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache /etc/init.d/docker restart 在容器中运行了traceroute,第一跳是10.1.1.1,当时它应该是192.168.1.1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.