使用Linux的bridge设备和iptable功能实现容器之间跨主机通信

2019-02-02| Category: linux, bridge, container| website:
work-single-image

如果玩过docker的网络肯定了解各种为了实现docker容器之间的跨主机方案。 比如docker自带的overlay到weave,fannel,calico等。 有通过GRE格式的overlay进行跨主机通信,或者通过主机路由形式的。 今天我们通过Linux自带的bridge功能来实现容器的跨主机通信。

使用Linux的bridge设备和iptable功能实现容器之间跨主机通信


如果玩过docker的网络肯定了解各种为了实现docker容器之间的跨主机方案。 比如docker自带的overlay到weave,fannel,calico等。 有通过GRE格式的overlay进行跨主机通信,或者通过主机路由形式的。 今天我们通过Linux自带的bridge功能来实现容器的跨主机通信。

拓扑图

图片 1.png

host1和host2分别有两个网口, eth0用于进行外部通信, eth1网卡之间通过交换机连接。 每个主机上都有三个容器, 三个容器具有不同网段的ip地址。 host1和host2上均添加了网桥设备。 并且网桥上配置了ip地址,然后将ETH1网卡接入了网桥设备上, 因此该网卡是类似的三层交换机。 也就是此时host1和host2之间已经可以通过内网联通了。

现在我们就先创建主机上的容器网络, 为了方便我们只创建netns来代替容器网络。

实践

1. 必须开启主机的路由转发功能
host1: # echo 1 > /proc/sys/net/ipv4/ip_forward
host2: # echo 1 > /proc/sys/net/ipv4/ip_forward

2. 创建虚拟网桥 并配置网桥地址
host1:
# ip link add bridge0 type bridge
# ip link set bridge0 up
# ip address add 172.17.1.1/24 dev bridge0

host2:
# ip link add bridge0 type bridge
# ip link set bridge0 up
# ip address add 172.17.1.2/24 dev bridge0

3. 检查是否创建网桥成功
host1:
# ping 172.17.1.2

4. 创建network namespace 模拟docker网络
host1:
# ip netns add c0
# ip netns add c1
# ip netns add c2

host2:
# ip netns add c0
# ip netns add c1
# ip netns add c2

5. 创建veth设备
host1:
# ip link add vethc00 type veth peer name vethc01
# ip link set vetchc01 netns c0
# ip netns exec c0 ip link set vethc01 up

# ip link add vethc10 type veth peer name vethc11
# ip link set vetchc11 netns c1
# ip netns exec c1 ip link set vethc11 up

# ip link add vethc20 type veth peer name vethc21
# ip link set vetchc21 netns c2
# ip netns exec c2 ip link set vethc21 up

host2:
# ip link add vethc00 type veth peer name vethc01
# ip link set vetchc01 netns c0
# ip netns exec c0 ip link set vethc01 up

# ip link add vethc10 type veth peer name vethc11
# ip link set vetchc11 netns c1
# ip netns exec c1 ip link set vethc11 up

# ip link add vethc20 type veth peer name vethc21
# ip link set vetchc21 netns c2
# ip netns exec c2 ip link set vethc21 up

6. 使用veth将netns和网桥连接上
host1:
# ip link set dev vethc00 master bridge0
# ip link set dev vethc10 master bridge0
# ip link set dev vethc20 master bridge0
# bridge link
host2:
# ip link set dev vethc00 master bridge0
# ip link set dev vethc10 master bridge0
# ip link set dev vethc20 master bridge0
# bridge link

7. 设置容器的地址
host1: 
# ip netns exec c0 ip address add 10.10.1.2/24 dev vethc01
# ip netns exec c1 ip address add 10.10.1.3/24 dev vethc11
# ip netns exec c2 ip address add 10.10.2.3/24 dev vethc21

host2: 
# ip netns exec c0 ip address add 10.10.1.4/24 dev vethc01
# ip netns exec c1 ip address add 10.10.2.4/24 dev vethc11
# ip netns exec c2 ip address add 10.10.2.5/24 dev vethc21

8. 配置容器路由 目的是将所有网络流量均转发到网桥
host1:
# 删除每个容器上的默认路由
# ip netns exec c0 ip route add 172.17.1.1 dev vethc01
# ip netns exec c0 ip route default via  172.17.1.1
# ip netns exec c1 ip route add 172.17.1.1 dev vethc11
# ip netns exec c1 ip route default via  172.17.1.1
# ip netns exec c2 ip route add 172.17.1.1 dev vethc21
# ip netns exec c2 ip route default via  172.17.1.1

# ip route add 10.10.1.2 dev bridge0
# ip route add 10.10.1.3 dev bridge0
# ip route add 10.10.2.3 dev bridge0

host2:
# 删除每个容器上的默认路由
# ip netns exec c0 ip route add 172.17.1.1 dev vethc01
# ip netns exec c0 ip route default via  172.17.1.1
# ip netns exec c1 ip route add 172.17.1.1 dev vethc11
# ip netns exec c1 ip route default via  172.17.1.1
# ip netns exec c2 ip route add 172.17.1.1 dev vethc21
# ip netns exec c2 ip route default via  172.17.1.1

# ip route add 10.10.1.4 dev bridge0
# ip route add 10.10.2.4 dev bridge0
# ip route add 10.10.2.5 dev bridge0


9. 检查每一个主机和主机上的容器是否想通 如果上述操作无误的话 应该是主机和容器可以互相联通的

10. 开始进行跨主机通信
分析:
如果host1的c0想访问host2 那么它肯定是通过host1的bridge0路由过去的, 那么如果想host2还能回复c0 则就要求了必须进行一次DNAT转换。

host1:

10.1.1 c0跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.1.2/32 -o bridge0 -j MASQUERADE

10.1.2 c1跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.1.3/32 -o bridge0 -j MASQUERADE

10.1.3 c2跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.2.3/32 -o bridge0 -j MASQUERADE

host2:

10.2.1 c0跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.1.4/32 -o bridge0 -j MASQUERADE

10.2.2 c1跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.2.4/32 -o bridge0 -j MASQUERADE

10.2.3 c2跨主机访问
# iptables -t nat -A POSTROUTING -s 10.10.2.5/32 -o bridge0 -j MASQUERADE


到了这里我们的容器之间就应该可以进行跨主机互相访问了。


11. 容器能够进行外部网络访问
分析:
如果想外部网络访问 那么流量应该是通过ETH0这个网卡出去。 默认情况下如果你的主机访问外网是通过ETH0, 那么默认的路由表就已经配置了通过此网卡到最近的网关地址。 此时同样只需要进行DNAT准换即可。

host1:

10.1.1 c0外网访问
# iptables -t nat -A POSTROUTING -s 10.10.1.2/32 -o eth0 -j MASQUERADE

10.1.2 c1外网访问
# iptables -t nat -A POSTROUTING -s 10.10.1.3/32 -o eth0 -j MASQUERADE

10.1.3 c2外网访问
# iptables -t nat -A POSTROUTING -s 10.10.2.3/32 -o eth0 -j MASQUERADE

host2:

10.2.1 c0外网访问
# iptables -t nat -A POSTROUTING -s 10.10.1.4/32 -o eth0 -j MASQUERADE

10.2.2 c1外网访问
# iptables -t nat -A POSTROUTING -s 10.10.2.4/32 -o eth0 -j MASQUERADE

10.2.3 c2外网访问
# iptables -t nat -A POSTROUTING -s 10.10.2.5/32 -o eth0 -j MASQUERADE

大功告成

注意

  1. 主机必须开启路由转发功能
  2. iptable表的默认规则设置成接受状态 最好能找一个空白的规则表做实验
  3. 所有的link设备应该处于up状态
  4. 如果出现不通时, 不要着急, 一步步排查。
  5. 尽量要先懂得其中原理再实验

总结

之前是做过一段时间docker的网络开发, 所以现在一直都有一些关注Linux方面的网络知识。 Linux的网络非常强大也很复杂, 本人也是只窥探冰山一角。 网上关于这方面的知识也不是很多, 所以希望能分析一些对还在从事docker网络相关的同学有所帮助。

Clients
wupeaking
date
2月2号, 2019
category
Investment, Business