Docker 网络管理
Docker 网络是容器之间以及容器与外部世界通信的基础。理解 Docker 网络对于构建分布式应用至关重要。
网络基础概念
为什么需要网络管理?
容器默认是隔离的,但实际应用中容器需要:
- 与其他容器通信(如 Web 服务访问数据库)
- 接受外部请求(如 Web 服务器)
- 访问外部网络(如调用第三方 API)
容器网络视角
从容器内部看,它只看到:
- 一个网络接口(通常是
eth0) - 一个 IP 地址
- 一个默认网关
- DNS 服务
容器不需要知道它连接的是什么类型的网络,也不需要知道其他网络对等方是否也是 Docker 容器。
网络类型
Docker 提供多种网络驱动,适用于不同场景:
Bridge(桥接网络)
默认网络类型,适用于同一主机上的容器通信。
# 查看默认网络
docker network ls
# 输出示例
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
e7f8g9h0i1j2 host host local
k3l4m5n6o7p8 none null local
默认 Bridge 网络:
# 不指定网络时,容器连接到默认 bridge
docker run -d --name web nginx
# 默认 bridge 的限制:
# 1. 容器之间只能通过 IP 地址通信
# 2. 无法使用容器名称解析
# 3. 不推荐用于生产环境
自定义 Bridge 网络(推荐):
# 创建自定义 bridge 网络
docker network create my-network
# 创建时指定子网和网关
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--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
# 容器可以通过名称互相访问
# web 容器可以通过 "db" 主机名连接数据库
自定义 Bridge 的优势:
| 特性 | 默认 Bridge | 自定义 Bridge |
|---|---|---|
| DNS 解析 | 只能用 IP | 可用容器名 |
| 网络隔离 | 所有容器互通 | 仅同网络容器互通 |
| 动态连接 | 需要重启容器 | 可动态连接/断开 |
| 配置灵活性 | 统一配置 | 可单独配置 |
Host(主机网络)
容器直接使用主机的网络命名空间,没有网络隔离。
# 使用 host 网络
docker run -d --name web --network host nginx
# 特点:
# - 容器没有独立 IP
# - 容器端口直接映射到主机
# - 性能最好,没有 NAT 转换
适用场景:
- 需要高性能网络的应用
- 需要处理大量端口的应用
- 网络监控工具
注意事项:
- 端口冲突:多个容器不能使用相同端口
- 安全性:容器可以直接访问主机网络
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 网络 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 主机 A │ │ 主机 B │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │容器 1 │ │ VXLAN 隧道 │ │容器 2 │ │ │
│ │ └───────┘ │◄────────────►│ └───────┘ │ │
│ │ eth0 │ │ eth0 │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
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 地址冲突
- 主机无法直接与同网段容器通信
网络管理命令
创建网络
# 基本创建
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
端口映射
发布端口
# 映射单个端口
docker run -d -p 8080:80 nginx
# 映射多个端口
docker run -d -p 8080:80 -p 8443:443 nginx
# 指定绑定地址
docker run -d -p 127.0.0.1:8080:80 nginx
# 随机端口映射
docker run -d -P nginx
# 指定协议
docker run -d -p 53:53/udp dns-server
端口映射格式:
-p [主机IP:]主机端口:容器端口[/协议]
查看端口映射
# 查看容器端口
docker port web
# 输出示例
80/tcp -> 0.0.0.0:8080
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
在自定义网络中,容器可以通过名称互相解析:
# 创建网络
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
实战示例
微服务网络架构
# 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网络:可从外部访问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 inspect web --format '{{json .NetworkSettings}}'
# 进入容器测试网络
docker exec -it web sh
# 在容器内执行
ping db
curl http://api:3000/health
nslookup db
# 查看容器 IP
docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
# 查看容器端口映射
docker port web
# 查看网络统计
docker network inspect my-network
常见问题
1. 容器无法解析其他容器名称
# 原因:使用默认 bridge 网络
# 解决:创建自定义网络
docker network create my-network
docker run --network my-network --name app myapp
docker run --network my-network --name db mysql
2. 容器无法访问外网
# 检查 IP 转发
sysctl net.ipv4.ip_forward
# 启用 IP 转发
sysctl -w net.ipv4.ip_forward=1
# 检查 NAT 规则
iptables -t nat -L -n
3. 端口被占用
# 查看端口占用
lsof -i :8080
netstat -tlnp | grep 8080
# 停止占用端口的容器
docker ps | grep 8080
docker stop <container_id>
最佳实践
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
# 注意端口冲突和安全风险
小结
本章我们学习了:
- 网络类型:bridge、host、overlay、none、macvlan
- 自定义网络:创建、配置和管理
- 端口映射:发布和查看端口
- DNS 配置:容器名称解析
- 网络隔离:多网络架构
- 故障排除:常见问题解决
练习
- 创建一个自定义 bridge 网络并启动两个容器互相通信
- 使用 Docker Compose 配置一个多服务的网络架构
- 配置一个只允许内部通信的网络
- 使用 host 网络模式运行一个 Web 服务器