在做与容器相关的网络时,overlay网络是大多数事情的核心。

在现实世界中,容器之间能彼此可靠和安全的通信是非常重要的,甚至他们在不同的网络中的不同主机上。这就是overlay网络到来的原因。它允许你创建一个扁平的、安全的、二层网络,来连接多个主机。连接到此网络的主机可直接通信。

Docker提供了原生overlay网络的支持,配置简单且安全。在其背后 ,其构建于libnetwork和drivers之上。

libnetwork是Container Network Model的典型实现,drivers是可插拔组件,可以用不同的网络技术和拓扑来实现。Docker提供了原生驱动 ,诸如overlay driver,第三方也提供了drivers。

隐藏在简单命令背后的是大量可移动的组件。在部署到生产环境和尝试解决问题之前需要了解这部分内容。

在swarm模式下构建和测试一个Docker  overlay网络

在下面的例子中,需要2个Docker主机,在两个分离的二层网络 ,通过路由器进行连接,如下图 所示:

docker compose 用overlay网络 docker网络模式overlay_网络

1)构建swarm

我们要做的第一件事是把两台主机 配置成两节点的swarm。在node1上运行命令:docker swarm init,使得node1成为manager,然后在node2上运行命令:docker swarm join,使得node2成为worker。

在node1上运行命令:

$ docker swarm init \

--advertise-addr=172.31.1.5 \

--listen-addr=172.31.1.5:2377

Swarm initialized: current node (1ex3...o3px) is now a manager.

在node2上运行命令:

$ docker swarm join \

--token SWMTKN-1-0hz2ec...2vye \

172.31.1.5:2377

This node joined a swarm as a worker.

至此,已经创建好了包含包含node1和node2两个节点的swarm。
2)创建一个overlay网络

创建一个叫做uber-net的overlay网络。
在node1(manager)上运行如下命令:docker network create -d overlay uber-net

通过上述命令创建了一个新的overlay网络,其对Swarm中的所有主机是可用的,还包含一个用TLS加密的控制平面。如果想加密数据平面,需要在命令中增加参数:-o encrypted。

可以使用 docker network ls命令来查看每一个节点上的网络。

docker compose 用overlay网络 docker网络模式overlay_网络_02

我们创建的uber-net网络在列表的低端。其他的网络当Docker安装的时候和初始化Swarm的时候创建。

如果在node2运行  docker network ls 命令,你将注意你不能看见uber-net网络。这是因为只有当运行中的容器连接到网络时,新网络才对worker节点可见。这种延迟方法通过减少网络数量提高了网络的可扩展性。
3)将服务连接到overlay网络

现在我们有一个overlay网络,让我们创建一个新的Docker服务并连接到overlay网络。我们将创建的服务包含两个副本(容器),因此一个可以运行在node1和另一个可以运行在node2。这将会自动化地扩展uber-net网络到node2.

在node1上执行如下命令:

docker service create --name test --network uber-net --replicas 2 ubuntu sleep infinity

创建了一个名为test的服务,并连接到uber-net网络,基于提供的镜像创建了2个副本(容器)。在上面的例子中,我们向容器发送了sleep命令保持容器运行,并且在停止后退出。

因为我们运行了2个副本(容器),Swarm有2个节点,在每一个节点上将会有一个副本。可以使用 docker service ps查看,如下所示:

docker compose 用overlay网络 docker网络模式overlay_docker_03

当Swarm在一个overlay网络上启动一个容器时,它自动把网络扩展到运行容器的节点上。这意味着在node2上可以看见uber-net网络。

4)测试overlay网络

现在用ping命令来测试overlay网络。

如下图所示,我们在分离的网络上得到2个Docker主机,二者都接入了单一的overlay网络。在每一个节点上都有一个容器连接到overlay网络。可以用ping命令来查看两个容器之间是否连通。

docker compose 用overlay网络 docker网络模式overlay_docker_04

为了执行测试,我们需要 知道每一个容器的IP地址,通过运行docker network inspect可以看到分配给overlay的子网

docker compose 用overlay网络 docker网络模式overlay_操作系统_05

 

上面是输出显示uber-net’s 的子网是 10.0.0.0/24。注意,这并不匹配任何有一个物理基础网络(172.31.1.0/24 和192.168.1.0/24).

在节点1和节点2上运行如下命令,这将会得到容器的ID和IP地址。

docker compose 用overlay网络 docker网络模式overlay_运维_06


最终的uber-net’s 的信息如下图所示:

docker compose 用overlay网络 docker网络模式overlay_docker_07

我们可以看到,二层overlay网络横跨两个主机,在overlay网络上每一个容器都有一个IP地址。这意味着node1上的容器可以使用10.0.0.4来ping node2上的容器。尽管这两个节点位于不同的二层基础网络,但是还可以ping通。

在node1上登录进容器,并ping远程容器。

在Linux Ubuntu 容器中需要安装ping工具包。如下所示:

docker compose 用overlay网络 docker网络模式overlay_操作系统_08

使用overlay网络,在node1上的容器可以ping通node2上的容器。也可以在容器内追踪ping命令的路由。

docker compose 用overlay网络 docker网络模式overlay_docker_09

到目前为止,我们已经通过单条命令创建了overlay网络,并向网络中接入了容器。容器分布在两个不同的主机上,两台主机分属于不同的二层网络。在找出两台容器的 IP 之后,验证了容器可以通过overlay网络直接连接。

工作原理

现在,我们已经知道如何构建和使用一个容器overlay网络。让我们找出背后的工作原理。

1)VXLAN 入门

首先,Docker overlay网络使用VXLAN隧道创建虚拟的二层overlay网络。在进行下一步之前,我们先快速了解VXLAN入门。

在最高层次上,VXLAN允许在已存在的3层基础设施上创建一个虚拟的2层网络。我们前面使用的例子创建了一个新的10.0.0.0/24二层网络,其基于3层IP网络,3层IP网络由2个2层网络组成-172.31.1.0/24 和192.168.1.0/24,如下图所示:

docker compose 用overlay网络 docker网络模式overlay_操作系统_10

VXLAN的美妙之处在于它是一种封装技术,使得已存在的路由和网络基础设施看起来像普通的IP/UDP数据包。处理起来无问题。

为了创建虚拟2层网络,通过基础3层IP基础设施创建了一个VXLAN隧道。你也许听过基础网络,常用于指3层以下的基础部分。

VXLAN隧道的每一端都有VXLAN岁宝端点(VTEP)。这个VTEP执行封装和解封装以及一些功能所必须的操作。如下图:

docker compose 用overlay网络 docker网络模式overlay_docker_11

2)梳理两容器例子
在这个例子中,我们有两个通过IP网络连接的主机。每一个主机运行一个单一容器,我们创建了一个单一的VXLAN网络并让容器连接到它。

为了完成这个,在每一个主机上创建一个新的sandbox。如前述章节所述,一个sandbox像一个容器,但是代替运行一个运行程序,它运行一个隔离的网络栈

在sandbox内部创建了一个虚拟交换机(又叫虚拟网桥)叫做Br0,同时也创建了一个VTEP,一端插入到Br0虚拟交换机,另一端插入到主机网络栈(VTEP)

主机网络栈的一端从主机所连接的基础网络中获取IP地址,并以UDP的方式绑定到端口4789,每一个主机上的两个VTEPs通过VXLAN创建了一个overlay。如下所示:

docker compose 用overlay网络 docker网络模式overlay_运维_12

这是VXLAN overlay网络的基础。

每个容器都得到它自己的虚拟Ethernet(veth)适配器,并插入到本地Br0虚拟交换机。拓扑结构如下所示。

docker compose 用overlay网络 docker网络模式overlay_操作系统_13


3)通信例子

现在,我们看到了主要的插拔元素,让我们看两个容器的通信。

我们把node1上的容器称为C1,把node2上的容器称为C2,假定C1想ping通C2.如下图所示:

docker compose 用overlay网络 docker网络模式overlay_docker_14

C1创建ping请求,并设置目的IP地址为C2的10.0.0.4地址。通过连接到Br0虚拟交换机的veth接口发送流量。虚拟交互机并不知道向什么地方发送packet,因为在它的MAC地址表里 没和目的IP地址对应的任何入口 。因此,它将数据包泛洪到所有的端口。连接到Br0的VTEP接口知道如何转发这个帧,因此用它自己的MAC地址响应。这是一个ARP代理重放。并且Br0交换机学习到了如何转发packet。因此它更新ARP路由表,将10.0.0.4映射到本地VTEP的MAC地址上。

 

现在 Br0 虚拟交换机已经学会如何将流量转发到 C2,所有未来发送到 C2 的包都会被直接转发到 VTEP 接口。VTEP 接口知道 C2,是因为所有新启动的容器都会将自己的网络详情采用网络内置 Gossip 协议发送给相同 Swarm 集群内的其他节点。
交换机会将包转发到 VTEP 接口,VTEP 完成数据帧的封装,这样就能在底层网络传输。具体来说,封装操作就是把 VXLAN Header 信息添加以太帧当中。
VXLAN Header 信息包含了 VXLAN 网络 ID(VNID),其作用是记录 VLAN 到 VXLAN 的映射关系。每个 VLAN 都对应一个 VNID,以便包可以在解析后被转发到正确的 VLAN。
封装的时候会将数据帧放到 UDP 包中,并设置 UDP 的目的 IP 字段为 node2 节点的 VTEP 的 IP 地址,同时设置 UDP Socket 端口为 4789。这种封装方式保证了底层网络即使不知道任何关于 VXLAN 的信息,也可以完成数据传输。
当包到达 node2 之后,内核发现目的端口为 UDP 端口 4789,同时还知道存在 VTEP 接口绑定到该 Socket。所以内核将包发给 VTEP,由 VTEP 读取 VNID,解压包信息,并根据 VNID 发送到本地名为 Br0 的连接到 VLAN 的交换机。在该交换机上,包被发送给容器 C2。
Docker 支持使用同样的覆盖网络实现三层路由。例如,读者可以创建包含两个子网的覆盖网络,Docker 会负责子网间的路由。创建的命令如 docker network create --subnet=10.1.1.0/24 --subnet=11.1.1.0/24 -d overlay prod-net。该命令会在 Sandbox 中创建两个虚拟交换机,默认支持路由。