适用场景:容器越跑越多,开始遇到「容器 A 怎么访问容器 B」「怎么让容器直拿公网 IPv6」「跨主机容器怎么互通」「为什么 Macvlan 下宿主机 ping 不通容器」这类问题
核心目标:把 Docker 五种网络驱动(bridge / host / macvlan / ipvlan / overlay)的原理、选型、踩坑一次讲清,配 Google Cloud/本机/路由器三套可执行方案
—
0. 五张网卡先认清(速查表)
| 驱动 | 拓扑模型 | 数据面 | 典型场景 | 注意点 |
| bridge | 虚拟交换机 + NAT(默认) | veth + 宿主机网桥 docker0 | 单机多容器默认通信 | 容器 IP 段私有,外部访问需 -p 映射 |
| host | 与宿主机共享网络栈 | 无隔离、直接复用宿主网卡 | 高性能/特殊协议(硬解、组播、DLNA) | 端口冲突、无网络隔离、容器可嗅探宿主流量 |
| macvlan | 每容器一张「虚拟 MAC」接入物理网络 | 独立 MAC、直通局域网 | 容器直拿公网 IPv6、服务直接暴露局域网 | 宿主机默认无法直接 ping 容器(需 macvlan 桥接) |
| ipvlan | 每容器一个「虚拟 IP」共享宿主 MAC | 共享 MAC、L3 转发 | 交换机限制 MAC 数量、单网卡多 IP | 部分交换机/无线 AP 不友好 |
| overlay | 跨主机 VXLAN 隧道 | 加密隧道(可选 IPSec/WireGuard) | Swarm/k8s 跨节点 Pod 互通 | 需 KV 存储/控制平面,单机用不着 |
| none | 无网络 | 仅 loopback | 跑完全隔离的批处理/安全沙箱 | 需手动加网络 |
—
1. bridge(默认网桥)——从零看懂 NAT
1.1 默认行为
# 启动一个容器
docker run -d --name web -p 8080:80 nginx
# 看默认网桥
docker network inspect bridge
# 输出关键字段:
# Subnet: 172.17.0.0/16
# Gateway: 172.17.0.1
# 容器 IP: 172.17.0.2
# 数据流: 外部 0.0.0.0:8080 → Docker iptables DNAT → 172.17.0.2:80
iptables -t nat -L DOCKER -n -v
本质:docker0 是一台「虚拟交换机」,每个容器通过 veth pair 接入;容器出网走 SNAT(MASQUERADE),入网走 DNAT(端口映射)。
1.2 自定义 bridge 网络(多容器互 ping + DNS 解析)
# docker-compose.yml
services:
db:
image: mysql:8
networks: [mynet]
app:
image: myapp:latest
networks: [mynet]
depends_on: [db]
networks:
mynet:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
gateway: 172.28.0.1
# 同一网络内,容器名自动解析为 IP(Docker 内置 DNS)
docker exec app ping db # 通
docker exec app ping db.mynet # 通(FQDN)
关键:自定义桥接网络比默认 bridge 好在——内置 DNS、容器名可解析、可配置子网/网关。生产环境绝不用默认 docker0。
1.3 网络隔离实战:前端 / 后端 / 数据库三段网
networks:
frontend:
backend:
dbnet:
services:
web:
networks: [frontend, backend]
api:
networks: [backend, dbnet]
db:
networks: [dbnet]
# db 无法被 web 直接访问,只能通过 api 中转
—
2. host(共享网络栈)——什么时候该用
| 该用 host | 不该用 host |
Jellyfin/Emby 硬解(需 /dev/dri + 组播 DLNA) | 普通 Web 应用 |
| Home Assistant(mDNS/广播发现设备) | 有状态服务需要网络隔离 |
| 高性能转发(网关、抓包、代理) | 需要多实例跑不同端口 |
| AdGuard Home / dnsmasq(监听 53 端口) | 端口冲突场景 |
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:stable
network_mode: host # 直接复用宿主机 IP:8123
# 不再需要 ports 映射
坑:network_mode: host 下 ports: 配置直接失效(Docker 会报 warning),服务监听什么端口就是什么端口。
—
3. macvlan(容器直拿局域网/公网 IP)——重点掌握
3.1 为什么需要 macvlan
- 你有一个固定公网 IPv6 段,想让每个容器直拿一个公网 IPv6 地址(无需
-p 映射)
- 想让容器在局域网里看起来就是一台物理机(固定 IPv4 + 独立 MAC)
- 服务要直接用 80/443,又不想跟宿主机 Nginx 抢端口
3.2 创建 macvlan 网络(双栈 IPv4+IPv6)
# 网卡 eth0,父接口开启混杂模式
ip link set eth0 promisc on
# 只 IPv4
docker network create -d macvlan \
--subnet=192.168.10.0/24 \
--gateway=192.168.10.1 \
--ip-range=192.168.10.128/25 \
-o parent=eth0 \
macvlan_lan
# 双栈(IPv4 + 公网 IPv6)
docker network create -d macvlan \
--subnet=192.168.10.0/24 \
--gateway=192.168.10.1 \
--ip-range=192.168.10.128/25 \
--subnet=2001:db8:abcd:1::/64 \
--gateway=2001:db8:abcd:1::1 \
-o parent=eth0 \
macvlan_dual
3.3 容器接入
services:
nginx:
image: nginx:latest
networks:
macvlan_dual:
ipv4_address: 192.168.10.150
ipv6_address: 2001:db8:abcd:1::150
networks:
macvlan_dual:
external: true
# 容器内直接拥有 192.168.10.150 + 2001:db8:abcd:1::150
docker exec nginx ip -6 addr show
curl -6 http://[2001:db8:abcd:1::150]
3.4 经典坑:宿主机无法 ping 容器 / 容器无法访问宿主机
原因:macvlan 是「横插一脚」到物理网,宿主机网卡不参与 macvlan 子网转发(二层隔离)。
解决:在宿主机建一个 macvlan 桥接接口
# 1. 宿主机加一个 macvlan 接口,接入同一 macvlan 网络
ip link add macvlan-host link eth0 type macvlan mode bridge
ip addr add 192.168.10.200/24 dev macvlan-host
ip link set macvlan-host up
# 2. 加路由:容器子网走 macvlan-host
ip route add 192.168.10.128/25 dev macvlan-host
# 3. 容器访问宿主机:给容器也留一条回宿主机真实 IP 的路由
ip route add 192.168.10.10/32 dev macvlan-host # 192.168.10.10 是宿主机 eth0 IP
持久化:写进 /etc/network/interfaces 或 systemd networkd 单元。
—
4. ipvlan(macvlan 的替代品,解决 MAC 限制)
| 对比 | macvlan | ipvlan |
| 每容器独立 MAC | ✅ | ❌(共享宿主 MAC) |
| 交换机 MAC 数量限制 | 易触发(家庭交换机/云厂商限 MAC) | 无此问题 |
| 无线/WPA 加密下 | 常被阻断(AP 丢多 MAC 帧) | 更可靠 |
| 支持模式 | bridge / VEPA / private | L2 / L3 / L3S |
| 适用 | 有线网络、MAC 无限制 | 云主机、无线、MAC 受限环境 |
# L2 模式(类比 macvlan bridge)
docker network create -d ipvlan \
--subnet=192.168.10.0/24 \
--gateway=192.168.10.1 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
ipvlan_lan
# L3 模式(容器走三层路由,跨网段无需网关 ARP)
docker network create -d ipvlan \
--subnet=192.168.20.0/24 \
-o parent=eth0 \
-o ipvlan_mode=l3 \
ipvlan_l3
云主机场景:很多云厂商限制每网卡 MAC 数量、或交换机做 port-security,此时 ipvlan 是唯一解。
—
5. overlay(跨主机容器互通,Swarm/K8s 底层)
5.1 单机用不到,但吊打其它方案的理解
# 初始化 Swarm(3 节点)
docker swarm init --advertise-addr <manager-ip>
# 工作节点加入
docker swarm join --token <token> <manager-ip>:2377
# 创建 overlay 网络(加密)
docker network create -d overlay --attachable --opt encrypted my-overlay
# 跨节点部署服务
docker service create --name web --network my-overlay --replicas 3 -p 8080:80 nginx
数据面:overlay 用 VXLAN(UDP 4789)封包,--opt encrypted 用 IPSec 加密。跨物理机容器互通,由 Swarm 的内置 DNS/blobstore 维护服务发现。
5.2 k3s 的 flannel / wireguard-native(轻量 overlay)
# k3s 安装时指定
INSTALL_K3S_EXEC="--flannel-backend=wireguard-native" sh -s -
# 或
INSTALL_K3S_EXEC="--flannel-backend=host-gw --flannel-iface=eth0"
| flannel 后端 | 模式 | 特点 |
| vxlan | overlay | 通用、跨网段、有封装开销 |
| host-gw | 直连路由 | 二层可达时性能最好,无隧道 |
| wireguard-native | 加密 overlay | 跨公网/不可信网络,天然加密 |
—
6. 经典网络拓扑方案(三套直接抄)
6.1 方案 A:单机容器互访 + 反代入口(最常用)
Internet → NPM (bridge, -p 80/443) → 各服务 (同一 bridge 网络,容器名互联)
services:
npm:
image: jc21/nginx-proxy-manager:latest
ports: ["80:80","443:443","81:81"]
networks: [webnet]
jellyfin:
image: jellyfin/jellyfin:latest
networks: [webnet]
alist:
image: xhofe/alist:latest
networks: [webnet]
networks:
webnet:
driver: bridge
6.2 方案 B:容器直连局域网 + 公网 IPv6(macvlan)
services:
nginx:
networks:
macvlan:
ipv4_address: 192.168.10.150
ipv6_address: 2001:db8:abcd:1::150
jellyfin:
networks:
macvlan:
ipv4_address: 192.168.10.151
ipv6_address: 2001:db8:abcd:1::151
networks:
macvlan:
external: true
6.3 方案 C:跨主机集群(Swarm/k3s overlay)
# 集群内服务用 overlay,入口用 keepalived VIP + Nginx/Ingress
services:
app:
deploy:
replicas: 3
networks: [backend_overlay]
nginx:
ports: ["80:80","443:443"]
networks: [frontend_overlay]
networks:
frontend_overlay: {external: true, driver: overlay}
backend_overlay: {external: true, driver: overlay}
—
7. 网络调试工具箱(出问题先跑这些)
# 1. 看容器实际网络配置
docker inspect <container> --format '{{json .NetworkSettings.Networks}}' | jq .
# 2. 进入网络命名空间排查
nsenter -t <container_pid> -n ip addr
nsenter -t <container_pid> -n ss -ltnp
nsenter -t <container_pid> -n ping -c 3 <目标>
# 3. 抓包(容器内)
nsenter -t <pid> -n tcpdump -i eth0 -nn port 80
# 4. 网络连通性一键诊断
docker run --rm -it --network=container:<target> nicolaka/netshoot
# netshoot 内置: curl, dig, nslookup, telnet, tcptraceroute, iptables, tcpdump...
# 5. 看 iptables/NAT 规则
iptables -t nat -L -n -v
iptables -t filter -L -n -v | grep -E 'DOCKER|docker'
# 6. DNS 排查
docker exec <container> cat /etc/resolv.conf # 应是 127.0.0.11
docker exec <container> nslookup <另一个容器名>
—
8. 常见坑 & 避坑指南
| 现象 | 原因 | 修正 |
| 容器间用 IP 不通、用容器名通 | 没加入同一 bridge 网络 | docker network connect <net> <container> 或 compose 里声明同一网络 |
| 宿主机 ping 不通 macvlan 容器 | macvlan 二层隔离 | 宿主机建 macvlan 桥接接口(见 3.4) |
| 容器能出网、外部连不进容器 | 端口未映射 / 防火墙拦截 | 检查 -p/ports:、iptables -L DOCKER、云安全组 |
| 容器内 DNS 解析失败 | 127.0.0.11 DNS 被禁 / 自定义 resolv.conf 覆盖 | 检查 /etc/docker/daemon.json dns 项、默认网桥 DNS |
host 网络端口冲突 | 多容器抢同一端口 | 换 bridge + 端口映射,或改服务监听端口 |
| bridge 容器访问宿主机 IP 失败 | 宿主机防火墙拦 docker0 网段 | 放行 172.17.0.0/16,或 iptables 加 trust |
| 云主机 macvlan 不通 | 云厂商限制 MAC/混杂模式 | 换 ipvlan,或云厂商开「混合模式/多 IP」 |
| overlay 跨节点不通 | UDP 4789 被防火墙拦 | 放行 4789/tcp、7946/tcp+udp、2377/tcp |
| IPv6 容器无地址 | 默认 bridge 未启用 IPv6 | daemon.json 开 "ipv6": true,自定义网络配 IPv6 子网 |
—
9. 性能速查(选型参考)
| 网络模式 | 吞吐(相对) | 延迟 | 开销来源 |
| host | 100% | 最低 | 无 |
| bridge | 95-98% | 略增 | veth + iptables NAT |
| macvlan/ipvlan | 98-100% | 低 | 无 NAT(L2 直通) |
| overlay(vxlan) | 70-85% | 略增 | VXLAN 封装 |
| overlay(wireguard) | 50-70% | 增 | 加密开销 |
结论:吞吐敏感 → host 或 macvlan;跨主机 + 安全 → wireguard overlay;默认 → 自定义 bridge。
—
(完)