Docker网络
# 一、Docker网络模式
Docker网络模式有四种模式(其实也可以说5种
):
- bridge(默认):容器使用独立net namespace,会为每一个容器分配,设置IP等,并将容器连接到一个docker0 的虚拟网桥,通过docker 0 网桥以及iptables nat 表配置与宿主机通信。
- host: 和宿主机公用同一个net namespace,容器中的网络环境和宿主机一模一样。
- container: 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围。
- none: :对于此容器,禁用所有联网。通常与自定义网络驱动程序一起使用。
- 自定义(Macvlan):Macvlan网络允许您为容器分配MAC地址,使其在网络上显示为物理设备。Docker守护程序通过其MAC地址将流量路由到容器。在处理希望直接连接到物理网络而不是通过Docker主机的网络堆栈进行路由的旧应用程序时,使用
macvlan
驱动程序有时是最佳选择。
总结
其实,Docker这几种网络模式也可以理解为,是否跟宿主机或者是否跟其它容器共享Network namespace而延伸出来的网络模式。
# 二、网络模式应用场景
# 2.1 bridge 模式
bridge
模式是Docker默认网络驱动模式,容器使用独立的NetWork NameSpace。 Docker进程启动时,会在宿主机上创建一个名为docker0
的虚拟网卡,同一个宿主机上的容器,都会连接到这个虚拟网卡上面,通过此网卡进行与外部或者与其它容器通信。
启动的容器都会从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关,在主机上创建一堆虚拟网卡 veth pair 设备,veth pair 是一种成对出现的特殊网络设备,可以把他们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0)在容器中,另一头(veth**)挂在网桥 docker0 上。具体的原理参考下面网络通信原理。
拓扑
示例
# 启动三个容器 默认就是bridge模式
[root@localhost ~]# docker run -d --name nginx-b1 nginx:latest
[root@localhost ~]# docker run -d --name nginx-b2 nginx:latest
[root@localhost ~]# docker run -d --name nginx-b3 nginx:latest
# 通过ip a可以看到创建的veth虚拟网络设备
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> 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
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:50:56:a4:78:54 brd ff:ff:ff:ff:ff:ff
inet 10.66.31.130/24 brd 10.66.31.255 scope global noprefixroute ens192
valid_lft forever preferred_lft forever
inet6 fe80::1a49:7ad8:da9b:d0b4/64 scope link tentative noprefixroute dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::f3f0:e911:6ed:35db/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:e1:fc:20:ef 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:e1ff:fefc:20ef/64 scope link
valid_lft forever preferred_lft forever
21: vethaad56ba@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 9a:b1:5b:f5:ee:e4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::98b1:5bff:fef5:eee4/64 scope link
valid_lft forever preferred_lft forever
23: veth8dd5b8a@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ee:12:83:d0:7a:34 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::ec12:83ff:fed0:7a34/64 scope link
valid_lft forever preferred_lft forever
25: veth90ba14f@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 2a:27:bb:f2:3c:b1 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::2827:bbff:fef2:3cb1/64 scope link
valid_lft forever preferred_lft forever
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 2.2 host 模式
host模式,指的是跟宿主机共享同一个Network Namespace,使用宿主机的IP地址,和宿主机共享端口范围,使用参数--net=host
指定。
示例:
# 以host模式启动容器
[root@localhost ~]# docker run -d --name nginx-h1 --net=host nginx:latest
1a3f2958b84d3987106c3ea1ada3c2cd29f83f51e4c85f531080768f3c80b829
# 通过查看运行的容器,可以看到PORTS也是没有端口映射的
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a3f2958b84d nginx:latest "/docker-entrypoint.…" 2 seconds ago Up 1 second nginx-h1
2
3
4
5
6
7
访问Nginx:
注意
host模式虽然网络性能比较好(不用docker0转发),使用起来比较方便,但是因为与宿主机共享IP、端口,所以如果运行容器较多,没有合理规范,会导致端口冲突,隔离性不好,也不安全。
# 2.3 container 模式
container模式指的是新创建的容器和已经存在的一个容器共享一个Network Namespace,Docker不会给容器分配IP,两个容器除了网络方面一样,其它诸如文件系统,pid等都是隔离的,两个容器网络通信直接是lo网卡
通信。
拓扑:
示例:
# 先启动Container01
[root@localhost ~]# docker run -d -it --name busybox-c1 busybox:latest /bin/sh
# 查看容器IP
[root@localhost ~]# docker exec busybox-c1 ip a
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
18: eth0@if19: <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
# 启动Container02
[root@localhost ~]# docker run -d -it --name busybox-c2 --net=container:busybox-c1 busybox:latest /bin/sh
# 查看Container02容器IP
[root@localhost ~]# docker exec busybox-c2 ip a
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
18: eth0@if19: <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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
可以看到两个容器IP是一样的,其实就类似于在宿主机直接启动2个应用,我们还可以通过查看容器的network namespace是否一样
注意
这种模式比host模式其实多了一层转发,但是隔离性比较好,有一个问题就是跟host模式一样,端口是公用的所以,使用的时候,需要提前规范化应用端口,避免端口冲突。
# 2.4 none 模式
--net=none指定
none模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。容器只有回环地址lo
,因为无法联网与外部通信,所以此模式安全性比较好,一般不会使用。
# 2.5 自定义模式
我们知道,对于Docker默认的网络模式bridge
,是没有办法指定容器IP的,只能有docker0
虚拟网卡随机分配,要想自己定义容器网段,指定容器IP,并且在同一个宿主机下面,容器之间通过主机名也是无法ping通。对于自定义网络模式这些都是可以实现。
创建自定义网络
# 创建自定义网络
[root@localhost ~]# docker network create -d bridge --gateway 172.10.0.1 --subnet 172.10.0.0/16 mynet
# 查看创建的网卡
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
310f2cda0917 bridge bridge local
6da2e1705162 host host local
39b86b0c1cbf mynet bridge local
a64d2d84fda0 none null local
2
3
4
5
6
7
8
9
10
- -d: 指网络桥接模式 默认也是bridge
- --gateway: 网关
- --subnet: 网络网段
- mynet: 自定义网络名称
使用自定义网络创建容器
# 创建容器busybox-c1 指定IP 为172.10.0.10
[root@localhost ~]# docker run -d -it --net mynet --ip 172.10.0.10 --name busybox-c1 --hostname busybox-c1 busybox:latest /bin/sh
# 查看容器busybox-c1 IP
# ## 容器IP为我们配置的IP地址
[root@localhost ~]# docker inspect busybox-c1 -f '{{ .NetworkSettings.Networks.mynet.IPAddress }}'
172.10.0.10
# 创建容器busybox-c2
[root@localhost ~]# docker run -d -it --net mynet --name busybox-c2 --hostname busybox-c2 busybox:latest /bin/sh
# 查看容器busybox-c2 IP
# ## 如果不知道IP,则会随机分配
[root@localhost ~]# docker inspect busybox-c2 -f '{{ .NetworkSettings.Networks.mynet.IPAddress }}'
172.10.0.2
2
3
4
5
6
7
8
9
10
11
12
13
14
测试主机名通信
# 2.6 Docker网络命令
# 查看docker网络列表
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
310f2cda0917 bridge bridge local
6da2e1705162 host host local
39b86b0c1cbf mynet bridge local
a64d2d84fda0 none null local
# 查看网络详细
[root@localhost ~]# docker network inspect 39b86b0c1cbf
[
{
"Name": "mynet",
"Id": "39b86b0c1cbffe813c5b290942eb12a27ba3f71aaaa5488d1bffe3e7db94e424",
"Created": "2023-02-16T15:09:23.879290158+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.10.0.0/16",
"Gateway": "172.10.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"86c20e829811ec3a87c6b665cac9e9220775a610b4c4b4275411afcd52384370": {
"Name": "busybox-c1",
"EndpointID": "05cdb7011c4eda1907cb8aa21bb55fe8c3e844d20dd211ab9084897fd952c986",
"MacAddress": "02:42:ac:0a:00:0a",
"IPv4Address": "172.10.0.10/16",
"IPv6Address": ""
},
"8e1bee9cbd3c6f7acbbc5409b133f9bfcd551ae3456f1b559d196e181d30e167": {
"Name": "busybox-c2",
"EndpointID": "c85b84ee6bc9bc3d88edaac95651e3bd9ef44fa689e1603dd3a0681271aa4170",
"MacAddress": "02:42:ac:0a:00:02",
"IPv4Address": "172.10.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 三、网络通信原理{#index}
说明
上面介绍了Docker的几种网络模式,这几种模式其实归类起来,就两种情况,一种是容器共享同一个网络命名空间(host、container),另一种是容器独立网络空间(bridge、none)。
# 3.1 同网络命名空间
这种模式其实很好理解,因为不同的容器都属于一个网络命名空间,等于网络、端口都是共享使用,看到的网络设备是一样的,都可以看到Loopback
设备可以理解为在同一个host主机模式上,不同应用之间访问,直接使用lo
地址即可。
# 3.2 不同网络命名空间
Docker在启动时,会在宿主机创建一个虚拟的网桥docker0
,Docker创建容器时,会根据docker0
定义的网段为容器随机分配一个IP地址。该docker0
网桥也是这些容器的网关,由于同一个宿主机内的容器IP都是由docker0
分配IP,并且都接入到同一个网桥,因此,这些容器之间通信直接使用容器IP即可。
查看默认网桥
[root@localhost ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:e1ff:fefc:20ef prefixlen 64 scopeid 0x20<link>
ether 02:42:e1:fc:20:ef txqueuelen 0 (Ethernet)
RX packets 5901 bytes 319886 (312.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6256 bytes 9299137 (8.8 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.66.31.130 netmask 255.255.255.0 broadcast 10.66.31.255
inet6 fe80::1a49:7ad8:da9b:d0b4 prefixlen 64 scopeid 0x20<link>
inet6 fe80::f3f0:e911:6ed:35db prefixlen 64 scopeid 0x20<link>
ether 00:50:56:a4:78:54 txqueuelen 1000 (Ethernet)
RX packets 176158 bytes 215680246 (205.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 132485 bytes 14988051 (14.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 44 bytes 5381 (5.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 44 bytes 5381 (5.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
与外部通信
# 因为docker网桥是虚拟网络设备,所以对于外部来说,Docker容器IP对外是不可见的,如果想要被外部访问,可以把应用端口通过-p的方式映射到宿主机上
# ## 这样我们直接访问宿主机的80端口也就可以访问到容器中的内容
# ## -P 如果使用大P则,无需知道端口映射,会随机映射到宿主机的一个端口
[root@localhost ~]# docker run -d --name nginx-b1 -p 80:80 nginx:latest
2
3
4
端口映射,背后的原理其实就是DNAT转换
,我们可以看下iptable规则
总结
笔记
Docker将 veth pair设备的一端放在新创建的容器中,并命名为eth0 (容器的网卡),另一端放在主机中,以veth*这样类似的名字命名,并将这个网络设备映射加入到docker0 网桥中。
对于同一个容器中docker创建的这对网络设备,无论在哪一端发送消息对端都可以收到,因此eth0与veth可以无障碍通信
对于容器之间通信(也就是不同veth之间通信),通过虚拟网桥转发通信,通过arp协议得到目标IP地址的mac地址,通过mac地址进行转发
对于容器与外部,通过docker0与宿主机eth0通过DNAT进行转发通信