一、Docker 网络简介

1、网络模式

Docker 基础网络类型一共有三种(bridge、host 和 none),还有两种由基础网络类型派生的网络类型(container 和 custom)。本文详细讲解这五种网络类型。

查看 docker 网络:

1
2
3
4
5
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
994c4bd64f70 bridge bridge local
9eeb43076ad2 host host local
ad2da4225045 none null local

网络模式说明:

网络模式 配置 说明
host --network=host 容器不会创建自己的网卡,配置 IP 等,而是使用宿主机的 IP 和端口
none --network=none 容器关闭网络功能,不进行任何网路设置
bridge --network=bridge 为每个容器分配 IP 。并将容器连接到 docker0 虚拟网桥上,这种模式是默认模式
container --network=container:NAME_or_ID 容器不会创建自己的网卡和IP,而是和一个指定的容器共享 IP 和端口
custom --network=new_bridge 为每个容器分配 IP 。并将容器连接到自定义的虚拟网桥上

2、组件原理

(1)网络命名空间(network namespace)

Network Namespace 是 linux 内核提供的用于实现网络虚拟化的重要功能,是 Linux 内核用来隔离不同容器间的网络资源(每个 Docker 容器都拥有一个独立的网络命名空间),网络命名空间主要隔离的资源包括:

  • iptables规则表

  • 路由规则表

  • 网络设备列表

网络空间结构如下图所示,当系统中拥有 2 个网络命名空间:

image-20220905132737725

由于不同的网络命名空间之间是相互隔离的,所以不同的网络命名空间之间并不能直接通信。 就好比两台电脑,如果没有任何网线连接,它们之间是不能通信的。所以,Linux 内核提供了 虚拟网络设备对(veth) 这个功能,用于解决不同网络命名空间之间的通信。

(2)虚拟网络设备对(veth-pair)

虚拟网络设备对(veth-pair)用于解决不同网络命名空间之间的通信,可以将其看成是两块有网线连接的网卡。只要将其中一块网卡放置到网络命名空间A,另外一块网卡放置到网络命名空间B,那么两个不同的网络命名空间就能够通信,如下图所示:

image-20220905133354142

如上图所示,veth0 与 veth1 组成一个虚拟网络设备对。虚拟网络设备对就像管道一样,只要向其中一端发送数据,就可以从另外一端接收到数据。

Docker 就是使用 虚拟网络设备对 来实现不同容器之间的通信,其原理如下图:

image-20220905134216059

(3)docker0 网桥

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。

Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

查看 docker0 网桥:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# ip a
...
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:60:fc:72:3b 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:60ff:fefc:723b/64 scope link
valid_lft forever preferred_lft forever
...

查看 docker0 详细:

1
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
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b2f23dec622af41e1976542bc1633f6c785f7a6ff4eeaaf2a779c761dba03941",
"Created": "2022-09-04T22:15:41.831270638-04:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

查看连接到 docker0 的虚拟网络设备对(veth-pair):

1
2
3
4
5
6
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024260fc723b no veth1870812
veth1de92fe
veth5b85033
vethbfd4b66

注意:每启动一个容器,就会生成一个 veth-pair 。

如果没有 brctl 这个命令,可以通过以下命令进行安装:

1
yum install -y bridge-utils	

二、网络模式详细说明

1、Host 模式

在 host 模式下,容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP 和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。

结构如下图所示:

image-20220902105014515

查看该网络模式配置:

1
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
[root@localhost ~]# docker network inspect host
[
{
"Name": "host",
"Id": "9eeb43076ad21e4891954ab2f6682213adaa52c64f3c3668ad8fa8d4493785e4",
"Created": "2022-06-19T23:25:06.060598453-04:00",
"Scope": "local",
"Driver": "host",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": []
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

2、None 模式

使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

结构如下图所示:

image-20220902153414430

查看该网络模式配置:

1
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
[root@localhost ~]# docker network inspect none
[
{
"Name": "none",
"Id": "ad2da4225045337a2192fc016ca7d7fc984a82a1452221e19e07d9a5dce0cb71",
"Created": "2022-06-19T23:25:05.946788343-04:00",
"Scope": "local",
"Driver": "null",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": []
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

3、Bridge 模式

Bridge 模式是 docker 的默认网络模式,不写 --network 参数,就是 bridge 网桥模式。

当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的Docker容器默认都会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

从 docker0子网中分配一个IP给容器使用,并设置 docker0 的IP地址为容器的默认网关。在主机上创建一对虚拟网卡 veth pair 设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以 vethxxx 这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过 brctl show 命令查看。

结构如下图所示:

docker_bridge

查看该网络模式配置:

1
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
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "44529eeb55f59f1af6d75c7d54fab3a5283b0e021ee91b7a1163c710d5dbc573",
"Created": "2022-09-05T04:34:26.929819942-04:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

4、Container 模式

Container 模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

结构如下图所示:

image-20220902152236371

在以上示例中,Docker 2 容器可以通过--network=container:Docker 1 参数,指定自己的网络和一个容器 Docker 1 共享 IP 和端口。

示例:

1
docker run -it --name docker2 --network=container:docker1  nginx:latest /bin/bash

5、Custom 模式

Custom 模式用于自定义 docker 网络。

结构如下图所示:

image-20220902153137551

如上图所示,新建自定义网桥 custom ,指定网络(192.168.10.0/24)和网关(192.168.10.1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 新建自定义网络
docker network create --driver bridge --subnet 192.168.10.0/24 --gateway 192.168.10.1 Custom

# 查看自定义网络,查看定义网络
docker network ls

NETWORK ID NAME DRIVER SCOPE
994c4bd64f70 bridge bridge local
9eeb43076ad2 host host local
24cd1b0769bd Custom bridge local
ad2da4225045 none null local

# 查看自定义网桥 IP 地址
[root@localhost ~]# ip addr

316: br-24cd1b0769bd: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:cb:b3:6c:a3 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.1/24 brd 192.168.10.255 scope global br-24cd1b0769bd
valid_lft forever preferred_lft forever

三、自定义网络实战 - 网络联通

在没有使用 connect 命令的情况下,不同网络间的容器是无法进行网络连接的。

如下图所示:container1 和 container2,使用不同的网络,所以无法联通。

image-20220906110732392

1、验证不同网络无法联通性

(1)创建容器 container1

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建容器
docker run -it --name container1 centosjava:latest /bin/bash

# 查看容器 IP
[root@41eea42d9980 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.8 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:08 txqueuelen 0 (Ethernet)
RX packets 8 bytes 656 (656.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...

(2)创建容器 container2

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建容器
docker run -it --name container2 --network Custom centosjava:latest /bin/bash

# 查看容器 IP
[root@7279af38152a /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.10.2 netmask 255.255.255.0 broadcast 192.168.10.255
ether 02:42:c0:a8:0a:02 txqueuelen 0 (Ethernet)
RX packets 12 bytes 1032 (1.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...

(3)两台容器之间互相 ping 测试

1
2
3
4
5
6
7
# 容器 container1 ping container2 ,不通
[root@41eea42d9980 /]# ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.

# 容器 container2 ping container1 ,不通
[root@7279af38152a /]# ping 172.17.0.8
PING 172.17.0.8 (172.17.0.8) 56(84) bytes of data.

2、不同网络之间网络联通

(1)将容器 container1 和容器 container2 分别添加到对方所在的网络中

1
2
docker network connect Custom container1
docker network connect bridge container2

(2)再次检查两台容器之间网络联通性

1
2
3
4
5
6
7
8
9
10
11
12
# 容器 container1 ping container2 ,通
[root@41eea42d9980 /]# ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=123 ttl=64 time=0.087 ms
64 bytes from 192.168.10.2: icmp_seq=124 ttl=64 time=0.085 ms


# 容器 container2 ping container1 ,通
[root@7279af38152a /]# ping 172.17.0.8
PING 172.17.0.8 (172.17.0.8) 56(84) bytes of data.
64 bytes from 172.17.0.8: icmp_seq=229 ttl=64 time=0.090 ms
64 bytes from 172.17.0.8: icmp_seq=230 ttl=64 time=0.077 ms

通过以上实战说明,docker 不同网络之间互联需要使用 connect 命令进行网络发布!!!