| 方案 | 驱动 | 核心原理 | 适用场景 | 配置复杂度 |
|---|---|---|---|---|
Overlay网络 |
|
VXLAN隧道+加密 |
Docker Swarm集群 |
中等 |
Macvlan网络 |
|
容器直连物理网卡 |
需要独立公网IP的容器 |
高 |
IPvlan网络 |
|
容器共享物理网卡MAC |
网络设备限制MAC数量的环境 |
高 |
| 方案 | 网络模式 | 核心特性 | 性能 | 安全性 |
|---|---|---|---|---|
Calico |
BGP路由/IPIP隧道 |
网络策略、IPAM、支持K8s |
高 |
企业级 |
Flannel |
VXLAN/host-gw/云厂商集成 |
简单易用、社区活跃 |
中 |
基础 |
Weave Net |
VXLAN+自有路由 |
内置加密、服务发现 |
中 |
良好 |
Cilium |
eBPF |
L3/L7策略、可观测性 |
极高 |
企业级 |
graph TB
subgraph 物理网络
Switch[物理交换机]
end
subgraph 主机A
NIC_A[物理网卡 eth0]
MACVLAN_A[Macvlan接口<br/>macvlan0]
Container_A[容器A IP: 172.29.0.10]
end
subgraph 主机B
NIC_B[物理网卡 eth0]
MACVLAN_B[Macvlan接口<br/>macvlan0]
Container_B[容器B IP: 172.29.0.11]
end
Container_A -->|直接通过| MACVLAN_A
MACVLAN_A -->|虚拟接口| NIC_A
NIC_A -->|物理网络| Switch
Container_B -->|直接通过| MACVLAN_B
MACVLAN_B -->|虚拟接口| NIC_B
NIC_B -->|物理网络| Switch
Container_A <-->|二层直接通信| Container_B
1. 环境准备
# 节点信息 节点A: IP: 192.168.1.71/24 网关: 192.168.1.1 物理网卡: ens34 Docker版本: 20.10+ 节点B: IP: 192.168.1.72/24 网关: 192.168.1.1 物理网卡: ens34 Docker版本: 20.10+
2. 网络拓扑规划
网络规划: 物理网络: 192.168.1.0/24 容器IP段: 172.29.0.0/16 网关: 172.29.0.254
3.具体操作步骤
a.检查网络环境
注意:我这里是ESXI8 开的虚拟机,需要在ESXI8控制台,网络-端口组-安全-接受混杂模式
# 在节点A和B上都执行 # 需要开启混杂模式,这里不做描述 # 确认物理网络配置 ip addr show ens34 # 输出示例: inet 192.168.1.71/24 brd 192.168.1.255 scope global ens34 # 检查macvlan模块是否已加载 lsmod | grep macvlan # 检查模块是否存在系统中 modinfo macvlan # 尝试手动加载模块 modprobe macvlan ## 永久启用 # 添加到模块加载列表 echo "macvlan" | sudo tee -a /etc/modules # 或者创建模块配置文件 echo "options macvlan" | sudo tee /etc/modprobe.d/macvlan.conf # 更新initramfs(某些系统需要) update-initramfs -u # 再次检查是否加载成功 lsmod | grep macvlan # 检查网络命名空间支持(Macvlan依赖此功能) ip netns add test_ns && echo "✅ 支持网络命名空间" && ip netns delete test_ns
b.创建macvlan网络
# 在节点A上创建 docker network create -d macvlan --subnet 172.29.0.0/16 --gateway 172.29.0.254 -o parent=ens34 macvlan-test # 在节点B上创建(使用相同的配置) docker network create -d macvlan --subnet 172.29.0.0/16 --gateway 172.29.0.254 -o parent=ens34 macvlan-test
参数详解:
--subnet: 物理网络的子网--gateway: 物理网络的网关--ip-range: 为该 Macvlan 网络分配的 IP 范围--aux-address: 排除宿主机的 IP,避免冲突-o parent: 指定父接口(物理网卡)-o macvlan_mode: Macvlan 模式(bridge 最常用)c.运行容器并测试
# 在节点A上运行容器 docker container run -d -it --name box1 --network macvlan-test --ip 172.29.0.71 busybox # 在节点B上运行容器 docker container run -d -it --name box2 --network macvlan-test --ip 172.29.0.72 busybox
d.测试夸主机通信
# 在节点A的容器中测试连接节点B的连通性
[root@docker-71 ~]# docker exec -it box1 sh
/ # ping -c 3 172.29.0.72
PING 172.29.0.72 (172.29.0.72): 56 data bytes
64 bytes from 172.29.0.72: seq=0 ttl=64 time=0.421 ms
64 bytes from 172.29.0.72: seq=1 ttl=64 time=0.573 ms
64 bytes from 172.29.0.72: seq=2 ttl=64 time=0.486 ms
--- 172.29.0.72 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.421/0.493/0.573 ms
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 26:ED:AB:B4:65:16
inet addr:172.29.0.71 Bcast:172.29.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1200 errors:0 dropped:205 overruns:0 frame:0
TX packets:1409 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:92250 (90.0 KiB) TX bytes:87346 (85.2 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:917 errors:0 dropped:0 overruns:0 frame:0
TX packets:917 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:102704 (100.2 KiB) TX bytes:102704 (100.2 KiB)
/ #
# 在节点B的容器中测试,测试到节点A容器的连通性
[root@docker-72 ~]# docker exec box2 sh -c "ping -c 3 172.29.0.71"
PING 172.29.0.71 (172.29.0.71): 56 data bytes
64 bytes from 172.29.0.71: seq=0 ttl=64 time=0.448 ms
64 bytes from 172.29.0.71: seq=1 ttl=64 time=0.581 ms
64 bytes from 172.29.0.71: seq=2 ttl=64 time=0.588 ms
--- 172.29.0.71 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.448/0.539/0.588 ms
e.解决macvlan无法访问外网的情况
#添加一个能够访问外网的网络类型 [root@docker-72 ~]# docker network connect bridge box2 [root@docker-72 ~]# docker exec box2 sh -c "ping -c 3 223.5.5.5"
Overlay 网络 是一种在现有网络之上构建的虚拟网络,它允许多个主机上的容器或虚拟机像在同一个局域网中一样通信,而无需修改底层物理网络结构。
目标:构建基于 Consul 服务发现的容器跨主机网络解决方案
架构:Consul 集群 + Docker 自定义网络 + 容器间服务发现
| 主机名 | IP 地址 | 角色 | 功能 |
|---|---|---|---|
consul-server-1 |
192.168.1.71 |
Consul Server + Docker 主机 |
Consul 主服务器,运行服务 |
consul-server-2 |
192.168.1.72 |
Consul Server + Docker 主机 |
Consul 备份服务器,运行服务 |
consul-client-1 |
192.168.1.73 |
Consul Client + Docker 主机 |
Consul 客户端,运行服务 |
物理网络:192.168.1.0/24
Overlay 网络:10.20.0.0/16
服务端口:
Consul HTTP: 8500
Consul DNS: 8600
Consul RPC: 8300
Consul Serf LAN: 8301
Consul Serf WAN: 8302
在 consul-server-1, consul-server-2, consul-client-1 上都执行:
# 1. 设置主机名 hostnamectl set-hostname consul-server-1 # 在 server-1 上执行 hostnamectl set-hostname consul-server-2 # 在 server-2 上执行 hostnamectl set-hostname consul-client-1 # 在 client-1 上执行 # 2. 编辑 hosts 文件 tee -a /etc/hosts << EOF 192.168.1.71 consul-server-1 192.168.1.72 consul-server-2 192.168.1.73 consul-client-1 EOF # 3. 安装 Docker 此处省略...
# 1. 创建 Consul 数据目录
mkdir -p /opt/consul/data
mkdir -p /etc/consul
# 2. 创建 Consul 配置文件
tee /etc/consul/server1.json << EOF
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"node_name": "consul-server-1",
"server": true,
"bootstrap_expect": 2,
"bind_addr": "0.0.0.0",
"client_addr": "0.0.0.0",
"advertise_addr": "192.168.1.71",
"ui": true,
"enable_syslog": false,
"ports": {
"dns": 8600,
"http": 8500,
"serf_lan": 8301,
"serf_wan": 8302,
"server": 8300
}
}
EOF
# 3. 启动 Consul Server
docker run -d \
--name=consul-server-1 \
--restart=always \
--net=host \
-v /opt/consul/data:/consul/data \
-v /etc/consul:/consul/config \
consul:1.15 agent \
-config-dir=/consul/config \
-node=consul-server-1 \
-bind=192.168.1.71 \
-advertise=192.168.1.71 \
-client=0.0.0.0 \
-datacenter=dc1 \
-server \
-bootstrap-expect=2 \
-ui
# 1. 创建目录
mkdir -p /opt/consul/data
mkdir -p /etc/consul
# 2. 创建配置
tee /etc/consul/server2.json << EOF
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"node_name": "consul-server-2",
"server": true,
"bind_addr": "0.0.0.0",
"client_addr": "0.0.0.0",
"advertise_addr": "192.168.1.72",
"ui": true,
"retry_join": ["192.168.1.71"],
"ports": {
"dns": 8600,
"http": 8500,
"serf_lan": 8301,
"serf_wan": 8302,
"server": 8300
}
}
EOF
# 3. 启动 Consul Server
docker run -d \
--name=consul-server-2 \
--restart=always \
--net=host \
-v /opt/consul/data:/consul/data \
-v /etc/consul:/consul/config \
consul:1.15 agent \
-config-dir=/consul/config \
-node=consul-server-2 \
-bind=192.168.1.72 \
-advertise=192.168.1.72 \
-client=0.0.0.0 \
-datacenter=dc1 \
-server \
-retry-join=192.168.1.71
# 1. 创建目录
mkdir -p /opt/consul/data
mkdir -p /etc/consul
# 2. 创建配置
tee /etc/consul/client1.json << EOF
{
"datacenter": "dc1",
"data_dir": "/opt/consul/data",
"log_level": "INFO",
"node_name": "consul-client-1",
"server": false,
"bind_addr": "0.0.0.0",
"client_addr": "0.0.0.0",
"advertise_addr": "192.168.1.73",
"ui": false,
"retry_join": ["192.168.1.71", "192.168.1.72"],
"ports": {
"dns": 8600,
"http": 8500,
"serf_lan": 8301,
"server": 8300
}
}
EOF
# 3. 启动 Consul Client
docker run -d \
--name=consul-client-1 \
--restart=always \
--net=host \
-v /opt/consul/data:/consul/data \
-v /etc/consul:/consul/config \
consul:1.15 agent \
-config-dir=/consul/config \
-node=consul-client-1 \
-bind=192.168.1.73 \
-advertise=192.168.1.73 \
-client=0.0.0.0 \
-datacenter=dc1 \
-retry-join=192.168.1.71
# 在任意节点执行 curl -s http://192.168.1.71:8500/v1/agent/members | jq . curl -s http://192.168.1.71:8500/v1/catalog/nodes | jq . # 访问 Web UI Consul Web UI: http://192.168.1.71:8500
在 Docker 中,Overlay 网络依赖于 Swarm 控制平面来分发网络配置到各节点,因此必须先初始化 Swarm。
# 1️⃣ 在当前节点(consul-server-1)初始化 Swarm. --advertise-addr指定该节点对外通告的 IP(必须是其他节点能访问的地址)。 docker swarm init --advertise-addr 192.168.1.71 # 2️⃣ 在其他节点加入 Swarm, # 测试只有加入manager时其它两个节点才会同步网络(worker不会),我也不知道为什么。 # 在 consul-server-2和 consul-client-1分别执行上一步输出的 docker swarm join manager...命令。 # 如果忘记了token,可以在manager节点查看: # 查看加入命令(worker) docker swarm join-token worker # 查看加入命令(manager) docker swarm join-token manager # 在 consul-server-2 执行 docker swarm join --token SWMTKN-1-xxxxxx 192.168.1.71:2377 # 在 consul-client-1 执行 docker swarm join --token SWMTKN-1-xxxxxx 192.168.1.71:2377 # 3️⃣ 验证 Swarm 状态 # 在 manager 节点(consul-server-1): [root@consul-server-1 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION qxcnxi779r3wvp43hvkhusipo consul-client-1 Ready Active Reachable 29.1.3 jn39c7awwa5l50tqccbdxjy6a * consul-server-1 Ready Active Leader 29.1.3 orc7648vp80ttrg1xzdk28obg consul-server-2 Ready Active Reachable 29.1.3
# 创建 Overlay 网络 docker network create \ --driver overlay \ --attachable \ --subnet 10.20.0.0/16 \ consul-overlay-net # 查看网络 docker network ls docker network inspect consul-overlay-net
任何客户端节点创建网络,其他客户端会通过consul来实时同步配置
# 在 consul-server-2 和 consul-client-1 上执行 docker network ls | grep overlay # 应该能看到 consul-overlay-net # 如果还是看不到,尝试刷新 Docker 网络 systemctl restart docker # 或者 docker swarm update --task-history-limit 5
# 1. 运行 Web 服务
docker run -d \
--name web-service-1 \
--network consul-overlay-net \
--label "consul=true" \
--label "service=web" \
--label "version=1.0" \
-p 8080:80 \
nginx:alpine
# 2. 注册服务到 Consul
docker exec consul-server-1 sh -c '
cat > /tmp/web-service.json << EOF
{
"ID": "web-service-1",
"Name": "web",
"Tags": ["nginx", "production"],
"Address": "$(hostname -i)",
"Port": 80,
"Check": {
"HTTP": "http://$(hostname -i):80",
"Interval": "10s",
"Timeout": "5s"
}
}
EOF
curl -X PUT http://127.0.0.1:8500/v1/agent/service/register -d @/tmp/web-service.json
'
# 1. 运行 Web 服务
docker run -d \
--name web-service-2 \
--network consul-overlay-net \
--label "consul=true" \
--label "service=web" \
--label "version=1.0" \
nginx:alpine
# 2. 注册服务到 Consul
WEB2_IP=$(docker inspect web-service-2 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
cat > /tmp/web-service-2.json << EOF
{
"ID": "web-service-2",
"Name": "web",
"Tags": ["nginx", "production"],
"Address": "$WEB2_IP",
"Port": 80,
"Check": {
"HTTP": "http://$WEB2_IP:80",
"Interval": "10s",
"Timeout": "5s"
}
}
EOF
curl -X PUT http://192.168.1.72:8500/v1/agent/service/register -d @/tmp/web-service-2.json
# 1. 运行 API 服务(用 nginx 模拟)
docker run -d \
--name api-service-1 \
--network consul-overlay-net \
--label "consul=true" \
--label "service=api" \
--label "version=1.0" \
nginx:alpine
# 2. 注册 API 服务
API_IP=$(docker inspect api-service-1 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
cat > /tmp/api-service.json << EOF
{
"ID": "api-service-1",
"Name": "api",
"Tags": ["rest", "backend"],
"Address": "$API_IP",
"Port": 80,
"Check": {
"HTTP": "http://$API_IP:80",
"Interval": "10s",
"Timeout": "5s"
}
}
EOF
curl -X PUT http://192.168.1.73:8500/v1/agent/service/register -d @/tmp/api-service.json
# 在 consul-server-1 上测试 docker exec web-service-1 ping -c 3 api-service-1 # 应该成功 # 在 consul-client-1 上测试 docker exec api-service-1 ping -c 3 web-service-1 # 应该成功
# 查看所有服务 curl -s http://192.168.1.71:8500/v1/catalog/services | jq . # 查看 web 服务详情 curl -s http://192.168.1.71:8500/v1/catalog/service/web | jq . # 查看健康的服务实例 curl -s "http://192.168.1.71:8500/v1/health/service/web?passing=true" | jq . # 查看集群状态 curl -s http://192.168.1.71:8500/v1/status/leader curl -s http://192.168.1.71:8500/v1/status/peers # 查看健康检查 curl -s http://192.168.1.71:8500/v1/health/state/critical
目标:实现 Docker 容器跨主机直接路由通信,无需 Overlay 网络
优点:高性能、无 NAT、支持网络策略、生产验证
后面到k8s再讲。