Docker 命令 之 网络(Network)

摘要

什么是Network(网络)?

  • 在 Docker 中,网络(Network) 是容器之间通信、容器与外部通信的重要机制。Docker 提供了一套灵活的网络模型,使得你可以自由配置容器的网络环境以适配不同场景。

  • 安装docker时,会自动在宿主机上安装一个 docker0 网络设备,它是一个网桥设备,用于 Docker 各容器及宿主机的网络通信。

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
# 查看宿主机网卡信息,可以找到docker0
$ 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:6b:88:11:66:39 brd ff:ff:ff:ff:ff:ff
inet 10.250.0.205/24 brd 10.250.0.255 scope global dynamic noprefixroute eth0
valid_lft 3076sec preferred_lft 3076sec
inet6 fe80::86b:88ff:fe11:6639/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:d6:d5:09:b1 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:d6ff:fed5:9b1/64 scope link
valid_lft forever preferred_lft forever
## 说明
eth0 宿主机的ip地址是 10.250.0.205
docker0 本身的ip地址是 172.17.0.1
docker0 的子网掩码是 255.255.0.0
docker0 的广播地址是 172.17.255.255
docker0 可以为容器分配的ip地址范围是 172.17.0.2-172.17.255.254,总计65534个ip地址
  • 既然docker0是一个网桥设备,我们可以通过如下命令来查看网桥的详细信息:

1
2
3
4
5
$ brctl show
## 输出结果
brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d6d509b1 no
  • 启动docker服务后,docker就为我们自动创建了三个网络,可以通过如下命令查看

1
2
3
4
5
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
4182e112bf34 bridge bridge local
958daf8a8a0d host host local
4674a17c6617 none null local
  • 这里NAMEbridge的网络就是与docker0设备相对应的网络,其也是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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#  启动一个容器,没有指定网络
docker run -itd --name ap1 alpine

# 此时在宿主机上查看网卡信息,会看到多出一个设备
ip addr
## 输出结果
10: vethc0e0cc3@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether c6:0d:02:c8:df:a4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::c40d:2ff:fec8:dfa4/64 scope link
valid_lft forever preferred_lft forever

# 查看容器的网卡信息
docker exec -it ap1 ip addr
## 输出结果
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
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

## 此时聪明的你已经发现了:宿主机的网卡与容器的网卡是成对出现的,并且基于序号进行关联
宿主机: 10 : veth c0e0cc3 @ if 9 # 序号 10 与 容器后缀的 10 匹配,veth 是虚拟网卡,c0e0cc3 是随机字符串,if 是 interface
容器: 9 : eth0 @ if 10 # 序号 9 与宿主机的后缀 9 匹配,eth0 是容器的网卡,if 是 interface

# 此时查看宿主机的网桥信息
brctl show
## 输出结果,可以在 interfaces 中看到 vethc0e0cc3,说明 vethc0e0cc3 已经加入到网桥中
bridge name bridge id STP enabled interfaces
docker0 8000.0242d6d509b1 no vethc0e0cc3

# 查看网络中的容器信息,这里 jq 是 json 格式化,可以通过 dnf install jq -y 安装
docker network inspect bridge --format '{{json .Containers}}' | jq
## 输出结果
{
"c2436a1d750cc3de3a6f8ab8a693af25b3371aa8f7f168d5561538dcd4a8ff2d": {
"Name": "ap1",
"EndpointID": "75469fddbbdeb8b7a328c1a1c3cc7070bb1a3e102f5fb97cc332dcbc22829af5",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}

# 获取容器的 IP 地址
docker exec ap1 hostname -i
## 输出结果
172.17.0.2

# 相同网络设备下的容器可以通过 IP 地址通信
# 我们再创建一个容器,并查看能否正常通信
docker run -itd --name ap2 alpine
docker exec -it ap2 ping 172.17.0.2
## 输出结果,说明可以通过IP地址通信
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.137 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.084 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.081 ms

# 查看网络中的容器
docker network inspect bridge --format '{{json .Containers}}' | jq
{
"9d3ed5be1916b14ec9befe3649c08cc9de247c595de248600f8ef8d0fc16c5cb": {
"Name": "ap2",
"EndpointID": "35615a7ec10842401ad8c40187c792555b5089551a8eca39ddff6734aeba549e",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"c2436a1d750cc3de3a6f8ab8a693af25b3371aa8f7f168d5561538dcd4a8ff2d": {
"Name": "ap1",
"EndpointID": "75469fddbbdeb8b7a328c1a1c3cc7070bb1a3e102f5fb97cc332dcbc22829af5",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}

# 测试是否可以通过容器名称访问
docker exec -it ap2 ping ap1
## 输出,不可以通过容器名称访问
ping: bad address 'ap1'
## 如果希望通过容器名称访问,我们可以通过 docker network create 创建一个新的网络

如果没有安装 brctl,可以通过如下方式安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# centos7:
yum install bridge-utils -y

# centos8: 不支持yum源安装,需要手动编译安装
# 下载源码安装,目前最新版本为1.7.1
wget https://mirrors.edge.kernel.org/pub/linux/utils/net/bridge-utils/bridge-utils-1.7.1.tar.gz
tar -zxvf bridge-utils-1.7.1.tar.gz
cd bridge-utils-1.7.1
# 需要先安装编译所需的工具和依赖
dnf install autoconf automake libtool make -y
# 因为源码目录中没有 configure 文件(但有 configure.ac),所以需要先运行如下命令生成 configure 文件
autoreconf -i
# 配置
./configure
# 编译 且 安装
make && make install
# 添加到环境变量
echo "export PATH=$PATH:/usr/local/sbin" >> ~/.bashrc
source ~/.bashrc
brctl --version
## 输出结果
bridge-utils, 1.7
  • Docker 默认的 bridge 网络和 Linux 内核中的 docker0 网桥是一一对应的关系。bridge 是 Docker 对网络的命名,而 docker0 是内核中网桥的名字。

  • docker0负责给连接其上的容器分配ip地址,并且是每个容器的默认网关。当容器需要访问外网时,会通过docker0转到宿主机的eth0上,所以只要宿主机可以访问外网,那么容器也可以访问外网。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看宿主机的路由表
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.250.0.1 0.0.0.0 UG 100 0 0 eth0
10.250.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0

# 查看ap1容器的路由表
$ docker exec -it ap1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.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 eth0
  • Docker Container 的 bridge 桥接模式可以参考下图

docker network 相关命令

命令 功能说明 示例 示例输出(简略)
docker network ls 列出所有 Docker 网络 docker network ls bridge, host, none 等网络名称
docker network inspect <网络名> 查看指定网络的详细信息(如 IP 范围、连接容器等) docker network inspect bridge 显示 JSON,含子网、网关、容器等信息
docker network create <网络名> 创建自定义网络(默认桥接) docker network create my-net my-net 网络 ID
docker network rm <网络名> 删除网络(不能有容器连接) docker network rm my-net 成功删除无提示,失败会有错误信息
docker network connect <网络名> <容器名> 将一个容器连接到指定网络 docker network connect my-net my-container 无输出,容器连接成功
docker network disconnect <网络名> <容器名> 将容器从网络中断开连接 docker network disconnect my-net my-container 无输出,断开成功
docker network prune 删除所有未使用的网络(慎用) docker network prune 会提示是否确认,清理未使用网络

docker network create : 创建网络

  • 语法

1
docker network create [OPTIONS] NETWORK_NAME
  • 常用参数说明表

参数 说明 示例
--driver-d 指定网络驱动类型,如 bridge, overlay, macvlan, host, none --driver bridge
--subnet 指定子网地址范围(CIDR) --subnet 192.168.100.0/24
--gateway 指定网关 IP 地址 --gateway 192.168.100.1
--ip-range 指定可分配的 IP 范围 --ip-range 192.168.100.0/25
--aux-address 保留某些 IP 地址不被分配 --aux-address="reserved=192.168.100.254"
--internal 创建一个内部网络(不能访问外部) --internal
--attachable 创建可供单独容器连接的网络(Swarm 中) --attachable
--label 添加标签 --label env=dev
--opt 提供自定义驱动选项 --opt encrypted=true
  • --driver-d : 常见的 Docker 网络驱动类型

类型 含义 是否支持端口映射 特点与应用场景
bridge(默认) 默认的桥接网络,容器通过虚拟网桥连接,共享宿主机的网络接口。 ✅ 是 默认模式,适用于单主机部署、多个容器需要互通的场景。可映射端口对外访问。
host 容器与宿主机共用网络命名空间,容器直接使用宿主机的 IP 和端口。 ❌ 否 无网络隔离,性能高,适用于高性能、低延迟场景(如游戏服务器)。
none 容器没有网络接口,完全隔离。 ❌ 否 用于安全性或测试网络不可达场景。
macvlan 为容器分配独立 MAC 和 IP,容器像物理主机一样出现在局域网中。 ✅ 是(少见) 适用于容器必须直接暴露在物理网络中的场景(如 DHCP 服务、ARP 广播)。
ipvlan(高级) 类似 macvlan,但不使用虚拟 MAC 地址。 ✅ 是(少见) 高级网络方案,适用于对网络拓扑精细控制的场景。
overlay 用于多主机之间容器通信,需要 Docker Swarm 支持。 ✅ 是(Swarm) 跨主机部署服务的必要手段,适合容器编排平台(如 Swarm、Kubernetes)。

前面我们说过,Docker会自动创建三个网络,即:bridge、host、none。对于单台宿主机的场景,绝大多数情况下我们都只会使用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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 创建一个名为my-network的网络
$ docker network create my-network
# 等同于,因为默认的网络驱动为bridge
$ docker network create --driver bridge my-network

# 查看网络,可以看到新建的网络的驱动为bridge
$ docker network ls -f name=my-network
NETWORK ID NAME DRIVER SCOPE
c2dbe1686790 my-network bridge local

# 查看宿主机的网桥信息
$ brctl show
## 输出,可以看到此时有一个网桥设备 br-c2dbe1686790,br: bridge,c2dbe1686790:network id
bridge name bridge id STP enabled interfaces
br-c2dbe1686790 8000.02425be81fae no
docker0 8000.0242d6d509b1 no vethc0e0cc3
vethf4b7577


# 查看宿主机的网卡
$ ip addr
## 输出,br-c2dbe1686790 网卡名称,其IP网段为 172.18.0.1/16
13: br-c2dbe1686790: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:5b:e8:1f:ae brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-c2dbe1686790
valid_lft forever preferred_lft forever

# 启动两个容器并添加到my-network网络中
$ docker run -itd --network my-network --name a1 alpine
$ docker run -itd --network my-network --name a2 alpine

$ ip addr
## 输出,可以看到两个容器都添加到了my-network网络中
bridge name bridge id STP enabled interfaces
br-c2dbe1686790 8000.02425be81fae no veth094946c
vetha2ba68a
docker0 8000.0242d6d509b1 no vethc0e0cc3
vethf4b7577

# 测试连通性,可以看到 a1可以ping通 a2,反过来 a2也可以ping通 a1
$ docker exec -it a1 ping a2
PING a2 (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=2.127 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.119 ms

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e0fb0614591e alpine "/bin/sh" 4 minutes ago Up 4 minutes a2
fc6e9477d2b7 alpine "/bin/sh" 4 minutes ago Up 4 minutes a1
9d3ed5be1916 alpine "/bin/sh" 4 hours ago Up 4 hours ap2
c2436a1d750c alpine "/bin/sh" 4 hours ago Up 4 hours ap1
# 不同网络中的容器之间不能相互ping通
## 通过容器名称无法ping通
$ docker exec -it a1 ping ap1
ping: bad address 'ap1'
## 通过ap1容器IP也无法ping通,这就实现了不同网络中的网络隔离
$ docker exec -it a1 ping 172.17.0.2
  • 如果创建网络时没有指定子网,则从docker0172.17.0.0/16往后排,比如我们上面创建的my-network,其子网就是172.18.0.0/16,如下命令创建一个bridge网络,并指定子网、网关等信息

1
2
3
4
5
$ docker network create \
--driver bridge \
--subnet 192.168.50.0/24 \
--gateway 192.168.50.1 \
my-custom-net

bridge 总结

  • 默认创建的 docker0 网络,是一个桥接网络,在 docker network 中的名称为 bridge,该网络下的容器可以通过IP地址相互访问,但不能通过容器名称访问

  • 通过docker network create <网络名称>创建的网络也是一个桥接网络,在这个网络下,容器可以通过IP地址相互访问,也能通过容器名称访问。

docker network ls : 列出所有网络

1
2
3
4
5
6
7
8
9
10
11
12
# 列出所有网络
$ docker network ls
# 输出指定的信息
$ docker network ls --format "table {{.ID}}\t{{.Name}}\t{{.Scope}}"
# 输出json格式
$ docker network ls --format "json"
# 过滤
$ docker network ls -f "driver=bridge"
# 不截断输出
$ docker network ls --no-trunc
# 只显示network id
$ docker network ls -q

docker network inspect : 查看网络详情

1
2
3
4
5
6
# 查看名称为bridge网络详情
$ docker network inspect bridge
# 查看当前网络下有哪些容器
$ docker network inspect bridge --format "{{.Containers}}"
# 输出json格式
$ docker network inspect bridge --format "json"

docker network prune : 清理没用的网络

1
2
3
4
5
6
# 删除所有未使用的网络
$ docker network prune
# 删除所有未使用的网络,无需确认
$ docker network prune -f
# 清理24小时内未使用的网络
$ docker network prune --filter "until=24h"

docker network rm : 删除网络

1
2
3
4
5
6
# 删除指定的网络
$ docker network rm <network-name>
# 删除所有网络
$ docker network rm $(docker network ls -q)
# 删除所有bridge网络,这里要注意,docker默认创建的3个网络是删除不掉的
$ docker network rm $(docker network ls -q -f driver=bridge)

docker network connect : 将容器连接到网络

1
2
# 将容器连接到网络,如果容器启动时忘记连接网络,这里可以手动添加
$ docker network connect <network-name> <container-name>

docker network disconnect : 将容器从网络断开

1
$ docker network disconnect <network-name> <container-name>

本文总结

  • 容器与网络是多对多的关系,即一个网络可以有多个容器,一个容器也可以连接到多个网络。

  • docker0 是 docker 默认创建的网络,不指定网络的情况下所有容器都连接到 docker0 网络。

  • docker0 是 bridge 网络,该网络中的容器之间可以通过 IP 互相访问,但不能通过容器名称访问。

  • 自建的 bridge 网络中的容器可以通过容器名称(或容器ID,但不常用)访问。

  • 不同的 bridge 网络中的容器不能互相访问。

项目 --link --network
功能 将一个容器链接到另一个容器,并设置环境变量和主机名映射 将容器加入到一个自定义网络中,实现灵活、隔离的网络通信
是否推荐 ❌ 不推荐(已废弃) ✅ 推荐使用
网络隔离 基于默认 bridge 网络,隔离性差 可以创建自定义网络(bridge、overlay 等),隔离性强
可扩展性 只适用于已运行的容器,连接固定 支持多个容器,灵活组合和动态扩展
DNS 支持 仅设置环境变量,不支持自动 DNS 自定义网络中支持容器名称作为 DNS 名称
生命周期 一方容器关闭,另一方仍保存过时链接 网络存在即可,容器生命周期不互相影响
安全性 所有容器共享 bridge,容易相互访问 自定义网络间默认隔离,安全性更好