Docker Swarm 负载均衡深度解析:VIP vs DNSRR 模式详解
一、Swarm 负载均衡核心概念
Docker Swarm 模式内置了完整的负载均衡体系,主要包含以下核心组件:
| 组件 | 功能说明 |
|---|---|
| 内置 DNS | 自动为集群中的每个服务分配 DNS 记录,实现服务发现 |
| 内部负载均衡 | 基于服务 DNS 名称在集群内部分发请求(VIP/DNSRR) |
| Ingress 负载均衡 | 通过 Ingress Network 暴露外部访问入口,实现跨节点路由 |
| Published Port | 自动分配 30000-32767 范围的端口,或手动指定端口 |
两种负载均衡模式对比
DNSRR 模式
DNS 记录
IP 列表轮询
容器 IP 1
容器 IP 2
容器 IP 3
VIP 模式
DNS 记录
虚拟 VIP IP
IPVS 负载均衡
容器 1
容器 2
容器 3
关键区别:
- VIP:DNS 解析到虚拟 IP,通过 IPVS 做负载均衡(类似 LVS)
- DNSRR:DNS 直接轮询返回多个容器 IP,客户端自行选择
二、网络架构:Ingress vs 自定义 Network
Swarm 中存在两种主要的网络类型,对应不同的负载均衡场景:
2.1 Ingress Network(外部访问入口)
Swarm 集群 - Ingress Network
外部请求
IPVS
IPVS
IPVS
访问 192.168.1.79:8080
客户端
Node 1
Ingress IP: 10.0.0.1
Node 2
Ingress IP: 10.0.0.1
Node 3
Ingress IP: 10.0.0.1
ServiceA
Task 1
ServiceA
Task 2
ServiceA
Task 3
Ingress 特点:
- 创建 Swarm 集群时自动生成
- 所有 Node 节点共享相同的 Ingress IP(把集群视为一台物理机)
- 端口暴露(
--publish)时自动使用 Ingress 网卡 - 通过 IPVS 模块实现跨节点负载均衡
2.2 自定义 Overlay Network(内部服务通信)
自定义 Network: my-network
DNSRR 模式流量路径
返回 IP 列表
轮流访问
轮流访问
轮流访问
Client Container
DNS 轮询
10.0.0.4
10.0.0.12
10.0.0.13
ServiceA/Task 1
ServiceA/Task 2
ServiceA/Task 3
VIP 模式流量路径
overlay
overlay
overlay
Client Container
DNS 解析
VIP: 10.0.0.2
IPVS 负载均衡
Network Sandbox
ServiceA/Task 1
ServiceA/Task 2
ServiceA/Task 3
自定义 Network 特点:
- 需要手动创建(
docker network create --driver overlay) - 为每个 Service 生成唯一的 VIP (VIP 模式)或多个容器 IP(DNSRR 模式)
- 用于服务间内部通信,具备服务发现能力
- 支持两种负载均衡模式:VIP 和 DNSRR
三、深入理解 VIP 与 DNSRR 模式
3.1 VIP 模式(默认)
Task 3 10.0.0.13 Task 2 10.0.0.12 Task 1 10.0.0.4 IPVS 模块 虚拟 VIP 10.0.0.2 Swarm DNS 客户端容器 Task 3 10.0.0.13 Task 2 10.0.0.12 Task 1 10.0.0.4 IPVS 模块 虚拟 VIP 10.0.0.2 Swarm DNS 客户端容器 nslookup my_web 返回 VIP 10.0.0.2 请求 my_web:80 数据包标记 fwmark 负载均衡算法选择后端 转发请求(第1次) 返回响应 请求 my_web:80 转发请求(第2次) 返回响应 请求 my_web:80 转发请求(第3次) 返回响应
VIP 模式工作原理:
- 自定义 Network 为每个 Service 生成一个虚拟 IP(VIP)
- 该 Service 下的所有容器 Task 共享这个 VIP
- 容器将端口暴露给虚拟 Service(而非物理机)
- 虚拟 Service 通过 IPVS 将请求负载到实际容器
- 类比:虚拟 Service = 物理服务器,容器 Task = 服务器上的 Docker 容器
适用场景:
- 短连接请求(HTTP REST API)
- 需要服务端负载均衡
- 端口不直接暴露给物理机
3.2 DNSRR 模式(DNS Round Robin)
Task 3 10.0.0.13 Task 2 10.0.0.12 Task 1 10.0.0.4 Swarm DNS 客户端容器 Task 3 10.0.0.13 Task 2 10.0.0.12 Task 1 10.0.0.4 Swarm DNS 客户端容器 10.0.0.4, 10.0.0.12, 10.0.0.13 nslookup my_web2 返回 3 个 IP 地址 直接访问 10.0.0.4(第1次) 返回响应 直接访问 10.0.0.12(第2次) 返回响应 直接访问 10.0.0.13(第3次) 返回响应
DNSRR 模式工作原理:
- 自定义 Network 为每个容器生成具体 IP
- 所有容器 IP 记录到 DNS 列表
- 客户端查询 DNS 时,轮询返回不同 IP
- 客户端直接访问具体容器 IP
- 负载均衡发生在客户端(DNS 层面)
适用场景:
- 长连接保持(WebSocket、数据库连接)
- 需要会话亲和性(Sticky Session)
- 客户端能处理多 IP 轮询
- 不支持端口对外暴露(仅内部通信)
四、容器网卡与网络选择
4.1 网卡类型总结
在 Swarm 模式下,一个容器可能拥有多张网卡:
Swarm 容器网络栈
可选网卡
基础网卡
容器访问外网
宿主机访问容器
服务间通信
外部访问入口
默认网卡
eth0
docker_bridge
与宿主机通信
自定义 Network 网卡
用于服务发现
Ingress 网卡
端口暴露时自动添加
Internet
Host
Other_Services
External
| 网卡类型 | 说明 | 使用条件 |
|---|---|---|
| 默认网卡 | 容器基础网络 | 始终存在 |
| docker_bridge | 与宿主机通信 | 始终存在 |
| 自定义 Network 网卡 | 服务发现、内部负载均衡 | 加入自定义 Network |
| Ingress 网卡 | 外部访问、跨节点路由 | 端口暴露(--publish) |
注意 :当端口暴露时(PublishMode=ingress),容器会同时拥有自定义 Network IP 和Ingress Network IP,两者同时生效。
4.2 负载均衡模式与网卡的关系
负载均衡模式选择
是
否
默认/VIP
DNSRR
端口是否暴露?
使用 Ingress Network
VIP 模式
使用自定义 Network
选择负载均衡模式
VIP 模式
虚拟 IP + IPVS
DNSRR 模式
DNS 轮询
关键结论:
- Ingress 负载均衡:固定使用 VIP 模式,基于 IPVS
- 自定义 Network 负载均衡:可选择 VIP(默认)或 DNSRR
- 端口暴露与否决定了是否使用 Ingress 网卡
五、实战实验:负载均衡测试
5.1 实验环境准备
bash
# 创建自定义 Overlay 网络
$ docker network create --driver overlay my-network
5.2 实验一:VIP 模式测试(默认)
步骤4: 测试访问
curl 192.168.1.79:8080
请求被负载均衡到 3 个副本
步骤3: 获取 VIP
docker service inspect -f '{{json .Endpoint.VirtualIPs}}' my_web
VIP: 10.0.0.2/24
步骤2: 暴露端口
docker service update --publish-add 8080:80 my_web
步骤1: 创建服务
docker service create --replicas 3
--network my-network
--name my_web nginx
执行命令:
bash
# 1. 创建 3 副本服务
$ docker service create \
--replicas 3 \
--network my-network \
--name my_web \
nginx
# 2. 添加端口暴露(使用 Ingress Network)
$ docker service update --publish-add 8080:80 my_web
# 3. 查看 VIP 信息
$ docker service inspect -f '{{json .Endpoint.VirtualIPs}}' my_web
[{"NetworkID":"3d1ut7rm89tvqvhh98wl3bxtx","Addr":"10.0.0.2/24"}]
# 4. 外部访问测试(自动负载均衡)
$ curl 192.168.1.79:8080
验证负载均衡 :
在每个副本容器中查看访问日志,确认请求被分发到不同容器。
5.3 实验二:DNSRR 模式测试
步骤4: Ping 测试轮询
ping my_web2
每次 ping 解析到不同 IP
步骤3: DNS 解析测试
nslookup my_web2
返回 3 个 IP:
10.0.0.4, 10.0.0.12, 10.0.0.13
步骤2: 进入容器测试
docker exec -it sh
步骤1: 创建 DNSRR 服务
docker service create
--replicas 3
--name my_web2
--network my-network
--endpoint-mode dnsrr
nginx
执行命令:
bash
# 1. 创建 DNSRR 模式服务
$ docker service create \
--replicas 3 \
--name my_web2 \
--network my-network \
--endpoint-mode dnsrr \
nginx
# 2. 进入工作节点容器
$ docker exec -it 06a5a7ae6e7e sh
# 3. 测试 DNS 解析
/ # nslookup my_web2
Server: 127.0.0.11
Address 1: 127.0.0.11
Name: my_web2
Address 1: 10.0.0.13
Address 2: 10.0.0.4
Address 3: 10.0.0.12
# 4. 多次 ping 测试轮询效果
/ # ping my_web2
PING my_web2 (10.0.0.4): 56 data bytes
...
/ # ping my_web2
PING my_web2 (10.0.0.12): 56 data bytes
...
/ # ping my_web2
PING my_web2 (10.0.0.13): 56 data bytes
关键观察:
- 每次
ping my_web2解析到不同的 IP 地址 - 负载均衡发生在 DNS 解析层面
- 客户端直接连接具体容器 IP
5.4 查看服务负载均衡模式
bash
# 查看当前服务的 Endpoint 模式
$ docker service inspect my_web
{
"EndpointSpec": {
"Mode": "vip"
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
}
}
六、端口暴露与网络选择深入分析
6.1 端口暴露的网络影响
端口暴露 --publish
内部通信
外部访问
Service
my_web
自定义 Network
my-network
VIP: 10.0.0.2
Ingress Network
自动创建
IP: 10.0.0.1
Published Port
8080:80
端口不暴露
仅使用
Service
my_web
自定义 Network
my-network
容器 IP: 10.0.0.x
关键结论:
- 不暴露端口:仅使用自定义 Network,无 Ingress 网卡
- 暴露端口:同时使用自定义 Network(用于服务发现)和 Ingress Network(用于外部访问)
- 两个网卡同时生效,容器拥有多个 IP
6.2 Ingress vs 自定义 Network 负载均衡对比
自定义 Network 负载均衡
VIP
DNSRR
服务间请求
DNS 查询
VIP or DNSRR?
VIP: 10.0.0.2
IPVS 分发
轮询返回
容器 IP 列表
目标容器
目标容器
特点: 服务发现
支持 VIP/DNSRR
内部通信
Ingress 负载均衡
外部请求
任意节点
8080端口
IPVS 负载均衡
目标容器 Task
特点: 跨节点路由
基于 IPVS
固定使用 VIP
| 特性 | Ingress Network | 自定义 Network |
|---|---|---|
| 使用场景 | 外部访问集群服务 | 内部服务间通信 |
| 负载均衡模式 | 固定 VIP(IPVS) | VIP 或 DNSRR |
| 服务发现 | 不支持 | 支持(内置 DNS) |
| 网络类型 | 特殊 Overlay(自动创建) | 自定义 Overlay(手动创建) |
| 端口要求 | 需要暴露端口 | 不需要暴露端口 |
| IP 分配 | 所有节点共享相同 Ingress IP | 每个 Service 唯一 VIP 或 多个容器 IP |
七、双层负载均衡架构扩展
在生产环境中,可以在 Swarm 内置负载均衡之上,再叠加一层外部负载均衡器(如 HAProxy、Nginx):
第二层: Swarm 负载均衡
第一层: 外部负载均衡
轮询
轮询
轮询
Node 3
Swarm LB
8080端口
my_web
Task 3
Node 2
Swarm LB
8080端口
my_web
Task 2
Node 1
Swarm LB
8080端口
my_web
Task 1
HAProxy
192.168.99.99:80
双层架构优势:
- 高可用:HAProxy 可配置多后端节点,单点故障自动剔除
- 灵活调度:支持权重、灰度发布、会话保持等高级功能
- SSL 终结:在 HAProxy 层处理 HTTPS,后端使用 HTTP
- 大规模扩展:突破 Swarm 内置负载均衡的性能瓶颈
配置示例:
haproxy
# HAProxy 配置示例
backend swarm_nodes
balance roundrobin
server node1 192.168.99.100:8080 check
server node2 192.168.99.101:8080 check
server node3 192.168.99.102:8080 check
八、模式选择与最佳实践
8.1 负载均衡模式选择决策树
是
否
是
否
是
否
开始选择负载均衡模式
需要外部访问?
使用 Ingress Network
固定 VIP 模式
使用自定义 Network
需要长连接/会话保持?
选择 DNSRR 模式
选择 VIP 模式
默认推荐
需要端口暴露?
⚠️ DNSRR 不支持端口暴露
考虑 VIP + 应用层会话保持
DNSRR 适合
服务间长连接
VIP 适合
短连接/HTTP API
8.2 最佳实践建议
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| HTTP REST API | VIP | 短连接,服务端负载均衡更可控 |
| WebSocket | DNSRR | 长连接保持,避免 VIP 连接粘滞 |
| 数据库连接 | DNSRR | 会话亲和性,连接池复用 |
| 外部 Web 服务 | VIP + Ingress | 需要端口暴露,跨节点路由 |
| 内部微服务通信 | VIP(默认)或 DNSRR | 根据连接特性选择 |
8.3 注意事项
-
DNSRR 不支持端口暴露:
bash# 错误示例:DNSRR 无法暴露端口到宿主机 docker service create --endpoint-mode dnsrr --publish 8080:80 nginx # 该配置不会生效,端口不会绑定到宿主机 -
VIP 模式的长连接问题:
- VIP 使用 IPVS,长连接可能粘滞在同一后端
- 建议应用层实现连接池刷新或会话超时
-
健康检查配置:
bash# 配置健康检查,确保故障容器及时剔除 docker service create \ --health-cmd "curl -f http://localhost/health || exit 1" \ --health-interval 5s \ --health-retries 3 \ nginx -
会话保持实现:
- Swarm 原生不支持会话保持(Sticky Session)
- 需要会话亲和性时,使用 DNSRR + 客户端 cookies
- 或叠加外部负载均衡器(HAProxy)实现
九、总结
Docker Swarm 提供了开箱即用的负载均衡能力,通过内置 DNS 和 IPVS 实现了服务发现和流量分发。核心要点如下:
-
两种负载均衡模式:
- VIP:虚拟 IP + IPVS,适合短连接,默认推荐
- DNSRR:DNS 轮询,适合长连接,不支持端口暴露
-
两种网络类型:
- Ingress Network:外部访问入口,固定使用 VIP
- 自定义 Network:内部服务通信,支持 VIP/DNSRR
-
容器多网卡:
- 基础网卡(默认 + docker_bridge)始终存在
- 自定义 Network 网卡用于服务发现
- Ingress 网卡在端口暴露时自动添加
-
扩展能力:
- 可叠加 HAProxy/Nginx 实现双层负载均衡
- 支持 SSL 终结、权重调度、灰度发布等高级功能