Docker 网络基础
每个容器默认拥有独立的网络命名空间(Network Namespace),意味着它有自己独立的:
- 网络接口(
eth0,IP 地址如172.17.0.2) - 路由表(如何到达其他网络)
- 端口空间(容器内的 80 端口与宿主机的 80 端口是独立的)
- iptables 规则
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 网络提供:
- 自动 DNS 解析:容器可以通过容器名(而非 IP)互相访问,如
http://db:5432 - 更好的隔离:只有加入同一网络的容器才能互相通信
- 动态连接/断开:运行中的容器可以随时加入或离开网络
- 可配置网段:可以自定义 IP 地址范围
# 创建用户自定义网络
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 网络的底层机制,有助于排查网络问题。
-
veth pair(虚拟以太网对)
当容器加入 bridge 网络时,Docker 创建一对 veth 接口:一端在容器内(命名为
eth0),另一端在宿主机的 docker0 网桥上。数据包从容器 eth0 发出,经过宿主机端的 veth,到达 docker0 网桥,再路由到目标。 -
iptables 规则
Docker 通过修改宿主机的 iptables 规则实现端口映射(DNAT)和网络隔离。
-p 8080:80会在 iptables 的 DOCKER 链中添加一条规则,将目标端口 8080 的 TCP 流量转发到容器 IP 的 80 端口。 -
Docker DNS
用户自定义网络中,Docker 内置了一个 DNS 服务器(
127.0.0.11)。容器通过该服务器将容器名解析为 IP,实现服务发现。默认的 docker0 网络不提供此功能。
# 查看 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 可能因重启而变化。