1. 概述
基于对net namespace的控制,docker可以为在容器创建隔离的网络环境,在隔离的网络环境下,容器具有完全独立的网络栈,与宿主机隔离,也可以使容器共享主机或者其他容器的网络命名空间,基本可以满足开发者在各种场景下的需要。按docker官方的说法,docker容器的网络有五种模式:
- bridge:docker默认的网络模式,为容器创建独立的网络命名空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。
- host:直接使用容器宿主机的网络命名空间。
- none:为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。
- 其他容器:与host模式类似,只是容器将与指定的容器共享网络命名空间。
- 用户自定义:docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。
1.1 bridge模式
bridge模式是docker默认的,也是开发者最常使用的网络模式。在这种模式下,docker为容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,实现容器之间、容器与宿主机之间的网络栈隔离。同时,通过宿主机上的docker0网桥,容器可以与宿主机乃至外界进行网络通信。其网络模型可以参考下图:
从该网络模型可以看出,容器从原理上是可以与宿主机乃至外界的其他机器通信的。同一宿主机上,容器之间都是连接到docker0这个网桥上的,它可以作为虚拟交换机使容器可以相互通信。然而,由于宿主机的IP地址与容器veth pair的 IP地址均不在同一个网段,故仅仅依靠veth pair和namespace的技术,还不足以使宿主机以外的网络主动发现容器的存在。为了使外界可以方位容器中的进程,docker采用了端口绑定的方式,也就是通过iptables的NAT,将宿主机上的端口端口流量转发到容器内的端口上
很明显,bridge模式的容器与外界通信时,必定会占用宿主机上的端口,从而与宿主机竞争端口资源,对宿主机端口的管理会是一个比较大的问题。同时,由于容器与外界通信是基于三层上iptables NAT,性能和效率上的损耗是可以预见的。
1.2 host模式
由于容器和宿主机共享同一个网络命名空间,换言之,容器的IP地址即为宿主机的IP地址。所以容器可以和宿主机一样,使用宿主机的任意网卡,实现和外界的通信。其网络模型可以参照下图:
采用host模式的容器,可以直接使用宿主机的IP地址与外界进行通信,若宿主机具有公有IP,那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行NAT转换,而且由于容器通信时,不再需要通过linux bridge等方式转发或者数据包的拆封,性能上有很大优势。当然,这种模式有优势,也就有劣势,主要包括以下几个方面:
- 最明显的就是容器不再拥有隔离、独立的网络栈。容器会与宿主机竞争网络栈的使用,并且容器的崩溃就可能导致宿主机崩溃,在生产环境中,这种问题可能是不被允许的。
- 容器内部将不再拥有所有的端口资源,因为一些端口已经被宿主机服务、bridge模式的容器端口绑定等其他服务占用掉了。
1.3 none模式
在这种模式下,容器有独立的网络栈,但不包含任何网络配置,只具有lo这个loopback网卡用于进程通信。也就是说,none模式为容器做了最少的网络设置,但是俗话说得好“少即是多”,在没有网络配置的情况下,通过第三方工具或者手工的方式,开发这任意定制容器的网络,提供了最高的灵活性。
用处:避免主机生成的任何密码或随机值被其他主机窃取,用于安全、生成随机码
1.4 其他容器
其他网络模式是docker中一种较为特别的网络的模式。在这个模式下的容器,会使用其他容器的网络命名空间,其网络隔离性会处于bridge桥接模式与host模式之间。当容器共享其他容器的网络命名空间,则在这两个容器之间不存在网络隔离,而她们又与宿主机以及除此之外其他的容器存在网络隔离。其网络模型可以参考下图:
在这种模式下的容器可以通过localhost来同一网络命名空间下的其他容器,传输效率较高。而且这种模式还节约了一定数量的网络资源,但它并没有改变容器与外界通信的方式。在一些特殊的场景中非常有用,例如,kubernetes的pod,kubernetes为pod创建一个基础设施容器,同一pod下的其他容器都以其他容器模式共享这个基础设施容器的网络命名空间,相互之间以localhost访问,构成一个统一的整体。
# docker run -itd --name web1 httpd
# docker run -it --network container:web1 httpd
1.5 自定义网络
在用户定义网络模式下,开发者可以使用任何docker支持的第三方网络driver来定制容器的网络。并且,docker 1.9以上的版本默认自带了bridge和overlay两种类型的自定义网络driver。可以用于集成calico、weave、openvswitch等第三方厂商的网络实现。
除了docker自带的bridge driver,其他的几种driver都可以实现容器的跨主机通信。而基于bdrige driver的网络,docker会自动为其创建iptables规则,保证与其他网络之间、与docker0之间的网络隔离。
bridge driver的所有行为都和默认的bridge模式完全一致。而overlay及其他driver,则可以实现容器的跨主机通信。
2. 操作部分:单主机网络—>bridge的默认设置与自定义
2.1 认识默认的docker网络
[root@localhost ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
ff5eadacfa0b: Pull complete
Digest: sha256:6dce4a9c1635c4c9b6a2b645e6613fa0238182fe13929808ee2258370d0f3497
Status: Downloaded newer image for busybox:latest
[root@localhost ~]# docker run -itd --name box1 busybox
8f2742fe263767f550558751a04691d1d09c8a880133b16e79c69fabadc15a64
[root@localhost ~]# docker exec -it box1 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 这里的6和7为全局网卡编号,可以看出容器的eth0网卡连接主机的7号网卡
# 我们退会主机查看一下7号
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:ff:a8 brd ff:ff:ff:ff:ff:ff
inet 192.168.20.121/24 brd 192.168.20.255 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fea8:ffa8/64 scope link
valid_lft forever preferred_lft forever
7: veth6431297@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether b2:69:41:f1:1c:cf brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::b069:41ff:fef1:1ccf/64 scope link
valid_lft forever preferred_lft forever
#查看主机的网卡信息,也可以看出来7号连接6号 且7号的网卡名是veth6431297
# 接下来我们看下,veth的这块网卡连接谁
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242645ced6f no veth6431297
virbr0 8000.5254002dca20 yes virbr0-nic
# 可以看出veth连接的是docker0网桥,这与前边我们讲的理论相吻合
整体逻辑:整体逻辑 容器网卡eth0 连接 网络命名空间 veth6431297,命名空间连接docker0
2.2 自建docker网络
细心的同学已经看到,在刚才的容器里,采用的是桥接网络,ip是172.17.0.2/16,我们也可以自己建立一个bridge网络,自己家里的bridge,如果没有指定网段,默认会在原来的网段上+1,变成172.18.0.0/16这个网段的ip
[root@localhost ~]# docker network create --driver bridge br1
5665c2db709020175ce8aa1c9e82e5fc00e4a9eee5663f24c9f3e33245b1be37
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5665c2db7090 br1 bridge local
0628284ab55f bridge bridge local
11133ab29281 host host local
30e06b21eaad none null local
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-5665c2db7090 8000.0242e18bb37b no
docker0 8000.0242645ced6f no veth6431297
virbr0 8000.5254002dca20 yes virbr0-nic
#可以看出,新添加的名字是br1的网卡,id编号是5665c2db7090,下边我们来看一下它的详细信息
[root@localhost ~]# docker network inspect 5665c2db7090
[
{
"Name": "br1",
"Id": "5665c2db709020175ce8aa1c9e82e5fc00e4a9eee5663f24c9f3e33245b1be37",
"Created": "2019-06-14T09:35:46.932806841+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
可以看到subnet字段: "Subnet": "172.18.0.0/16", 与我们说的相同
下边我们来看一下,容器如何使用这个新定义的网络
[root@localhost ~]# docker run -itd --name box2 --network br1 busybox
426d726c423ab8b8aeeece1e0887a900a77b483e158937f81fb94214e8106716
[root@localhost ~]# docker exec -it box2 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
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
valid_lft forever preferred_lft forever
/ # exit
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a8:ff:a8 brd ff:ff:ff:ff:ff:ff
inet 192.168.20.121/24 brd 192.168.20.255 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fea8:ffa8/64 scope link
valid_lft forever preferred_lft forever
······
10: veth92a8542@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5665c2db7090 state UP
link/ether 26:7c:3b:18:ba:7d brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::247c:3bff:fe18:ba7d/64 scope link
valid_lft forever preferred_lft forever
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-5665c2db7090 8000.0242e18bb37b no veth92a8542
docker0 8000.0242645ced6f no veth6431297
virbr0 8000.5254002dca20 yes virbr0-nic
这里的表示方法br-5665c2db7090 前边的br表示是桥接网络,也就是bridge,后边的编号就是我们
之前通过docker network ls查看到的br1的network id
2.3 自定义网段的bridge网络
1) 自动分配容器ip
[root@localhost ~]# docker network create --driver bridge --subnet 182.16.23.2/24 --gateway 182.16.23.1 br3
50f8e1974b393a00f6fad8579d638707a87ee575eb17765ec74356212a517d04
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5665c2db7090 br1 bridge local
76f956a053a2 br2 bridge local
50f8e1974b39 br3 bridge local
0628284ab55f bridge bridge local
11133ab29281 host host local
30e06b21eaad none null local
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-50f8e1974b39 8000.024238623b3a no #此处没有连接的veth网卡
br-5665c2db7090 8000.0242e18bb37b no veth92a8542
br-76f956a053a2 8000.0242fbebe518 no veth2932623
docker0 8000.0242645ced6f no veth6431297
virbr0 8000.5254002dca20 yes virbr0-nic
[root@localhost ~]# docker run -itd --name box4 --network br3 busybox
ae565251a3e297cd2b99eaeb619138f980a7f3ea173b2a437340397e049ea714
[root@localhost ~]# docker exec -it box4 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:b6:10:17:02 brd ff:ff:ff:ff:ff:ff
inet 182.16.23.2/24 brd 182.16.23.255 scope global eth0
valid_lft forever preferred_lft forever
2)手动指定容器ip
[root@localhost ~]# docker run -itd --name box5 --network br3 --ip 182.16.23.22 busybox
73d2c2cf69b1f8ebd2f06f073816975acd6277cf0915873d80aa6b4ffe5aac9a
[root@localhost ~]# docker exec -it box5 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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@if18: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:b6:10:17:16 brd ff:ff:ff:ff:ff:ff
inet 182.16.23.22/24 brd 182.16.23.255 scope global eth0
valid_lft forever preferred_lft forever