CNM
CNM (Container Network Model) 是 Docker 发布的容器网络标准。也有不是Docker发布的标准,例如CNI.CNI是由以Kubernetes为代表的公司发布的标准。主要还是因为Kubernetes面对的是多容器而不是单容器,所以标准和docker的不一样,有专门的博客来介绍为什么Kubernetes不选择CNM作为容器的网络标准。虽然最后k8s的CNI成为了容器网络标准,但是libnetwork的容器网络模式使得pod中容器共享网络环境成为可能。
libnetwork
Docker网络部分代码被抽离并单独成为了Docker的网络库,即libnetwork。
(图片来自网络,侵删)
从上图可以看到libnetwork和docker daemon之间的关系。Docker daemon通过调用libnetwork对外提供的API完成网络的创建和管理等功能。libnetwork中则使用了CNM来完成网络功能的提供。同时也可以看到CNM的三个重要元素:
沙箱(sandbox):沙箱代表一个容器网络栈的信息。可以对容器的接口、路由和DNS设置等进行管理。沙盒的实现可以是Linux network namespace、FreeBSD Jail或者类似的机制。一个沙盒可以有多个端点和多个网络。
接入点(Endpoint):接入点将沙箱连接到网络中,代表容器的网络接口,接入点的实现通常是 Linux 的 veth 设备。一个接入点只可以属于一个网络并且只属于一个沙箱。
网络(Network):网络是一组可以直接互相联通的接入点。网络的实现可以是Linux bridge、VLAN等。它将多接入点组成一个子网,并且多个接入点之间可以相互通信。
了解了CNM,接下来我们来认识libnetwork的四种常见网络模式:
- null 空网络模式:可以帮助我们构建一个没有网络接入的容器环境,以保障数据安全。
- bridge 桥接模式:可以打通容器与容器间网络通信的需求。
- host 主机网络模式:可以让容器内的进程共享主机网络,从而监听或修改主机网络。
- container 网络模式:可以将两个容器放在同一个网络命名空间内,让两个业务通过 localhost 即可实现访问。
(1)null 空网络模式
空网意味着没有网络信息,就是一个单纯的没有连接网络的一个容器。我们可以使用docker命令来创建一个空网模式的容器来检查容器内部的网络信息,首先我们可以先打开正常模式的容器来看一下网络信息:
/yapi # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:12:00:02
inet addr:172.18.0.2 Bcast:172.18.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:597521 errors:0 dropped:0 overruns:0 frame:0
TX packets:1194752 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:126660347 (120.7 MiB) TX bytes:254347460 (242.5 MiB)
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:38 errors:0 dropped:0 overruns:0 frame:0
TX packets:38 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2362 (2.3 KiB) TX bytes:2362 (2.3 KiB)
/yapi # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
可以看到里面是有网卡信息eth0,ip也正常展示。接下来我们新建空网模式的容器,再来观察它的网卡信息和路由信息:
[root@VM-12-13-centos ~]# docker run --net=none -it busybox
/ # 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)
/ # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
/ #
可以看到,新建的容器没有eth0的信息,并且没有ip信息。这种模式如果不进行特定的配置是无法正常使用的,但是优点也非常明显,它给了用户最大的自由度来自定义容器的网络环境。
(2)bridge 桥接模式
bridge桥接模式是Docker网络模型中的一种常见网络模式,它可以让多个容器之间相互通信,也可以与外部网络进行通信。在bridge模式下,每个容器都会分配一个唯一的IP地址,并且可以通过容器名称或者IP地址互相访问。我们先来了解关于Docker的bridge模式的实现原理。
linux veth 和linux bridge
(图片来自网络,侵删)
docker 的bridge模式就是这样的,可以看到docker0像一个交换机,把不同的容器连通,是他们可以互相通信。一般我们在使用docker做网络映射时,通常都加了-p指定端口,用来做容器间的通信或者与容器外部通信。
(3)host 主机网络模式
host 主机网络模式让容器内的进程共享主机的网络栈,从而使得容器内的应用程序能够直接与主机和外部网络进行通信,同时也可以避免了端口映射和NAT等额外的网络开销。使用这种模式的时候,libnetwork并不会创建独立的network namespace。同样,我们查看host模式下容器内部的网络信息
[root@VM-12-13-centos ~]# docker run -it --net=host busybox
/ # ip a
1: lo: mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc mq qlen 1000
link/ether 52:54:00:4f:76:46 brd ff:ff:ff:ff:ff:ff
inet 10.0.12.13/22 brd 10.0.15.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe4f:7646/64 scope link
valid_lft forever preferred_lft forever
3: br-85f2d3e30fa7: mtu 1500 qdisc noqueue
link/ether 02:42:3e:54:35:a5 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.1/16 brd 172.22.255.255 scope global br-85f2d3e30fa7
valid_lft forever preferred_lft forever
4: br-c42d4a549c65: mtu 1500 qdisc noqueue
link/ether 02:42:39:a3:4a:26 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-c42d4a549c65
valid_lft forever preferred_lft forever
inet6 fe80::42:39ff:fea3:4a26/64 scope link
valid_lft forever preferred_lft forever
5: docker0: mtu 1500 qdisc noqueue
link/ether 02:42:fd:43:ed:84 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:fdff:fe43:ed84/64 scope link
valid_lft forever preferred_lft forever
6: br-f057a92711b7: mtu 1500 qdisc noqueue
link/ether 02:42:78:b5:0a:b4 brd ff:ff:ff:ff:ff:ff
inet 172.21.0.1/16 brd 172.21.255.255 scope global br-f057a92711b7
valid_lft forever preferred_lft forever
/ #
可以看到容器内的网络环境与主机完全一致。但是,但是,容器其他方面,如文件系统、进程列表等还是和宿主机隔离的。所以如果是集群规模比较大的情况,还是不是用这种host模式的。
(4)container 网络模式
container 网络模式可以将多个容器放在同一个网络命名空间内。当这些容器需要共享网络,但其他资源仍然需要隔离时就可以使用 container 网络模式,例如我们开发了一个 http 服务,但又想使用 nginx 的一些特性,让 nginx 代理外部的请求然后转发给自己的业务,这时我们使用 container 网络模式将自己开发的服务和 nginx 服务部署到同一个网络命名空间中。
当创建一个新容器时,我们可以使用以下命令将其加入到已存在的网络命名空间:
docker run -d --name my-nginx1 nginx
docker run -d --name my-nginx2 --network container:my-nginx1 nginx
通过上述命令,我们可以在my-nginx2容器中通过 curl http://localhost来访问my-nginx1容器的Web服务。在container模式下,所有的容器都共享同一个网络栈和IP地址,因此它们之间的网络性能通常比bridge模式更高,但安全性可能会降低。
总结:
- Docker使用CNM为通信标准来完成网络实现;
- CNM三要素:沙箱(Sandbox)、接入点(Endpoint)、网络(Network);
- Libnetwork 常见四种网络模式:null 空网络模式、bridge 桥接模式、host 主机网络模式、container 网络模式;