docker是通过libnetwork进行网络管理的,libnetwork中使用了CNM模型(container network model), CNM定义了构建容器虚拟化网络的模型,定义了开发网络驱动的标准化接口和组件。
CNM模型定义了docker网络的基本模型以及支持模型的接口,不同的驱动实现了模型接口为libnetwork提供服务。
CNM模型包含三个部分:
- sandbox: 容器网络协议栈,对容器的接口、路由、DNS等进行管理
- endpoint: 通信端点,实现可以是veth pair。端点可以加入sandbox和network,相当于容器的接口网卡。一个端点只能属于一个网络且只能属于一个sandbox.
- network: 一组可以相互联通的端点,实现可以是bridge,vlan。一个网络可以有多个端点。
sandbox相当于容器自己的虚拟网络协议栈,network相当于一个网桥,而endpoint可以看作是veth pair,一端作为容器的网口,一端连接在网桥上,从而实现容器对外通信,和容器相互通信。
以上面的图为例,演示一下如何创建一个具有上述网络拓扑的三容器应用。我们先不关心docker到底用的什么底层驱动。
首先创建两个网络:frontend和backend。
docker network create backend
docker network create frontend
然后创建三个容器并加入对应的网络:
docker run -it -name container1 --net backend busybox
docker run -it -name container2 --net backend busybox
docker run -it -name container3 --net frontend busybox
其实这里面隐含了两个步骤,为container1,2,3创建sandbox,并且创建endpoint,连接sandbox和network。同一个network内的container能够相互通信。
最后将container2加入到frontend
docker network connect frontend container2
bridge驱动实现机制
下面来分析一下bridge驱动是如何实现的上述功能。
首先在一台linux设备上安装和启动docker后,可以看到多了一个docker0网卡,假设IP为172.17.0.1/16,并且会看到有一条路由
这条路由的意思是协议栈中任何去往171.17.0.0/16的包都从docker0发出,也就是说访问容器内任一容器的包都会被协议栈发往docker0, 然后通过网桥的mac表就能找到对应的容器。进入docker容器内部查看路由表,发现容器内部的网关就是docker0网卡的地址
并且容器内部的网卡IP与docker0 IP是同一个网段
关于docker网络命名空间的问题
当 docker 容器被创建出来后,你会发现使用 ip netns 命令无法看到容器对应的网络命名空间。这是因为 ip netns 命令是从 /var/run/netns 文件夹中读取内容的,而 docker 容器的网络命名空间不是在 /var/run/netns 下,而是位于 /proc/[pid]/ns/net。想要使用 ip netns 命令去管理 docker 容器的网络命名空间,就需要将它的网络命名空间显示在 /var/run/netns 目录下,那就要先找到容器的网络命名空间在哪里,然后做一个软链接即可。
首先查询容器的PID。
然后创建软链接,建议指定在 /var/run/netns/ 中的名字,因为每个容器都是net。
此时就可以用 ip netns 命令去管理 docker 容器的网络命名空间了。