Docker Overlay 网络的核心工作(以跨节点容器通信为例)
Docker 的 overlay
网络是一种基于 VXLAN(Virtual Extensible LAN)的多主机网络模式,专为 Docker Swarm 集群设计,用于实现跨节点的容器通信。它通过虚拟二层网络,允许容器在不同主机上像在同一局域网内一样通信。Docker 在实现 overlay
网络时,协调用户态(Docker 守护进程、libnetwork)和内核态(Linux 网络栈、VXLAN 模块),完成从网络创建到数据包转发的复杂工作。以下是以 Markdown 格式输出的详细讲解,以两个跨节点容器(例如节点 1 的 gindemo1
和节点 2 的 gindemo2
)通信为例,深入分析 Docker 的核心工作,并具体展示路由表和数据包转发流程。
我们假设以下场景:
- 节点 1:IP
192.168.1.9
,主机名node01
,运行容器gindemo1
(IP:10.0.1.2
)。 - 节点 2:IP
192.168.1.10
,主机名node02
,运行容器gindemo2
(IP:10.0.1.6
)。 - 网络:
my-overlay-network
,VNI(VXLAN Network Identifier)为4097
,子网10.0.1.0/24
,网关10.0.1.1
。 - 通信:
gindemo2
(节点 2)访问gindemo1
(节点 1),例如curl http://gindemo1
。
Docker 在实现这一通信的过程中,完成了以下核心工作,并涉及具体的路由表配置。
1. 网络创建与配置
Docker 通过 libnetwork
和 Swarm 控制平面创建和管理 overlay
网络。
-
创建网络:
- 命令:
docker network create -d overlay --attachable my-overlay-network
- 工作:
- 使用
overlay
驱动分配网络 ID 和子网(10.0.1.0/24
)。 - 生成唯一的 VNI(
4097
),存储在配置中(com.docker.network.driver.overlay.vxlanid_list
)。 - 配置 MTU(默认 1450,适应 VXLAN 50 字节开销)。
- 设置
attachable: true
,允许手动附加容器(避免not manually attachable
错误)。
- 使用
- 命令:
-
Swarm 同步:
- 将网络配置(VNI、子网、网关)存储在 Swarm 的分布式键值存储(Raft 协议)。
- 通过 TCP 2377(Swarm 管理)和 TCP/UDP 7946(gossip 协议)端口,分发到节点 1 和节点 2。
- 确保节点 2 无需手动创建
my-overlay-network
,直接使用集群配置。
-
IPAM(IP 地址管理):
- 分配子网
10.0.1.0/24
,网关10.0.1.1
。 - 为容器动态分配 IP:
gindemo1
(10.0.1.2
)、gindemo2
(10.0.1.6
)。
- 分配子网
2. VXLAN 接口与桥接网络初始化
Docker 在每个节点上创建 VXLAN 接口和桥接网络,支持跨节点二层通信。
-
VXLAN 接口:
- 在节点 1 和节点 2 上为
my-overlay-network
创建 VXLAN 接口(例如vxlan0
)。 - 配置:
- VNI:
4097
。 - 目标端口:UDP 4789(VXLAN 默认端口)。
- 绑定物理网卡:
enp0s3
(你的节点 2 网卡)。 - MTU:1450。
- VNI:
- 示例(节点 2,若接口正常显示):
输出:ip -d link show vxlan0
10: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group defaultlink/ether 02:42:ac:11:00:06 brd ff:ff:ff:ff:ff:ffvxlan id 4097 srcport 0 0 dstport 4789 ...
- 在节点 1 和节点 2 上为
-
桥接网络:
- 创建桥接接口(例如
br-xxxx
),连接 VXLAN 接口和容器虚拟接口。 - 示例(节点 2,假设
brctl
已安装):
输出:brctl show
bridge name bridge id STP enabled interfaces br-xxxx 8000.0242ac110006 no vxlan0veth30344ea docker_gwbridge 8000.0242bf32025b no vetha7fbe75
vxlan0
:VXLAN 接口,处理跨节点通信。veth30344ea
:gindemo2
的虚拟接口。docker_gwbridge
:用于外部网络连接。
- 创建桥接接口(例如
-
你的情况:
- 节点 2 未显示 VXLAN 接口(
ip -d link show type vxlan
无输出),但抓包确认 VNI 4097 和通信正常,说明 VXLAN 功能通过内核或其他机制运行。
- 节点 2 未显示 VXLAN 接口(
3. 容器网络命名空间配置
Docker 为每个容器创建独立的网络命名空间,配置 IP、MAC 和路由。
-
命名空间创建:
- 为
gindemo1
和gindemo2
创建网络命名空间,隔离网络栈。 - 示例(节点 2,
gindemo2
):
输出:docker inspect gindemo2 | grep -A 5 Network
"Networks": {"my-overlay-network": {"IPAddress": "10.0.1.6","Gateway": "10.0.1.1","EndpointID": "xxxx","MacAddress": "02:42:ac:11:00:06"}}
- 为
-
虚拟接口:
- 创建
veth
接口对:- 容器端:
eth0
(IP:10.0.1.6
,MAC:02:42:ac:11:00:06
)。 - 主机端:
veth30344ea
,连接到桥接接口(br-xxxx
或docker_gwbridge
)。
- 容器端:
- 示例(容器内):
输出:docker exec -it gindemo2 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536inet 127.0.0.1/8 scope host lo 30: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450inet 10.0.1.6/24 brd 10.0.1.255 scope global eth0
- 创建
-
路由表配置:
- Docker 为容器配置路由表,确保数据包通过网关或直接发送到目标。
- 示例(
gindemo2
容器内):
输出:docker exec -it gindemo2 ip route
default via 10.0.1.1 dev eth0 10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.6
- 解释:
- 默认路由:通过网关
10.0.1.1
(虚拟网关,由 Docker 实现)。 - 本地子网:
10.0.1.0/24
直接通过eth0
访问,覆盖gindemo1
(10.0.1.2
)。
- 默认路由:通过网关
- 解释:
-
主机路由表:
- 主机上为
my-overlay-network
配置路由,确保数据包进入 VXLAN 隧道。 - 示例(节点 2 主机):
输出:ip route
default via 192.168.1.1 dev enp0s3 10.0.1.0/24 dev br-xxxx proto kernel scope link src 10.0.1.1 192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.10
- 解释:
10.0.1.0/24
:通过桥接接口(br-xxxx
)访问,网关10.0.1.1
。- 外部流量通过物理网卡
enp0s3
和默认网关192.168.1.1
。
- 解释:
- 主机上为
4. DNS 解析与服务发现
Docker 提供内置 DNS 服务,简化容器间通信。
-
DNS 配置:
- Swarm 维护分布式 DNS 记录,将容器名称映射到 IP。
- 示例:
gindemo1
解析为10.0.1.2
,gindemo2
解析为10.0.1.6
。 - 配置存储在 Swarm 键值存储中,通过 gossip 协议同步。
-
通信过程:
gindemo2
执行curl http://gindemo1
:- DNS 查询
gindemo1
,解析到10.0.1.2
。 - 数据包通过容器路由表发送到
eth0
。
- DNS 查询
-
你的抓包:
- 抓包显示
10.0.1.6
(gindemo2
)访问10.0.1.2
(gindemo1
)的websm
端口(80 或 443),DNS 解析正常。
- 抓包显示
5. VXLAN 数据包封装与转发
Docker 协调内核 VXLAN 模块,完成数据包的封装和跨节点转发。
-
数据包生成:
gindemo2
(10.0.1.6
)发送 HTTP 请求到gindemo1
(10.0.1.2
)。- 容器内路由表:
10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.6
- 数据包通过
eth0
发送,目标 IP10.0.1.2
,MAC 未解析(需 ARP)。
- 数据包通过
-
ARP 解析:
- Docker 通过 Swarm 的 gossip 协议或多播,解析
10.0.1.2
的 MAC(例如02:42:ac:11:00:02
)。 - FDB 表记录 MAC 到节点映射:
输出(节点 2):bridge fdb show dev vxlan0
02:42:ac:11:00:02 dst 192.168.1.9 self
- 表示
gindemo1
的 MAC 映射到节点 1(192.168.1.9
)。
- 表示
- Docker 通过 Swarm 的 gossip 协议或多播,解析
-
VXLAN 封装:
- 数据包进入主机桥接接口(
br-xxxx
),转发到 VXLAN 接口(vxlan0
)。 - 内核 VXLAN 模块封装数据包:
- 内部帧:源
10.0.1.6
(MAC:02:42:ac:11:00:06
),目标10.0.1.2
(MAC:02:42:ac:11:00:02
)。 - VXLAN 头:VNI
4097
。 - 外部 UDP:源
192.168.1.10
(动态端口,例如 50730),目标192.168.1.9:4789
。 - 外部 IP:源
192.168.1.10
,目标192.168.1.9
。
- 内部帧:源
- 你的抓包:
07:22:12.656724 IP 192.168.1.10.50730 > 192.168.1.9.vxlan: VXLAN, flags [I] (0x08), vni 4097 IP 10.0.1.6.35126 > 10.0.1.2.websm: Flags [S], seq 2498547307, win 28200, ...
- 数据包进入主机桥接接口(
-
转发:
- 数据包通过物理网卡(
enp0s3
)发送到节点 1。 - 主机路由表(节点 2):
192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.10
- 目标
192.168.1.9
通过enp0s3
直接发送。
- 目标
- 数据包通过物理网卡(
-
节点 1 解封装:
- 节点 1 接收 UDP 4789 数据包,VXLAN 接口(
vxlan0
)解封装。 - 内部帧转发到
gindemo1
(10.0.1.2
)的eth0
。 - 节点 1 路由表(容器内):
default via 10.0.1.1 dev eth0 10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.2
- 节点 1 接收 UDP 4789 数据包,VXLAN 接口(
6. iptables 和外部连接
Docker 配置 iptables,支持容器与外部网络的通信。
-
NAT 配置:
- 为端口映射(例如
-p 8081:80
)配置iptables
的nat
表(DOCKER
链)。 - 示例(节点 2):
输出:sudo iptables -t nat -L DOCKER
Chain DOCKER (2 references) target prot opt source destination DNAT tcp -- anywhere 0.0.0.0/0 tcp dpt:8081 to:10.0.1.6:80
- 为端口映射(例如
-
docker_gwbridge:
- 连接容器到外部网络,处理外部访问(例如通过
192.168.1.10:8081
访问gindemo2
)。 - 你的输出:
10: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...link/ether 02:42:bf:32:02:5b ... 24: vetha7fbe75@if23: ... master docker_gwbridge ... 31: veth30344ea@if30: ... master docker_gwbridge ...
- 连接容器到外部网络,处理外部访问(例如通过
-
防火墙:
- 确保 UDP 4789(VXLAN)、TCP 2377(Swarm 管理)、TCP/UDP 7946(gossip)端口开放。
- 示例:
sudo ufw allow 4789/udp
7. Swarm 控制平面管理
Docker Swarm 提供分布式管理,确保 overlay
网络跨节点一致。
-
网络同步:
- 通过 Raft 协议存储网络配置(VNI、子网、FDB)。
- 节点 2 无需手动创建
my-overlay-network
,Swarm 自动分发。
-
服务发现:
- Swarm 维护 DNS 记录,动态更新容器 IP 和名称。
- 示例:
gindemo2
查询gindemo1
,返回10.0.1.2
。
-
故障处理:
- 检测节点状态(
docker node ls
),重新同步配置。 - 你的问题(节点 2 无 VXLAN 接口)可能因同步或内核问题,Docker 仍通过其他机制维持通信。
- 检测节点状态(
8. 性能优化
Docker 优化 overlay
网络的性能。
-
内核 VXLAN:
- 使用 Linux 内核的 VXLAN 模块,高效处理封装。
- 支持网卡硬件卸载(如果支持)。
-
多播/单播:
- 默认使用多播分发 ARP,动态切换到单播(你的环境中可能为单播)。
- FDB 表动态更新:
02:42:ac:11:00:02 dst 192.168.1.9 self
-
MTU:
- 设置 MTU 1450,适应 VXLAN 封装。
- 你的抓包显示
mss 1410
,确认 MTU 正确。
9. 调试与监控
Docker 提供工具支持 overlay
网络调试。
-
网络检查:
docker network inspect my-overlay-network
:显示 VNI(4097
)、子网、容器 IP。docker inspect gindemo2
:确认 IP 和 MAC。
-
抓包:
- 你的抓包:
IP 192.168.1.10.50730 > 192.168.1.9.vxlan: VXLAN, flags [I] (0x08), vni 4097
- 验证 VNI 和通信。
- 你的抓包:
-
日志:
journalctl -u docker
:记录网络错误。
10. 异常处理
Docker 处理 overlay
网络的异常情况。
-
你的问题(节点 2 无 VXLAN 接口):
- 可能原因:VXLAN 模块未加载(
modprobe vxlan
)、iproute2
过旧、Docker 网络栈异常。 - Docker 仍通过内核直接处理 VXLAN 流量(抓包确认),但接口未显示。
- 可能原因:VXLAN 模块未加载(
-
解决方案:
- 加载 VXLAN 模块:
sudo modprobe vxlan
- 更新
iproute2
:sudo yum update iproute
- 重启 Docker:
systemctl restart docker
- 加载 VXLAN 模块:
具体路由表和数据包流程
以下是 gindemo2
(节点 2,10.0.1.6
)访问 gindemo1
(节点 1,10.0.1.2
)的详细流程,包含路由表。
1. gindemo2 容器(节点 2)
- 路由表:
docker exec -it gindemo2 ip route
default via 10.0.1.1 dev eth0 10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.6
- 流程:
curl http://gindemo1
解析为10.0.1.2
(Swarm DNS)。- 数据包:源
10.0.1.6:35126
,目标10.0.1.2:80
(HTTP)。 - 路由匹配
10.0.1.0/24
,通过eth0
发送。 - ARP 请求
10.0.1.2
的 MAC,解析为02:42:ac:11:00:02
。
2. 节点 2 主机
- 路由表:
ip route
default via 192.168.1.1 dev enp0s3 10.0.1.0/24 dev br-xxxx proto kernel scope link src 10.0.1.1 192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.10
- FDB 表:
bridge fdb show dev vxlan0
02:42:ac:11:00:02 dst 192.168.1.9 self
- 流程:
- 数据包从
veth30344ea
进入桥接接口(br-xxxx
)。 - VXLAN 接口(
vxlan0
,若存在)封装数据包:- 内部:源
10.0.1.6
(MAC:02:42:ac:11:00:06
),目标10.0.1.2
(MAC:02:42:ac:11:00:02
)。 - 外部:源
192.168.1.10:50730
,目标192.168.1.9:4789
,VNI4097
。
- 内部:源
- 路由匹配
192.168.1.0/24
,通过enp0s3
发送。
- 数据包从
3. 节点 1 主机
- 路由表:
ip route
default via 192.168.1.1 dev enp0s3 10.0.1.0/24 dev br-yyyy proto kernel scope link src 10.0.1.1 192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.9
- 流程:
- 接收 UDP 4789 数据包,VXLAN 接口解封装。
- 内部帧转发到桥接接口(
br-yyyy
),匹配10.0.1.2
。 - 数据包通过
veth
接口送达gindemo1
的eth0
。
4. gindemo1 容器(节点 1)
- 路由表:
docker exec -it gindemo1 ip route
default via 10.0.1.1 dev eth0 10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.2
- 流程:
- 接收数据包,处理 HTTP 请求。
- 响应通过相同路径返回(反向封装,VXLAN 到节点 2)。
总结
Docker 在 overlay
网络中完成以下核心工作:
- 网络创建:分配 VNI(
4097
)、子网(10.0.1.0/24
),通过 Swarm 同步。 - VXLAN 初始化:创建 VXLAN 接口(
vxlan0
)、桥接接口(br-xxxx
)。 - 容器配置:分配 IP(
10.0.1.2
,10.0.1.6
)、MAC,配置路由表和veth
接口。 - DNS 解析:提供 Swarm DNS,解析
gindemo1
到10.0.1.2
。 - 数据封装:封装 VXLAN 数据包(外部:
192.168.1.10
到192.168.1.9
,VNI4097
),维护 FDB。 - iptables:配置 NAT 和端口映射,连接
docker_gwbridge
。 - Swarm 管理:分布式同步,故障恢复。
- 优化与调试:内核 VXLAN、MTU 1450、抓包支持。