https://thenewstack.io/how-ebpf-streamlines-the-service-mesh/
本文译自 How eBPF Streamlines the Service Mesh[1]。
今天有几个服务网格的产品和项目,承诺简化应用微服务之间的连接,同时提供额外的功能,如安全连接、可观察性和流量管理。但正如我们在过去几年中反复看到的那样,对服务网格的兴奋已经被对额外的[2]复杂性[3]和开销的[4]实际担忧所抑制[5]。让我们来探讨一下 eBPF[6] 是如何让我们精简服务网格[7],使服务网格的数据平面更有效率,更容易部署。
今天的 Kubernetes 服务网格解决方案要求你在每一个应用 pod 上添加一个代理 sidecar 容器,如 Envoy[8] 或 Linkerd-proxy[9]。这是正确的:即使在一个非常小的环境中,比如说有 20 个服务,每个服务运行五个 pod,分布在三个节点上,你也有 100 个代理容器。无论代理的实现多么小和有效,这种纯粹的重复都会耗费资源。
每个代理使用的内存与它需要能够通信的服务数量有关。Pranay Singhal 写了他配置 Istio 的经验[10],将每个代理的消耗从 1GB 左右减少到更合理的 60-70MB。但是,即使在我们的小环境中,在三个节点上有 100 个代理,这种优化配置仍然需要每个节点 2GB 左右。

来自redhat.com/architect/why-when-service-mesh——每个微服务都有自己的代理sidecar
为什么我们需要所有这些 sidecar?这种模式允许代理容器与 pod 中的应用容器共享一个网络命名空间。网络命名空间是 Linux 内核的结构,它允许容器和 pod 拥有自己独立的网络堆栈,将容器化的应用程序相互隔离。这使得应用之间互不相干,这就是为什么你可以让尽可能多的 pod 在 80 端口上运行一个 web 应用 —— 网络命名空间意味着它们各自拥有自己的 80 端口。代理必须共享相同的网络命名空间,这样它就可以拦截和处理进出应用容器的流量。
eBPF[11] 是一种内核技术,允许自定义程序在内核中运行。这些程序在响应事件时运行,有成千上万个可能的事件,eBPF 程序可以被附加到这些事件上。这些事件包括轨迹点、进入或退出任何功能(在内核或用户空间)或对服务网格来说很重要的 —— 抵达的网络数据包。
重要的是,每个节点只有一个内核;在一个节点上运行的所有容器(也就是所有的 pod)共享同一个内核。如果你在内核中添加一个 eBPF 程序到一个事件中,它将被触发,无论哪个进程引起该事件,无论它是在应用容器中运行还是直接运行在主机上。

每台主机一个内核
这就是为什么 eBPF 对于 Kubernetes 中的任何一种 instrumentation 来说都是如此令人兴奋的技术 —— 你只需要在每个节点上添加一次 instrumentation ,所有的应用程序 pod 都会被覆盖。无论你是在寻求可观察性、安全性还是网络,由 eBPF 驱动的解决方案都可以在不需要 sidecar 的情况下对应用进行检测。
基于 eBPF 的 Cilium[12] 项目(最近 以孵化级别加入云计算基金会[13])将这种 “无 sidecar” 模式带到了服务网格的世界。除了传统的 sidecar 模型,Cilium 还支持每个节点使用一个 Envoy 代理实例运行服务网格的数据平面。使用我们前面的例子,这就把代理实例的数量从 100 个减少到只有 3 个。

用无sidecar代理模式减少代理实例
在 sidecar 模型中,指定每个应用 pod 的 YAML 需要被修改以添加 sidecar 容器。这通常是自动化的 —— 例如,使用一个 mutating webhook,在每个应用 pod 部署的时候注入 sidecar。