一.Docker容器的四种网络模式
容器的本质是一个被隔离的进程,而这个进程又有其独立的网络栈,即网卡(Network Interface)、回环设备(Loopback Device)、路由表(Routing Table)和 iptables 规则。单机时代的容器网络实际上有三种通信需求,分别是:
容器之间通信 容器与宿主机之间通信 容器与外部主机通信
Docker在创建容器时有四种网络模式,bridge为默认不需要用–net去指定,其他三种模式需要在创建容器时使用–net去指定。
bridge模式,使用–net=bridge指定,默认设置。
none模式,使用–net=none指定。
host模式,使用–net=host指定。
container模式,使用–net=container:容器名称或ID指定
二.四种网络模式的实现原理
Bridge桥接方式
实现的步骤如下:
(1) Docker Daemon 利用 veth pair 技术,在宿主机上创建两个虚拟网络接口设备,假设为 veth0 和 veth1。而 veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。
(2) Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0 网桥上。保证宿主机的网络报文可以发往 veth0;
(3) Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下,并被改名为 eth0。 如此一来,保证宿主机的网络报文若发往 veth0,则立即会被 eth0 接收,实现宿主机到 Docker Container 网络的联通性;同时,也保证 Docker Container 单独使用 eth0,实现容器网络环境的隔离性。
bridge 桥接模式下的 Docker Container 在使用时,并非为开发者包办了一切。最明显的是, 该模式下 Docker Container 不具有一个公有 IP,即和宿主机的 eth0 不处于同一个网段。导致 的结果是宿主机以外的世界不能直接和容器进行通信。虽然 NAT 模式经过中间处理实现了 这一点,但是 NAT 模式仍然存在问题与不便,如:容器均需要在宿主机上竞争端口,容器 内部服务的访问者需要使用服务发现获知服务的外部端口等。另外 NAT 模式由于是在三层 网络上的实现手段,故肯定会影响网络的传输效率。
大致的过程图:
Host 网络模式
host 模式是 bridge 桥接模式很好的补充。采用 host 模式的 Docker Container,可以直接使用 宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公 有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换。当然, 有这样的方便,肯定会损失部分其他的特性,最明显的是 Docker Container 网络环境隔离性 的弱化,即容器不再拥有隔离、独立的网络栈。另外,使用 host 模式的 Docker Container 虽 然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱 化,该容器会与宿主机共享竞争网络栈的使用;另外,容器内部将不再拥有所有的端口资 源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以 bridge 网 络模式容器的端口映射。
大致的结构图:
Container 网络模式
(1) 查找 other container(即需要被共享网络环境的容器)的网络 namespace;
(2) 将新创建的 Docker Container(也是需要共享其他网络的容器)的 namespace,使用 other container 的 namespace。
Docker Container 的 other container 网络模式,可以用来更好的服务于容器间的通信。 在这种模式下的 Docker Container 可以通过 localhost 来访问 namespace 下的其他容器,传输 效率较高。虽然多个容器共享网络环境,但是多个容器形成的整体依然与宿主机以及其他 容器形成网络隔离。另外,这种模式还节约了一定数量的网络资源。但是需要注意的是,它并没有改善容器与宿主机以外世界通信的情况。
大致结构图如下:
None 网络模式:
网络环境为 none,即不为 Docker Container 任何的网络环境。一旦 Docker Container 采用了 none 网络模式,那么容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。 可以说 none 模式为 Docker Container 做了极少的网络设定,但是俗话说得好“少即是多”,在 没有网络配置的情况下,作为 Docker 开发者,才能在这基础做其他无限多可能的网络定制 开发。这也恰巧体现了 Docker 设计理念的开放。
三.docker 网络的基本管理
1.查看docker网络的模式:docker network ls
docker network ls
NETWORK ID NAME DRIVER SCOPE
21ca12d3508c bridge bridge local
f4ff71176a19 host host local
b12b1dd0542e none null local
2.通过pid来获取容器的id
3.bridge网络的配置过程
(1)利用ubuntu的镜像创建一个新的容器
docker load -i ubuntu.tar
docker run -it --name vm1 ubuntu ##这是开启了一个交互的界面,因为没有 -d,所以直接进入了一个界面
root@a84448835d94:/# [root@foundation79 images]# ##ctrl + pq ##退出交互的界面
查看网络桥接的信息
brctl show ##在开启docker服务之后,会自动的创建一个docker0,用于网络桥接,bridge是默认的网络模式
上图显示,因为开启了一个新的容器,所以会自动的创建docker0这个网桥,并且docker0后面有相应的接口信息
docker stop vm1 ##关闭容器
brctl show ##查看网桥后面的接口信息,没有了。则表示docker默认的是bridge网络模式,刚才的接口信息是vm1的
(2)创建一个bridge模式的网络
docker network create --driver bridge my_net1 ##默认的设置ip和网关
89b97e70be47c758684ca2bd34e86242a0643be9a01fd297f5487b5cdef4f82c
docker network ls
NETWORK ID NAME DRIVER SCOPE
5c0d59e54cbc bridge bridge local
082bf249cd4b host host local
89b97e70be47 my_net1 bridge local
bf84585ce5e5 none null local
ip addr
14: br-89b97e70be47: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:fd:98:f5:ac brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-89b97e70be47
valid_lft forever preferred_lft forever
docker network inspect my_net1
{
"Subnet": "172.18.0.0/16", ##默认单调递增,18,19...
"Gateway": "172.18.0.1"
}
(3)再创建一个bridge的网络 (自定义ip和网关)
docker network create --driver bridge --subnet 172.20.0.0/24 --gateway 172.20.0.1 my_net2 ##自定义
docker network ls
NETWORK ID NAME DRIVER SCOPE
5c0d59e54cbc bridge bridge local
082bf249cd4b host host local
89b97e70be47 my_net1 bridge local
8f8e4347cd7d my_net2 bridge local
bf84585ce5e5 none null local
docker network inspect my_net2
{
"Subnet": "172.20.0.0/24",
"Gateway": "172.20.0.1"
}
ip addr
14: br-89b97e70be47: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:fd:98:f5:ac brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-89b97e70be47
valid_lft forever preferred_lft forever
15: br-8f8e4347cd7d: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:e7:f9:c5:d8 brd ff:ff:ff:ff:ff:ff
inet 172.20.0.1/24 brd 172.20.0.255 scope global br-8f8e4347cd7d
valid_lft forever preferred_lft forever
(4)创建容器
docker load -i ubuntu.tar
docker run -it --name vm1 --net my_net1 ubuntu
root@bfd519244ae0:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
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 ##容器IP为172.18.0.2/16
valid_lft forever preferred_lft forever
(5)重新开启一个shell
docker run -it --name vm2 --net my_net2 ip 172.20.0.10 ubuntu
root@b010954d0ef6:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:14:00:0a brd ff:ff:ff:ff:ff:ff
inet 172.20.0.10/24 brd 172.20.0.255 scope global eth0 ##上面开启容器的命令因为设定了容器的ip,所以就不会自己设置相关的ip
valid_lft forever preferred_lft forever
root@b010954d0ef6:/# ping 172.20.0.1 ## ping网关可以通
PING 172.20.0.1 (172.20.0.1) 56(84) bytes of data.
64 bytes from 172.20.0.1: icmp_seq=1 ttl=64 time=0.103 ms
64 bytes from 172.20.0.1: icmp_seq=2 ttl=64 time=0.116 ms
^C
root@b010954d0ef6:/# ping 172.18.0.2 ##但是因为两个容器不在一个网段,所以容器间不能ping通
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 999ms
(6)建立两个容器之间的连接
docker network connect my_net1 vm2 ##给容器 vm2,在172.18.0.0/16这个网段上,创建一对虚拟网卡,一端在主机上,一端在docker容器上。
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:14:00:0a brd ff:ff:ff:ff:ff:ff
inet 172.20.0.10/24 brd 172.20.0.255 scope global eth0
valid_lft forever preferred_lft forever
20: eth1@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
root@b010954d0ef6:/# ping 172.18.0.2 ##此时可以ping通
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.128 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.089 ms
^C
4.docker的跨主机网络方案——macvlan
macvlan是Linux操作系统内核提供的网络虚拟化方案之一,更准确的说法是网卡虚拟化方案。它可以为一张物理网卡设置多个mac地址,相当于物理网卡施展了影分身之术,由一个变多个,同时要求物理网卡打开混杂模式。针对每个mac地址,都可以设置IP地址,本来是一块物理网卡连接到交换机,现在是多块虚拟网卡连接到交换机。macvlan应该很简单
实验环境
创建两台虚拟机server1,server2,都是两块网卡(先添加一个网卡,后面会要求添加) 两台虚拟机都安装docker,都导入ubuntu.tar镜像
server1上配置:
docker network create -d macvlan --subnet 172.25.1.0/24 --gateway 172.25.1.1 -o parent=eth0 mac_net1
docker network ls
NETWORK ID NAME DRIVER SCOPE
590d47201aeb bridge bridge local
7acae6b7116a docker_gwbridge bridge local
aabe3d541f65 host host local
8xbgnhdmo84i ingress overlay swarm
a7a0fc0f184b mac_net1 macvlan local
85ed6bc9ad0b none null local
docker run -it --name vm1 --net mac_net1 --ip 172.25.1.10 ubuntu
root@4f333ebdc7bb:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
16: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether 02:42:ac:19:01:0a brd ff:ff:ff:ff:ff:ff
inet 172.25.1.10/24 brd 172.25.1.255 scope global eth0
valid_lft forever preferred_lft forever
server2上配置:
docker network create -d macvlan --subnet 172.25.1.0/24 --gateway 172.25.1.1 -o parent=eth0 mac_net1
65605f0175a07dc9feb7966ae63ed3b50b647b8d2745885669cbbb71096fb576
docker run -it --name vm1 --net mac_net1 --ip 172.25.1.11 ubuntu
root@e1ed97672c79:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
12: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether 02:42:ac:19:01:0b brd ff:ff:ff:ff:ff:ff
inet 172.25.1.11/24 brd 172.25.1.255 scope global eth0
valid_lft forever preferred_lft forever
root@e1ed97672c79:/# ping 172.25.1.10 ##可以ping通,则互联成功
测试:两个server是否可以相互ping通(不需要像上面一样,配置容器间的互联)
PING 172.25.1.10 (172.25.1.10) 56(84) bytes of data.
64 bytes from 172.25.1.10: icmp_seq=1 ttl=64 time=0.683 ms
再添加一块网卡:
server1上进行配置:
ip link set up eth1
ip link set eth1 promisc on
docker network create -d macvlan --subnet 172.25.2.0/24 --gateway 172.25.2.1 -o parent=eth1 mac_net2
docker run -it --name vm2 --net mac_net2 --ip 172.25.2.10 ubuntu
root@282e8ddf9c14:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
17: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether 02:42:ac:19:02:0a brd ff:ff:ff:ff:ff:ff
inet 172.25.2.10/24 brd 172.25.2.255 scope global eth0
valid_lft forever preferred_lft forever
server2上的配置:
docker network create -d macvlan --subnet 172.25.2.0/24 --gateway 172.25.2.1 -o parent=eth1 mac_net2
docker run -it --name vm2 --net mac_net2 --ip 172.25.2.11 ubuntu
测试:
在server2的另一个网卡上,ping 172.25.2.10,可以ping通;
在server2的之前的网卡上,ping 172.25.1.10,却ping不通。
如何解决上面的同一个虚拟机上,不同网段的docker容器得互通问题呢?
server1上配置:
docker network create -d macvlan --subnet 172.25.3.0/24 --gateway 172.25.3.1 -o parent=eth1.1 mac_net3
8c1ffd97639e45ff9f4b73c61e83355bf91cba9ff73052811657706db1cad167
docker run -it --name vm3 --net mac_net3 --ip 172.25.3.10 ubuntu
server2上配置:
docker network create -d macvlan --subnet 172.25.3.0/24 --gateway 172.25.3.1 -o parent=eth1.1 mac_net3
docker run -it --name vm3 --net mac_net3 --ip 172.25.3.11 ubuntu
root@e1ed97672c79:/# ping 172.25.3.10
PING 172.25.1.10 (172.25.1.10) 56(84) bytes of data.
64 bytes from 172.25.1.10: icmp_seq=1 ttl=64 time=0.683 ms
root@e1ed97672c79:/# ping 172.25.2.10
PING 172.25.1.10 (172.25.1.10) 56(84) bytes of data.
64 bytes from 172.25.1.10: icmp_seq=1 ttl=64 time=0.683 ms