目录
(1)bridge桥接模式——和宿主机的网卡之间搭建一个“桥”
(3)<--link>用来链接2个容器(也可以提供自动解析)
macvlan网络在二层上是隔离的,所以不同macvlan网络的容器是不能通信的。
一、docker网络
1、Docker原生网络
docker的镜像是令人称道的地方,但网络功能还是相对薄弱的部分。
docker安装后会自动创建3种网络:bridge(桥接)、host(主机网络)、none(禁用)
(1)bridge桥接模式——和宿主机的网卡之间搭建一个“桥”
查看docker网络可以使用以下命令查看:
docker network ls #查看docker的当前网络
装好docker后,会在网络管理看到一个“docker网桥”,如下所示。这个docker网桥和宿主机上的bridge是一个意思.
先启动一个demo,通过网桥管理工具查看其网络。可以看到容器的网关为<172.17.0.1>,这个IP就是“docker0网桥”的IP。
#查看容器网关
[root@server1 ~]# docker inspect demo | grep Gateway
"Gateway": "172.17.0.1",
"IPv6Gateway": "",
"Gateway": "172.17.0.1",
"IPv6Gateway": "",
#显示docker0网桥信息
[root@server1 ~]# ip addr show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:da:84:49:53 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:daff:fe84:4953/64 scope link
valid_lft forever preferred_lft forever
#查看网桥信息
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242da844953 no vethd8486ce
查看宿主机的火墙,显示将所有<172.17.0.0>网段的数据做个NAT转发出去。
也就是说,容器每次启动时都会获得一个IP,且IP地址是递增的。某个容器停止后会释放该IP地址,因此存在一个问题:某个容器停止后重新启动,获得的IP和之前的不一样,即容器IP是动态的。
bridge桥接模式示意图:
因此docker网络的基本结构是:容器运行时获得一个IP<例如:172.17.0.3>,且容器的数据通过网关转发<例如:172.17.0.1>,宿主机的火墙将此网段的数据<例如:172.17.0.0>做一个地址伪装转发出去,与宿主机的网络<例如:172.25.254.1>进行数据交换。
这种桥接方式的特点:bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的。容器通过宿主机的NAT规则后可以访问外网。
(2)host模式——直接使用宿主机的网卡
host模式连接示意图:
host模式特点:可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
新建一个容器demo2,网络模式设置为host,查看网桥信息,并没有这个
#新建demo2,网络模式为host
docker run -d --name demo2 --network host nginx
#查看网桥
brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242da844953 no vethd8486ce
#查看demo2的网络信息
docker inspect demo2 |tail -n 20
容器demo2的网络信息里,没有任何IP信息。
但是容器demo2会创建一个新的虚拟网卡,用于共享主机的IP
进入容器验证:
#前台运行一个demo容器并设置网络模式为host
docker run -it --name demo --network host busybox
查看容器的网络信息,和宿主机的IP是一样的。也就是说,访问宿主机的IP地址就可以访问到这个容器。
(3)none模式——无网络模式
none模式是指禁用网络功能,只有lo接口,在容器创建时使用。
一般用于没有必要联网的容器。
2、Docker自定义网络
自定义网络模式中,docker提供了三种自定义网络驱动: bridge 、overlay 、macvlan。
bridge驱动类似默认的bridge网络模式,但增加了一些新的功能;
overlay和macvlan是用于创建跨主机网络。
建议使用自定义的网络来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。
(1)自定义网桥的自动解析功能
要将所有的容器都接入到自定义网桥上
第一步:创建docker网桥“mynet1”
#创建网桥mynet1
[root@server1 ~]# docker network create mynet1
589b73ba7f509b987c894f24e9fadbe4efa61f99bfdebce75167e7240621a172
#显示doker所有网络
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
0ed20a2c714a bridge bridge local
0c3ce8c7cca6 host host local
589b73ba7f50 mynet1 bridge local
887bac06fb39 none null local
第二步:正常启动一个容器,命名为demo1,根据IP递增原则,demo1的IP地址为<172.14.0.2>。
第三步:再进入另一个容器busybox,IP被分配为<172.17.0.3>,可以直接ping通demo1的IP地址,但是无法ping通demo1域名。
第四步:重启一个demo1容器,但是将其网络设置为“mynet1”网桥。进入另一个容器bubybox中(也是同样的网桥),可以直接ping通域名而不需要知道demo1的IP。
#启动容器demo1,并设置网络模式为”--network mynet1“
[root@server1 ~]# docker run -d --name demo1 --network mynet1 nginx
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
br-589b73ba7f50 8000.0242148d3277 no veth488fc01
docker0 8000.0242da844953 no
#启动容器,并设置网络模式为”--network mynet1“
[root@server1 ~]# docker run -it --rm --network mynet1 busybox
/ # ping demo1
PING demo1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.115 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.114 ms
第五步:自动解析的应用
先启动demo1,测得IP为<172.18.0.2>。当demo1故障时,启动demo2后,测得demo2的IP为<172.18.0.2>这是因为demo1挂掉后IP被释放;接着demo1恢复正常,测得demo1的IP为<172.18.0.3>。
因此无论容器的IP为多少,只需要容器的名字就可以访问容器。
(2)自定义网桥的自定义参数与网络隔离/互通
第一步:删除上一步中创建的容器demo1和demo2。删除网桥。
#删除docker网桥mynet1
[root@server1 ~]# docker network rm mynet1
mynet1
#查看docker的网络列表
[root@server1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
0ed20a2c714a bridge bridge local
0c3ce8c7cca6 host host local
887bac06fb39 none null local
第二步:创建网桥时指定参数
#创建docker网桥并指定网关和子网
docker network create --subnet 172.20.0.0/24 --gateway 172.20.0.1 mynet1
#查看docker网桥信息
docker network inspect mynet1
"Subnet": "172.20.0.0/24",
"Gateway": "172.20.0.1"
第三步:创建容器时指定容器IP
创建容器demo1时指定IP(--ip参数可以手动指定IP),通过curl测试,可以连接demo1。
#创建容器demo1并指定IP为<172.20.0.111>
docker run -d --name demo1 --network mynet1 --ip 172.20.0.111 nginx
#测试容器demo1的IP
curl 172.20.0.111
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
第四步:创建不同网段的mynet2
#创建自定义参数docker网桥mynet2
docker network create --subnet 172.30.0.0/24 --gateway 172.30.0.1 mynet2
#创建mynet2网段下的容器demo2
docker run -d --name demo2 --network mynet2 --ip 172.30.0.111 nginx
#查看网桥,不同的网段连在不同网桥
[root@server1 ~]# brctl show
bridge name bridge id STP enabled interfaces
br-67c9e18787ca 8000.0242c07abce9 no vethc43fc6d
br-eb5283bda938 8000.02424180a480 no veth7546f60
第五步:测试网络隔离
进入容器busybox中,指定的是mynet1的网桥,因此可以连接同一网桥上的容器,而不能连接另一网桥mynet2上的容器
第六步:测试网桥互通
网桥互通原理图:
不同网段的网桥自然会隔离,但是如果有需求让不同网桥之间通信,就需要设置网桥互通。
接着上一步实验。按<Ctrl>+<p>+<q>将busybox容器打入后台后,查看其进程信息。该容器原本是属于“mynet1网桥”,向该容器中添加“mynet2网桥”(添加网桥命令如下),添加完成后,再次进入该容器。测试后可以连接mynet2网段的主机了。
本质上是,向容器中添加了一个虚拟网卡,两个网卡连接不同网段,实现数据互通。
#显示正在进行的容器
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8f0dc0391906 busybox "sh" 3 minutes ago Up 3 minutes cranky_wright
#向该容器中添加另一网段的网桥,可以是容器ID也可以是容器名称
docker network connect mynet2 cranky_wright
#重新进入该容器
docker attach cranky_wright
3、Docker容器通信
(1)同一网桥上的不同容器通信
容器之间除了使用ip通信外,还可以使用容器名称通信。
docker 1.10开始,内嵌了一个DNS server。 dns解析功能必须在自定义网络中使用。 启动容器时使用 --name 参数指定容器名称。
(2)Joined容器——容器的网络被其他容器共享
和之前的host模型很相似,host模式下,容器共享主机的网络;此模式下,容器共享容器的网络。
处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用localhost高效快速通信。
Joined容器一种较为特别的网络模式。在容器创建时使用--network=container:vm1指定。(vm1指定的是运行的容器名)
[root@server1 ~]# docker run --rm -it --network container:demo1 busybox
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> 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
142: eth0@if143: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:14:00:6f brd ff:ff:ff:ff:ff:ff
inet 172.20.0.111/24 brd 172.20.0.255 scope global eth0
valid_lft forever preferred_lft forever
注意:这种情况下,两个容器之间的监听端口不能有冲突。比如demo1启用nginx时监听的是80端口,如果共享网络时启动的容器监听的也是80端口,那么就会报错!!!
(3)<--link>用来链接2个容器(也可以提供自动解析)
--link的格式: --link <name or id>:alias,其中name和id是源容器的name和id,alias是源容器在link下的别名。
实际上是在容器内部对<--link>设置的别名做了一个解析。
[root@server1 ~]# docker run -d --name demo1 nginx
373242dc7da15c2a27a83ad5dbd3f28a04eed30389035985e1234b71272dad69
[root@server1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
373242dc7da1 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 80/tcp demo1
[root@server1 ~]#
[root@server1 ~]# docker run --rm -it --link demo1:webserver busybox
/ # cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 webserver 373242dc7da1 demo1
172.17.0.3 b72c55a4b53e
(4)容器间通信
已知容器数据出去时,用到的是火墙策略的SNAT模式:
容器数据进来时,即外网访问容器用到了docker-proxy和iptables DNAT:
宿主机访问本机容器使用的是iptables DNAT
外部主机访问容器或容器之间的访问是docker-proxy实现
原理图如下所示:
测试:
启动一个容器demo1,并将宿主机80端口和容器80端口做映射。做好映射后,查看宿主机的火墙策略,发现已经新加了一个DNAT策略。那么外部网络就可以通过访问宿主机的80端口直接访问容器内部。
docker-proxy和iptables的双重网络机制:
外网访问容器有docker-proxy和iptables两种方式,只要其中任意一种方式存在,外网都可以访问容器内部。(也就是双层保险)
以下以iptables失效为例。
清除掉iptables中的火墙策略后,通过外网还可以访问。
这是因为docker网络除了DNAT之外还有docker-proxy机制,接下来强制结束docker-proxy进程,外部网络再次访问时无法访问。
4、跨主机容器网络
以上都只是介绍了同一宿主机下的不同docker之间的通信。在实际生产环境中,需要不同主机之间的容器进行通信。
(1)跨主机网络解决方案
**跨主机网络解决方案:
(1)docker原生的overlay和macvlan
(2)第三方的flannel、weave、calico
**众多网络方案是如何与docker集成在一起的:
libnetwork docker容器网络库
CNM (Container Network Model)这个模型对容器网络进行了抽象
(2)CNM(容器网络模型)介绍
CNM(容器网络模型)分三类组件:
(1)Sandbox:容器网络栈,包含容器接口、dns、路由表。(namespace)
(2)Endpoint:作用是将sandbox接入network (veth pair)
(3)Network:包含一组endpoint,同一network的endpoint可以通信。
(3) macvlan网络方案实现
定义:Linux kernel提供的一种网卡虚拟化技术。
优点:无需Linux bridge,直接使用物理接口,性能极好。
第一步:准备两台虚拟机,并清除docker中不用的network网络
docker network prune #清除docker不用的网桥
第二步:启用eht0网卡的混杂模式
两台虚拟机都要打开网卡混杂模式。
ip link set eth0 promisc on #打开网卡混杂模式
第三步:创建macvlan网卡
在两台虚拟机上都执行以下命令,都在eth0上创建macvlan。
注意:两台虚拟机上创建的macvlan的子网和网关必须一样(在同一网段)
docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth0 mynet1
# 创建网卡; -d 指定macvlan模式; -o 指定此macvlan的父级为eth0(即直接跟此网卡通信); 网卡名为mynet1
第四步:分别启动容器并测试
为容器设置IP时需要管理员手动设置,不会自动分配。便于管理!!!
在server1中:IP设置为<172.20.0.111>
root@server1 ~]# docker run -it --name demo1 --network mynet1 --ip 172.20.0.111 busybox
/ # ip addr
185: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:14:00:6f brd ff:ff:ff:ff:ff:ff
inet 172.20.0.111/24 brd 172.20.0.255 scope global eth0
在server2中:IP设置为<172.20.0.222>
[root@server2 ~]# docker run -it --name demo1 --network mynet1 --ip 172.20.0.222 busybox
/ # ip addr
25: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:14:00:de brd ff:ff:ff:ff:ff:ff
inet 172.20.0.222/24 brd 172.20.0.255 scope global eth0
/ # ping 172.20.0.111
PING 172.20.0.111 (172.20.0.111): 56 data bytes
64 bytes from 172.20.0.111: seq=0 ttl=64 time=0.613 ms
64 bytes from 172.20.0.111: seq=1 ttl=64 time=0.477 ms
测试结果显示:不同宿主机下的不同容器实现了互通
(4)macvlan的网络扩展
优点:
一、没有新建linux bridge
二、容器的接口直接与主机网卡连接,无需NAT或端口映射。
局限性:
macvlan会独占主机网卡,但可以使用vlan子接口实现多macvlan网络
vlan可以将物理二层网络划分为4094个逻辑网络,彼此隔离,vlan id取值为1~4094。
解决方法1——新建多个网卡
每个macvlan的子网都会独占一个网卡。因此在实际生产环境中,会有成千上万台设备,如果只是增加网卡的话,在硬件层面不现实。
ip link set eth1 up #激活eth1网卡
ip link set eth1 promisc on #打开eth1混杂模式
docker network create -d macvlan --subnet 172.30.0.0/24 --gateway 172.30.0.1 -o parent=eth1 mynet2
#在eht1上创建macvlan模式网卡mynet2,并指定子网和网关为<172.30.0.0>
解决方法2——vlan子接口
也就是说在每个网卡上开启多个vlan子接口,每个接口用于一个子网。
#创建mynet3,父级网卡为eth1.1,子网为<172.40.0.0>
docker network create -d macvlan --subnet 172.40.0.0/24 --gateway 172.40.0.1 -o parent=eth1.1 mynet3
#创建mynet4,父级网卡为eth1.2,子网为<172.40.0.0>
docker network create -d macvlan --subnet 172.50.0.0/24 --gateway 172.50.0.1 -o parent=eth1.2 mynet4
#创建mynet5,父级网卡为eth1.3,子网为<172.40.0.0>
docker network create -d macvlan --subnet 172.60.0.0/24 --gateway 172.60.0.1 -o parent=eth1.3 mynet5
查看宿主机网卡信息,在eth1上出现很多子接口。
(5)macvlan网络间的隔离和连通
macvlan网络在二层上是隔离的,所以不同macvlan网络的容器是不能通信的。
可以在三层上通过网关将macvlan网络连通起来。(也就是之前提到过的不同docker之间的通信:<docker network connect mynet2 cranky_wright>)
docker本身不做任何限制,像传统vlan网络那样管理即可。