Cilium netkit:容器网络性能的最后前沿

边际收益理论认为 ,微小但显著的改进可以带来巨大的成果。这种方法被应用于英国自行车队的训练,使他们在最近的奥运会赛事中取得了巨大的成功。Cilium 相继推出的网络增强功能是“边际收益”的另一个案例研究。就像自行车训练方案能够最大限度地发挥运动员的身体和精神表现一样,Cilium 引入的每一项改进——XDP BPF路由 BIG TCP等……——都旨在从网络中获得尽可能多的性能。

在现代网络中,无论您是否运行耗费大量带宽且对延迟敏感的 AI 工作负载,每一毫秒都很重要。

Cilium netkit 是 Cilium 的最新功能,于 1.16 版本中推出,旨在优化容器网络性能。Cilium 是第一个提供对netkit内置支持的公共项目,netkit 是 Linux 网络设备,于 6.7 内核版本中引入,由 Isovalent 工程师开发。

Netkit 已经显示出显著的改进:

好消息,我们已经完成了概念验证 (PoC),与 veth 相比,netkit 的 CPS (每秒连接数) 提高了 12%(太不可思议了!)。现在我们计划尽可能多地在我们的 DC 中使用 netkit。字节跳动云计算开发人员汤晨

Cilium netkit 的目标是实现原本无法实现的目标:主机网络和容器网络之间的网络性能匹配

有接近吗?让我们来一探究竟。

高性能网络专家

为了让 Kubernetes 集群获得最佳网络性能,请与 eBPF 和 Isovalent 的创建者合作。请求演示

容器网络之前的一系列妥协

多年来,容器化和云原生架构的优势已经变得如此明显,以至于我们往往会忘记在此过程中做出的妥协。很简单——抽象是有代价的。虚拟机的性能永远不可能像裸机那样快,但这是一个可以接受的权衡:我们知道虚拟化提供了显着的可扩展性和灵活性优势。

容器化和 Kubernetes 也是如此:现在,我们都了解它们为基础设施工程师和应用程序开发人员带来的好处。但为了支持这种模式,我们不得不在此过程中做出一些让步,其中最严重的就是网络性能。

这些限制的影响是容器网络性能和主机网络性能之间的性能大幅下降了 35% 。

使用 Cilium 1.16,性能损失现在可以被消除。

让我们回顾一下标准 Kubernetes 网络数据路径架构的一些限制以及 Cilium 如何解决这些限制,最后介绍 Cilium 1.16 引入的最新变化(如果您已经熟悉 Cilium,可以直接跳到新内容)。

障碍#1:Kube-Proxy 和 iptables

在原始 Kubernetes 部署中,Kube-Proxy 将通过 iptables 或 ipvs 系统处理 NAT 和服务负载平衡。我们在之前的博客文章中详细讨论了 Kube-Proxy 的局限性,这里无需重复,尤其是当iptables 创建者本人认为 eBPF 更合适时

简而言之:Kube-Proxy 对 iptables 的依赖(一种不适合 Kubernetes 的变动和规模的技术)增加了显著的开销,如下图所示。

捷径 #1:基于 eBPF 的 Kube-Proxy 替代品

再次强调,Jeremy关于 kube-proxy 替代品的优秀博客文章应该会告诉您所有您需要了解的关于为什么应该使用 Cilium 基于 eBPF 的 kube-proxy 替代品的信息。阅读它以了解更多背景信息,但一图胜千言,请将 Cilium 基于 eBPF 的 Kube-Proxy 替代品与前一个替代品的简单性进行比较:

eBPF 的 kube-proxy 替代品的性能明显优于原来的产品,特别是在规模方面:

eBPF kube-proxy 替代品现在是 Cilium 的基础方面:它也是下面突出显示的调整功能的要求。

障碍 #2:上层堆栈转发

当流量离开 Pod 时,通常需要将Pod 的源 IP伪装为其离开的主机上的 IP。

伪装

强制执行这种形式的网络转换传统上是通过将数据包发送到网络堆栈上,以便netfilter子系统更改 IP 地址来完成的。鉴于伪装需要连接跟踪器查看来自两个方向的流量以避免无效连接导致的丢包,来自主机网卡的返回入口流量也必须发送到网络堆栈上。

伪装还需要咨询内核的路由层以了解下一跳地址/接口并填充目标/源 MAC 地址。

上层堆栈转发导致孤立套接字

总结一下,伪装流量会导致内部网络堆栈处理出站和入站流量。这会带来不必要的延迟和处理,但还不止于此:它会破坏 TCP 流量控制机制。在将流量转发到堆栈上时,数据包的套接字关联在主机堆栈的转发层中变为孤立状态。

换句话说,即使数据包仍在由内部系统处理,系统也会通知应用程序流量已离开节点。这意味着应用程序可能没有意识到系统何时拥塞,从而影响 TCP 背压过程并最终导致缓冲区膨胀并对性能产生重大影响(我在之前关于另一个网络性能功能 – BBR 的博客文章中写到了缓冲区膨胀):

捷径 #2:基于 eBPF 的主机路由

考虑到转向上层网络堆栈会造成多大影响,Cilium 和 eBPF 开发人员考虑实施基于 eBPF 的主机路由,以便为离开和进入 Pod 的数据包提供更直接的路由。他们创建了两个 Linux 内核 BPF 助手 –bpf_redirect_peer()bpf_redirect_neigh()– 并在 Cilium 代码库中进行了适当的更改,并在Cilium 1.9中发布了基于 eBPF 的主机路由。

和 分别转发来自 Pod 和到 Pod 的流量,避免将数据包推送到网络堆栈bpf_redirect_neigh()bpf_redirect_peer()

bpf_redirect_peer()确保物理 NIC 可以一次性将数据包推送到位于不同 Pod 命名空间中的应用程序套接字中。

bpf_redirect_neigh()将解析下一跳的 L2 地址并将数据包重定向到 NIC 驱动程序,同时保持数据包的套接字关联。这可以避免出现孤立套接字并确保 TCP 堆栈有适当的背压。

网络性能的提升是巨大的。

不过,正如您在橙色条(“veth + BPF 主机路由”)和黄色条(“主机(基线/最佳情况)”)之间看到的,仍然存在明显的性能差距。

我们还能做什么来关闭它?

障碍 #3:TCP 数据包太小

100+Gbps 网络接口卡的推出产生了意想不到的后果:CPU 如何处理每秒 800 万个数据包(假设 MTU 为 1,538 字节)?

正如我在 Cilium 博客文章 BIG TCP中深入解释的那样,减少数据包数量的一种方法就是将它们分组在一起。虽然 GRO(通用接收卸载)和 TSO(传输分段卸载)可以将数据包批量处理在一起,但由于 IP 标头中的长度字段,我们受到64K数据包大小的限制。

如果我们可以将数据包分组为更大的数据包会怎么样?

捷径#3:BIG TCP

借助 Cilium 上的 BIG TCP,我们现在可以将 TCP 数据包组合成一个超大的192K数据包;减少对 CPU 的影响并显著提高性能。

再次,吞吐量测试的结果非常惊人:

了解有关BIG TCP 背后的历史以及如何在 Cilium 上使用它的更多信息,或者在BIG TCP 实验室中亲自测试它。

障碍4:传统的虚拟电缆

大多数容器网络接口 (CNI) 插件(包括 Cilium 1.15 版本之前版本)都会通过虚拟以太网设备 ( veth ) 将 Kubernetes Pod 附加到其托管的节点。veth通常将网络命名空间中的 Pod 连接到主机网络命名空间中的节点,这也是容器可以访问其他容器或外部工作负载的方式。

让我们查看集群中的 Pod(运行 Cilium 1.15)并检查其网络接口。

# ip a show eth0
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 9a:f9:bd:42:0c:ae brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.2.114/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::98f9:bdff:fe42:cae/64 scope link 
       valid_lft forever preferred_lft forever

# ip -d l
[...]
11: lxc3c34280cf99e@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 86:fe:01:80:08:ac brd ff:ff:ff:ff:ff:ff link-netns cni-8ba2017a-96bd-f18d-d09a-8e48c9284121 promiscuity 0  allmulti 0 minmtu 68 maxmtu 65535 
    veth addrgenmode eui64 numtxqueues 8 numrxqueues 8 gso_max_size 65536 gso_max_segs 65535 tso_max_size 524280 tso_max_segs 65535 gro_max_size 65536

您可以在上面的输出中看到eth0,通过虚拟以太网(veth)接口与if11另一个命名空间中主机上的另一个接口配对。

当我们检查节点时,我们可以看到虚拟以太网配对的另一端:

11: lxcfd04d472bcde@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 2e:f9:fc:35:d1:95 brd ff:ff:ff:ff:ff:ff link-netns cni-f24a815a-65e8-fc9e-be8e-c62d419c8b6f
    inet6 fe80::2cf9:fcff:fe35:d195/64 scope link 
       valid_lft forever preferred_lft forever

虽然它被广泛采用为 Linux 容器网络技术,但它是 15 年前在Linux 内核 2.6.24中引入的技术,并且存在一些缺点。

第一个是veth依赖第 2 层通信和 ARP 来让容器与 veth 对的另一端进行通信。这是一个人为的、不必要的步骤:Pod 不应该进行 ARP 解析。

更重要的是,veth 会带来性能损失。当流量离开 Pod 前往另一个节点或节点外流量进入 Pod 时,数据必须首先进入容器内的网络堆栈、切换命名空间、在主机命名空间上进行处理,然后才能发送并由网络接口​​处理以进行重新传输。这看起来可能无害,但整个过程(包括通过每个 CPU 的积压队列发送数据包)在压力下可能会变得代价高昂。

捷径#4:引入 netkit

Isovalent 工程师 Daniel Borkmann 和 Nikolay Aleksandrov最近Netkit引入 Linux 内核 (6.7)。Daniel 领导了本篇博文中重点介绍的许多改进,并在最近的 KubeCon 演讲中介绍了这些改进:

https://youtube.com/watch?v=AVEBcZc0YsQ%3Ffeature%3Doembed

Netkit 基于一个原创概念:如果我们可以将 BPF 程序直接加载到 Pod 中并使其更接近源头,会怎么样?

一个主要的好处是能够更早地做出网络决策。例如,对于发往节点外工作负载的 Pod 出口流量,netkit 会重定向到物理设备,而无需经过每个 CPU 的积压队列。

这是性能显著提升的原因之一,正如您将在后面的图表中看到的。

在第一次迭代中,netkit 设备成对发货,包括主设备和对等设备。主设备通常位于主机命名空间中,而其对等设备则位于 Pod 的命名空间内。

只有主设备才能管理自身及其对等设备的 BPF 程序。这是 Cilium netkit 带来的另一个好处:Pod 内的任何人都无法删除 BPF 程序。由于 Cilium 是 Pod 上运行的 BPF 程序的所有者,因此它还可以防止 BPF 程序相互冲突的情况,或者更糟的是,一个基于 eBPF 的应用程序卸载另一个应用程序安装的 BPF 程序。

Cilium 是第一个受益于 netkit 的公共项目,它将为 Pod 设置 netkit 设备,而不是 veth。Cilium netkit 将默认支持 L3,从而消除 ARP 引入的延迟和管理开销(L2 也是一个受支持的选项)。

Netkit 实践:详细了解如何替换 veth

现在让我们测试一下。首先,让我们安装 Cilium 1.16 并netkit启用该选项:

$ cilium install --version 1.16.0-rc.1 --namespace kube-system \
--set routingMode=native \
--set bpf.masquerade=true \
--set kubeProxyReplacement=true \
--set ipam.mode=kubernetes \
--set autoDirectNodeRoutes=true \
--set ipv4NativeRoutingCIDR="10.0.0.0/8" \
--set bpf.datapathMode=netkit

让我们再检查一下 Cilium netkit 是否已启用:

$ kubectl exec -n kube-system -it -c cilium-agent cilium-2qjjb -- cilium status | grep "Device Mode"
Device Mode:            netkit

让我们bpftool在 Cilium 代理上运行,看看哪些 eBPF 程序已加载。我们可以看到,netkit我们的容器虚拟设备已加载程序(tcx最近对用于其他附件的 tc BPF 数据和控件进行了重新设计):

$ kubectl exec -n kube-system -it -c cilium-agent cilium-2qjjb -- bpftool net
xdp:

tc:
cilium_net(2) tcx/ingress cil_to_host prog_id 3002 link_id 42 
cilium_host(3) tcx/ingress cil_to_host prog_id 2985 link_id 40 
cilium_host(3) tcx/egress cil_from_host prog_id 2986 link_id 41 
eth0(9) tcx/ingress cil_from_netdev prog_id 3006 link_id 43 
eth0(9) tcx/egress cil_to_netdev prog_id 3012 link_id 44 
lxc07d02daab9b3(108) netkit/peer cil_from_container prog_id 4416 link_id 207 
lxc_health(110) netkit/peer cil_from_container prog_id 4429 link_id 208 

flow_dissector:

netfilter:

让我们获取有关网络接口的更多详细信息:

$ kubectl exec -it pod-worker -- ip -d l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 allmulti 0 minmtu 0 maxmtu 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 524280 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 
107: eth0@if108: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 
    netkit mode l3 type peer policy blackhole numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 524280 tso_max_segs 65535 gro_max_size 65536 gso_ipv4_max_size 65536 gro_ipv4_max_size 65536 

与运行 Cilium 1.15 时的早期输出相比,显著的区别是:

  • netkit用来代替veth
  • mode l3现已使用,取代了 L2,并且不再需要 ARP
  • peer policy blackhole意味着如果没有附加 BPF 程序,则任何时候都不会有任何流量泄漏出 Pod。

现在让我们回顾一下最终的性能测试(您可以在Daniel 的 KubeCon 幻灯片中找到有关如何运行它们的更多信息)。首先,延迟;我们现在将 Pod 到 Pod 的延迟降低到与主机到主机一样低。

吞吐量也是一样 – TCP 上的性能同样高。

现在,我们已经达到了性能平等。让我们最后一次回顾一下我们是如何实现这一目标的:

局限性笔记解决方案
限制 1:基于 iptables 的 Kube-ProxyKube-proxy 依赖于 iptables——一种不适合 Kubernetes 的规模和流失率的技术。使用基于eBPF的高性能kube-proxy替代品
限制 #2:上层堆栈转发来自 Pod 的流量必须通过网络堆栈转发,从而导致额外的延迟并影响 TCP 吞吐量。利用BPF 主机路由避免流量穿越网络堆栈。
限制 #3:TCP 数据包太小100G 网络正在推动对比标准 64K 大小更大的 TCP 数据包的需求。Cilium 上的BIG TCP在进入网络堆栈时将 TCP 数据包的大小增加三倍,从而提高了性能和吞吐量。
限制4:传统的虚拟电缆容器通过传统的 veth 设备连接,这存在性能缺陷,尤其是对于非节点流量。启用netkit以实现离节点流量的快速网络命名空间切换。

最后的话

最近的 Linux Plumbing Conference 演讲中,Martin KaFai Lau(Meta 软件工程师)分享了一些最近的测试结果,将实时 Facebook 生产流量的软件中断 (softirq) 负载与 veth 和 netkit 的最佳基线进行了比较。

如上所示,虽然我们可以观察到 veth 和 baseline 之间的性能影响,但 netkit 的性能与主机没有区别。

转自:https://isovalent.com/blog/post/cilium-netkit-a-new-container-networking-paradigm-for-the-ai-era/

error: Content is protected !!