当你开始大规模使用Docker时,你会发现需要了解很多关于网络的知识。Docker作为目前最火的轻量级容器技术,因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求
Docker使用Linux桥接(参考《Linux虚拟网络技术》),在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器
当你安装Docker时,它会自动创建三个网络。bridge(创建容器默认连接到此网络)、 none 、host。你可以使用以下docker network ls命令列出这些网络:
该bridge网络代表docker0,所有Docker安装中存在的网络。除非你使用该docker run --network=选项指定,否则Docker守护程序默认将容器连接到此网络
我们在使用docker run创建Docker容器时,可以用 --net 选项指定容器的网络模式,Docker可以有以下4种网络模式:
host模式:使用 --net=host 指定。
none模式:使用 --net=none 指定。
bridge模式:使用 --net=bridge 指定,默认设置。
container模式:使用 --net=container:NAME_or_ID 指定
Docker网络模式 | 配置 | 说明 |
---|---|---|
host模式 | –net=host | 容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 |
container模式 | –net=container:NAME_or_ID | 容器和另外一个容器共享Network namespace。创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围 |
bridge模式 | –net=bridge | (默认为该模式)此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。 |
none模式 | –net=none | 该模式关闭了容器的网络功能。 |
host模式
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
Host模式如下图所示:
众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。
一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
例如,我们在172.25.6.1/24的机器上用host模式启动一个ubuntu容器
[root@server1 ~]# docker run -it --network=host ubuntu
可以看到,容器的网络使用的时宿主机的网络,但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过--network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
None模式示意图:
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
bridge模式是docker的默认网络模式,不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
bridge模式如下图所示:
建议使用自定义的网桥来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。Docker提供了创建这些网络的默认网络驱动程序,你可以创建一个新的Bridge网络,Overlay或Macvlan网络。你还可以创建一个网络插件或远程网络进行完整的自定义和控制。
你可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络。此外,您可以连接并断开网络中的运行容器,而无需重新启动容器。当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供
1、bridge
一个bridge网络是Docker中最常用的网络类型。桥接网络类似于默认bridge网络,但添加一些新功能并删除一些旧的能力。以下示例创建一些桥接网络,并对这些网络上的容器执行一些实验
docker network create --driver bridge new_bridge
2 、Macvlan
Macvlan是一个新的尝试,是真正的网络虚拟化技术的转折点。Linux实现非常轻量级,因为与传统的Linux Bridge隔离相比,它们只是简单地与一个Linux以太网接口或子接口相关联,以实现网络之间的分离和与物理网络的连接。
Macvlan提供了许多独特的功能,并有充足的空间进一步创新与各种模式。这些方法的两个高级优点是绕过Linux网桥的正面性能以及移动部件少的简单性。删除传统上驻留在Docker主机NIC和容器接口之间的网桥留下了一个非常简单的设置,包括容器接口,直接连接到Docker主机接口。由于在这些情况下没有端口映射,因此可以轻松访问外部服务。
3、overlay网络
overlay网络用于连接不同机器上的docker容器,允许不同机器上的容器相互通信,同时支持对消息进行加密,当我们初始化一个swarm或是加入到一个swarm中时,在docker主机上会出现两种网络:
1、称为ingress的overlay网络,用于传递集群服务的控制或是数据消息,若在创建swarm服务时没有指定连接用户自定义的overlay网络,将会加入到默认的ingress网络
2、名为docker_gwbridge桥接网络会连接swarm中所有独立的docker系统进程
可以使用docker network create创建自定义的overlay网络,容器以及服务可以加入多个网络,只有同一网络中的容器可以相互交换信息,可以将单一容器或是swarm服务连接到overlay网络中,但是两者在overlay网络中的行为会有所不同,接下来会描述两者在overlay网络中的共同行为以及不同行为
两者在overlay网络中的公共操作
注意事项:
如果想要连接到overlay网络,请确保连接前下列端口没有服务,并且服务器防火墙要允许下列端口通过:
TCP端口2377,用于集群管理信息的交流
TCP、UDP端口7946用于集群中节点的交流
UDP端口4789用于overlay网络中数据报的发送与接收
无论是否需要使用集群服务,都需要使用docker swarm init初始化本地主机为swarm管理节点或是使用docker swarm join加入现有得swarm
1、创建自定义overlay网络的方式:
docker network create -d overlay my-overlay
2、为了创建可以让单一容器、服务相互交流的网络,需要在命令中加入--attachable参数: 同时也可以指定IP地址范围、子网掩码、网关等其他选项
docker network create -d overlay --attachable my-attachable-overlay
3、在overlay网络中,默认情况下传递的控制信息是加密的,它们使用AES加密算法,每隔12小时交换一次密钥。
可以在创建overlay网络时加上--opt encrypted来加密容器间通信的信息,例如:
docker network create --opt encrypted --driver overlay --attachable my-attachable-multi-host-network
这种加密会带来不可忽视的性能损失,开启后,docker会在所有集群节点之间创建IPSEC通道,使用的加密算法以及密钥交换的周期与控制信息的一致,在windows中,无法提供这种加密服务
17.05或是更高版本的docker容器允许更改ingress网络的配置,如果想要更改ingress网络的配置,首先要移除ingress网络,并在配置后重新创建,如果集群中的服务发布了端口,需要先在集群中移除这个服务,如果没有发布端口,则在ingress网络重新启动前,集群中服务无法进行负载均衡,接下来举一个例子:
1、使用命令docker network inspect ingress确保ingress网络存在,移除所有发布了端口的服务,否则下一步会失败
2、移除ingress网络
$ docker network rm ingress
WARNING! Before removing the routing-mesh network, make sure all the nodes
in your swarm run the same docker engine version. Otherwise, removal may not
be effective and functionality of newly created ingress networks will be
impaired.
Are you sure you want to continue? [y/N]
3、使用--ingress参数创建overlay网络,设置必要的配置参数,ingress网络的名称不一定就是ingress,例如下列将其命名为my-ingress
$ docker network create \
--driver overlay \
--ingress \
--subnet=10.11.0.0/16 \
--gateway=10.11.0.2 \
--opt com.docker.network.driver.mtu=1200 \
my-ingress
4、让第一步中的服务重新连接该网络
docker_gwbridge是一个虚拟网桥,连接docker系统进程所处的物理网络和overlay网络,它存在于docker的内核中,如果我们想配置这个网络,首先需要确保主机不在集群中,接着进行下面的步骤:
$ sudo ip link set docker_gwbridge down
$ sudo ip link del dev docker_gwbridge
$ docker network create \
--subnet 10.11.0.0/16 \
--opt com.docker.network.bridge.name=docker_gwbridge \
--opt com.docker.network.bridge.enable_icc=false \
--opt com.docker.network.bridge.enable_ip_masquerade=true \
docker_gwbridge
overlay网络中的服务彼此可以使用对方的任意接口,如果外界想要访问服务,则需要使用docker service create或是docker service update指令的-p或是-publish参数发布端口,格式如下:
swarm服务默认会使用一个叫做routing mesh的东西,当我们向任何集群节点上已发布的端口发送请求时(无论它是否运行给定的服务),请求将被重定向到运行该服务的容器中,这就是routing mesh的功能,如果不想使用routing mesh,官方提供的方式如下:
默认情况下,控制信息和容器之间的通信信息是在同一个网络中传输的,如果想要使两者不在一个网络上传输,在将服务加入到集群时,需要指定--advertise-addr
以及 --datapath-addr参数
如果创建的ingress网络未指定--attachable参数,则ingress网络只允许服务连接到overlay网络中,想要连接单一容器到overlay网络,就必须在创建overlay网络时添加--attachable参数,并且单一容器并不是暴露所有端口到overlay网络中,需要自己发布: