接上篇网络虚拟化之副本网卡技术MACVLAN,基础的虚拟网络技术基本都认识到,本文来看看Docker中是怎么用的。网络虚拟化的运用才是真正开始解决问题。

几个核心点:
1.Docker开箱即用的网络模式只有3种
2.允许自己定制网络,制定给Docker容器
3.底层技术并没有脱离之前介绍的基础技术范畴
4.虽然当前是K8S的天下,但Docker也是发布了第一个容器网络的业界标准
相对Kubernetes,Docker的网络模型要相对简单很多。但也最适合我们来充分理解前面介绍的各类网络虚拟化的基础技术。
Docker 提供的三种开箱即用的网络方案:
1. 桥接模式
为每个容器创建veth pair,同时主机上有一个Linux bridge,同一个主机上容器二层网络联通bridge,但不解决主机间网络通信,Linux bridge可以设置IP充当路由器角色,所以可以配置做到容器访问主机设备和外部网络,详细参见:网络虚拟化之虚拟交换机技术Linux Bridge
2. 主机模式
该模式下,Docker 不会为新容器创建独立的网络名称空间,这样容器一切的网络设施,如网卡、网络栈等都直接使用宿主机上的真实设施,容器也就不会拥有自己独立的 IP 地址。此模式下与外界通信无须进行 NAT 转换,没有性能损耗,但缺点也十分明显,没有隔离就无法避免网络资源的冲突,譬如端口号不允许重复。
说白了,这种模式容器直接复用的主机网卡、主机网络,性能损耗最低,但达不到容器隔离的目的。
3. 空模式
Docker 会给新容器创建独立的网络名称空间,但是不会创建任何虚拟的网络设备,此时容器能看到的只有一个回环设备(Loopback Device)而已。提供这种方式是为了方便用户去做自定义的网络配置,如自己增加网络设备、自己管理 IP 地址,等等。
由此,一般可以定制网络模式。有如下几种:
1. 容器模式
多个容器共享一个容器网络,本质上是加入同一个网络空间,共享网络资源。该网络空间下的网络设备就自行定义了。
2. MACVLAN模式
先创建一个副本网卡,为容器指定这个副本网卡,容器通过副本网卡的 MAC 地址使用宿主机上的物理设备。在追求通信性能的场合,这种网络是最好的选择。
Docker 的 MACVLAN 只支持 Bridge 通信模式,因此在功能表现上与桥接模式相类似。
3.Overlay 模式
使用 docker network create -d overlay 创建,Docker 说的 Overlay 网络实际上就是特指 VXLAN,主要用于 Docker Swarm 服务之间进行通信。然而由于 Docker Swarm 败于Kubernetes,在一些简化场景使用Docker Swarm时会用到,并未成为主流。
安装好Docker后,使用docker network ls 可以看到Docker支持的三种网络模式。
NETWORK ID NAME DRIVER SCOPE
6cd2e38e5103 bridge bridge local
401779e10c7e host host local
a6700b7c7b97 none null local
创建一个测试容器:
- docker create --name mnginx \
- --network bridge \
- --publish 27777:80 \
- nginx:latest
创建后,执行
docker start mnginx
再执行,查看刚启动的容器
docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- b2246463d5ae nginx:latest "/docker-entrypoin..." 27 seconds ago Up 2 seconds 0.0.0.0:27777->80/tcp mnginx
此时查看主机的网络设备,会增加如下两个:
docker0: flags=4163
mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:c0ff:fe93:b9c5 prefixlen 64 scopeid 0x20
ether 02:42:c0:93:b9:c5 txqueuelen 0 (Ethernet)
RX packets 96234 bytes 5256444 (5.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 102552 bytes 1673521274 (1.5 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0veth8edc141: flags=4163
mtu 1500
inet6 fe80::8449:40ff:fee2:5680 prefixlen 64 scopeid 0x20
ether 86:49:40:e2:56:80 txqueuelen 0 (Ethernet)
RX packets 8 bytes 656 (656.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8 bytes 656 (656.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0即为一个Linux Bridge,带有IP地址,可以将发往该地址的请求转发给主机网络栈,充当容器的路由器。
veth8edc141是指派给容器veth pair中的一端,连接在网桥docker0上,通过bridge link可以看到:
30: veth8edc141 state UP @(null):
mtu 1500 master docker0 state forwarding priority 32 cost 2
veth的另一端如何查看呢?你会发现通过常规方法
ip netns list
查不到容器定义的网络命名空间。
在 docker 中,默认情况下不会将容器network namespace 添加到 linux 运行时数据中(从 /run 挂载到 /var/run)。
怎么办呢?请参考:docker(Kubernetes)环境如何查看network namespace
可以查看到如下容器内的网络设备信息:
1: lo:
mtu 65536 qdisc noqueue state UNKNOWN group default 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
29: eth0@if30:mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2/64 scope link
valid_lft forever preferred_lft forever
在容器内veth pair另一端被命名为eth0@if30,ip地址是172.17.0.2。
在容器内ping主机IP和docker0 IP,看下结果:

nsenter -t 4227 -n ping 172.17.0.1
上述命令参数含义请参阅docker(Kubernetes)环境如何查看network namespace。
Docker的主机模式跟上述示例中的端口映射的效果一样,其他网络定制模式可以试验一下,底层还是在VXLAN、MacVlan的技术应用或者多种网络技术的组合。