七天用Go写个docker(网络篇)

0.docker 网络实现#

docker 网络分为两部分,一部分是 docker 与宿主机之间的通信,另一部分是 docker 与外部网络之间的通信。docker 与宿主机之间的通信是通过 veth 和 bridge 虚拟网络接口实现的,而与外部网络通信是通过 iptables 流量转发实现的。

1. veth 和 bridge#

veth 是一个虚拟的网络设备,它都是成对出现的,你可以把它理解成马里奥里面的水管,马里奥从一个水管进去,会从另一个水管里面出来,在这里,马里奥就是流量,这两个水管就是一对 veth,我们看下实际操作

  1. 先创建两个网络 namespace

    ip netns add ns1
    ip netns add ns2
  2. 创建一对 Veth

    ip link add veth1 type veth peer name veth2
  3. 为网络 ns1 和 ns2 设置 veth

    ip link set veth1 netns ns1
    ip link set veth2 netns ns2
  4. 设置 veth1、veth2 的网络地址

    ip netns exec ns1 ifconfig veth1 172.15.0.1/24 up
    ip netns exec ns2 ifconfig veth2 172.15.0.2/24 up
  5. 将 ns1 的流量默认从 veth1 中流出

    default 代表 0.0.0.0/0 即在 ns1 中的流量都经过 veth1 的网络设备流出

ip netns exec ns1 route add default dev veth1

通过上面 5 步,我们就将一头水管(veth1)安在了 ns1 上,另一头水管(veth2)安在了 ns2 上,并且设置了 ns1 的流量从 veth1 中流出,这样从 ns1 里面流出的流量都会经过 veth1 流向 veth2, 也就是流到 ns2 上。这样就实现了 ns1 和 ns2 的网络互通。

测试一下,我们在 ns1 中去 ping ns2 的 ip 地址看是否可以 ping 通

可以看到,已经完全可以 ping 通了,证明通过 veth 我们已经将这两个隔离的 namespace 的网络打通了。

bridge#

bridge 也就是网桥,它是一个非常简单的玩意,你可以把它理解成一个交换机,只不过它只有两个口,并且是一个虚拟的网络设备。

  1. 创建一个网桥
    brctl addbr br0
  2. 挂载网络设备
    brctl addif br0 eth0
    brctl addif br0 veth1
  3. 配置路由规则
    # 将宿主机上的某个网段请求路由到br0的网桥上
    route add –net 172.15.0.2/24 dev br0
    我们将网桥 br0 一端挂在了 eth0 网卡上,另一端挂在了 veth1 上这样流量就能从宿主机流到我们的隔离的 ns1 上

2. iptables#

解决了容器的 namesepace 与宿主机之间的通信,我们看下如何解决 ns 与外部网络的通信,我们知道,ns 的 ip 地址是我们自己定义的,这样当 ns 去访问外部网络时,外部网络是不认识这个请求包里面的源地址的,而外部网络也没办法去访问我们 ns 里面的 ip 地址,因为它只认识我们宿主机的 ip 地址。这时我们就需要用到 iptables 了。

2.1 MASQUERADE#

iptables 中的 MASQUERADE 功能可以帮助我们更改请求包的源地址,这样当 ns 请求外部网络时,我们把源地址改成我们宿主机的是不是就可以了,看下实际配置:

  1. 打开 ip 转发
    sysctl -w net.ipv4.conf.all.forwarding=1
  2. 对 ns1 中发出的包添加地址转换(更换源地址)
    iptables -t nat -A POSTROUTING -s 172.15.0.0/24 -o eth0 -j MASQUERADE

2.2 DNAT#

iptables 中的 DNAT 功能可以修改我们请求包的目的地址,并将请求转发到我们目的地址上面,这样当外部网络访问我们宿主机的某个端口时,我们将该包的目标地址改成我们 ns 的地址,直接转发到我们 ns 里面是不是就可以了,看下实际配置:

将宿主机上 80 端口的请求转发到 ns1 的 ip 上

iptables -t nat -A POSTROUTING -p tcp —dport 80 -j DNAT —to destination 172.15.0.1:80
附言#

以上内容都是我自己的理解,如果理解的有偏差欢迎大家一起留言讨论,针对 iptables 我后续会出一系列教程教大家如何使用这个神器,后续文章都会首发于我微信公众号,欢迎大家关注

本作品采用《CC 协议》,转载必须注明作者和本文链接