Docker提供了多种内置的网络模式,用于在容器之间建立网络连接。这些网络模式,包括桥接网络、主机网络、无网络模式。我们将主要探讨每种网络模式的优缺点、适用场景。
桥接网络
桥接网络是Docker的默认网络模式。在桥接网络中,Docker会为每个容器创建一个虚拟网络接口,并为容器分配一个IP地址。容器可以通过桥接网络与主机和其他容器进行通信。
桥接网络适用于单主机上的多个容器之间的通信,例如微服务架构中的多个服务容器之间的通信。
优点
隔离性:每个容器都有独立的网络命名空间,相互之间隔离,不会互相干扰。
简单易用:桥接网络是默认的网络模式,无需额外配置,容器可以直接进行通信。
跨主机通信:可以通过端口映射实现容器与主机之间的通信,也可以使用Overlay网络实现跨主机通信。
缺点
性能损失:桥接网络需要进行网络地址转换(NAT),可能会引入一定的性能损失。
端口冲突:如果多个容器使用相同的端口号,可能会导致端口冲突。
使用默认网络
桥接网络是默认的网络模式,无需额外配置。通过docker run
命令创建容器时,可以使用--network bridge参数
指定使用桥接网络,当然因为是默认的,也可以选择不加。
创建两个容器,不指定网络名称:
$ docker container run -d --rm --name box1 busybox /bin/sh -c "while true; do sleep 3600; done"
688c366577417888ad3835fa87370003c163a2a9c31ab6e109e2114b5528be6d
$ docker container run -d --rm --name box2 busybox /bin/sh -c "while true; do sleep 3600; done"
141723898adb64074b223d8823a4b7c8712eb405d4c1542212e09c4f17682c18
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
141723898adb busybox "/bin/sh -c 'while t…" 12 seconds ago Up 10 seconds box2
688c36657741 busybox "/bin/sh -c 'while t…" 59 seconds ago Up 58 seconds box1
查询容器使用的网络名称:
$ docker container inspect box3
[
{
"Id": "688c366577417888ad3835fa87370003c163a2a9c31ab6e109e2114b5528be6d",
。。。 。。。
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "28e45b9b8f4b19c29f07c732883ccfeab6640dc37a0824b2790f6ea0b7a0ef6a",
"EndpointID": "21781c6badbb0beb33320c6e9cb307582e60ed02050d5102f9dd21d579d1b5d9",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
可以看到容器默认使用的网络为bridge。
使用自定义网络
创建一个网络名称为mynetwork
的网络:
$ docker network create mynetwork
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
28e45b9b8f4b bridge bridge local
29c08e905dab host host local
975a764ee90c mynetwork bridge local
d466db2f54a1 none null local
创建容器时指定网络名称:
$ docker container run -d --rm --name box3 --network=mynetwork busybox /bin/sh -c "while true; do sleep 3600; do
ne"
25ab55282fb9c132e33b67d01f14f870c0ab6cfa2c4b11245a2f865267607d05
$ docker container run -d --rm --name box4 --network=mynetwork busybox /bin/sh -c "while true; do sleep 3600; do
ne"
a8e936928386993b701c7669eb5cdfc4759749d22ab750ebdee5e6970dec2946
查询容器使用的网络名称:
[
{
"Id": "25ab55282fb9c132e33b67d01f14f870c0ab6cfa2c4b11245a2f865267607d05",
。。。。。。
"Networks": {
"mynetwork": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"25ab55282fb9"
],
"NetworkID": "975a764ee90c7184df5e1dccec3dd0e9b61d4277f0f64b5a7dd12d5d3bdba31c",
"EndpointID": "f11f396221cab0a09565175b87bf5c492dc127d11231f6c098ca7a3be018d592",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": null
}
}
}
}
]
使用自定义网络的容器之间可以使用容器的名称进行通讯,而使用默认网络的容器之间不能使用容器的名称进行通讯:
$ docker container exec -it box3 ping box4
PING box4 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.780 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.069 ms
64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.123 ms
^C
--- box4 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.324/0.780 ms
从Docker 1.10版本开始,docker daemon实现了一个内嵌的DNS server,使容器可以直接通过容器名称通信。这样,容器1和容器2可以通过它们的容器名称 “container1” 和 “container2” 进行通信,而不必记住它们的IP地址。
容器间通信的原理
查看网络的详细信息:
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "28e45b9b8f4b19c29f07c732883ccfeab6640dc37a0824b2790f6ea0b7a0ef6a",
"Created": "2023-10-07T10:05:47.8123729+08: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": {
"141723898adb64074b223d8823a4b7c8712eb405d4c1542212e09c4f17682c18": {
"Name": "box2",
"EndpointID": "ba6e4d32c3346cdeff733f1cb3dea2b1693db5212f8fb3c66ed9c83ea5bb0656",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"688c366577417888ad3835fa87370003c163a2a9c31ab6e109e2114b5528be6d": {
"Name": "box1",
"EndpointID": "21781c6badbb0beb33320c6e9cb307582e60ed02050d5102f9dd21d579d1b5d9",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"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": {}
}
]
可以看到这个网络的网关地址、网段、子网掩码,以及哪些容器使用了这个网络,连接在同一个网络的容器具有相同的网段和相同的网关地址,他们之间可以通过网关地址互相通信。
可以使用命令brctl
来查看操作系统中bridge的名称:
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242af8acacd no veth4bdfd60
vethe5ed0e4
brctl
使用前需要安装, 对于CentOS, 可以通过sudo yum install -y bridge-utils
安装。对于Ubuntu, 可以通过sudo apt-get install -y bridge-utils
。
容器对外通信的原理
查看容器内的路由:
$ docker container exec -it box3 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
查看主机的路由:
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.32.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.17.32.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-975a764ee90c
主机网络
在主机网络模式下,容器与主机共享网络命名空间,直接使用主机的网络接口和IP地址。容器可以通过主机网络与主机和其他容器进行通信。
主机网络模式适用于对网络性能要求较高的场景,例如需要直接访问主机上的网络资源或与主机进行高性能通信的容器。
优点
性能优势:与桥接网络相比,主机网络模式可以提供更高的网络性能,因为容器直接使用主机的网络接口,无需进行额外的网络地址转换。
简化网络配置:容器与主机共享网络命名空间,无需进行端口映射或网络转发配置。
缺点
安全性降低:容器与主机共享网络命名空间,容器可以直接访问主机上的网络资源,可能会增加安全风险。
端口冲突:如果多个容器使用相同的端口号,可能会导致端口冲突。
使用主机网络
在创建容器时,可以使用–network host参数指定使用主机网络模式。
$ docker container run --rm -it -d --name nginx --network=host nginx
121043d8367c0be214d5ac459b2890601e1013faf581b5a2ff9a9c9842c5f085
$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
可以看到容器内的IP和主机的IP是一致的:
$ hostname -I
172.17.35.208 172.17.0.1 172.18.0.1
$ docker container exec -it tomcat hostname -I
172.17.35.208 172.17.0.1 172.18.0.1
注意端口冲突:由于容器与主机共享网络命名空间,需要确保容器使用的端口号在主机上是唯一的。
考虑安全性:主机网络模式可能会降低容器的安全性,需要谨慎使用,并确保适当的安全措施。
无网络
在无网络模式下,容器没有网络接口,与外部网络完全隔离。这种模式适用于不需要网络连接的容器,例如批处理任务或与网络无关的应用。
注意容器需求:确保选择无网络模式的容器真正不需要进行网络通信,以避免功能受限或无法满足业务需求。
优点
安全性增强:无网络模式下的容器与外部网络完全隔离,可以提供更高的安全性。
资源节省:无网络模式下的容器不需要网络接口和IP地址,可以节省网络资源。
缺点
无法进行网络通信:容器无法与外部网络或其他容器进行通信。
使用无网络模式
在创建容器时,可以使用–network none参数指定使用无网络模式。
$ docker container run --rm -it --network=none centos bash
[root@0550683ce170 /]# ip addr
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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
可以看到容器只有一个本地回环地址。