Published at 2022-11-12 | Last Update 2022-11-12
本文翻译自 KubeCon+CloudNativeCon North America 2022 的一篇分享: 100 Gbit/s Clusters with Cilium: Building Tomorrow’s Networking Data Plane。
作者 Daniel Borkmann, Nikolay Aleksandrov, Nico Vibert 都来自 Isovalent(Cilium 母公司)。 翻译时补充了一些背景知识、代码片段和链接,以方便理解。
翻译已获得 Daniel 授权。
由于译者水平有限,本文不免存在遗漏或错误之处。如有疑问,请查阅原文。
以下是译文。
今天的大部分 K8s 用户使用的还是纯 IPv4 网络(IPv4-only),或称 IPv4 单栈网络; 也有一些用户正在从 IPv4 单栈迁移到 IPv4/IPv6 双栈上, 最终目标是实现 IPv6 单栈网络,或称纯 IPv6 网络(IPv6-only)。 纯 IPv6 网络的 k8s 集群不仅 IPAM 更加灵活,集群规模更大,而且可以解锁很多新的网络和 eBPF 特性,能更好地满足数据密集型应用的需求。
本文将展示纯 IPv6 k8s 集群的优势以其面临的问题,以及 Cilium 的数据平面是如何解决这些问题的。内容包括:
IPv6 + BIG TCP
支持 100Gbps/socket
;提升吞吐的同时还能降低延迟;meta device
,替代 veth pair 取得更极致的网络性能;当前大型数据中心面临三个方面的问题:
其中一个重要原因是它们都构建在 IPv4 基础之上,后者已经发挥到极限了。 那么,换成 IPv6 能解决问题吗?答案是能,而且能同时解决规模和性能需求。 要解释这一点,我们需要回顾一下并不久远的“历史”。
Cilium 是作为一个纯 IPv6 容器网络实验项目(”The Cilium Experiment”)启动的, 下面这张截图就是我们在 2016 年 LinuxCon 的分享, Cilium: Fast IPv6 Container Networking with BPF and XDP,
几个开发者来自 RedHat 的内核和 OVS 相关开发团队。
由于构建在 IPv6-only 之上,因此 Cilium 自带了很多 IPv6 相比 IPv4 的优势, 例如扩展更好、更灵活、地址空间充裕,无需 NAT 等等;
更重要的是,为了取得最高效率,Cilium 将 datapath 构建在 eBPF 之上, 这与之前的网络模型完全不同。
但与大多数过于前卫的项目一样,纯 IPv6 的前提条件很快被现实打脸。
先来看一下当时(2016)年容器领域的 IPv6 生态:
K8s (CNI):基本功能有了
Docker (libnetwork):还在实现的路上
因此,面对众多实际需求,我们不得不为 Cilium 添加 IPv4 的支持。
现在再来看看 6 年之后的今天(2022),容器领域的 IPv6 支持进展:
K8s 官方
GA v1.23
)用户的实际需求其实比较明确:通过 IPv6 单栈获得更大的 IPAM 灵活性, 以及更多的 headroom 来做一些以前(IPv4)做不到的事情。
实现方式:构建 IPv6 单栈隔离岛,作为一个完全没用历史负担的环境(clean-slate), 然后将存量的应用/服务迁移到这个环境中来。当然,这其中仍然有一些地方与 IPv4 打交道,除非真空隔离或者没有任何外部依赖。
说到外部依赖,我们就来看下如今互联网的 IPv6 部署普及情况。根据 whynoipv6.com 提供的数据, 当前(2022.11) Alexa 排名前 1000 的网站中,
总共收录的 90 万个网站中,只有 34% 的有 IPv6。
大量的生态系统还在路上,例如,GitHub 还不能通过 IPv6 clone 代码。
外部依赖短期内全部支持 IPv6 不现实,但通过 4/6 转换,其实就不影响我们先把数据内的 集群和应用 IPv6 化,享受 IPv6 带来的性能和便利性。 下面就来看如何基于 Cilium 部署一个纯 IPv6 的 k8s 集群,并解决与外部 IPv4 服务的互联互通问题。
IPv6-only K8s 与传统 IPv4 服务的对接,使用的是 NAT46/NAT64
,
也就是做 IPv4/IPv6 地址的转换。
这个听上去可能比较简单,但要在 Linux 内核中实现其实是有挑战的,例如 基于 iptables/netfilter 架构就无法实现这个功能, 因为内核网络栈太复杂了,牵一发而动全身;好消息是,基于 eBPF 架构能。
4.8+
)我们早在 2016 年就对内核 tc BPF 层添加了 NAT46/64 的支持:
通过 bpf_skb_change_proto()
实现 4/6 转换。
Android 的 CLAT 组件也是通过这个 helper 将手机连接到 IPv6-only 蜂窝网的。
v1.12+
)如下图所示,几个部分:
思路其实很简单,
通过 Cilium L4LB 节点做 NAT46/64 转换;
将 IPv4 流量路由到数据中心的边缘节点(边界),经过转换之后再进入 IPv6 网络;反向是类似的。
具体工作在 tc BPF 或 XDP 层。
通过 bpf_skb_change_proto()
完成 4/6 转换。
Cilium L4LB 现在的 NAT46/64 功能支持:
{IPv4,6} VIP -> {IPv4,6} Backend
规则例子,VIP 是 IPv4,backends 是 IPv6 pods:
$ cilium service list
ID Frontend Service Type Backend
1 1.2.3.4:80 ExternalIPs 1 => [f00d::1]:60 (active)
2 => [f00d::2]:70 (active)
3 => [f00d::3]:80 (active)
另一个例子,
$ cilium service list
ID Frontend Service Type Backend
1 [cafe::1]:80 ExternalIPs 1 => 1.2.3.4:8080 (active)
2 => 4.5.6.7:8090 (active)
IPv4 -> IPv6-only
)这里有两种实现方式,Cilium 都支持,各有优缺点。
这种模式下,NAT46 网关是有状态的,
VIP:port
映射到 IPv6 VIP:port
(exposed to public natively);基于 eBPF/XDP,高性能;
下面是通过 Service 实现的 NAT46 规则(也就是“状态”):
好处:
VIP:port
到 K8s 集群的 IPv6 VIP:port
的映射,与后者完全解耦NAT46 GW 甚至还能通过 weighted Maglev 负载均衡算法,将请求转发到多个集群上;
Maglev weights 在 Cilium 1.13 合并。
缺点:
这种方式是通过 IPv6 协议原生的 IPv4/IPv6 地址映射实现的,因此无需控制平面下发 service 规则来实现 NAT46/64:
转发规则:
优点:
64:ff9b::/96
范围,RFC6052;loadBalancerSourceRanges can restrict LB service access for external IPv4 clients。
K8s documentation on LB source ranges,Cilium 已经支持。
缺点:
64:ff9b::/96
网段;IPv6-only -> IPv4
)DNS64 承担了关键角色。
$ nslookup github.com
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
Name: github.com
Address: 20.205.243.166
$ nslookup -query=AAAA github.com
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
*** Can't find github.com: No answer
$ nslookup -query=AAAA github.com 2001:4860:4860::6464
Server: 2001:4860:4860::6464
Address: 2001:4860:4860::6464#53
Non-authoritative answer:
Name: github.com
Address: 64:ff9b::8c52:7904 # 8c52:7904 -> 140.82.121.4 Embedded IPv4 addresse
Google 的 public DNS64 服务。
https://coredns.io/plugins/dns64/
优点:
缺点:
IPAM 管理更复杂,因为 pods/nodes 需要 secondary 64:ff9b::/96
prefix 地址段;
可以通过有状态 NAT64 GW 解决:Pods use their primary IPv6 address, and GW does NAT to its own IPv4 address.
至此,IPv6-only k8s 集群与传统 IPv4 网络交互问题都解决了,那接下来呢?
IPv6 不仅解决扩展性问题,而且为未来的性能需求奠定了基础。接下来看 Cilium + BIG TCP。
支持数据中心内的单个 socket 达到 100Gbps+ 带宽。
大数据、AI、机器学习以及其他网络密集型应用。
BIG TCP 并不是一个适应于大部分场景的通用方案,而是针对数据密集型应用的优化,在这些场景下能显著提升网络性能。
文档:
来计算一下,如果以 MTU=1538
的配置,要达到 100Gbps 带宽,
100Gbit/s ÷ 1538Byte ÷ 8bit/Byte == 8.15Mpps (123ns/packet)
可以看到,
815 万
个包,或者说,123ns
。对于内核协议栈这个庞然大物来说,这个性能是无法达到的,例如一次 cache miss 就会导致性能急剧下降。 降低 pps 会使这个目标变得更容易,在总带宽不变的情况下,这就意味着要增大包长(packet length)。 局域网里面使用超过 1.5K 的 MTU 大包已经是常规操作,经过适当的配置之后,可以用到最大 64KB/packet。 后面会看到这个限制是怎么来的。
大包就需要批处理:GRO、TSO。如下图所示:
以 RX 方向的 GRO 为例,GRO 会将重组之后的 total payload 长度更新到 IPv4 头的 tot_len
字段,这是一个
16bit
整数,单位是字节,因此最大只能表示 64KB 的包。
TX 方向的 TSO 也有一样的限制。也就是说,使用 IPv4,我们在 TX/RX 方向最大只能支持 64KB 的大包。
内核能支持更大的 batch 吗?能,使用 IPv6。
BIG TCP 的解决方式是在本地插入一个 Hop-By-Hop (HBH) IPv6 扩展头。 “本地”的意思是“在这台 node 上”,也就是说 HBH 头不会发出去,只在本机内使用。
此外,还需要对应调整 MTU 大小。
目前这个上限是 512KB,但是未来需要时,很容易扩展。 这个字段是 32bit,因此理论上最大能支持 4GB 的超大包(jumbo packets)。
5.19+
)BIG TCP 合并到了内核 5.19+,
此外,它还需要网卡驱动的支持。
v1.13+
)BIG TCP 的支持将出现在 Cilium 1.13。
文档:Performance: tuning: IPv6 BIG TCP
5.19+
启用了开关之后,Cilium 将自动为 host/pod devices 设置 IPv6 BIG TCP,过程透明。
延迟:
# Back to back: AMD Ryzen 9 3950X @ 3.5 GHz, 128G RAM @ 3.2 GHz, PCIe 4.0, ConnectX-6 Dx, mlx5 driver 78
$ netperf -t TCP_RR -H <remote pod> -- -r 80000,80000 -O MIN_LATENCY,P90_LATENCY,P99_LATENCY,THROUGHPUT
TPS:
# Back to back: AMD Ryzen 9 3950X @ 3.5 GHz, 128G RAM @ 3.2 GHz, PCIe 4.0, ConnectX-6 Dx, mlx5 driver 79
$ netperf -t TCP_RR -H <remote pod> -- -r 80000,80000 -O MIN_LATENCY,P90_LATENCY,P99_LATENCY,THROUGHPUT
带宽:
iperf3 不支持 mmap()’ed TCP. 在这里的测试中,最大的开销就是 copy from/to userspace,因此最大速度卡在了 60Gbps,
需要的改进:The Path To TCP 4K MTU and RX ZeroCopy。
更广泛意义上来说,BIG TCP 只是 Cilium 整体数据平面的一块拼图。 那么完整的数据平面长什么样子?
总体原则:高度可扩展,极致性能。
提供的能力:
常规(默认):物理网卡和容器虚拟网卡之间要经过内核网络栈,
eBPF host routing:物理网卡通过 bpf_redirect_{peer,neigh}
直通 veth pair:
以上 eBPF host routing 双向转发效果:
现在我们正在开发一个称为 meta device 的虚拟设备,替换 veth pair:
为什么引入 meta:将 pod-specific BPF 程序从 tc bpf 移动到 meta layer。 对于 meta device 来说,eBPF 程序成为了 pod 内的 device 自身的一部分。 但不会由 pod 来修改或 unload eBPF 程序,而仍然由宿主机 namespace 内的 cilium 来统一管理。
延迟更低,pod 的网络延迟已经接近 host network 应用的延迟。
Internals for veth (today):
veth_xmit()
Internals for meta (new):
meta_xmit()
代码:
# Back to back: AMD Ryzen 9 3950X @ 3.5 GHz, 128G RAM @ 3.2 GHz, PCIe 4.0, ConnectX-6 Dx, mlx5 driver
$ netperf -t TCP_RR -H <remote pod> -- -O MIN_LATENCY,P90_LATENCY,P99_LATENCY,THROUGHPUT
# Back to back: AMD Ryzen 9 3950X @ 3.5 GHz, 128G RAM @ 3.2 GHz, PCIe 4.0, ConnectX-6 Dx, mlx5 driver
$ netperf -t TCP_RR -H <remote pod> -- -O MIN_LATENCY,P90_LATENCY,P99_LATENCY,THROUGHPUT
# Back to back: AMD Ryzen 9 3950X @ 3.5 GHz, 128G RAM @ 3.2 GHz, PCIe 4.0, ConnectX-6 Dx, mlx5 driver
$ netperf -t TCP_STREAM -H <remote pod> -l 60
本节为译注。
未来已来,只是分布尚不均匀(The future is already here – it’s just not very evenly distributed. William Ford Gibson)。
Cilium 数据平面的核心功能:
除了“(译)深入理解 Cilium 的 eBPF 收发包路径(KubeCon, 2019)” ,其他都来自 Cilium 团队分享: