Flannel

76次阅读
没有评论

共计 4569 个字符,预计需要花费 12 分钟才能阅读完成。

Flannel-UDP 在 kubernetes 中如何工作,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

Kubernetes 是用于大规模管理容器化应用程序出色的编排工具。但是,您可能知道,使用 kubernetes 并非易事,尤其是后端网络实现。我在网络中遇到了许多问题,花了我很多时间弄清楚它是如何工作的。

我想以最简单的实现为例,来解释 kubernetes 的网络工作。

Kubernetes 网络模型

下图显示了 kubernetes 集群的简单图像:

kubernetes 中的 pod

Kubernetes 管理 Linux 机器集群(可能是 ECS 之类的云 VM 或物理服务器),在每台主机上,kubernetes 运行任意数量的 Pod,在每个 Pod 中可以有任意数量的容器。用户的应用程序正在这些容器之一中运行。

对于 kubernetes,Pod 是最小的管理单元,并且一个 Pod 中的所有容器共享相同的网络名称空间,这意味着它们具有相同的网络接口并且可以使用 *localhost* 相互连接

在官方文件中说 kubernetes 网络模式要求:

所有容器无需 NAT 即可与所有其他容器通信所有节点都可以与所有容器通信(反之亦然),而无需 NAT 容器所看到的 IP 其他人所看到的 IP 一样

我们可以按照上述要求将所有容器替换为 Pod,因为容器与 Pod 网络共享。

基本上,这意味着所有 Pod 都应该能够与群集中的其他 Pod 自由通信,即使它们位于不同的主机中,并且它们也使用自己的 IP 地址相互识别,就像基础主机不存在一样。此外,主机也应该能够使用自己的 IP 地址与任何 Pod 通信,而无需任何地址转换。

Kubernetes 不提供任何默认的网络实现,而是仅定义模型,并由其他工具来实现。如今有很多实现,Flannel 是其中之一,也是最简单的之一。在以下各节中,我将解释 Flannel 的 UDP 模式实现。

 The Overlay Network

Flannel 是由 CoreOS 创建的,用于 Kubernetes 网络,也可以用作其他目的的通用软件定义网络解决方案。

为了满足 kubernetes 的网络要求,flannel 的想法很简单:创建另一个在主机网络之上运行的扁平网络,这就是所谓的覆盖网络 overlay network。在此覆盖网络中,所有容器(Pod)将被分配一个 IP 地址,它们通过直接调用彼此的 IP 地址来相互通信。

为了帮助解释,我在 AWS 上使用了一个小型的测试 kubernetes 集群,该集群中有 3 个 Kubernetes 节点。网络如下所示:

flannel network

此群集中有三个网络:

AWS VPC 网络:所有实例都在一个 VPC 子网中 172.20.32.0/19。它们已经在此范围内分配了 ip 地址,所有主机都可以彼此连接,因为它们位于同一 LAN 中。

Flannel overlay network:flannel 创建了另一个网络 100.96.0.0/16,它是一个更大的网络,可以容纳 65536 个地址,并且遍及所有 kubernetes 节点,将在此范围内为每个 Pod 分配一个地址,稍后我们将看到 flannel 如何实现此目的。

主机内 docker 网络:在每个主机内部,flannel 为该主机中的所有 pod 分配了一个网络 100.96.x.0/24,它可以容纳 256 地址。docker 桥接接口 docker0 将使用此网络创建新容器。

通过这种设计,每个容器都有其自己的 IP 地址,都属于覆盖子网 100.96.0.0/16。同一主机内的容器可以通过 docker bridge 网络接口 Docker0 相互通信,这很简单,因此在本文中我将跳过。为了在主机上与覆盖网络中的其他容器进行跨主机通信,flannel 使用内核路由表和 UDP 封装来实现该功能,以下各节对此进行了说明。

  跨主机容器通信

假设具有 IP 地址的节点 1 中的容器(我们将其称为容器 1)100.96.1.2 要使用 IP 地址连接到节点 2 中的容器(我们将其称为容器 2)100.96.2.3,让我们看看覆盖网络如何启用数据包通过。

跨主机通信

第一个 container- 1 使用创建一个 IP 数据包 src: 100.96.1.2 – dst: 100.96.2.3,该数据包将作为容器的网关进入 docker0 网桥。

在每个主机中,flannel 运行一个名为的守护进程 flanneld,它在内核的路由表中创建一些路由规则,这是节点 1 的路由表的样子:

admin@ip-172-20-33-102:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.33.102

 

如我们所见,数据包的目标地址 100.96.2.3 位于更大的覆盖网络中 100.96.0.0/16,因此它与第二条规则匹配,现在内核知道应该将数据包发送到 flannel0。

flannel0TUN 是由我们的 flanneld 守护进程创建的 TUN 设备,TUN 是在 Linux 内核中实现的软件接口,它可以在用户程序和内核之间传递原始 ip 数据包。它在两个方向上起作用:

将 IP 数据包写入
    flannel0 设备时,该数据包将直接发送到内核,内核将根据其路由表对数据包进行路由当 IP 数据包到达内核,并且路由表说应该将其路由到
    flannel0 设备时,内核会将数据包直接发送到创建该设备的
    flanneld 进程,该进程是守护进程。

当内核将数据包发送到 TUN 设备时,它将直接进入 flanneld 进程,它看到目标地址为 100.96.2.3,尽管从图中可以看出该地址属于在 Node 2 上运行的容器,但是如何 flanneld 知道呢?

Flannel 碰巧将某些信息存储在名为 etcd 的键值存储服务中,如果您知道 kubernetes,则不应感到惊讶。在 flannel,我们可以将其视为常规键值存储。

Flannel 将子网映射信息存储到 etcd 服务中,我们可以使用以下 etcdctl 命令查看它:

admin@ip-172-20-33-102:~$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24
admin@ip-172-20-33-102:~$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{PublicIP : 172.20.54.98}

 

因此,每个 flanneld 进程查询 etcd 都知道每个子网属于哪个主机,并将目标 ip 地址与 etcd 中存储的所有子网密钥进行比较。在本例中,该地址 100.96.2.3 将与子网匹配 100.96.2.0-24,并且如我们所见,存储在此键中的值表示 Node ip 为 172.20.54.98。

现在 flanneld 知道了目的地址,然后将原始 IP 数据包包装到 UDP 数据包中,以其自己的主机 ip 作为源地址,而目标主机的 IP 作为目的地址。在每个主机中,该 flanneld 进程将侦听默认的 UDP 端口:8285。因此,只需要将 UDP 数据包的目标端口设置为 8285,然后通过网络发送它。

UDP 数据包到达目标主机后,内核的 IP 堆栈会将数据包发送到 flanneld 进程,因为那是用户进程在 UDP 端口上侦听:8285。然后 flanneld 将获得 UDP 数据包的有效负载,该数据包是由原始容器生成的原始 IP 数据包,只需将其写入 TUN 设备 flannel0,然后该数据包将直接传递到内核,这就是 TUN 的工作方式。

与节点 1 相同,路由表将决定此数据包的去向,让我们看一下节点 2 的路由表:

admin@ip-172-20-54-98:~$ ip route
default via 172.20.32.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.2.0
100.96.2.0/24 dev docker0 proto kernel scope link src 100.96.2.1
172.20.32.0/19 dev eth0 proto kernel scope link src 172.20.54.98

 

IP 数据包的目标地址是 100.96.2.3,内核将采用最精确的匹配,这是第三条规则。数据包将发送到 docker0 设备。就像 docker0 桥接设备一样,此主机中的所有容器都连接到该桥接器,最终目的地容器 2 将看到并接收到该数据包。

最终,我们的数据包完成了一种传递到目标的方式,当 contianer- 2 将数据包发送回容器 1 时,反向路由将以完全相同的方式工作。这就是跨主机容器通信的工作方式。

  使用 Docker 网络进行配置

在以上解释中,我们遗漏了一点。这就是我们如何配置 docker 使用较小的子网 100.96.x.0/24?

碰巧 flanneld 会将其子网信息写入主机中的文件中:

admin@ip-172-20-33-102:~$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=100.96.0.0/16
FLANNEL_SUBNET=100.96.1.1/24
FLANNEL_MTU=8973
FLANNEL_IPMASQ=true

 

该信息将用于配置 docker 守护程序的选项,因此 docker 可以将 FLANNEL_SUBNET 用作其桥接网络,然后主机容器网络将起作用:

dockerd --bip = $ FLANNEL_SUBNET --mtu = $ FLANNEL_MTU

 
  数据包复制和性能

较新版本的 flannel 不建议将 UDP 封装用于生产,它表示仅应将其用于调试和测试目的。原因之一是性能。

尽管 flannel0TUN 设备提供了一种通过内核获取和发送数据包的简单方法,但它会降低性能:必须将数据包从用户空间来回复制到内核空间:

封包复制

如上所述,必须从原始容器进程发送数据包,然后在用户空间和内核空间之间复制 3 次,这将显着增加网络开销,因此,如果可以的话,应避免在生产中使用 UDP。

Flannel 是 kubernetes 网络模型的最简单实现之一。它使用现有的 Docker 网络和带有守护进程的额外 Tun 设备进行 UDP 封装。我解释了核心部分的详细信息:跨主机容器通信,并简要提到了性能损失。

看完上述内容,你们掌握 Flannel-UDP 在 kubernetes 中如何工作的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注丸趣 TV 行业资讯频道,感谢各位的阅读!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计4569字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)