>

适用场景:容器越跑越多,开始遇到「容器 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 限制)

对比macvlanipvlan
每容器独立 MAC✅❌(共享宿主 MAC)
交换机 MAC 数量限制易触发(家庭交换机/云厂商限 MAC)无此问题
无线/WPA 加密下常被阻断(AP 丢多 MAC 帧)更可靠
支持模式bridge / VEPA / privateL2 / 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 后端模式特点
vxlanoverlay通用、跨网段、有封装开销
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 未启用 IPv6daemon.json 开 "ipv6": true,自定义网络配 IPv6 子网

9. 性能速查(选型参考)

网络模式吞吐(相对)延迟开销来源
host100%最低无
bridge95-98%略增veth + iptables NAT
macvlan/ipvlan98-100%低无 NAT(L2 直通)
overlay(vxlan)70-85%略增VXLAN 封装
overlay(wireguard)50-70%增加密开销
>

结论:吞吐敏感 → host 或 macvlan;跨主机 + 安全 → wireguard overlay;默认 → 自定义 bridge。


(完)

— 感谢阅读 —

一起交流

分享你的思考,让讨论更进一步。