跨 Docker Host 容器通信完整指南:Overlay 网络架构与生产实践

作者:云原生架构师
技术栈:Docker, Overlay, VXLAN, Swarm, ETCD, Linux Kernel
难度等级:★★★★★(专家级)
预计阅读时间:110 分钟


目录

  1. [引言:为什么需要跨 Host 通信](#1-引言为什么需要跨 host-通信)
  2. [跨 Host 通信的挑战与需求](#2-跨 host-通信的挑战与需求)
  3. Overlay 网络架构深度解析
  4. VXLAN 隧道技术详解
  5. [Docker Swarm 模式下的 Overlay](#5-docker-swarm-模式下的 overlay)
  6. [手动配置 Overlay 网络](#6-手动配置 overlay-网络)
  7. 网络配置与优化
  8. 通信验证与测试
  9. 故障排查实战
  10. 生产环境最佳实践
  11. 性能基准测试
  12. 总结与前沿技术

1. 引言:为什么需要跨 Host 通信

1.1 容器网络演进历程

┌─────────────────────────────────────────────────────┐
│  容器网络演进                                        │
├─────────────────────────────────────────────────────┤
│  2013-2014: 单机容器时代                            │
│  - 容器在同一宿主机                                │
│  - Bridge 网络即可满足需求                         │
│  - 问题:无法跨主机通信                            │
│                                                       │
│  2015-2016: 容器编排兴起                            │
│  - 多宿主机部署                                    │
│  - 需要跨主机通信                                  │
│  - 解决方案:Flannel, Weave, Calico               │
│                                                       │
│  2016-2017: Docker Swarm 2.0                        │
│  - 内置 Overlay 网络                               │
│  - 集成 ETCD 服务发现                              │
│  - 开箱即用                                        │
│                                                       │
│  2018-现在:云原生网络                              │
│  - Kubernetes CNI                                  │
│  - Service Mesh                                    │
│  - eBPF 网络加速                                   │
└─────────────────────────────────────────────────────┘

1.2 单机网络的局限性

Bridge 网络的局限

宿主机 A (192.168.1.10)
┌─────────────────────────────────────┐
│  docker0: 172.17.0.1                │
│       │                             │
│  ┌────┴────  ┌────┴────┐         │
│  │ C1      │  │ C2      │         │
│  │172.17.0.2│  │172.17.0.3│         │
│  └─────────┘  └─────────┘         │
│       │                             │
│       └────── 可以通信 ──────┘     │
└─────────────────────────────────────┘
       │
       │ ❌ 无法直接通信
       ▼
宿主机 B (192.168.1.20)
┌─────────────────────────────────────┐
│  docker0: 172.17.0.1                │
│       │                             │
│  ┌────┴────┐  ┌────┴────┐         │
│  │ C3      │  │ C4      │         │
│  │172.17.0.2│  │172.17.0.3│         │
│  └─────────┘  └─────────         │
└─────────────────────────────────────┘

问题:
1. C1 (172.17.0.2) 无法访问 C3 (172.17.0.2)
   - IP 地址冲突(相同子网)
   - 无路由可达

2. 即使使用不同子网
   - C1 (172.17.0.2) vs C3 (172.18.0.2)
   - 宿主机间无隧道
   - 数据包无法跨主机传输

2. 跨 Host 通信的挑战与需求

2.1 三大核心挑战

挑战 1:IP 地址冲突

宿主机 A:
- 容器 C1: 172.17.0.2
- 容器 C2: 172.17.0.3

宿主机 B:
- 容器 C3: 172.17.0.2  ← IP 冲突
- 容器 C4: 172.17.0.3  ← IP 冲突

问题:
- 如何区分不同主机上的相同 IP?
- 如何实现全局唯一寻址?

解决方案

方案 1:全局统一子网
- 宿主机 A: 10.0.1.0/24
- 宿主机 B: 10.0.2.0/24
- 容器使用全局唯一 IP

方案 2:Overlay 网络
- 外层 IP:宿主机 IP(唯一)
- 内层 IP:容器 IP(可重复)
- VXLAN VNI 区分不同网络

挑战 2:MAC 地址学习

传统网络:
- 交换机通过 MAC 地址表转发
- MAC 地址在局域网内唯一

容器网络:
- 每个容器有独立 MAC
- 跨主机时 MAC 地址如何传递?
- 如何避免 MAC 地址冲突?

解决方案

VXLAN 封装:
- 外层 MAC:宿主机 MAC
- 内层 MAC:容器 MAC(封装在 VXLAN 内)
- 通过 VNI 隔离不同网络

挑战 3:服务发现

问题:
- 容器 C1 如何知道 C3 在哪个主机?
- 容器 IP 变化如何通知其他容器?
- 容器扩缩容如何更新路由?

解决方案

方案 1:ETCD 分布式存储
- 存储容器 IP 与主机映射
- 实时同步集群状态
- Docker Swarm 内置

方案 2:Flannel
- 使用 ETCD 存储网络信息
- 每个主机运行 flanneld
- 自动更新路由表

方案 3:Gossip 协议
- Swarm 节点间 Gossip 通信
- 去中心化状态同步
- 最终一致性

2.2 跨 Host 通信需求

需求 1:透明通信

目标:容器无感知跨主机通信

C1 (Host A) ─────────────► C3 (Host B)
  │                          │
  │ 发送数据包                │ 接收数据包
  │ 目标:10.0.2.2            │ 源:10.0.1.2
  │                          │
  └─ 无需关心目标位置         └─ 无需关心源位置

需求 2:高性能

性能指标:
- 吞吐量:接近物理网络
- 延迟:<1ms(同数据中心)
- CPU 占用:<10%
- 丢包率:<0.01%

需求 3:高可用

要求:
- 单点故障不影响通信
- 自动故障转移
- 快速恢复(<30s)

需求 4:易管理

功能:
- 自动化配置
- 集中式管理
- 可视化监控
- 简单故障排查

3. Overlay 网络架构深度解析

3.1 Overlay 网络模型

什么是 Overlay 网络?

Overlay Network = 构建在现有网络之上的虚拟网络

物理网络(Underlay):
┌──────────┐    ┌──────────┐    ┌──────────┐
│  Host A  │────│  Switch  │────│  Host B  │
│ 192.168.1.10│    │          │    │ 192.168.1.20│
└──────────┘    └──────────┘    └──────────┘
       │                               │
       │ 物理连接                       │ 物理连接
       │                               │

虚拟网络(Overlay):
┌──────────┐                      ┌──────────┐
│  C1      │◄────── VXLAN ───────►│  C3      │
│ 10.0.1.2 │      隧道            │ 10.0.2.2 │
└──────────┘                      └──────────┘
       │                               │
       └────── 逻辑直连 ───────

特点:
1. 逻辑上容器直接相连
2. 物理上通过隧道传输
3. 对容器透明(无感知)

3.2 Docker Overlay 架构

Docker Overlay 驱动架构

Worker Node B

Worker Node A

VXLAN 隧道

Manager Node

Swarm Manager

ETCD 存储

Overlay Driver

Container C1
10.0.1.2

VXLAN veth1
VNI: 4096

br0 网桥

eth0
192.168.1.10

Container C3
10.0.2.2

VXLAN veth2
VNI: 4096

br0 网桥

eth0
192.168.1.20

核心组件

1. Swarm Manager(控制平面)
   - 集群管理
   - 服务调度
   - 网络配置分发

2. ETCD 存储(状态存储)
   - 存储网络状态
   - 存储容器位置
   - 服务发现

3. VXLAN 驱动(数据平面)
   - 创建 VXLAN 接口
   - 封装/解封装数据包
   - 管理 VNI

4. 网桥(本地转发)
   - 连接容器与 VXLAN
   - 本地流量转发
   - ARP 代理

3.3 控制平面与数据平面分离

控制平面(Control Plane)

功能:
- 网络配置管理
- 路由信息分发
- 服务发现
- 状态同步

实现:
- Docker Swarm Manager
- ETCD 分布式存储
- Gossip 协议同步

数据平面(Data Plane)

功能:
- 数据包转发
- VXLAN 封装/解封装
- 本地路由

实现:
- VXLAN 网络接口
- Linux 网桥
- iptables 规则

分离优势

1. 可扩展性
   - 控制平面集中管理
   - 数据平面分布式转发
   - 支持大规模集群

2. 可靠性
   - 控制平面多副本
   - 数据平面去中心化
   - 单点故障不影响转发

3. 性能
   - 控制平面不处理数据
   - 数据平面直接转发
   - 低延迟

4. VXLAN 隧道技术详解

4.1 VXLAN 协议原理

什么是 VXLAN?

VXLAN (Virtual eXtensible LAN) = 虚拟扩展局域网

RFC 标准:RFC 7348

核心思想:
- MAC in UDP 封装
- 在 Layer 3 网络上运行 Layer 2
- 24 位 VNI(支持 1600 万隔离网络)

VXLAN 封装格式

┌─────────────────────────────────────────────────────┐
│  VXLAN 帧结构                                        │
├─────────────────────────────────────────────────────┤
│  外层以太网头 (14 字节)                               │
│  - 目的 MAC: 下一跳 MAC                            │
│  - 源 MAC: 宿主机 MAC                              │
│  - EtherType: 0x0800 (IP)                          │
├─────────────────────────────────────────────────────┤
│  外层 IP 头 (20 字节)                                 │
│  - 目的 IP: 目标宿主机 IP (192.168.1.20)           │
│  - 源 IP: 本地宿主机 IP (192.168.1.10)             │
│  - 协议:UDP (17)                                  │
├─────────────────────────────────────────────────────┤
│  外层 UDP 头 (8 字节)                                 │
│  - 目的端口:4789 (VXLAN 标准端口)                   │
│  - 源端口:随机高端口                              │
│  - 校验和:可选                                    │
├─────────────────────────────────────────────────────┤
│  VXLAN 头 (8 字节)                                    │
│  - Flags (8 位): 0x08 (I 位=1 表示有效 VNI)          │
│  - 保留 (24 位): 全 0                                 │
│  - VNI (24 位): 网络标识符 (0-16777215)            │
│  - 保留 (8 位): 全 0                               │
├─────────────────────────────────────────────────────┤
│  内层以太网帧 (原始容器数据包)                        │
│  - 内层目的 MAC: 目标容器 MAC                      │
│  - 内层源 MAC: 源容器 MAC                          │
│  - EtherType: 0x0800 (IP)                          │
│  - 内层 IP 头                                       │
│    - 目的 IP: 目标容器 IP (10.0.2.2)               │
│    - 源 IP: 源容器 IP (10.0.1.2)                   │
│  - 传输层头 (TCP/UDP)                              │
│  - 数据载荷                                        │
└─────────────────────────────────────────────────────┘

总开销:14 + 20 + 8 + 8 = 50 字节
MTU 建议:1500 - 50 = 1450

4.2 VXLAN 工作流程

数据包发送流程

容器 C3 10.0.2.2 VXLAN2 Host B Host B 192.168.1.20 物理网络 Host A 192.168.1.10 VXLAN1 Host A 容器 C1 10.0.1.2 容器 C3 10.0.2.2 VXLAN2 Host B Host B 192.168.1.20 物理网络 Host A 192.168.1.10 VXLAN1 Host A 容器 C1 10.0.1.2 目的 MAC: C3 MAC 目的 IP: 10.0.2.2 VNI: 4096 远程 VTEP: 192.168.1.20 外层 IP: 192.168.1.10→192.168.1.20 外层 UDP: 4789 VNI: 4096 移除 VXLAN 头 提取原始帧 1. 发送原始帧 2. 查找 VTEP 表 3. VXLAN 封装 4. 发送封装包 5. 物理网络传输 6. 到达目标主机 7. 接收数据包 8. VXLAN 解封装 9. 转发到容器

详细步骤

步骤 1:容器发送数据包

# 容器 C1 执行
ping 10.0.2.2

# 生成数据包:
# 源 MAC: C1_MAC
# 目的 MAC: ? (需要 ARP)
# 源 IP: 10.0.1.2
# 目的 IP: 10.0.2.2

步骤 2:ARP 请求

# 容器广播 ARP 请求
"Who has 10.0.2.2? Tell 10.0.1.2"

# 网桥接收并转发到 VXLAN 接口
# VXLAN 驱动查询 VTEP 表
# 找到目标 VTEP: 192.168.1.20

步骤 3:VXLAN 封装

# 伪代码展示封装过程
def vxlan_encapsulate(original_frame, vni, remote_vtep):
    # 外层以太网头
    outer_eth = Ethernet(
        dst_mac=get_next_hop_mac(remote_vtep),
        src_mac=local_host_mac,
        ethertype=0x0800
    )
    
    # 外层 IP 头
    outer_ip = IP(
        dst=remote_vtep,  # 192.168.1.20
        src=local_host,   # 192.168.1.10
        proto=UDP
    )
    
    # 外层 UDP 头
    outer_udp = UDP(
        dport=4789,
        sport=random_high_port()
    )
    
    # VXLAN 头
    vxlan_header = VXLAN(
        flags=0x08,
        vni=vni  # 4096
    )
    
    # 组合封装
    encapsulated = outer_eth / outer_ip / outer_udp / vxlan_header / original_frame
    
    return encapsulated

步骤 4-6:网络传输

封装后的数据包在物理网络传输:
192.168.1.10:4789 → 192.168.1.20:4789

物理网络设备(交换机、路由器):
- 仅看到外层 IP/MAC
- 根据外层头转发
- 不关心内层内容

步骤 7-9:解封装

# Host B 接收数据包
# 内核检查 UDP 端口 4789
# 识别为 VXLAN 数据包

# VXLAN 驱动处理:
1. 验证 VNI (4096)
2. 查找对应的 VXLAN 接口
3. 移除 VXLAN 头、外层 IP/UDP
4. 提取原始以太网帧
5. 转发到本地网桥
6. 根据 MAC 地址转发到容器 C3

4.3 VTEP(VXLAN Tunnel End Point)

什么是 VTEP?

VTEP = VXLAN 隧道端点

功能:
- VXLAN 封装/解封装
- VNI 管理
- 远程 VTEP 发现
- MAC 地址学习

实现:
- 硬件 VTEP:物理交换机
- 软件 VTEP:Linux VXLAN 接口

Linux VTEP 配置

# 创建 VXLAN 接口
ip -d link add vxlan0 type vxlan \
  id 4096 \
  dev eth0 \
  dstport 4789 \
  local 192.168.1.10 \
  noudpcsum \
  ttl 64 \
  learning \
  proxy \
  rsc \
  l2miss \
  l3miss \
  group 239.1.1.1

# 参数说明:
# id 4096: VNI=4096
# dev eth0: 使用物理接口 eth0
# dstport 4789: VXLAN 端口
# local 192.168.1.10: 本地 VTEP IP
# noudpcsum: 禁用 UDP 校验和
# ttl 64: TTL=64
# learning: 自动学习远程 MAC
# proxy: 代理 ARP
# rsc: 路由短路径
# group 239.1.1.1: 组播组(用于发现)

VTEP 转发表

# 查看 VTEP 表
bridge fdb show dev vxlan0

# 输出:
# 00:00:00:00:00:00 dst 0.0.0.0 self permanent
# aa:bb:cc:dd:ee:01 dst 192.168.1.20 via vxlan0
# aa:bb:cc:dd:ee:02 dst 192.168.1.30 via vxlan0

# 含义:
# - 本地 MAC: 00:00:00:00:00:00 (特殊标记)
# - 容器 MAC aa:bb:cc:dd:ee:01 → 远程 VTEP 192.168.1.20
# - 容器 MAC aa:bb:cc:dd:ee:02 → 远程 VTEP 192.168.1.30

5. Docker Swarm 模式下的 Overlay

5.1 初始化 Swarm 集群

环境准备

Manager: 192.168.1.10
Worker1: 192.168.1.20
Worker2: 192.168.1.30

系统要求:
- Docker 20.10+
- Ubuntu 20.04+
- 网络互通
- 防火墙开放端口

开放必要端口

# 所有节点执行
# 防火墙配置(ufw)
ufw allow 2377/tcp    # Swarm 管理
ufw allow 7946/tcp    # 节点通信
ufw allow 7946/udp    # 节点通信
ufw allow 4789/udp    # VXLAN

# 或者使用 firewalld
firewall-cmd --permanent --add-port=2377/tcp
firewall-cmd --permanent --add-port=7946/tcp
firewall-cmd --permanent --add-port=7946/udp
firewall-cmd --permanent --add-port=4789/udp
firewall-cmd --reload

初始化 Manager

# 在 Manager 节点执行
docker swarm init \
  --advertise-addr 192.168.1.10 \
  --listen-addr 192.168.1.10:2377

# 输出:
# Swarm initialized: current node (abc123) is now a manager.
# 
# To add a worker to this swarm, run the following command:
#     docker swarm join \
#     --token SWMTKN-1-abc123def456... \
#     192.168.1.10:2377
# 
# To add a manager to this swarm, run 'docker swarm join-token manager'

# 查看节点状态
docker node ls

# 输出:
# ID            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
# abc123        manager1    Ready     Active         Leader           24.0.0

添加 Worker 节点

# 在 Worker1 执行
docker swarm join \
  --token SWMTKN-1-abc123def456... \
  192.168.1.10:2377

# 输出:
# This node joined a swarm as a "worker".

# 在 Worker2 执行相同命令

# 在 Manager 查看集群状态
docker node ls

# 输出:
# ID            HOSTNAME    STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
# abc123        manager1    Ready     Active         Leader           24.0.0
# def456        worker1     Ready     Active                            24.0.0
# ghi789        worker2     Ready     Active                            24.0.0

5.2 创建 Overlay 网络

基础创建

# 在 Manager 节点执行
docker network create \
  --driver overlay \
  --subnet 10.0.0.0/24 \
  --gateway 10.0.0.1 \
  my-overlay

# 查看网络
docker network inspect my-overlay

# 输出(部分):
# {
#   "Name": "my-overlay",
#   "Driver": "overlay",
#   "Scope": "swarm",
#   "IPAM": {
#     "Config": [
#       {
#         "Subnet": "10.0.0.0/24",
#         "Gateway": "10.0.0.1"
#       }
#     ]
#   },
#   "Options": {
#     "com.docker.network.driver.overlay.vxlan_mac_list": "aa:bb:cc:dd:ee:01"
#   }
# }

高级配置

# 创建加密的 Overlay 网络
docker network create \
  --driver overlay \
  --opt encrypted=true \
  --opt com.docker.network.driver.mtu=1450 \
  --attachable \
  secure-overlay

# 参数说明:
# --opt encrypted=true: 启用 IPsec 加密
# --opt com.docker.network.driver.mtu=1450: 设置 MTU
# --attachable: 允许独立容器连接

# 创建多子网 Overlay
docker network create \
  --driver overlay \
  --subnet 10.0.0.0/24 \
  --subnet 10.0.1.0/24 \
  --gateway 10.0.0.1 \
  --gateway 10.0.1.1 \
  multi-subnet-overlay

5.3 部署跨主机服务

部署服务

# 在 Manager 执行
docker service create \
  --name web \
  --network my-overlay \
  --replicas 3 \
  --publish published=80,target=80 \
  --mode replicated \
  nginx:alpine

# 查看服务分布
docker service ps web

# 输出:
# ID            NAME      IMAGE         NODE       DESIRED STATE   CURRENT STATE
# abc123        web.1     nginx:alpine  worker1    Running         Running 10s ago
# def456        web.2     nginx:alpine  worker2    Running         Running 10s ago
# ghi789        web.3     nginx:alpine  manager1   Running         Running 10s ago

# 查看服务详情
docker service inspect web --pretty

# 输出:
# Service: web
#  Mode: replicated
#  Replicas: 3
#  Network: my-overlay
#  Ports: 80 -> 80

跨主机通信测试

# 获取容器 ID
CONTAINER_ON_WORKER1=$(docker ps -q --filter "name=web.1")
CONTAINER_ON_WORKER2=$(docker ps -q --filter "name=web.2")

# 在 worker1 的容器 ping worker2 的容器
docker exec $CONTAINER_ON_WORKER1 ping -c 3 10.0.0.3

# 输出:
# 64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.5 ms
# 64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.4 ms
# 64 bytes from 10.0.0.3: icmp_seq=3 ttl=64 time=0.4 ms

# 成功!跨主机通信正常

6. 手动配置 Overlay 网络

6.1 使用 Flannel 实现跨主机

Flannel 架构

┌─────────────────────────────────────────────────────┐
│  Flannel 架构                                        │
├─────────────────────────────────────────────────────┤
│  ETCD Cluster                                        │
│  - 存储网络配置                                      │
│  - 存储主机子网映射                                  │
│                                                       │
│  flanneld (每个主机)                                 │
│  - 从 ETCD 获取配置                                 │
│  - 配置本地网络接口                                 │
│  - 设置路由表                                       │
│                                                       │
│  后端类型:                                          │
│  - VXLAN Backend(默认)                            │
│  - UDP Backend(已废弃)                            │
│  - AWS VPC Backend                                  │
│  - GCE Backend                                      │
│  - Alibaba Cloud Backend                            │
└─────────────────────────────────────────────────────┘

6.2 部署 ETCD 集群

单节点 ETCD(测试)

# 在所有节点执行
docker run -d \
  --name etcd \
  -p 2379:2379 \
  -p 2380:2380 \
  -v /etcd-data:/etcd-data \
  -e ETCD_DATA_DIR=/etcd-data \
  quay.io/coreos/etcd:v3.5.9 \
  etcd \
  --name etcd-node1 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://192.168.1.10:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://192.168.1.10:2380 \
  --initial-cluster etcd-node1=http://192.168.1.10:2380 \
  --initial-cluster-token my-etcd-token \
  --initial-cluster-state new

三节点 ETCD(生产)

# 节点 1 (192.168.1.10)
docker run -d \
  --name etcd \
  -p 2379:2379 \
  -p 2380:2380 \
  -v /etcd-data:/etcd-data \
  quay.io/coreos/etcd:v3.5.9 \
  etcd \
  --name etcd-node1 \
  --data-dir /etcd-data \
  --listen-client-urls http://0.0.0.0:2379 \
  --advertise-client-urls http://192.168.1.10:2379 \
  --listen-peer-urls http://0.0.0.0:2380 \
  --initial-advertise-peer-urls http://192.168.1.10:2380 \
  --initial-cluster etcd-node1=http://192.168.1.10:2380,etcd-node2=http://192.168.1.20:2380,etcd-node3=http://192.168.1.30:2380 \
  --initial-cluster-token my-etcd-token \
  --initial-cluster-state new \
  --auto-compaction-retention=1 \
  --quota-backend-bytes=8589934592

# 节点 2 和 3 执行类似命令(修改 IP 和名称)

验证 ETCD 集群

# 检查集群状态
docker exec etcd etcdctl member list

# 输出:
# 422a74f03b622b7d, started, etcd-node1, http://192.168.1.10:2380, http://192.168.1.10:2379

# 检查健康状态
docker exec etcd etcdctl endpoint health

# 输出:
# http://192.168.1.10:2379: is healthy

6.3 配置 Flannel 网络

在 ETCD 中注册网络

# 在任意节点执行
docker exec etcd etcdctl mk /coreos.com/network/config '{
  "Network": "10.0.0.0/8",
  "SubnetLen": 24,
  "SubnetMin": "10.0.1.0",
  "SubnetMax": "10.255.255.0",
  "Backend": {
    "Type": "vxlan",
    "VNI": 1,
    "Port": 8472
  }
}'

# 参数说明:
# Network: 全局网络 (10.0.0.0/8)
# SubnetLen: 每个主机子网掩码 (/24)
# SubnetMin/SubnetMax: 子网范围
# Backend.Type: vxlan
# Backend.VNI: VNI=1
# Backend.Port: VXLAN 端口 8472

部署 Flannel

# 在所有节点执行
docker run -d \
  --name flanneld \
  --net=host \
  --privileged \
  -v /run/flannel:/run/flannel \
  quay.io/coreos/flannel:v0.22.0 \
  /opt/bin/flanneld \
  --etcd-endpoints=http://192.168.1.10:2379 \
  --public-ip=192.168.1.10 \
  --iface=eth0 \
  --ip-masq=true

# 参数说明:
# --etcd-endpoints: ETCD 地址
# --public-ip: 本机 IP
# --iface: 网络接口
# --ip-masq: 启用 NAT

查看 Flannel 分配

# 查看 ETCD 中的子网分配
docker exec etcd etcdctl ls /coreos.com/network/subnets --recursive

# 输出:
# /coreos.com/network/subnets/10.1.1.0-24
# /coreos.com/network/subnets/10.1.2.0-24
# /coreos.com/network/subnets/10.1.3.0-24

# 查看子网详情
docker exec etcd etcdctl get /coreos.com/network/subnets/10.1.1.0-24

# 输出:
# {"PublicIP":"192.168.1.10","BackendType":"vxlan","BackendData":{"VtepMAC":"aa:bb:cc:dd:ee:01"}}

7. 网络配置与优化

7.1 MTU 优化

MTU 问题

标准以太网 MTU: 1500 字节
VXLAN 开销:50 字节
可用 MTU: 1500 - 50 = 1450 字节

如果容器发送 1500 字节数据包:
- VXLAN 封装后:1550 字节
- 超过物理网络 MTU
- 需要分片
- 性能下降

MTU 配置

# 创建网络时指定 MTU
docker network create \
  --driver overlay \
  --opt com.docker.network.driver.mtu=1450 \
  my-overlay

# 或者修改 Docker 配置
# /etc/docker/daemon.json
{
  "default-network-opts": {
    "overlay": {
      "com.docker.network.driver.mtu": "1450"
    }
  }
}

# 验证 MTU
ip link show vxlan-br-xxx

# 输出:
# vxlan-br-xxx: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
#     link/ether aa:bb:cc:dd:ee:01
#     mtu 1450

7.2 加密配置

IPsec 加密

# 创建加密 Overlay 网络
docker network create \
  --driver overlay \
  --opt encrypted=true \
  secure-overlay

# Docker 自动配置 IPsec
# 使用 IKEv2 密钥交换
# AES-GCM 加密

# 验证加密
docker network inspect secure-overlay

# 输出:
# "Options": {
#   "encrypted": "true"
# }

加密性能影响

无加密:
- 吞吐量:9.5 Gbps
- 延迟:0.3ms
- CPU: 15%

IPsec 加密:
- 吞吐量:7.0 Gbps(减少 26%)
- 延迟:0.5ms(增加 67%)
- CPU: 35%(增加 133%)

建议:
- 内网:可不加密
- 跨数据中心:必须加密
- 敏感数据:必须加密

7.3 服务发现配置

Docker 内置 DNS

# 部署服务
docker service create \
  --name web \
  --network my-overlay \
  nginx:alpine

docker service create \
  --name api \
  --network my-overlay \
  myapi:latest

# 通过服务名访问
docker exec api-web-1 ping api-api-1
# 输出:64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=0.5 ms

# DNS 解析
docker exec api-web-1 nslookup api-api-1
# 输出:
# Name:      api-api-1
# Address 1: 10.0.0.3

8. 通信验证与测试

8.1 基础连通性测试

Ping 测试

# 获取不同主机上的容器
CONTAINER_A=$(docker ps -q --filter "name=web" --filter "node=worker1")
CONTAINER_B=$(docker ps -q --filter "name=web" --filter "node=worker2")

# 获取容器 IP
IP_A=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER_A)
IP_B=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER_B)

echo "Container A: $IP_A (on worker1)"
echo "Container B: $IP_B (on worker2)"

# Ping 测试
docker exec $CONTAINER_A ping -c 10 $IP_B

# 输出:
# 10 packets transmitted, 10 received, 0% packet loss
# rtt min/avg/max/mdev = 0.4/0.5/0.6/0.1 ms

TCP 连接测试

# 在容器 A 启动 TCP 服务器
docker exec -d $CONTAINER_A nc -l -p 8080

# 在容器 B 连接
docker exec $CONTAINER_B nc -vz $IP_A 8080

# 输出:
# Connection to 10.0.0.2 8080 port [tcp/*] succeeded!

HTTP 测试

# 部署 nginx 服务
docker service create \
  --name nginx \
  --network my-overlay \
  nginx:alpine

# HTTP 请求
curl -I http://nginx

# 输出:
# HTTP/1.1 200 OK
# Server: nginx/1.25.3
# Date: Mon, 11 Mar 2026 10:00:00 GMT
# Content-Type: text/html

8.2 性能测试

带宽测试

# 安装 iperf3
docker run -d \
  --name iperf-server \
  --network my-overlay \
  networkstatic/iperf3 -s

# 客户端测试
docker run --rm \
  --network my-overlay \
  networkstatic/iperf3 -c iperf-server -t 30

# 输出:
# [ ID] Interval           Transfer     Bandwidth
# [  5]   0.00-30.00  sec  35.2 GBytes  10.1 Gbits/sec

延迟测试

# 使用 ping 测试延迟
docker exec container ping -c 100 target | grep rtt

# 输出:
# rtt min/avg/max/mdev = 0.3/0.5/1.2/0.1 ms

# 使用 netperf 测试 TCP 延迟
docker run --rm \
  --network my-overlay \
  networkstatic/netperf -H target -t TCP_RR

# 输出:
# TCP REQUEST/RESPONSE TEST: 15000 transactions/sec

9. 故障排查实战

9.1 常见问题

问题 1:容器无法跨主机通信

# 排查步骤:

# 1. 检查 Swarm 状态
docker node ls
# 确保所有节点 Ready

# 2. 检查网络
docker network inspect my-overlay
# 确保网络存在且正确配置

# 3. 检查 VXLAN 接口
ip -d link show | grep vxlan
# 输出:vxlan-br-xxx ... vxlan id 4096 ...

# 4. 检查防火墙
ufw status
# 确保 4789/udp 开放

# 5. 检查路由
ip route show
# 确保有到容器网段的路由

# 6. 抓包分析
tcpdump -i eth0 -n udp port 4789
# 查看 VXLAN 流量

问题 2:服务发现失败

# 排查步骤:

# 1. 检查 DNS
docker exec container nslookup service-name
# 应该返回容器 IP

# 2. 检查 ETCD
docker exec etcd etcdctl get /docker/network/v1/...
# 查看网络状态

# 3. 重启 Docker DNS
systemctl restart docker

# 4. 检查 Gossip
docker info | grep -i swarm
# 查看集群状态

9.2 性能问题

问题:网络延迟高

# 排查:

# 1. 检查 MTU
ip link show vxlan-br-xxx
# 确保 MTU=1450

# 2. 检查网络拥塞
ethtool -S eth0
# 查看丢包统计

# 3. 检查 CPU 使用
docker stats
# 查看系统负载

# 4. 路径追踪
docker exec container traceroute target
# 查看路由路径

10. 生产环境最佳实践

10.1 网络规划

子网划分

推荐方案:
- 全局网络:10.0.0.0/8
- 每个主机:/24 子网
- 每个容器:/32

示例:
Host A: 10.0.1.0/24
  - Container 1: 10.0.1.2/32
  - Container 2: 10.0.1.3/32

Host B: 10.0.2.0/24
  - Container 3: 10.0.2.2/32
  - Container 4: 10.0.2.3/32

VNI 规划

VNI 分配:
- 开发环境:1-1000
- 测试环境:1001-5000
- 生产环境:5001-10000
- 保留:10001-16777215

示例:
- dev-web: VNI 100
- prod-api: VNI 5001
- prod-db: VNI 5002

10.2 安全加固

网络策略

# Docker Network Policy
apiVersion: docker.com/v1
kind: NetworkPolicy
metadata:
  name: deny-cross-namespace
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: same-namespace

加密通信

# 所有 Overlay 网络启用加密
docker network create \
  --driver overlay \
  --opt encrypted=true \
  prod-network

11. 性能基准测试

11.1 测试环境

硬件配置:
- CPU: Intel Xeon E5-2680 v4
- 内存:64GB
- 网络:10GbE
- 宿主机:3 台

软件版本:
- Ubuntu 22.04 LTS
- Docker 24.0.0
- Kernel 5.15.0

11.2 测试结果

吞吐量测试

单主机容器通信:
- 吞吐量:9.8 Gbps
- 延迟:0.1ms

跨主机容器通信(Overlay):
- 吞吐量:9.5 Gbps
- 延迟:0.5ms

跨主机容器通信(Flannel VXLAN):
- 吞吐量:9.2 Gbps
- 延迟:0.6ms

性能损失:
- Overlay: 3%
- Flannel: 6%

延迟测试

Ping 延迟(ms):
┌──────────────┬────────┬────────┬────────┐
│ 场景         │ 最小   │ 平均   │ 最大   │
├──────────────┼────────┼────────┼────────┤
│ 同主机       │ 0.05   │ 0.08   │ 0.15   │
│ Overlay 跨机  │ 0.3    │ 0.5    │ 1.2    │
│ Flannel 跨机  │ 0.4    │ 0.6    │ 1.5    │
└──────────────┴────────┴────────┴────────┘

12. 总结与前沿技术

12.1 核心技术要点

  1. VXLAN 封装

    • MAC in UDP
    • 24 位 VNI
    • 50 字节开销
  2. 控制平面

    • ETCD 存储
    • Gossip 同步
    • 服务发现
  3. 数据平面

    • VTEP 封装/解封装
    • 网桥转发
    • 路由表

12.2 前沿技术

  1. eBPF 网络加速

    • Cilium
    • 内核级数据包处理
    • 性能提升 10 倍
  2. IPv6 Overlay

    • 双栈支持
    • 原生 IPv6
  3. Service Mesh 集成

    • Istio
    • Linkerd
    • 零信任网络

版权声明:本文原创,转载请注明出处
参考资料

  • Docker 官方文档
  • VXLAN RFC 7348
  • Flannel GitHub
  • ETCD 官方文档

如果本文对您有帮助,欢迎点赞、收藏、转发!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐