技术博客

Docker 网络进阶:overlay/macvlan 网络模式与自定义网络实战

深入讲解 Docker 网络的进阶模式:overlay 网络实现跨主机容器通信(Swarm 场景)、macvlan 让容器拥有真实 MAC 地址和物理网段 IP、ipvlan 模式对比、自定义 bridge 网络的子网/网关/IP 固定分配,以及容器网络诊断工具(ip/netstat/tcpdump/ss)在排障中的实战使用。

Docker网络overlaymacvlanipvlan跨主机通信进阶

掌握了 bridge/host/none 三种基础网络模式后,本文进入进阶网络:overlay 用于跨主机容器通信,macvlan 让容器直接接入物理网络,自定义 bridge 提供精细化的网段管理。这些是企业生产环境中不可避免的场景。

网络模式全景

Docker 网络驱动(Driver):

bridge     → 同一宿主机内的容器通信(默认,最常用)
host       → 容器直接使用宿主机网络
none       → 无网络
overlay    → 跨主机容器通信(Swarm/多主机场景)
macvlan    → 容器直接分配物理网段 IP,有真实 MAC 地址
ipvlan     → 类似 macvlan,但共享宿主机 MAC(适合交换机有 MAC 限制时)

一、自定义 Bridge 网络进阶配置

固定 IP 分配

# 创建指定子网的自定义网络
docker network create \
    --driver bridge \
    --subnet 172.20.0.0/24 \
    --gateway 172.20.0.1 \
    --ip-range 172.20.0.128/25 \    # 可分配 IP 范围(128-254)
    --opt "com.docker.network.bridge.name=br-myapp" \  # 宿主机网桥名
    myapp-net

# 给容器指定固定 IP
docker run -d \
    --network myapp-net \
    --ip 172.20.0.10 \
    --name mysql \
    mysql:8.0

docker run -d \
    --network myapp-net \
    --ip 172.20.0.11 \
    --name redis \
    redis:7.2

docker run -d \
    --network myapp-net \
    --ip 172.20.0.20 \
    --name api \
    myapp:v1

自定义 bridge 的额外选项

# 选项列表
docker network create \
    --driver bridge \
    --subnet 10.0.1.0/24 \
    --opt "com.docker.network.bridge.enable_ip_masquerade=true" \   # NAT(默认 true)
    --opt "com.docker.network.bridge.enable_icc=true" \             # 容器间通信(默认 true)
    --opt "com.docker.network.bridge.host_binding_ipv4=0.0.0.0" \  # 宿主机绑定 IP
    --opt "com.docker.network.mtu=1450" \                           # MTU(overlay 需要调小)
    internal-net                                                     # 不联网的隔离网络

# 创建完全隔离(无互联网访问)的内部网络
docker network create \
    --driver bridge \
    --internal \            # 禁止访问外部网络
    --subnet 10.99.0.0/24 \
    air-gapped-net

docker run -d --network air-gapped-net myapp
# 容器内 curl google.com → 失败(无法访问外网)
# 容器间仍然可以通信

一个容器连接多个网络

# 场景:API 服务需要同时访问 frontend 和 backend 网络
docker network create frontend-net --subnet 172.21.0.0/24
docker network create backend-net --subnet 172.22.0.0/24

# 启动时只连接一个网络
docker run -d --name api --network frontend-net myapp:v1

# 事后连接第二个网络
docker network connect backend-net api

# 现在 api 有两个网卡,可以同时访问两个网段
docker inspect api --format '{{json .NetworkSettings.Networks}}' | jq 'keys'
# ["backend-net", "frontend-net"]

# 断开某个网络
docker network disconnect backend-net api

二、Overlay 网络 — 跨主机容器通信

Overlay 网络基于 VXLAN,将多台物理主机的容器网络“叠加”成一个逻辑网络。

前置条件

# Overlay 需要 Docker Swarm 模式(overlay 是 Swarm 的基础网络)
# 或者使用 --attachable 参数允许非 Swarm 容器加入

# Swarm 所需端口(需要开放):
# 2377/tcp  → Swarm 管理通信
# 7946/tcp  → 节点间通信
# 7946/udp  → 节点间通信
# 4789/udp  → VXLAN overlay 流量

# 初始化 Swarm(在管理节点)
docker swarm init --advertise-addr <管理节点IP>
# 输出 join token

# 其他节点加入 Swarm
docker swarm join --token <token> <管理节点IP>:2377

创建和使用 Overlay 网络

# 创建 overlay 网络
docker network create \
    --driver overlay \
    --subnet 10.10.0.0/24 \
    --attachable \              # 允许普通容器(非 Service)加入
    overlay-net

# 在不同主机上运行容器,同在 overlay 网络中
# 主机1
docker run -d --name service-a --network overlay-net myapp-a:v1

# 主机2(不同物理机!)
docker run -d --name service-b --network overlay-net myapp-b:v1

# service-b 可以直接访问 service-a:
docker exec service-b curl http://service-a:8080
# VXLAN 封装流量,穿越物理网络,就像在同一个局域网

Overlay 的工作原理

VXLAN 封装过程:

主机1(192.168.1.10)                主机2(192.168.1.20)
  ┌────────────────┐                    ┌────────────────┐
  │ service-a      │                    │ service-b      │
  │ 10.10.0.2      │                    │ 10.10.0.3      │
  └───────┬────────┘                    └────────┬───────┘
          │ 容器层流量                             │
    ┌─────┴──────┐                        ┌──────┴─────┐
    │ overlay net│                        │ overlay net│
    │ (VTEP)     │                        │ (VTEP)     │
    └─────┬──────┘                        └──────┬─────┘
          │ UDP 4789 封装                         │
    ┌─────┴──────┐   物理网络传输          ┌──────┴─────┐
    │ eth0       │ ──────────────────────→ │ eth0       │
    │192.168.1.10│                         │192.168.1.20│
    └────────────┘                         └────────────┘

service-a 发包到 service-b(10.10.0.3):
  → VTEP 查 FDB 表,知道 10.10.0.3 在 192.168.1.20
  → 用 UDP 封装(VXLAN ID + 原始帧)
  → 发送到 192.168.1.20:4789
  → 对方 VTEP 解封装,交给 service-b

三、Macvlan 网络 — 容器直连物理网络

Macvlan 让每个容器有独立的 MAC 地址,直接获取物理网段的 IP,就像一台真实的机器。

适用场景

✅ 容器需要被当作普通主机对待(有物理网段 IP,可被外部直接访问)
✅ 需要处理非 TCP/UDP 流量(VRRP、PIM 等协议)
✅ 现有基础设施按 IP 分配访问控制(防火墙按 IP 规则)
✅ 监控系统需要容器直接响应特定 IP

❌ 不适合大量容器(每个容器占用一个物理 IP)
❌ 宿主机与容器不能直接通信(需要 macvlan bridge 变通)

创建 Macvlan 网络

# 先确认宿主机的物理网卡名称
ip addr show
# eth0: inet 192.168.1.100/24

# 在交换机/路由器上开启混杂模式(如果需要)
ip link set eth0 promisc on

# 创建 macvlan 网络
docker network create \
    --driver macvlan \
    --subnet 192.168.1.0/24 \          # 与物理网络相同子网!
    --gateway 192.168.1.1 \            # 物理网关
    --ip-range 192.168.1.200/29 \      # 容器 IP 范围(200-207,避免与其他设备冲突)
    --opt parent=eth0 \                # 绑定的物理网卡
    macvlan-net

# 启动容器,直接拥有物理 IP
docker run -d \
    --network macvlan-net \
    --ip 192.168.1.200 \              # 物理网段的真实 IP
    --name container-a \
    nginx

# 从物理网络的其他机器访问
curl http://192.168.1.200            # 直接访问!不需要端口映射

Macvlan VLAN 模式(802.1q)

# 创建 VLAN 子接口
ip link add link eth0 name eth0.100 type vlan id 100

# 在 VLAN 100 上创建 macvlan 网络
docker network create \
    --driver macvlan \
    --subnet 10.100.0.0/24 \
    --gateway 10.100.0.1 \
    --opt parent=eth0.100 \           # 绑定 VLAN 子接口
    macvlan-vlan100

# 不同 VLAN 的容器完全隔离

宿主机与 Macvlan 容器通信(变通方案)

# Macvlan 的限制:宿主机和容器之间不能直接通信
# 变通:创建一个 macvlan 接口在宿主机上

# 创建 macvlan bridge(特殊模式)
docker network create \
    --driver macvlan \
    --subnet 192.168.1.0/24 \
    --gateway 192.168.1.1 \
    --ip-range 192.168.1.200/29 \
    --opt parent=eth0 \
    --opt macvlan_mode=bridge \       # bridge 模式(默认就是 bridge)
    macvlan-net

# 在宿主机上添加 macvlan 接口
ip link add macvlan-shim link eth0 type macvlan mode bridge
ip addr add 192.168.1.250/32 dev macvlan-shim   # 宿主机侧 IP
ip link set macvlan-shim up
ip route add 192.168.1.200/29 dev macvlan-shim  # 路由到容器 IP 段

# 现在宿主机可以 ping 容器了
ping 192.168.1.200

四、IPvlan 网络

# IPvlan 与 macvlan 区别:所有容器共享宿主机 MAC,只有 IP 不同
# 适合:交换机对每个端口的 MAC 数量有限制时

docker network create \
    --driver ipvlan \
    --subnet 192.168.1.0/24 \
    --gateway 192.168.1.1 \
    --ip-range 192.168.1.210/29 \
    --opt parent=eth0 \
    --opt ipvlan_mode=l2 \            # l2(二层,默认)或 l3(三层路由)
    ipvlan-net

docker run -d \
    --network ipvlan-net \
    --ip 192.168.1.210 \
    nginx

五、网络排障工具

容器内诊断

# 进入容器后的网络诊断
docker exec -it myapp bash

# 查看容器网络接口
ip addr show
ip route show

# 测试连通性
ping -c 3 mysql              # ping 其他服务(通过容器名)
ping -c 3 8.8.8.8            # 测试外网访问
traceroute mysql             # 路由追踪

# 测试端口连通性
nc -zv mysql 3306            # netcat 测试 TCP 端口
curl -v http://api:8080/health

# DNS 解析
nslookup mysql               # 解析容器名
cat /etc/resolv.conf         # 查看 DNS 配置

# 查看监听端口
ss -tlnp                     # 容器内监听的端口
netstat -tlnp

宿主机诊断

# 查看 Docker 网络
docker network ls
docker network inspect bridge | jq '.[0].Containers'

# 查看容器 IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# 从宿主机抓包(分析容器网络流量)
# 找到容器对应的 veth 接口
CONTAINER_ID=$(docker inspect -f '{{.Id}}' myapp)
# 找 veth pair
ip link | grep -A1 "${CONTAINER_ID:0:12}"

# 抓包
tcpdump -i veth3a4b5c6 -n -w /tmp/container.pcap
# 用 Wireshark 分析 pcap 文件

# 抓 docker0 网桥的所有流量
tcpdump -i docker0 -n host 172.17.0.2

# 检查 iptables(NAT 和过滤规则)
sudo iptables -t nat -L DOCKER -n -v     # Docker NAT 规则
sudo iptables -L DOCKER-USER -n -v       # 用户自定义规则

# 检查容器间通信是否被 iptables 阻断
sudo iptables -L DOCKER-ISOLATION-STAGE-1 -n -v

网络性能测试

# 测试容器间网络带宽(iperf3)
# 服务端(另一个容器内)
docker run -d --name iperf-server --network mynet networkstatic/iperf3 -s

# 客户端
docker run --rm --network mynet networkstatic/iperf3 \
    -c iperf-server -t 10

# 典型结果:
# bridge 网络内:约 10-40 Gbps(取决于宿主机)
# overlay 网络:约 1-5 Gbps(VXLAN 封装开销)
# macvlan:约等于物理网卡速度
# host 网络:物理网卡速度(无软件开销)

网络模式选型指南

场景                              推荐网络模式     理由
──────────────────────────────────────────────────────────────
单机多服务(开发/小规模生产)       自定义 bridge    DNS解析+隔离,最简单
多机容器互通(Swarm集群)          overlay          VXLAN跨主机
容器需要物理IP(NAT穿透/监控)     macvlan          直接使用物理网段
交换机MAC数量受限                   ipvlan           共享宿主机MAC
高性能网络(监控组件)              host             零开销
CI任务(无需网络)                  none             隔离安全

小结

Docker 网络的核心理解:网络模式决定容器如何与外界通信

自定义 bridge 够用 90% 的场景(DNS + 隔离);overlay 是 Swarm 集群的网络基础,本质是 VXLAN 跨主机封装;macvlan 让容器真正“融入”物理网络,适合需要物理 IP 的特殊场景。

排障时先 docker network inspect 看网络配置,再进容器用 ip/ping/nc 测试,宿主机用 tcpdump 抓包确认流量路径。