logo
34
0
WeChat Login
update: 规范文档

Docker 网络管理详解

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

目录

Docker 网络命令详解

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

# 列出所有网络
docker network ls

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

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

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

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

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

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

网络类型及实践案例

容器默认网络

新创建一个容器时,会默认连接到一个叫"默认 Bridge"的网络。而所有连接该网络的容器可以通过这座"桥梁"通信。

Q:这个"默认 Bridge 网络"是什么?是谁创建的?它的通信原理是?

A:它是由 Docker 自动创建的,是一个名为 docker0 的 Linux 网桥(使用 ip addr 命令可以看到),功能上就像是一个虚拟的交换机,将容器相互连接,容器之间可以通过这个网桥进行通信。连接到它的每个容器都会被分配一个 IP 地址,相互之间可以通过 IP 地址进行通信。

Docker0

Q:除了 Bridge 网络,还有哪些网络类型?

A:除了 Bridge 网络,Docker 还支持 Host 网络、None 网络和自定义网络。Host 网络直接将容器连接到主机网络,None 网络禁用了容器的网络功能,自定义网络则允许用户创建自己的网络。这些网络类型各有优缺点,可以根据实际需求选择使用。接下来,我们将分别介绍这些网络类型及其实践案例。

1. Bridge 网络

实践案例一:默认 Bridge 网络

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

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

# 查看默认 bridge 网络的 ID
docker network ls
docker network inspect bridge -f '{{.ID}}'
# 或者 docker network inspect bridge | jq '.[0] | {Id, Name, Driver}'

# 查看容器是否连接到默认 bridge 网络
docker network inspect bridge -f '{{.Containers}}'
docker inspect container1 -f '{{range .NetworkSettings.Networks}}{{.NetworkID}}{{end}}'
docker inspect container2 -f '{{range .NetworkSettings.Networks}}{{.NetworkID}}{{end}}'

# 查看容器的 IP 地址
docker inspect container1 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
docker inspect container2 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

# 进入容器1,尝试通过 IP 访问容器2(IP 需根据上一步的实际输出替换)
docker exec -it container1 curl http://172.17.0.3

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

在默认 bridge 网络下,容器之间只能通过 IP 地址互相访问,不支持通过容器名称来通信。通过 IP 访问需要记住容器的 IP 地址,这显然不是个好办法。

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

为了解决这个限制,Docker 提供了用户自定义 bridge 网络的功能。 通过创建自定义 bridge 网络,容器可通过稳定的名称直接互访,无需依赖 IP 地址,从而简化了记忆 IP 的难度。

接下来,我们在案例中使用自定义网络:尝试将两个容器连接到同一个网络,然后通过容器名称进行通信:

# 创建自定义 bridge 网络
docker network create \
    --driver bridge \
    my-bridge-network

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

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

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

Docker 中默认的 bridge 网络与自定义 bridge 网络在容器名称解析上的差异,核心原因在于 DNS 服务的启用机制网络配置的隔离性。默认的 bridge 网络(即 docker0 虚拟网桥)不提供容器名称解析功能。容器间若需通信,必须通过 IP 地址。因此默认网络下的容器 IP 可能因重启或容器重建而变化,导致依赖 IP 的通信失效。

而自定义 bridge 网络启用 Docker 内置的 DNS 服务器(127.0.0.11),容器可通过名称直接解析其他容器的 IP 地址。容器启动时,Docker 自动将 /etc/resolv.conf中的 DNS 服务器指向 127.0.0.11

# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.

nameserver 127.0.0.11
options ndots:0

# Based on host file: '/etc/resolv.conf' (internal resolver)
# ExtServers: [10.235.16.19 183.60.83.19 183.60.82.98]
# Overrides: [nameservers]
# Option ndots from: host

而使用默认 bridge 网络创建的容器,/etc/resolv.conf 文件中的内容如下( nameserver 是 Docker 引擎根据宿主机 DNS 配置自动生成的):

# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.

nameserver 10.235.16.19
nameserver 183.60.83.19
nameserver 183.60.82.98
options ndots:0

# Based on host file: '/etc/resolv.conf' (legacy)
# Overrides: [nameservers]
# Option ndots from: host

运行完两个实践案例后运行 ip addr

或者 ip -c -brief addr,一行显示一个网络接口,简洁直观

或者 ip -j addr | jq,JSON 格式输出

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: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:04:e2:12:94 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:4ff:fee2:1294/64 scope link 
       valid_lft forever preferred_lft forever
4: veth05895da@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 4a:ef:37:ba:83:f7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::48ef:37ff:feba:83f7/64 scope link 
       valid_lft forever preferred_lft forever
6: veth6c3945d@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 9a:7f:92:75:ab:4c brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::987f:92ff:fe75:ab4c/64 scope link 
       valid_lft forever preferred_lft forever
7: br-b99d1aa4ad94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:8a:15:98:5a brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.1/16 brd 172.19.255.255 scope global br-b99d1aa4ad94
       valid_lft forever preferred_lft forever
    inet6 fe80::42:8aff:fe15:985a/64 scope link 
       valid_lft forever preferred_lft forever
9: vethf02e559@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-b99d1aa4ad94 state UP group default 
    link/ether 8a:27:a3:8e:3f:61 brd ff:ff:ff:ff:ff:ff link-netnsid 3
    inet6 fe80::8827:a3ff:fe8e:3f61/64 scope link 
       valid_lft forever preferred_lft forever
11: vethdd4a1c7@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-b99d1aa4ad94 state UP group default 
    link/ether 5e:d7:fd:f7:7b:b6 brd ff:ff:ff:ff:ff:ff link-netnsid 4
    inet6 fe80::5cd7:fdff:fef7:7bb6/64 scope link 
       valid_lft forever preferred_lft forever
363437: eth0@if363438: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:10 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.16/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

在 container1 中执行 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
3: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

在 custom-container1 中执行 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
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
       valid_lft forever preferred_lft forever

网络结构图解

┌──────────────────────────────────────────────────────────────────────────────────┐
│                                 宿主机 (Host)                                    │
├──────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│  ┌──────────────────────────────────┐    ┌──────────────────────────────────┐    │
│  │  [if2] docker0 (默认网桥)         │    │  [if7] br-xxx (自定义网桥)        │    │
│  │        172.18.0.1/16              │    │        172.19.0.1/16              │    │
│  ├──────────────────────────────────┤    ├──────────────────────────────────┤    │
│  │  [if4] veth05895da ──┐           │    │  [if9]  vethf02e559 ──┐          │    │
│  │  [if6] veth6c3945d ──┤           │    │  [if11] vethdd4a1c7 ──┤          │    │
│  └───────────────────────┼──────────┘    └────────────────────────┼─────────┘    │
│                          │                                       │              │
│              ┌───────────┴───────────┐               ┌───────────┴──────────┐   │
│              │                       │               │                      │   │
│  ┌───────────▼──────────┐ ┌──────────▼──────────┐ ┌──▼──────────────┐ ┌─────▼────────────┐
│  │     container1       │ │     container2      │ │ custom-container1│ │ custom-container2│
│  │ [if3] eth0@if4       │ │ [if5] eth0@if6      │ │ [if8] eth0@if9   │ │ [if10] eth0@if11 │
│  │       172.18.0.2     │ │       172.18.0.3     │ │       172.19.0.2 │ │        172.19.0.3│
│  └──────────────────────┘ └─────────────────────┘ └─────────────────┘ └──────────────────┘
│                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────┘

关键点解读

1. 两个网桥,互相隔离

网桥名称连接的容器
docker0默认 bridge 网络container1, container2
br-b99d1aa4ad94自定义 bridge 网络custom-container1, custom-container2

2. ifindex(接口序号)

ip addr 输出中每行开头的数字就是 ifindex(interface index),它是内核为每个网络接口分配的唯一标识:

1: lo                    ← 序号 1
2: docker0               ← 序号 2
4: veth05895da@if3       ← 序号 4(序号 3 的接口已被删除,不会回收)
7: br-b99d1aa4ad94       ← 序号 7

ifindex 有三个关键特性:

  • 全局唯一:同一网络命名空间内不会重复
  • 单调递增:每创建一个新接口序号 +1,永不回退
  • 删除不回收:接口销毁后序号作废,因此输出中会出现不连续的数字(如缺少 3、5)

@ifX 的含义:表示 veth pair 对端的 ifindex,是定位"网线另一头"的关键依据:

4: veth05895da@if3
   ↑ 本端 ifindex=4(在宿主机)   ↑ 对端 ifindex=3(在容器内,即容器的 eth0)

3. eth0 与 veth

  • eth0 是容器内部看到的主网络接口,就像一台电脑的网卡。每个容器都有自己独立的 eth0,这是 Linux Network Namespace 隔离的结果——容器以为自己独占一套网络栈。
  • veth(Virtual Ethernet)是成对创建的虚拟网络设备。一端在容器内(就是 eth0),另一端在宿主机上(就是 vethXXX)。
容器命名空间                      宿主机命名空间
┌─────────────┐                ┌──────────────────────────┐
│ eth0 (if3)  │◄── veth pair ──►│ veth05895da (if4)       │
│             │  同一对设备的两端  │       │ master=docker0  │
└─────────────┘                │       ▼                  │
                               │    docker0 (网桥)         │──► 外部网络
                               └──────────────────────────┘
概念物理世界类比
eth0电脑上的网口
vethXXX网线的另一头(插在交换机上)
docker0 / br-xxx交换机
veth pair一根网线(两头不可分离)

关键点eth0vethXXX 是同一个 veth pair 的两端——从容器内看叫 eth0,从宿主机看叫 vethXXX。数据从容器 eth0 发出后自动到达宿主机的 vethXXX,再经网桥转发到目标。每创建一个容器,Docker 就创建一对 veth,容器删除时这对 veth 一起销毁。

4. veth pair 对应关系

每个容器通过 veth pair 连接到网桥,就像一根虚拟网线的两端:

容器内: eth0@if4  <──────────────>  宿主机: veth05895da@if3
        └─ 一对虚拟网卡,像一根网线的两端 ─┘

对应关系(通过 @ifX 序号匹配): 4 个 veth* 接口 ↔ 4 个运行中的容器

容器容器内网卡宿主机 veth所属网桥
container1eth0@if4veth05895da@if3docker0
container2eth0@if6veth6c3945d@if5docker0
custom-container1eth0@if9vethf02e559@if8br-b99d1aa4ad94
custom-container2eth0@if11vethdd4a1c7@if10br-b99d1aa4ad94

5. 自定义网络不使用 docker0

ip addr 输出可以清楚看到,veth 设备的 master 字段指明了所属网桥:

# 默认网络的容器 → master docker0
veth05895da@if3: ... master docker0 ...
veth6c3945d@if5: ... master docker0 ...

# 自定义网络的容器 → master br-b99d1aa4ad94
vethf02e559@if8: ... master br-b99d1aa4ad94 ...
vethdd4a1c7@if10: ... master br-b99d1aa4ad94 ...

这就是为什么不同 bridge 网络的容器默认无法互通——它们连接在不同的虚拟交换机上。

2. Host 网络

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

特点:

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

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

# 启动一个 Nginx 容器,使用 host 网络(为了避免端口冲突,容器1 启动 alpine/curl 即可)
docker run -itd \
    --name host1 \
    --network host \
    alpine \
    sh -c "apk add --no-cache curl && sh"
docker run -d \
    --name host2 \
    --network host \
    nginx

# 查询宿主机端口 eth0 是 HOST_IP
ip addr 

# 登入容器1,通过宿主机ip访问容器2
docker exec -it host1 curl http://${HOST_IP}:80

# 注意:因为容器使用主机的网络端口,而主机的端口一旦使用,就不能再被其他容器使用,否则会提示端口冲突。
docker run -d \
    --name host-3 \
    --network host \
    nginx
    
docker logs host-3

# 精简镜像可能没有 ip/ifconfig:可以临时安装
# Debian/Ubuntu:apt-get update && apt-get install -y iproute2

3. Container 网络

Container 网络模式允许一个容器共享另一个容器的网络命名空间,两个容器将拥有相同的 IP 地址、网络接口和端口空间。

特点:

  • 多个容器共享同一个网络栈
  • 共享 IP 地址和端口空间
  • 容器间可通过 localhost 直接通信
  • Kubernetes Pod 多容器网络的实现基础

应用场景:

  • Sidecar 模式(如日志收集、代理)
  • 需要紧密网络协作的容器组
  • 模拟 Kubernetes Pod 行为

实践案例:两个容器共享网络命名空间

# 1. 启动网络提供者容器(运行 nginx)
docker run -d --name net-provider nginx

# 2. 启动网络消费者,共享网络(后台运行,保持容器存活)
docker run -d --name net-consumer --network container:net-provider alpine sleep 3600

# 3. 在 net-consumer 容器内,通过 localhost 访问 nginx
docker exec net-consumer wget -qO- http://localhost:80
# 输出: nginx 欢迎页面 HTML

# 4. 查看 net-provider 的 IP 地址
docker inspect net-provider -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
# 输出示例: 172.17.0.2

# 5. 在 net-consumer 容器内查看网络接口
docker exec net-consumer ip addr
# 输出示例:
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...
#     inet 127.0.0.1/8 scope host lo
# 18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
#     inet 172.17.0.2/16 ...  ← 与 net-provider 的 IP 完全相同

# 6. 确认 net-consumer 的网络模式
docker inspect net-consumer -f '{{.HostConfig.NetworkMode}}'
# 输出: container:<net-provider-id>

# 7. 验证端口空间共享(80端口已被 nginx 占用)
docker exec net-consumer sh -c "nc -l -p 80"
# 输出: nc: bind: Address already in use
# 说明两个容器共享同一个端口空间

与其他网络模式对比:

特性Bridge 网络Host 网络Container 网络
网络隔离容器间隔离无隔离共享容器间无隔离
IP 地址各自独立使用宿主机 IP共享同一 IP
localhost 通信不可直接通信与宿主机共享可直接通信
端口冲突与宿主机冲突共享容器间冲突

Kubernetes Pod 原理:K8s Pod 中的多个容器就是使用类似 Container 网络的机制,共享同一个网络命名空间,因此 Pod 内的容器可以通过 localhost 相互访问。

# 清理
docker rm -f net-provider net-consumer

4. None 网络

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

特点:

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

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

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

5. Overlay 网络

Overlay 网络是 Docker 用于实现跨主机容器通信的网络驱动,主要用于 Docker Swarm 集群环境。它通过在不同主机的物理网络之上创建虚拟网络,使用 VXLAN 技术在主机间建立隧道,从而实现容器间的透明通信。

特点:

  • 支持跨主机容器通信
  • 使用 VXLAN 技术建立隧道
  • 每个容器获得虚拟 IP
  • 支持网络加密
  • 提供负载均衡和服务发现

应用场景:

  • 微服务架构
  • 分布式应用
  • 分布式数据库集群
  • 消息队列集群
  • 需要跨主机通信的容器化应用

在 Overlay 网络中,容器之间可以直接通过虚拟 IP 进行通信,而不需要关心容器具体运行在哪个主机上。Overlay 网络支持网络加密,能确保跨主机通信的安全性, 同时还提供了负载均衡和服务发现等特性,是构建大规模容器集群的重要基础设施。

注意:Overlay 网络需要 Docker Swarm 或其他集群编排工具支持,本课程环境为单机,因此仅做原理介绍。

6. macvlan/ipvlan 网络

让容器获得"局域网中的独立 IP",适合对接传统网络;需网络环境与路由配置到位。

注意:macvlan/ipvlan 网络依赖特定的网络环境配置(如网卡混杂模式、路由规则),本课程仅做原理介绍。

课后实践

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

自定义 bridge 网络是目前 Docker 网络通信中最常用的方式。下面通过一个实际案例来演示自定义 bridge 网络的使用。

前置准备:本案例需要使用 web-app 目录下的代码和 Dockerfile,请确保该目录存在。

# 创建web应用的镜像
docker build -t web-app web-app

# 创建自定义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
# 访问应用
curl http://localhost:5000

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

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

清理环境

# 删除课后实践容器
docker rm -f web-app redis-server
docker rm -f custom-container1 custom-container2

# 删除默认 bridge 案例容器
docker rm -f container1 container2

# 删除 Host 网络案例容器
docker rm -f host1 host2 host-3

# 删除自定义网络
docker network rm my-bridge-network

# 删除镜像
docker rmi web-app redis:alpine