原文:https://docs.projectcalico.org/about/about-ebpf

eBPF 是一种 Linux 内核功能,允许将快速而安全的微型程序加载到内核中以自定义其操作。

通过本文档,您将了解:

  • eBPF 的一般背景。
  • eBPF 的各种用途。
  • Calico 如何在 eBPF 数据平面中使用 eBPF。

什么是 eBPF?

eBPF 是嵌入在 Linux 内核中的 “虚拟机”。它允许将微型程序加载到内核中,并附加到钩子上,而钩子会在发生某些事件时触发。这样可以自定义内核的行为(有时是很严格的)。尽管每种钩子的 eBPF 虚拟机都相同,但是钩子的功能有很大差异。虽然将程序加载到内核中可能很危险,但内核通过非常严格的静态验证器运行所有程序。验证程序会将程序沙盒化,以确保它只能访问允许的内存部分,并确保它必须快速终止。

为什么称为 eBPF?

eBPF 是 “扩展 Berkeley 包过滤器” 的缩写。Berkeley 包过滤器是一种更早,更专业的虚拟机,专门用于过滤数据包。诸如tcpdump此类使用此“经典” BPF VM 来选择应发送到用户空间进行分析的数据包的工具。eBPF 是 BPF 的一个扩展版本,适用于内核内部的通用用途。尽管名称不为人知,但 eBPF 的用途不仅仅限于数据包过滤。

eBPF 可以做什么?

eBPF 类型

内核中可以将 eBPF 程序附加到几类钩子。eBPF 程序的功能在很大程度上取决于它所连接的钩子:

  • Tracing 程序可以附加到内核中很大一部分功能上。跟踪程序对于收集统计信息和内核的深入调试很有用。 大多数跟踪钩子仅允许对该函数正在处理的数据进行只读访问,但是有些跟踪钩子允许修改数据。Calico 团队使用跟踪程序来帮助在开发过程中调试 Calico。例如,找出内核为何意外丢弃数据包的原因。
  • Traffic Control (tc) 程序可以在入口和出口附加到给定的网络设备。内核为每个数据包执行一次程序。由于钩子是用于数据包处理的,因此内核允许程序修改或扩展数据包、删除数据包,将其标记为排队或将数据包重定向到另一个接口。Calico 的 eBPF 数据平面基于这种钩子。我们使用 tc 程序对 Kubernetes 服务进行负载平衡,实施网络策略,并为已建立连接的流量创建快速路径。
  • XDP或“ eXpress 数据路径”实际上是 eBPF 钩子的名称。每个网络设备都有一个 XDP 入口钩子,在内核为数据包分配套接字缓冲区之前,它会为每个传入数据包触发一次。XDP 可以为诸如 DoS 保护(在 Calico 的标准 Linux 数据平面中支持)和入口负载平衡(在 facebook 的 Katran 中使用)之类的用例提供出色的性能。XDP 的缺点是,它需要网络设备驱动程序支持才能获得良好的性能,并且与 Pod 网络的互操作性不是很好。
  • 几种类型的套接字程序可以在套接字上进行各种操作。例如,允许 eBPF 程序更改新创建的套接字的目标 IP,或强制套接字绑定到“正确的”源 IP 地址。Calico 使用此类程序进行 Kubernetes Services 的连接时负载平衡。这减少了开销,因为在数据包处理路径上没有DNAT
  • 有各种与安全性相关的钩子,允许以各种方式管理程序行为。例如,seccomp钩子允许以细粒度的方式管理系统调用。
  • 而且……当您准备好此功能时,可能还会有更多的问题。eBPF 正在内核中进行大量开发。

内核通过“辅助功能”公开每个钩子的功能。例如,该 tc 钩子具有帮助程序功能以调整数据包的大小,但是该助手在跟踪钩子中不可用。使用 eBPF 的挑战之一是不同的内核版本支持不同的帮助程序,而缺少帮助程序可能导致无法实现特定功能。

BPF 映射

附加到 eBPF 钩子的程序可以访问 BPF maps。BPF 映射有两个主要用途:

  • 它们允许 BPF 程序存储和检索长期存在的数据。
  • 它们允许 BPF 程序和用户空间程序之间的通信。BPF 程序可以读取由用户空间写入的数据,反之亦然。

BPF 映射有很多类型,包括一些允许在程序之间跳转的特殊类型,还有一些充当队列和堆栈而不是严格用作键/值映射。Calico 使用映射来跟踪活动连接,并使用策略和服务 NAT 信息配置 BPF 程序。由于映射访问可能相对昂贵,因此 Calico 的目标是仅对已建立的流中的每个数据包执行单个映射查找。

可以使用 bpftool 内核随附的命令行工具检查 bpf 映射的内容。

Calico 的 eBPF 数据平面

Calico 的 eBPF 数据平面是标准 Linux 数据平面(基于 iptables)的替代方案。虽然标准数据平面通过与 kube-proxy 以及您自己的 iptables 规则互通来关注兼容性,但是 eBPF 数据平面着重于性能,延迟和改善用户体验,而这些功能是标准数据平面所无法实现的。作为其一部分,eBPF 数据平面用 eBPF 实现代替了 kube-proxy。主要的“用户体验”功能是在流量到达 NodePort 时保留来自群集外部流量的源 IP。这使您的服务端日志和网络策略在该路径上更加有用。

功能比较

尽管 eBPF 数据平面具有一些标准 Linux 数据平面所缺少的功能,但反之亦然:

功能标准 Linux 数据平面eBPF 数据平面
吞吐量专为 10GBit +设计专为 40GBit +设计
第一个封包延迟低(kube-proxy 服务延迟是主要的因素)更低
后续数据包延迟更低
保留群集中的源 IP
保留外部源 IP只有 externalTrafficPolicy: Local
Direct Server Return不支持支持(需要兼容的基础网络)
连接跟踪Linux 内核的 conntrack 表(大小可以调整)BPF 地图(固定大小)
策略规则映射到 iptables 规则映射到 BPF 指令
策略选择器映射到 IP 集映射到 BPF 映射
Kubernetes 服务kube-proxy iptables 或 IPVS 模式BPF program and maps
IPIP支持支持(由于内核限制,没有性能优势)
VXLAN支持支持
Wireguard支持支持
Other routing支持支持
支持第三方 CNI 插件是(仅兼容插件)是(仅兼容插件)
与其他 iptables 规则兼容是(可以在其他规则之上或之下编写规则)部分 iptables 绕过工作负载流量
XDP DoS 保护支持不支持(尚未)
IPv6支持不支持(尚未)
主机端点策略支持不支持(尚未)
企业版可用不支持(尚未)

架构概述

Calico 的 eBPF 数据面附加 eBPF 程序到 tc 钩子上的每个 Calico 接口以及您的数据和隧道接口。这允许 Calico 尽早发现工作负载包,并通过绕过 iptables 和内核通常会进行的其他包处理的快速路径来处理它们。

The logic to implement load balancing and packet parsing is pre-compiled ahead of time and relies on a set of BPF maps to store the NAT frontend and backend information. One map stores the metadata of the service, allowing for externalTrafficPolicy and “sticky” services to be honoured. A second map stores the IPs of the backing pods.

在 eBPF 模式下,Calico 使用 BPF MAP 存储策略选择器匹配的 IP 集,从而将您的策略转换为优化的 eBPF 字节码。

为了提高服务的性能,Calico 还通过挂接到 BPF 套接字钩子来实现连接到负载平衡。当程序试图连接到 Kubernetes 服务时,Calico 会尝试拦截连接,并将套接字配置为直接连接后端 pod 的 IP。这从服务连接中删除了所有的 NAT 开销。