Chapter 05

Docker 网络

理解容器网络隔离原理,掌握容器间通信与端口映射

Docker 网络基础

每个容器默认拥有独立的网络命名空间(Network Namespace),意味着它有自己独立的:

Docker 通过网络驱动(Network Driver)管理容器的网络连接,提供多种网络模式。

网络驱动类型

bridge(桥接网络)— 默认

Docker 安装时会创建一个名为 docker0 的虚拟网桥。所有使用默认 bridge 网络的容器都连接到这个网桥,通过 IP 地址互相通信。


# 默认情况下,容器连接到 docker0 网桥
# 宿主机 IP:172.17.0.1
# 容器 A:172.17.0.2
# 容器 B:172.17.0.3

宿主机
├── docker0(172.17.0.1)
│   ├── container-a(172.17.0.2)
│   └── container-b(172.17.0.3)
└── eth0(宿主机外网接口)
⚠️

默认 bridge 网络的限制 — 默认 bridge 网络中的容器只能通过 IP 地址互相访问,无法通过容器名称(hostname)访问。这在容器 IP 可能变化的场景下很不方便。推荐使用用户自定义 bridge 网络。

host(宿主机网络)

容器直接共享宿主机的网络命名空间,没有网络隔离。容器内绑定 80 端口,宿主机的 80 端口就被占用。


# 使用 host 网络(不需要 -p 端口映射)
docker run --network host nginx
💡

Linux 最佳,Mac 不支持 — host 网络只在 Linux 上有意义(直接使用宿主机网络,性能最好)。在 macOS 上,Docker 运行在 Linux VM 中,host 网络实际上是该 VM 的网络,而非 Mac 的网络。

none(无网络)

容器完全没有网络接口(只有 loopback),适合需要完全网络隔离的批处理任务。


docker run --network none alpine sh

overlay(覆盖网络)

跨多台宿主机的容器通信网络,用于 Docker Swarm 模式。容器可以跨机器通过名称互相访问,底层通过 VXLAN 封装实现。


# 在 Swarm 中创建 overlay 网络
docker network create --driver overlay my-overlay

用户自定义 Bridge 网络的优势

强烈推荐为自己的应用创建专属网络,而不使用默认的 bridge。用户自定义 bridge 网络提供:


# 创建用户自定义网络
docker network create my-app-network

# 两个容器加入同一网络
docker run -d --name db --network my-app-network postgres:16
docker run -d --name web --network my-app-network myapp:1.0

# web 容器内可以直接用容器名访问 db!
# 如:postgresql://db:5432/mydb

端口映射(Port Mapping)

容器的网络是隔离的,外部(宿主机或网络中的其他机器)默认无法访问容器端口。通过端口映射,Docker 在宿主机上配置 iptables 规则,将宿主机端口的流量转发给容器。


# 映射宿主机 8080 到容器 80
docker run -p 8080:80 nginx

# 绑定到特定 IP(只允许 localhost 访问)
docker run -p 127.0.0.1:8080:80 nginx

# 随机分配宿主机端口
docker run -p 80 nginx    # 宿主机随机端口 → 容器80

# 映射多个端口
docker run -p 80:80 -p 443:443 nginx

# 查看实际映射的端口
docker port my-nginx
⚠️

安全提示 — 默认 -p 8080:80 会监听所有网络接口(0.0.0.0),意味着网络上的任何机器都可访问。在生产环境中,建议使用 -p 127.0.0.1:8080:80 并通过 Nginx/负载均衡器转发外部流量。

容器间通信示例

一个典型场景:Web 应用通过数据库容器名直接访问数据库:


# 1. 创建应用网络
docker network create webapp-net

# 2. 启动数据库容器(仅在内网,不对外暴露端口)
docker run -d \
  --name postgres-db \
  --network webapp-net \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  postgres:16-alpine

# 3. 启动 Web 应用,通过容器名 'postgres-db' 连接数据库
docker run -d \
  --name web-app \
  --network webapp-net \
  -p 3000:3000 \
  -e DATABASE_URL="postgresql://postgres:secret@postgres-db:5432/myapp" \
  myapp:1.0

# web-app 容器内部可以用 postgres-db 作为主机名访问数据库

网络命令速查


# 列出所有网络
docker network ls

# 创建网络(默认 bridge 驱动)
docker network create my-net

# 创建并指定网段
docker network create --subnet 192.168.100.0/24 my-net

# 查看网络详情(连接的容器、IP 等)
docker network inspect my-net

# 将运行中的容器连接到网络
docker network connect my-net my-container

# 断开容器与网络的连接
docker network disconnect my-net my-container

# 删除网络(需先断开所有容器)
docker network rm my-net

# 清理所有未使用网络
docker network prune

实战:三容器网络架构

搭建前端 + 后端 API + 数据库的三层架构,每层之间网络隔离:


# 创建两个网络:前端-后端 / 后端-数据库
docker network create frontend-net
docker network create backend-net

# 数据库:只在 backend-net(对前端不可见)
docker run -d --name db \
  --network backend-net \
  -e POSTGRES_PASSWORD=secret \
  postgres:16-alpine

# 后端 API:同时加入两个网络
docker run -d --name api \
  --network backend-net \
  -e DB_HOST=db \
  myapi:1.0
docker network connect frontend-net api  # 再加入前端网络

# 前端:只在 frontend-net,通过 'api' 容器名访问后端
docker run -d --name web \
  --network frontend-net \
  -p 80:80 \
  -e API_URL=http://api:8000 \
  myfrontend:1.0

# 结果:web → api → db
# web 无法直接访问 db(不在同一网络)

网络驱动内部原理

理解 Docker 网络的底层机制,有助于排查网络问题。

# 查看 Docker 创建的 iptables 规则
sudo iptables -t nat -L DOCKER -n

# 查看容器的网络接口
docker exec my-nginx ip addr show

# 验证 DNS 解析(在同一网络的容器内)
docker exec web-app nslookup postgres-db

MacVLAN 网络(高级)

MacVLAN 让容器直接连接到物理网络,获得真实的 MAC 地址和 IP,就像物理机一样。适合需要容器直接接入物理网络的特殊场景:

# 创建 MacVLAN 网络(parent 是宿主机物理接口)
docker network create -d macvlan \
  --subnet 192.168.1.0/24 \
  --gateway 192.168.1.1 \
  --opt parent=eth0 \
  macvlan-net

# 容器会直接获得物理网络的 IP
docker run -d \
  --network macvlan-net \
  --ip 192.168.1.200 \
  nginx
⚠️

MacVLAN 的限制 — 宿主机与 MacVLAN 容器之间默认无法通信(因为交换机的混杂模式限制)。需要额外配置 MacVLAN 的 bridge 子接口才能解决。不推荐在不了解网络底层的情况下使用。

网络故障排查

⚠️

常见问题:容器无法访问外网 — 原因可能是:(1) 宿主机未开启 IP 转发(sysctl net.ipv4.ip_forward 应为 1);(2) iptables 规则被防火墙软件(如 ufw)重置,导致 Docker 的 MASQUERADE 规则失效;(3) DNS 解析问题(容器内 /etc/resolv.conf 配置错误)。

# 排查:检查容器内 DNS 配置
docker exec my-container cat /etc/resolv.conf

# 排查:测试容器到宿主机的连通性
docker exec my-container ping 172.17.0.1   # docker0 地址

# 排查:测试容器间连通性
docker exec container-a ping container-b

# 排查:查看容器的端口监听情况
docker exec my-container netstat -tlnp

# 修复:检查 IP 转发是否开启
sysctl net.ipv4.ip_forward
# 如果输出 0,则需要开启:
sudo sysctl -w net.ipv4.ip_forward=1

自定义 DNS 和搜索域

# 为容器配置自定义 DNS 服务器
docker run \
  --dns 8.8.8.8 \           # 主 DNS
  --dns 8.8.4.4 \           # 备用 DNS
  --dns-search example.com \ # 自动补全域名后缀
  nginx

# 全局配置(/etc/docker/daemon.json)
# {
#   "dns": ["8.8.8.8", "8.8.4.4"],
#   "dns-search": ["example.com"]
# }

# 在容器内添加 hosts 条目
docker run --add-host legacy-service:192.168.1.100 nginx

本章小结 — Docker 网络通过 veth pair + Linux 网桥 + iptables 实现容器隔离和通信。关键原则:优先使用用户自定义 bridge 网络(自动 DNS 解析);生产环境用 -p 127.0.0.1:PORT:PORT 限制端口绑定范围;容器间通信通过容器名而非 IP,IP 可能因重启而变化。