跳到主要内容

Docker 网络管理

Docker 网络是容器之间以及容器与外部世界通信的基础。理解 Docker 网络对于构建分布式应用至关重要。

网络基础概念

为什么需要网络管理

容器默认是隔离的,但实际应用中容器需要:

  • 与其他容器通信(如 Web 服务访问数据库)
  • 接受外部请求(如 Web 服务器暴露端口)
  • 访问外部网络(如调用第三方 API)

从容器内部看,它只看到一个网络接口(通常为 eth0)、一个 IP 地址、一个默认网关和 DNS 服务。容器不需要知道底层网络类型,也不需要知道通信的对端是否也是 Docker 容器。这种透明性是容器网络设计的核心原则。

Docker 网络的工作原理

Docker 使用 Linux 内核的网络命名空间(Network Namespace)实现网络隔离。每个容器都有自己的网络命名空间,包含独立的网络栈(接口、路由表、iptables 规则等)。

网络命名空间(Network Namespace)

网络命名空间是 Linux 内核提供的隔离机制,可以让不同的进程看到完全独立的网络栈。每个命名空间包含:

组件说明
网络设备独立的网卡接口(如 eth0、lo)
IP 地址独立的 IP 地址配置
路由表独立的路由规则
iptables独立的防火墙规则
/proc/net独立的网络统计信息

当容器启动时,Docker 会为它创建一个新的网络命名空间。在容器内执行 ip addrifconfig 只能看到属于该命名空间的网络接口。

Veth Pair(虚拟以太网对)

Veth Pair 是一对虚拟网络设备,它们像一根虚拟网线的两端:

┌─────────────────┐              ┌─────────────────┐
│ 容器命名空间 │ │ 主机命名空间 │
│ │ │ │
│ ┌───────┐ │ veth pair │ ┌───────┐ │
│ │ eth0 │◄────┼──────────────┼───►│vethxxx│ │
│ └───────┘ │ │ └───────┘ │
│ 172.17.0.2 │ │ (连接到网桥) │
└─────────────────┘ └─────────────────┘

关键特点:

  • 一端写入的数据会立即在另一端出现
  • 一端在容器内(通常命名为 eth0),另一端在主机上
  • 主机端的 veth 设备连接到 Docker 网桥

Linux Bridge(网桥)

Docker 在主机上创建一个虚拟网桥(默认为 docker0),类似于一个虚拟交换机:

                主机网络栈
┌──────────────────────────────────────┐
│ docker0 网桥 │
│ 172.17.0.1 │
│ ┌─────┬─────┬─────┬─────┬─────┐ │
│ │veth1│veth2│veth3│veth4│ ... │ │
│ └──┬──┴──┬──┴──┬──┴──┬──┴─────┘ │
└─────│─────│─────│─────│──────────────┘
│ │ │ │
┌─────┴─┐ ┌─┴───┐ ┌─┴───┐ ┌─┴───┐
│容器1 │ │容器2 │ │容器3 │ │容器4 │
│.0.2 │ │.0.3 │ │.0.4 │ │.0.5 │
└───────┘ └──────┘ └──────┘ └──────┘

网桥的工作方式:

  1. 二层转发:根据 MAC 地址转发数据帧
  2. ARP 学习:自动学习端口对应的 MAC 地址
  3. 隔离广播域:同一网桥内的容器在同一个广播域

数据包流转过程

当容器 A(172.17.0.2)访问容器 B(172.17.0.3)时:

1. 容器 A 构造数据包
┌─────────────────────────────────────┐
│ 源IP: 172.17.0.2 目标IP: 172.17.0.3 │
│ 源MAC: aa:bb:cc 目标MAC: ? │
└─────────────────────────────────────┘

2. 容器 A 发送 ARP 请求
"谁有 172.17.0.3?请告诉 172.17.0.2"

3. 网桥收到 ARP 请求并广播到所有端口
容器 B 收到请求后回复自己的 MAC 地址

4. 容器 A 发送数据包
┌─────────────────────────────────────┐
│ 源IP: 172.17.0.2 目标IP: 172.17.0.3 │
│ 源MAC: aa:bb:cc 目标MAC: dd:ee:ff │
└─────────────────────────────────────┘

5. 网桥根据 MAC 地址表转发到正确端口
数据包到达容器 B

当容器访问外部网络时,数据包需要经过 NAT(网络地址转换):

1. 容器发送数据包到外部服务器
┌─────────────────────────────────────┐
│ 源IP: 172.17.0.2 目标IP: 8.8.8.8 │
└─────────────────────────────────────┘

2. 数据包到达网桥,主机作为网关
网桥将数据包转发到主机网络栈

3. iptables 执行 SNAT(源地址转换)
┌─────────────────────────────────────┐
│ 源IP: 192.168.1.100 目标IP: 8.8.8.8 │ ← 源IP 变为主机 IP
└─────────────────────────────────────┘

4. 数据包从主机网卡发出到外部网络

网络类型

Docker 提供多种网络驱动,适用于不同场景。

Bridge(桥接网络)

Bridge 是默认的网络类型,适用于同一主机上的容器通信。它是大多数单机应用的首选网络方案。

查看默认网络:

# 列出所有 Docker 网络
docker network ls

# 输出示例:
# NETWORK ID NAME DRIVER SCOPE
# a1b2c3d4e5f6 bridge bridge local ← 默认网桥
# e7f8g9h0i1j2 host host local ← 主机网络
# k3l4m5n6o7p8 none null local ← 无网络

默认 Bridge 网络:

# 不指定 --network 时,容器自动连接到默认 bridge 网络
docker run -d --name web nginx

# 查看默认网桥的详细信息
docker network inspect bridge

# 默认 bridge 网络的特点:
# 1. 所有未指定网络的容器都会连接到这里
# 2. 容器之间只能通过 IP 地址通信(无 DNS 解析)
# 3. 容器重启后 IP 可能变化
# 4. 不适合生产环境使用

让我们通过一个实例来理解默认 bridge 的限制:

# 启动两个容器,都不指定网络(使用默认 bridge)
docker run -d --name container-a alpine sleep 3600
docker run -d --name container-b alpine sleep 3600

# 在 container-a 中尝试通过名称访问 container-b
docker exec container-a ping -c 2 container-b
# 输出:ping: bad address 'container-b'
# 原因:默认 bridge 不支持容器名称 DNS 解析

# 只能通过 IP 地址访问
docker inspect container-b --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
# 假设输出:172.17.0.3

docker exec container-a ping -c 2 172.17.0.3
# 这次可以 ping 通,但 IP 地址不直观且可能变化

自定义 Bridge 网络(推荐):

# 创建自定义 bridge 网络(使用默认配置)
docker network create my-network

# 创建时指定子网和网关(推荐做法)
docker network create \
--driver bridge \ # 指定驱动类型
--subnet 172.20.0.0/16 \ # 子网范围:可容纳 65534 个 IP
--gateway 172.20.0.1 \ # 网关地址
my-network

# 连接容器到自定义网络
docker run -d --name web --network my-network nginx
docker run -d --name db --network my-network mysql

# 验证 DNS 解析功能
docker exec web ping -c 2 db
# 输出:PING db (172.20.0.3): 56 data bytes
# 成功!容器可以通过名称互相访问

自定义 Bridge 相比默认 Bridge 有明显优势:

特性默认 Bridge自定义 Bridge说明
DNS 解析只能用 IP可用容器名自定义网络内置 DNS 服务器
网络隔离所有容器互通仅同网络容器互通更好的安全隔离
动态连接需要重启容器可动态连接/断开运行时灵活调整
配置灵活性统一配置可单独配置每个网络独立设置
IP 地址管理随机分配可指定子网和静态 IP更可控的 IP 规划

指定静态 IP 地址:

# 创建网络时指定子网
docker network create --subnet 172.20.0.0/16 mynet

# 运行容器时指定固定 IP
docker run -d --name web \
--network mynet \
--ip 172.20.0.100 \ # 指定固定 IP 地址
nginx

# 验证 IP 地址
docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
# 输出:172.20.0.100

Host(主机网络)

Host 网络模式让容器直接使用主机的网络命名空间,没有网络隔离。这意味着容器不会获得独立的 IP 地址,而是共享主机的网络栈。

工作原理对比:

普通模式(bridge):
┌──────────────────────────────────────┐
│ 主机网络栈 │
│ IP: 192.168.1.100 │
│ ┌─────────────────────────────┐ │
│ │ docker0 网桥 │ │
│ │ 172.17.0.1 │ │
│ │ ┌─────┐ ┌─────┐ │ │
│ │ │veth1│ │veth2│ │ │
│ │ └──┬──┘ └──┬──┘ │ │
│ └─────│───────────│───────────┘ │
│ │ │ │
│ ┌────┴────┐ ┌────┴────┐ │
│ │ 容器1 │ │ 容器2 │ │
│ │172.17. │ │172.17. │ │
│ │ 0.2 │ │ 0.3 │ │
│ └─────────┘ └─────────┘ │
└──────────────────────────────────────┘

Host 模式:
┌──────────────────────────────────────┐
│ 主机网络栈 │
│ IP: 192.168.1.100 │
│ │
│ ┌─────────────────────────────┐ │
│ │ 容器直接使用主机网络 │ │
│ │ 没有独立的网络命名空间 │ │
│ │ │ │
│ │ 监听 80 端口 = 主机 80 端口 │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────────┘

使用示例:

# 使用 host 网络模式运行 Nginx
docker run -d --name web --network host nginx

# 注意:不需要 -p 参数,容器直接使用主机端口
# Nginx 监听的 80 端口就是主机的 80 端口

# 验证:直接访问主机 IP 的 80 端口
curl http://localhost:80

适用场景:

场景说明
高性能网络应用没有 NAT 转换开销,网络性能最佳
需要处理大量端口避免配置大量端口映射
网络监控工具需要监控主机网络流量
性能测试需要消除网络虚拟化开销

注意事项:

# 问题 1:端口冲突
# 多个容器不能使用相同端口
docker run -d --network host --name web1 nginx # 监听 80 端口
docker run -d --network host --name web2 nginx # 启动失败!端口冲突

# 问题 2:安全性降低
# 容器可以直接访问主机网络,可能带来安全风险
# 建议只在信任的容器使用此模式

# 问题 3:跨平台兼容性
# host 模式在 Docker Desktop (Windows/macOS) 上行为不同
# Docker Desktop 运行在虚拟机中,host 网络是虚拟机的网络

性能对比示例:

# 使用 bridge 网络(有 NAT 开销)
docker run -d --name nginx-bridge -p 8080:80 nginx
# 客户端 -> 主机:8080 -> NAT -> 容器:80

# 使用 host 网络(无 NAT 开销)
docker run -d --name nginx-host --network host nginx
# 客户端 -> 主机:80 -> 容器:80

# 性能测试(使用 ab 或 wrk)
ab -n 10000 -c 100 http://localhost:8080/ # bridge 模式
ab -n 10000 -c 100 http://localhost:80/ # host 模式,通常更快

None(无网络)

完全禁用容器网络。

# 创建无网络的容器
docker run -d --name isolated --network none alpine

# 特点:
# - 只有 loopback 接口
# - 完全网络隔离
# - 适合安全敏感场景

适用场景:

  • 安全敏感的计算任务
  • 不需要网络的批处理作业

Overlay(覆盖网络)

用于跨主机的容器通信,是 Docker Swarm 集群的默认网络。

# 创建 overlay 网络(需要 Swarm 模式)
docker network create --driver overlay my-overlay

# 在 Swarm 服务中使用
docker service create --name web --network my-overlay nginx

Overlay 网络的工作原理是通过 VXLAN 隧道技术,在不同主机之间建立虚拟网络。容器之间就像在同一局域网中通信,底层的跨主机传输由 Docker 自动处理。

Macvlan

让容器拥有独立的 MAC 地址,在网络上表现为物理设备。

# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan

# 运行容器
docker run -d --network my-macvlan nginx

适用场景:

  • 需要容器直接暴露在物理网络
  • 传统应用迁移到容器环境

注意事项:

  • 可能导致 IP 地址冲突
  • 主机无法直接与同网段容器通信

IPvlan

IPvlan 与 Macvlan 类似,但不为容器分配独立的 MAC 地址。容器共享主机的 MAC 地址,通过 IP 地址区分。这在某些网络环境(如交换机限制 MAC 地址数量)下特别有用。

# 创建 IPvlan L2 模式网络
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-ipvlan

# 创建 IPvlan L3 模式网络
docker network create -d ipvlan \
--subnet=192.168.2.0/24 \
--gateway=192.168.2.1 \
-o parent=eth0 \
-o ipvlan_mode=l3 \
my-ipvlan-l3

IPvlan 模式说明

模式说明
L2(默认)二层模式,容器与主机在同一子网,通过二层交换通信
L3三层模式,容器在不同子网,需要路由支持,主机与容器可以直接通信

IPvlan 与 Macvlan 的区别

特性MacvlanIPvlan
MAC 地址每个容器独立共享主机 MAC
交换机限制可能受限(MAC 数量限制)无此限制
主机与容器通信不支持(默认)L3 模式支持
适用场景需要独立 MAC 的场景MAC 地址受限的环境

IPvlan L2 模式示例

# 创建 IPvlan L2 网络
docker network create -d ipvlan \
--subnet=192.168.100.0/24 \
--gateway=192.168.100.1 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
ipvlan-l2

# 运行容器
docker run -d --network ipvlan-l2 --ip=192.168.100.10 nginx

# 注意:L2 模式下主机无法直接与容器通信

IPvlan L3 模式示例

# 创建 IPvlan L3 网络(容器使用独立子网)
docker network create -d ipvlan \
--subnet=10.10.0.0/24 \
-o parent=eth0 \
-o ipvlan_mode=l3 \
ipvlan-l3

# 运行容器
docker run -d --network ipvlan-l3 --ip=10.10.0.10 nginx

# L3 模式优势:主机可以直接与容器通信
ping 10.10.0.10 # 从主机可以 ping 通容器

适用场景

  • 网络交换机限制 MAC 地址数量(IPvlan 只使用一个 MAC)
  • 需要容器使用特定 IP 地址
  • 需要主机与容器直接通信(L3 模式)
  • 嵌套虚拟化环境

网络管理命令

创建网络

# 基本创建
docker network create my-network

# 指定驱动类型
docker network create --driver bridge my-bridge

# 指定子网
docker network create --subnet 172.20.0.0/16 my-network

# 指定 IP 范围
docker network create \
--subnet 172.20.0.0/16 \
--ip-range 172.20.1.0/24 \
my-network

# 指定网关
docker network create \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
my-network

# 启用 IPv6
docker network create --ipv6 --subnet 2001:db8::/64 my-ipv6

# 设置网络选项
docker network create \
--driver bridge \
--opt com.docker.network.bridge.enable_icc=true \
--opt com.docker.network.driver.mtu=1500 \
my-network

查看网络

# 列出所有网络
docker network ls

# 查看网络详情
docker network inspect my-network

# 查看网络中的容器
docker network inspect my-network --format '{{range .Containers}}{{.Name}} {{end}}'

# 过滤网络
docker network ls --filter driver=bridge

连接容器到网络

# 创建时连接
docker run -d --name web --network my-network nginx

# 连接运行中的容器
docker network connect my-network web

# 连接时指定 IP
docker network connect --ip 172.20.0.100 my-network web

# 连接到多个网络
docker network connect network1 web
docker network connect network2 web

断开容器连接

# 断开容器网络
docker network disconnect my-network web

删除网络

# 删除网络
docker network rm my-network

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

端口映射

端口映射(Port Mapping)是容器与外部世界通信的关键机制。它将容器内部的服务端口暴露给主机,使外部可以访问容器内的服务。

端口映射原理

Docker 通过 iptables NAT 规则实现端口映射。当外部请求到达主机端口时,Docker 会将请求转发到容器内部。

外部请求流向:
NAT 转换
外部客户端 ──────► 主机:8080 ─────────────────► 容器:80
│ │
│ │
┌─────┴─────┐ ┌─────┴─────┐
│ iptables │ │ 容器进程 │
│ DNAT 规则 │ │ nginx │
└───────────┘ └───────────┘

数据包变化:
入站:目标 IP:Port 从 主机:8080 变为 容器IP:80
出站:源 IP:Port 从 容器IP:80 变为 主机:8080

查看 iptables 规则:

# 查看 NAT 表中的 Docker 规则
sudo iptables -t nat -L DOCKER -n

# 输出示例:
# Chain DOCKER (2 references)
# target prot opt source destination
# DNAT tcp -- anywhere anywhere tcp dpt:8080 to:172.17.0.2:80
# 这条规则将访问主机 8080 端口的流量转发到容器 172.17.0.2 的 80 端口

发布端口

基本端口映射:

# 将主机的 8080 端口映射到容器的 80 端口
# 格式:-p 主机端口:容器端口
docker run -d -p 8080:80 --name web nginx

# 访问方式:http://主机IP:8080
# 实际请求会被转发到容器内的 80 端口

指定绑定地址:

# 只允许本地访问(安全做法)
# 格式:-p 绑定IP:主机端口:容器端口
docker run -d -p 127.0.0.1:8080:80 --name web nginx

# 效果:只有主机本身可以访问 localhost:8080
# 外部网络无法直接访问此端口
# 常用于:数据库管理界面、调试接口等

# 绑定到特定网卡 IP
docker run -d -p 192.168.1.100:80:80 --name web nginx
# 只有访问 192.168.1.100:80 才能到达容器

多端口映射:

# 映射多个端口
docker run -d \
-p 80:80 \ # HTTP
-p 443:443 \ # HTTPS
-p 8080:8080 \ # 管理界面
--name web \
nginx

# 映射端口范围
docker run -d -p 8000-8005:8000-8005 --name app myapp
# 主机的 8000-8005 端口分别映射到容器的 8000-8005 端口

指定协议:

# 默认是 TCP,可以指定 UDP
docker run -d \
-p 53:53/udp \ # UDP 端口
-p 53:53/tcp \ # TCP 端口
--name dns \
coredns/coredns

# DNS 服务通常需要同时监听 TCP 和 UDP 的 53 端口

随机端口映射:

# 使用 -P(大写)自动映射所有 EXPOSE 的端口
docker run -d -P --name web nginx

# 查看映射的随机端口
docker port web
# 输出:80/tcp -> 0.0.0.0:32768
# Docker 自动分配了一个主机端口(32768)

查看端口映射

# 查看容器的端口映射情况
docker port web

# 输出示例:
# 80/tcp -> 0.0.0.0:8080
# 443/tcp -> 0.0.0.0:8443

# 查看特定端口的映射
docker port web 80
# 输出:0.0.0.0:8080

# 通过 inspect 查看
docker inspect web --format '{{json .NetworkSettings.Ports}}'

端口映射与防火墙

重要提示:Docker 的端口映射会直接修改防火墙规则,可能绕过系统防火墙(如 ufw、firewalld)。

# 即使 ufw 禁止了端口,Docker 映射的端口仍然可以访问
# 解决方案:绑定到 localhost
docker run -d -p 127.0.0.1:8080:80 nginx

# 或使用 Docker 的 --iptables=false 选项(需要手动配置 NAT)

防火墙后端选择(Docker Engine v29+)

Docker Engine v29 引入了实验性的 nftables 防火墙后端支持。nftables 是 iptables 的现代替代品,提供了更好的性能和更简洁的语法。

后端说明适用场景
iptables传统后端,稳定成熟大多数环境(默认)
nftables现代后端,性能更好新系统,需要与现有 nftables 规则集成

启用 nftables 后端(实验性)

// /etc/docker/daemon.json
{
"firewall-backend": "nftables"
}

重启 Docker 后生效:

sudo systemctl restart docker

# 验证当前防火墙后端
docker info | grep -i firewall

nftables 后端注意事项

  • 使用 nftables 后端时,Docker 不会自动启用 IP 转发
  • 如果需要桥接网络功能,必须手动启用 IP 转发:
# 启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1

# 持久化配置
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ipforward.conf
  • 或在 daemon.json 中禁用 IP 转发检查(但某些功能可能受限):
{
"firewall-backend": "nftables",
"ip-forward": false
}

查看当前防火墙规则

# 使用 iptables 后端时
sudo iptables -t nat -L -n
sudo iptables -L DOCKER-USER -n

# 使用 nftables 后端时
sudo nft list ruleset | grep docker

端口映射最佳实践

场景推荐配置原因
Web 服务-p 80:80-p 443:443需要外部访问
数据库-p 127.0.0.1:3306:3306只允许本地访问,增加安全性
管理界面-p 127.0.0.1:8080:8080通过反向代理或 SSH 隧道访问
内部服务不映射端口,使用 Docker 网络服务间通过内部网络通信

DNS 配置

容器 DNS

# 自定义 DNS 服务器
docker run -d --dns 8.8.8.8 --dns 8.8.4.4 nginx

# 添加 DNS 搜索域
docker run -d --dns-search example.com nginx

# 添加 hosts 记录
docker run -d --add-host myapp.local:192.168.1.100 nginx

# 设置主机名
docker run -d --hostname mycontainer nginx

网络 DNS

在自定义网络中,容器可以通过名称互相解析。这是自定义网络相比默认 bridge 最重要的优势之一。

# 创建网络
docker network create app-network

# 启动数据库
docker run -d --name db --network app-network mysql

# 启动应用,可以通过 "db" 主机名访问数据库
docker run -d --name app --network app-network myapp

网络配置选项

Bridge 网络选项

选项说明默认值
com.docker.network.bridge.enable_icc启用/禁用容器间通信true
com.docker.network.bridge.enable_ip_masquerade启用 IP 伪装true
com.docker.network.driver.mtu设置 MTU0(无限制)
# 创建禁用容器间通信的网络
docker network create \
--driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
isolated-network

连接选项

# 连接时指定 IP
docker network connect --ip 172.20.0.50 my-network web

# 连接时指定别名
docker network connect --alias db-primary my-network db

# 连接时设置优先级
docker network connect --priority 100 my-network web

实战示例

微服务网络架构

使用 Docker Compose 配置典型的微服务网络:

# compose.yaml
services:
frontend:
image: nginx
networks:
- frontend
ports:
- "80:80"

api:
image: myapi
networks:
- frontend
- backend
depends_on:
- db
- redis

db:
image: mysql
networks:
- backend
volumes:
- db-data:/var/lib/mysql

redis:
image: redis
networks:
- backend

networks:
frontend:
backend:
internal: true # 内部网络,无法访问外部

volumes:
db-data:

网络隔离说明:

  • frontend 网络:可从外部访问,前端和 API 共用
  • backend 网络:内部网络,API、数据库、缓存互通
  • 数据库和缓存不暴露到外网,保证安全性

多容器通信示例

# 创建应用网络
docker network create app-network

# 启动 Redis
docker run -d --name redis --network app-network redis:alpine

# 启动 PostgreSQL
docker run -d --name postgres \
--network app-network \
-e POSTGRES_PASSWORD=secret \
postgres:15-alpine

# 启动后端应用
docker run -d --name backend \
--network app-network \
-e REDIS_URL=redis://redis:6379 \
-e DATABASE_URL=postgres://postgres:secret@postgres:5432/db \
myapp:latest

# 启动前端
docker run -d --name frontend \
--network app-network \
-p 80:80 \
nginx:alpine

跨主机网络(Swarm)

# 初始化 Swarm
docker swarm init

# 创建 overlay 网络
docker network create --driver overlay --attachable my-overlay

# 在 overlay 网络中运行服务
docker service create --name web --network my-overlay -p 80:80 nginx

# 添加更多服务
docker service create --name api --network my-overlay myapi

网络故障排除

容器网络问题可能涉及多个层面:容器配置、网络驱动、主机网络、防火墙等。系统化的排查方法可以快速定位问题根源。

常用诊断命令

查看网络配置:

# 查看所有网络
docker network ls

# 查看网络详情,包括连接的容器
docker network inspect my-network

# 查看容器的网络配置
docker inspect web --format '{{json .NetworkSettings}}' | jq

# 只看 IP 地址
docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

# 查看容器的网络模式
docker inspect web --format '{{.HostConfig.NetworkMode}}'

在容器内测试网络:

# 进入容器的交互式终端
docker exec -it web sh

# 在容器内执行网络测试命令
# 1. 查看网络接口
ip addr
# 或
ifconfig

# 2. 查看路由表
ip route
# 默认路由示例:default via 172.17.0.1 dev eth0

# 3. 查看 DNS 配置
cat /etc/resolv.conf

# 4. 测试连通性
ping -c 3 db # 测试容器间连通性
ping -c 3 8.8.8.8 # 测试外网 IP 连通性
ping -c 3 google.com # 测试 DNS 解析

# 5. 测试端口
nc -zv db 3306 # 测试 TCP 端口连通性
curl -v http://api:3000/health # 测试 HTTP 服务

# 6. DNS 查询
nslookup db # 查询容器名
nslookup google.com # 查询外网域名
dig db # 详细 DNS 查询

使用调试容器:

当目标容器缺少网络工具时,可以使用专用的调试容器:

# nicolaka/netshoot 包含丰富的网络诊断工具
# 进入目标容器的网络命名空间
docker run --rm -it --network container:web nicolaka/netshoot

# 在调试容器中可以使用各种工具:
# dig, nslookup, curl, wget, ping, nc, ss, tcpdump, iperf 等

# 示例:抓取容器的网络流量
docker run --rm -it --network container:web nicolaka/netshoot tcpdump -i eth0

# 示例:测试 DNS 解析
docker run --rm -it --network container:web nicolaka/netshoot dig db

# 示例:查看网络连接
docker run --rm -it --network container:web nicolaka/netshoot ss -tuln

常见问题排查

问题 1:容器无法解析其他容器名称

# 症状
docker exec web ping db
# ping: db: Name or service not known

# 排查步骤
# 1. 检查是否使用了自定义网络
docker inspect web --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}'
# 如果输出 "bridge",说明使用了默认网络

# 2. 默认 bridge 网络不支持 DNS 解析
# 解决方案:创建自定义网络
docker network create mynet
docker network connect mynet web
docker network connect mynet db

# 3. 验证 DNS 解析
docker exec web ping db
# 应该能 ping 通了

问题 2:容器无法访问外网

# 症状
docker exec web ping 8.8.8.8
# connect: Network is unreachable

# 排查步骤
# 1. 检查主机是否能访问外网
ping 8.8.8.8

# 2. 检查 IP 转发是否启用
sysctl net.ipv4.ip_forward
# 输出应该是:net.ipv4.ip_forward = 1

# 如果是 0,启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1
# 持久化配置
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ipforward.conf

# 3. 检查 NAT 规则
sudo iptables -t nat -L POSTROUTING -n

# 4. 检查 DNS 配置
docker exec web cat /etc/resolv.conf

问题 3:端口被占用

# 症状
docker run -d -p 80:80 nginx
# Error: bind: address already in use

# 排查步骤
# 1. 查看端口占用(Linux)
sudo lsof -i :80
sudo netstat -tlnp | grep :80
sudo ss -tlnp | grep :80

# 查看端口占用(Windows)
netstat -ano | findstr :80

# 2. 找到并停止占用进程
# Linux
sudo kill -9 <PID>

# Windows
taskkill /PID <PID> /F

# 3. 或者使用其他端口
docker run -d -p 8080:80 nginx

问题 4:容器间网络不通

# 症状
docker exec web ping db
# 无法 ping 通

# 排查步骤
# 1. 确认容器在同一网络
docker network inspect mynet --format '{{range .Containers}}{{.Name}} {{end}}'
# 应该看到 web 和 db

# 2. 检查容器的 IP 地址
docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
docker inspect db --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

# 3. 检查防火墙规则
sudo iptables -L DOCKER-USER -n

# 4. 检查是否启用了内部通信
docker network inspect mynet --format '{{.Options}}'
# 如果 "com.docker.network.bridge.enable_icc": "false",则禁用了内部通信

# 5. 尝试重建网络
docker network create --driver bridge new-mynet
docker network connect new-mynet web
docker network connect new-mynet db

问题 5:DNS 解析缓慢或失败

# 症状
docker exec web curl http://example.com
# 长时间等待或解析失败

# 排查步骤
# 1. 测试 DNS 解析时间
docker exec web time nslookup google.com

# 2. 更换 DNS 服务器
docker run -d --dns 8.8.8.8 --dns 8.8.4.4 --name web nginx

# 3. 或配置 Docker daemon
# /etc/docker/daemon.json
{
"dns": ["8.8.8.8", "8.8.4.4"]
}

# 4. 检查主机的 DNS 配置
cat /etc/resolv.conf

网络诊断流程图

容器网络问题


┌─────────────────┐
│ 检查容器状态 │──► 容器是否运行?
└────────┬────────┘


┌─────────────────┐
│ 检查网络配置 │──► 是否在正确的网络?
└────────┬────────┘


┌─────────────────┐
│ 测试容器间通信 │──► 同网络容器能否互访?
└────────┬────────┘


┌─────────────────┐
│ 测试外网连接 │──► 能否访问外部网络?
└────────┬────────┘


┌─────────────────┐
│ 检查 DNS 解析 │──► 域名能否解析?
└────────┬────────┘


┌─────────────────┐
│ 检查防火墙规则 │──► iptables 是否阻止?
└─────────────────┘

网络安全

容器间通信安全

默认情况下,同一 bridge 网络中的容器可以相互通信。在生产环境中,应该遵循最小权限原则:

# 创建禁用内部通信的网络
docker network create \
--driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
isolated-network

# 或使用 iptables 规则限制
iptables -I DOCKER-USER -i br-xxxx -o br-xxxx -j DROP

限制容器能力

# 禁止容器获取新权限
docker run --security-opt=no-new-privileges myapp

# 只读文件系统
docker run --read-only --tmpfs /tmp myapp

# 限制系统调用
docker run --security-opt seccomp=custom-profile.json myapp

网络加密

对于 overlay 网络,可以启用加密保护容器间通信:

# 创建加密的 overlay 网络
docker network create --driver overlay --opt encrypted my-secure-overlay

敏感端口保护

# 生产环境推荐配置
services:
api:
ports:
# 只绑定本地地址
- "127.0.0.1:3000:3000"
networks:
- internal

nginx:
ports:
- "80:80"
- "443:443"
networks:
- frontend
- internal

networks:
frontend:
internal:
internal: true

最佳实践

1. 使用自定义网络

# 不推荐:使用默认 bridge
docker run --name app myapp
docker run --name db mysql
# 问题:app 无法通过 "db" 名称访问数据库

# 推荐:使用自定义网络
docker network create app-network
docker run --name app --network app-network myapp
docker run --name db --network app-network mysql

2. 网络隔离

# 将需要隔离的服务放在不同网络
services:
public-api:
networks:
- public
- internal

admin-api:
networks:
- internal

db:
networks:
- internal

networks:
public:
internal:
internal: true # 内部网络

3. 限制网络访问

# 只暴露必要的端口,绑定到本地
docker run -d -p 127.0.0.1:8080:80 nginx

# 使用 internal 网络隔离敏感服务
docker network create --internal internal-network

4. 合理使用 host 网络

# 仅在需要极致性能时使用
docker run --network host nginx

# 注意端口冲突和安全风险

5. 生产环境网络架构建议

                    ┌─────────────────┐
│ 外部流量 │
└────────┬────────┘

┌────────▼────────┐
│ Nginx/LB │
│ (public网络) │
└────────┬────────┘

┌──────────────┼──────────────┐
│ │ │
┌────────▼──────┐ ┌────▼──────┐ ┌─────▼───────┐
│ API 服务 │ │ API 服务 │ │ API 服务 │
│ (public+内部) │ │(public+内部)│ │(public+内部)│
└────────┬──────┘ └────┬──────┘ └─────┬───────┘
│ │ │
└──────────────┼──────────────┘

┌────────▼────────┐
│ 数据库/缓存 │
│ (内部网络) │
└─────────────────┘

关键原则:

  • 外部服务只放在 public 网络
  • 数据库等敏感服务只放在 internal 网络
  • API 服务跨网络,作为中间层

小结

Docker 网络管理的核心内容:

  • 网络类型:bridge(默认隔离)、host(共享主机网络)、overlay(跨主机)、none(无网络)、macvlan(物理网络直连)
  • 自定义网络docker network create 创建,容器可通过名称互相访问
  • 端口映射-p host:container 发布端口,-P 随机端口
  • DNS 配置:自定义网络内置 DNS,支持容器名称解析
  • 网络隔离:使用多个网络实现服务隔离,internal: true 禁止外网访问
  • 网络加密:Overlay 网络支持 --opt encrypted 加密通信
  • 故障排除docker network inspect 查看配置、进入容器测试连通性

练习

  1. 创建一个自定义 bridge 网络并启动两个容器互相通信
  2. 使用 Docker Compose 配置一个多服务的网络架构
  3. 配置一个只允许内部通信的网络
  4. 使用 host 网络模式运行一个 Web 服务
  5. 设计一个三层网络架构(前端、API、数据库)

参考资源