Docker 网络是容器通信的基础设施,它使容器能够安全地进行互联互通。在 Docker 中,每个容器都可以被分配到一个或多个网络中,容器可以通过网络进行通信,就像物理机或虚拟机在网络中通信一样。

Docker 网络命令详解

在开始学习不同类型的网络之前,我们先来了解一下 Docker 的常用网络命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 列出所有网络
docker network ls

# 检查网络详情
docker network inspect <network-name>

# 创建自定义网络
docker network create [options] <network-name>

# 将容器连接到网络
docker network connect <network-name> <container-name>

# 断开容器与网络的连接
docker network disconnect <network-name> <container-name>

# 删除网络
docker network rm <network-name>

# 删除所有未使用的网络
docker network prune

网络类型及实践案例

1. Bridge 网络(桥接网络)

Bridge 网络是 Docker 的默认网络驱动程序。当你创建一个容器而不指定网络时,它会自动添加到默认的 bridge 网络中。Bridge 网络在单机环境下使用非常广泛,它通过软件网桥实现容器间的通信。

Bridge 网络的工作原理

Bridge 网络就像是 Docker 中的一个虚拟交换机,它在宿主机上创建一个名为 docker0 的网桥,
所有连接到这个网桥的容器都可以通过它进行通信。当你安装 Docker 时,会自动创建一个默认的 bridge 网络,
它使用 172.17.0.0/16 这个网段,所有未指定网络的容器都会自动连接到这个默认网络中。
不过,默认的 bridge 网络功能比较简单,容器之间只能通过 IP 地址互相访问,不支持通过容器名称来通信。

为了解决这个限制,Docker 提供了用户自定义 bridge 网络的功能。
当你创建自己的 bridge 网络时,连接到这个网络的容器就能获得更多便利的特性:容器之间可以通过名称相互访问,
网络隔离性更好,还可以随时将容器从网络中添加或移除。这就像是给容器们创建了一个独立的局域网,
既安全又方便管理。比如说,你可以把一个应用的前端、后端和数据库容器都放在同一个自定义 bridge 网络中,
它们就能通过容器名称轻松地相互通信,同时又与其他应用的容器网络保持隔离。

实践案例一:默认 Bridge 网络

让我们先来看看默认 bridge 网络的行为:

我们现构建一个装有 ping, curl 等指令的 nginx 镜像,方便我们在容器内容观察网络行为。

1
2
# 构建 nginx 镜像
docker build -t my-nginx nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看默认 bridge 网络信息
docker network inspect bridge

# 启动两个容器
docker run -d --name container1 my-nginx
docker run -d --name container2 my-nginx

# 查看容器的网络配置
docker inspect container1 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
docker inspect container2 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

# 进入容器1,尝试通过 IP 访问容器2
docker exec -it container1 curl http://172.17.0.3

# 注意:在默认 bridge 网络中,无法通过容器名称访问
docker exec -it container1 curl http://container2 # 这将失败

Docker 默认的 bridge 网络存在通信限制:容器间只能通过易变的 IP 地址互相访问,无法使用固定的容器名称进行通信。
这种 IP 依赖会导致服务地址变更时需要人工调整配置,增加维护成本。通过创建自定义 bridge 网络,
容器可通过稳定的名称直接互访,这种自动化的服务发现机制正是 Docker Compose 实现容器编排的基础——编排工具会自动创建专用网络,
使多容器应用能够通过服务名称维持稳定的通信链路。

image-20250626022705199

实践案例二:自定义 Bridge 网络

现在让我们看看自定义 bridge 网络的优势:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建自定义 bridge 网络
docker network create \
--driver bridge \
--subnet=172.20.0.0/16 \
--gateway=172.20.0.1 \
my-bridge-network

# 启动两个容器,连接到自定义网络
docker run -d \
--name custom-container1 \
--network my-bridge-network \
my-nginx

docker run -d \
--name custom-container2 \
--network my-bridge-network \
my-nginx

# 现在可以通过容器名称访问
docker exec -it custom-container1 curl http://custom-container2

image-20250626022953354

docker network create ...

  • docker network create: 创建一个新的 Docker 虚拟网络。
  • --driver bridge: 指定网络的驱动类型。
    • bridge (桥接) 是最常用的驱动。它会创建一个私有的内部网络,容器连接到这个网络上后,可以互相通信,也可以通过宿主机的网络接口与外部通信(如果需要的话)。你可以把它想象成你家里的路由器,它创建了一个局域网,所有连上 Wi-Fi 的设备(手机、电脑)都在这个局域网里,可以互相访问。
  • --subnet=172.20.0.0/16: 指定这个网络的子网。
    • 这是一个 CIDR 地址块表示法。172.20.0.0 是网络地址,/16 表示子网掩码,决定了这个网络里可以容纳多少个 IP 地址(这里是 2^16 - 2 = 65534 个)。
    • 作用: 当有容器加入这个网络时,Docker 会从这个 172.20.x.x 的地址池里给它分配一个私有 IP 地址。这让你可以对容器的 IP 地址有一个预期的规划。
  • --gateway=172.20.0.1: 指定这个网络的网关地址。
    • 网关是这个虚拟局域网的“出口”。容器需要访问外部网络时,流量都会经过这个地址。通常我们会把网络地址段的第一个可用 IP (.1) 设置为网关。
  • my-bridge-network: 你给这个网络起的名字。

docker run ... --network my-bridge-network ...

  • --network my-bridge-network: 这是 docker run 命令中一个非常关键的参数。它的作用是,在容器启动时,就把它“插”到名为 my-bridge-network 的网络上。
  • 执行完这两步后,custom-container1custom-container2 就都成为了 my-bridge-network 这个局域网的成员。它们各自会被分配一个 IP 地址,例如 172.20.0.2172.20.0.3

docker exec -it custom-container1 curl http://custom-container2

  • 这是最神奇的地方!

  • 当你进入 custom-container1 内部,执行 curl http://custom-container2 时,请求成功了。

  • 为什么可以用名字 custom-container2

    • 答案: Docker 为所有连接到自定义桥接网络 (my-bridge-network) 的容器提供了一个内置的 DNS 服务器。

    • 工作原理: 当你在

      1
      custom-container1

      里尝试访问

      1
      custom-container2

      这个主机名时:

      1. custom-container1 的网络配置会将这个 DNS 查询发送到 Docker 内置的 DNS 服务器。
      2. 这个 DNS 服务器维护着一张“域名-IP”映射表,其中包含了 my-bridge-network 网络上所有容器的名称和它们的内部 IP 地址。它知道 "custom-container2" 对应的 IP 是 172.20.0.3(举例)。
      3. DNS 服务器将 172.20.0.3 这个地址返回给 custom-container1
      4. curl 程序拿到了这个 IP 地址,然后向 172.20.0.380 端口(http 默认端口)发起请求,从而成功访问到了 custom-container2 里的 Nginx 服务。

✅【额外建议或常见错误】

  1. 自定义网络 vs. 默认网络:
    • 当你安装完 Docker,它会自动创建一个名为 bridge 的默认网络。如果你 docker run 时不指定 --network 参数,容器就会被连接到这个默认网络上。
    • 一个巨大的区别: 在默认的 bridge 网络上,这种基于容器名的自动服务发现是不支持的(或者说需要使用过时的 --link 参数)。两个在默认网络里的容器无法直接通过名字互相访问。
    • 因此,最佳实践是:只要你有两个或以上的容器需要互相通信,就应该为它们创建一个自定义的桥接网络,而不是使用默认网络。
  2. 应用场景:
    • 这个功能是 Docker Compose 和微服务架构的基石。想象一个典型的 Web 应用:一个 web-app 容器(跑着你的 Python/Java/Node.js 代码)和一个 db 容器(跑着 MySQL/PostgreSQL)。
    • 你只需要把它们都连接到同一个自定义网络 my-app-net 上,你的 web-app 代码里连接数据库的地址就可以直接写成 host="db",而不需要关心 db 容器的具体 IP 是什么。这让配置变得极其简单和稳定。
  3. 调试技巧:
    • 你可以使用 docker network inspect my-bridge-network 命令来查看网络的详细信息,包括哪些容器连接到了这个网络上,以及它们各自的 IP 地址。这在排查网络问题时非常有用。

实践案例三: 使用自定义 Bridge 网络演示 Web 应用与 Redis 通信

这个案例展示了在实际应用中如何使用自定义 bridge 网络:

首先,创建一个简单的 Web 应用:

1
2
3

# 构建web-app镜像
docker build -t web-app web-app

然后创建网络并运行容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建自定义 bridge 网络
docker network create my-bridge-network

# 启动 Redis 容器
docker run -d \
--name redis-server \
--network my-bridge-network \
redis:alpine

# 启动 Web 应用容器
docker run -d \
--name web-app \
--network my-bridge-network \
-p 5000:5000 \
web-app

现在可以测试应用了:

1
2
3
4
5
6
7
8
9
# 访问应用
curl http://localhost:5000

# 多次访问,观察计数器增加
curl http://localhost:5000
curl http://localhost:5000

# 查看 Redis 中的数据
docker exec -it redis-server redis-cli get hits

image-20250626023541746

image-20250626023602853

docker exec -it redis-server redis-cli get hits:在不进入容器交互式 Shell 的情况下,直接从宿主机终端对 redis-server 容器执行一条 Redis 客户端命令,以获取 hits 这个键(key)对应的值(value)。

  • redis-cli get hits:
    • 这是真正要在 redis-server 容器内部执行的完整命令字符串。我们可以再把它拆分为三部分:
    • redis-cli: 这是 Redis 官方提供的命令行界面 (Command-Line Interface) 工具。它预装在官方的 redis 镜像中,是你与 Redis 数据库交互的“遥控器”。
    • get: 这是 redis-cli 的一个子命令,也是 Redis 数据库最基础的命令之一。它的作用是获取 (GET) 一个指定键 (key) 所存储的值 (value)。
    • hits: 这是你提供给 get 命令的参数,即你想要查询的键(key)的名称

所以,整条命令串起来的含义就是:“Docker,请在 redis-server 容器里,帮我运行 redis-cli 工具,用它来 get 一个名叫 hits 的键的值,并把结果显示给我。”

清理环境:

1
2
3
4
5
# 停止并删除容器
docker stop web-app redis-server
docker rm web-app redis-server
docker network rm my-bridge-network
docker rmi web-app redis:alpine

2. Host 网络

Host 网络移除了容器和 Docker 主机之间的网络隔离,直接使用主机的网络。

特点:

  • 最佳网络性能
  • 直接使用主机的网络栈
  • 没有网络隔离
  • 端口直接绑定到主机上

实践案例:使用 Host 网络运行 Nginx 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用 host 网络运行 Nginx
docker run -d \
--name nginx-host \
--network host \
my-nginx

# 直接通过主机的 80 端口访问
curl http://localhost:80

# 因为使用了 host 网络,容器直接使用主机的 80 端口, 所以当我们再次启动一个 Nginx 容器时,会报端口冲突的错误
docker run -d \
--name nginx-host-2 \
--network host \
my-nginx

docker logs nginx-host-2

3. None 网络

None 网络完全禁用了容器的网络功能,容器在这个网络中没有任何外部网络接口。

特点:

  • 完全隔离的网络环境
  • 容器没有网络接口
  • 适用于不需要网络的批处理任务

实践案例:使用 None 网络运行独立计算任务

1
2
# 运行一个计算密集型任务,不需要网络
docker run --network none alpine sh -c 'for i in $(seq 1 10); do echo $((i*i)); done'

4. Overlay 网络

Overlay 网络是 Docker 用于实现跨主机容器通信的网络驱动,主要用于 Docker Swarm 集群环境。
它通过在不同主机的物理网络之上创建虚拟网络,使用 VXLAN 技术在主机间建立隧道,从而实现容器间的透明通信。
在 Overlay 网络中,每个容器都会获得一个虚拟 IP,容器之间可以直接通过这个 IP 进行通信,
而不需要关心容器具体运行在哪个主机上。这种网络类型特别适合于微服务架构、分布式应用以及需要跨主机通信的容器化应用,
例如分布式数据库集群、消息队列集群等。Overlay 网络支持网络加密,能确保跨主机通信的安全性,
同时还提供了负载均衡和服务发现等特性,是构建大规模容器集群的重要基础设施。