Docker网络现状
为了解决容器网络性能低下、功能不足的问题,Docker启动了子项目“Libnetwork”。Libnetwork提出了新的容器网络模型(Container Network Model,简称CNM),定义了标准的API用于为容器配置网络,其底层可以适配各种网络驱动(如图Docker-network01所示)。CNM有三个:
-
沙盒
沙盒是一个隔离的网络运行环境,保存了容器网络栈的配置,包括了对网络接口、路由表和DNS配置的管理。在Linux平台上,沙盒是用Linux Network Namespace实现的,在其他平台上可能是不同的概念,如FreeBSD Jail。一个沙盒可以包括来自多个网络的多个Endpoint(端点)。 -
Endpoint
Endpoint将沙盒加入一个网络,Endpoint的实现可以是一对veth pair或者OVS内部端口,当前的Libnetwork使用的是veth pair。一个Endpoint只能隶属于一个沙盒及一个网络。通过给沙盒增加多个Endpoint可以将一个沙盒加入多个网络。 -
网络
网络包括一组能互相通信的Endpoint。网络的实现可以是Linux bridge、vlan等。
从CNM的概念角度讲,Libnetwork的出现使得Docker具备了跨主机多子网的能力,同一个子网内的不同容器可以运行在不同主机上。比如,同属于192.168.0.0/24子网IP地址分别为192.168.0.1和192.168.0.2的容器可以位于不同的主机上且可以直接通信,而持有IP 192.168.1.1的容器即使与IP为192.168.0.1的容器处于同一主机也不能互通。
Libnetwork已经实现了五种驱动(driver):
-
bridge
Docker默认的容器网络驱动。Container通过一对veth pair连接到docker0网桥上,由Docker为容器动态分配IP及配置路由、防火墙规则等。 -
host
容器与主机共享同一Network Namespace,共享同一套网络协议栈、路由表及iptables规则等。容器与主机看到的是相同的网络视图。 -
null
容器内网络配置为空,需要用户手动为容器配置网络接口及路由等。 -
remote
Docker网络插件的实现。Remote driver使得Libnetwork可以通过HTTP RESTful API对接第三方的网络方案,类似SocketPlane的SDN方案只要实现了约定的HTTP URL处理函数及底层的网络接口配置方法,就可以替换Docker原生的网络实现。 -
overlay
Docker原生的跨主机多子网网络方案。主要通过使用Linux bridge和vxlan隧道实现,底层通过类似于etcd或consul的KV存储系统实现多机的信息同步。overlay驱动当前还未正式发布,但开发者可以通过编译实验版的Docker来尝试使用,Docker实验版同时提供了额外的network和service子命令来进行更灵活的网络操作,不过,需要内核版本>=3.16才可正常使用。
以上五种驱动已经随Docker 1.8一同发布。Docker的网络模块是可插拔式的,默认有五种网络模式可以选择,通过docker network ls这个命令来查看本机中所有的网络模式。
[root@VM_0_14_centos ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c79756cf9cde bridge bridge local
204025a5abbc host host local
9b9024f5ac40 macvlan macvlan local
6478888548d8 none null local
p2e02u1zhn8x overlay overlay swarm
bridge
Docker daemon启动时会在主机创建一个Linux网桥(默认为docker0,可通过-b参数手动指定)。容器启动时,Docker会创建一对veth pair(虚拟网络接口)设备,veth设备的特点是成对存在,从一端进入的数据会同时出现在另一端。Docker会将一端挂载到docker0网桥上,另一端放入容器的Network Namespace内,从而实现容器与主机通信的目的。bridge模式下的网络拓扑图如图docker-network02所示:
在桥接模式下,Docker容器与Internet的通信,以及不同容器之间的通信,都是通过iptables规则控制的。总之,Docker网络的初始化动作包括:创建docker0网桥、为docker0网桥新建子网及路由、创建相应的iptables规则等。Bridge模式是Docker默认的容器运行模式,以bridge模式启动的容器,默认会从172.17.42.1/16子网内分配IP。
bridge模式是docker的默认网络模式,此模式会为每一个容器设置network namespace、ip地址等,在docker启动时候,就会在主机上创建一个名为docker0的虚拟网桥,在该主机上启动的docker容器都会连接到这个虚拟网桥上,这样就可以和同一宿主机上桥接模式的其它容器进行通信啦。
#运行一个名为box1的busybox容器,网络模式是bridge
[root@VM_0_14_centos ~]# docker run -itd --name box1 busybox 24d88c0b3af9df06c367e9991c7628a2eaeb54e4f40a5326585fcf1274ea2f8f
[root@VM_0_14_centos ~]# docker exec -it box1 sh / # ip addr show eth0 68: eth0@if69: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever
#运行一个名为box2的busybox容器,网络模式是bridge
[root@VM_0_14_centos ~]# docker run -itd --name box2 busybox f3980be667731ae36aa567910f4a7e80fcc877c33dec59b70f7dc6e49f8fe3f2
[root@VM_0_14_centos ~]# docker exec -it box2 sh / # ping 172.18.0.2 PING 172.18.0.2 (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.146 ms 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.120 ms ...
从上面的示例可以看出,同一节点下的容器默认都是可以彼此交流哒~
host
host模式下,容器不会设置自己的network namespace、ip等,而是和宿主机共用,通过--network host可以将容器直接绑定在Docker主机的网络,没有网络隔离,但是其它方面,比如文件系统、进程列表还是与宿主机隔离的。外界也可以直接访问容器。
#运行一个名为box3的busybox容器,网络模式是host
[root@VM_0_14_centos ~]# docker run -itd --network host --name box3 busybox
none
使用的none模式后,这个容器就是封闭的,不会去参与网络通信,这样就能够保证容器的安全性。
#启动一个名为box3的busybox容器,网络模式是none [root@VM_0_14_centos ~]# docker run -itd --network none --name box3 busybox f431bffbd88712f940aee745d7a1e6795f3ef545f79bb2151628f9198c9b1c1e
[root@VM_0_14_centos ~]# docker exec -it box3 sh / # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
macvlan
对于某一些应用程序,比如需要监视网络流量,期望直接连接到物理网络,这种情况下,可以使用macvlan的网络模式,docker会为容器的虚拟网络接口分配MAC地址。
创建一个macvlan网络
$ docker network create -d macvlan --subnet=172.16.86.0/24 --gateway=172.16.86.1 -o parent=eth0 macvlann
启动一个busybox容器,网络模式是macvlan
[root@VM_0_14_centos ~]# docker run -itd --network macvlann --name box busybox 77436b9c2c1638c498396ff765c27ffec885ff62d892c2064f41497460037156
[root@VM_0_14_centos ~]# docker exec box ip addr show eth0 67: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:10:56:03 brd ff:ff:ff:ff:ff:ff inet 172.16.86.3/24 brd 172.16.86.255 scope global eth0 valid_lft forever preferred_lft forever
[root@VM_0_14_centos ~]# docker exec box ip route\ default via 172.16.86.1 dev eth0 172.16.86.0/24 dev eth0 scope link src 172.16.86.3
overlay
overlay网络是使用在swarm集群中,用于连接不同主机上的docker容器,允许不同宿主机上的容器相互通信。
#通过此命令我们可以创建集群中的manager,在输出信息中会包含一个token
[root@VM_0_14_centos ~]# docker swarm init
然后执行以下命令将工作节点加入集群
$ docker swarm join --token <token> <manager_host>:2377
新建一个overlay网络
[root@VM_0_14_centos ~]# docker network create --driver=overlay --attachable overlay
#列出docker swarm中所有节点,其中VM_0_14_centos这个节点是leader,其它两个节点是工作节点 [root@VM_0_14_centos ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION sa3m7r6m1lg4iz6dcfvyggr6s * VM_0_14_centos Ready Active Leader 19.03.11 azpq5fgozz5rd9y4lm11u69wq VM_0_15_centos Ready Active 19.03.11 5mbf4l9k0y8zg69il7pk5ztxw VM_0_16_centos Ready Active 19.03.7
分别在manager节点和work节点上启动一个busybox容器,并连接到overlay网络
$ docker run -it --network overlay --name box4 sh
然后我们在同一个overlay网络下的容器中互相去ping对方,是可以连接哒~
我们也可以利用overlay网络去创建一个集群服务,使用docker swarm去管理我们的集群服务。现在创建一个五副本的连接到overlay网络的nginx服务,暴露端口为80;
$ docker service create --name my-nginx --publish target=80,published=80 --replicas=5 --network overlay nginx
利用docker ps命令我们可以发现工作节点上也启动了nginx应用,这个就是通过overlay来实现不同主机中容器之间的通信。细心的小伙伴还会发现在任一节点结束一个副本,集群服务就会重启一个新的副本,会一直保持节点内的nginx副本数量为五个,有木有觉得还蛮有意思的!
[root@VM_0_15_centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1bf24ed438cf nginx:latest "/docker-entrypoint.…" 16 minutes ago Up 16 minutes 80/tcp my-nginx.3.lcrhn4eelu1d5z4ln1ss9dczq
overlay网络模型在docker集群节点间的加入了一层虚拟网络,它有独立的虚拟网段,因此docker容器发送的内容,会先发送到虚拟子网,再由虚拟子网包装为宿主机的真实网址进行发送。
总 结
docker网络就介绍到这啦,通过上面的实践我们不难发现:
1、在需要多个docker容器在同一个宿主机上进行通信,最好就直接使用默认的bridge模式;
2、当多个应用程序需要组成集群提供高可用服务时候,overlay肯定是最佳的选择;
3、host模式对于优化性能以及在容器需要处理大量端口的情况下很有用,因为它不需要NAT,并且也不会为每个端口创建“ userland-proxy”。当想要容器对网络传输效率有较高要求,就可以选择host模式,但是要注意端口占用的问题哦~