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 addr 或 ifconfig 只能看到属于该命名空间的网络接口。
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 │
└───────┘ └──────┘ └──────┘ └──────┘
网桥的工作方式:
- 二层转发:根据 MAC 地址转发数据帧
- ARP 学习:自动学习端口对应的 MAC 地址
- 隔离广播域:同一网桥内的容器在同一个广播域
数据包流转过程
当容器 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 的区别:
| 特性 | Macvlan | IPvlan |
|---|---|---|
| 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 | 设置 MTU | 0(无限制) |
# 创建禁用容器间通信的网络
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查看配置、进入容器测试连通性
练习
- 创建一个自定义 bridge 网络并启动两个容器互相通信
- 使用 Docker Compose 配置一个多服务的网络架构
- 配置一个只允许内部通信的网络
- 使用 host 网络模式运行一个 Web 服务
- 设计一个三层网络架构(前端、API、数据库)