关于云原生:eBPF技术应用云原生网络实践系列之基于socket的service-龙蜥技术

简介:如何应用 socket eBPF进一步晋升Service 网络的转发性能?

背景介绍

Kubernetes 中的网络性能,次要包含 POD 网络,service 网络和网络策略组成。其中 POD 网络和网络策略,都是规定了模型,没有提供默认实现。而 service 网络作为 Kubernetes 的特色局部,官网版本继续演进了多种实现:

在 Kubernetes 中先后呈现的几种 Service 实现中,整体都是为了提供更高的性能和扩展性。

Service 网络,实质上是一个分布式的服务器负载平衡,通过 daemonset 形式部署的 kube-proxy,监听 endpoint 和 service 资源,并在 node 本地生成转发表项。目前在生产环境中次要是 iptables 和 IPVS 形式,原理如下:

在本文中,介绍应用 socket eBPF 在 socket 层面实现负载平衡的逻辑,打消了逐报文 NAT 转换解决,进一步晋升 Service 网络的转发性能。

基于 socket eBPF 的数据面实现

socket eBPF 数据面简介

无论 kube-proxy 采纳 IPVS 还是 tc eBPF 服务网络减速模式,每个从 pod 收回网络申请都必然通过 IPVS 或者 tc eBPF,即 POD <–> Service <–> POD,随着流量的减少必然会有性能开销, 那么是否能够间接在连贯中将 service的clusterIP 的地址间接换成对应的 pod ip。基于 Kube-proxy+IPVS 实现的 service 网络服务,是基于逐报解决 +session 的形式来实现。

利用 socket eBPF,能够在不必间接解决报文和 NAT 转换的前提下,实现了负载平衡逻辑。Service 网络在同步上优化成 POD <–> POD,从而使Service 网络性能根本等同于 POD 网络。软件结构如下:

在 Linux 内核中,利用 BPF_PROG_TYPE_CGROUP_SOCK 类型的 eBPF hook 能够针对 socket 零碎调用挂接 hook,插入必要的 EBPF 程序。

  • 通过 attach 到特定的 cgroup 的文件描述符,能够管制 hook 接口的作用范畴。
  • 利用 sock eBPF hook,咱们能够在 socket 层面劫持特定的 socket 接口,来实现实现负载平衡逻辑。
  • POD-SVC-POD 的转发行为转换成 POD-POD 的转发行为。

以后 Linux 内核中不断完善相干的 hook,反对更多的 bpf_attach_type,局部间隔如下:

BPF_CGROUP_INET_SOCK_CREATE

BPF_CGROUP_INET4_BIND

BPF_CGROUP_INET4_CONNECT

BPF_CGROUP_UDP4_SENDMSG

BPF_CGROUP_UDP4_RECVMSG

BPF_CGROUP_GETSOCKOPT

BPF_CGROUP_INET4_GETPEERNAME

BPF_CGROUP_INET_SOCK_RELEASE

TCP 工作流程

TCP 因为是有基于连贯的,所以实现十分扼要,只须要 hook connect 零碎调用即可,如下所示:

connect 零碎调用劫持逻辑:

  1. 从 connect 调用上下文中取 dip+dport,查找 svc 表。找不到则不解决返回。
  2. 查找亲和性会话,如果找到,失去 backend_id,转 4。否则转 3。
  3. 随机调度,调配一个 backend。
  4. 依据 backend_id,查 be 表,失去 be 的 IP+ 端口。
  5. 更新亲和性信息。
  6. 批改 connect 调用上下文中的 dip+dport 为 be 的 ip+port。
  7. 实现。

在 socket 层面就实现了端口转换,对于 TCP 的 clusterip 拜访,基本上能够等同于 POD 之间东西向的通信,将 clusterip 的开销降到最低。

  • 不须要逐包的 dnat 行为。
  • 不须要逐包的查找 svc 的行为。

UDP 工作流程

UDP 因为是无连贯的,实现要简单一些,如下图所示:

nat_sk 表的定义参见:LB4_REVERSE_NAT_SK_MAP

劫持 connect 和 sendmsg 零碎调用:

  1. 从零碎调用调用上下文中取 dip+dport,查找 svc 表。找不到则不解决返回。
  2. 查找亲和性会话,如果找到,失去 backend_id,转 4,否则转 3。
  3. 随机调度,调配一个 backend。
  4. 依据 backend_id,查 be 表,失去 be 的 IP+端口。
  5. 更新亲和性的相干表。
  6. 更新 nat_sk 表,key 为 be 的 ip+port,value 为 svc的vip+vport。
  7. 批改零碎调用上下文中的 dip+dport 为 be 的 ip + port。
  8. 实现。

劫持 recvmsg 零碎调用

  1. 从零碎调用上下文中远端 IP+port,查找 NAT_SK 表,找不到则不解决返回。
  2. 找到,取出其中的 IP+port,用来查找 svc 表,找不到,则删除 nat_sk 对应表项,返回。
  3. 应用 nat_sk 中找到的 ip+port,设置零碎调用上下文中远端的 IP+port。
  4. 实现。

对于地址修改问题

基于 socket eBPF 实现的 clusterIP,在上述根本转发原理之外,还有一些非凡的细节须要思考,其中一个须要非凡思考就是 peer address 的问题。和 IPVS之类的实现不同,在 socket eBPF 的 clusterIP 上,client 是和间接和 backend 通信的,两头的 service 被旁路了。回顾一下转发门路如下:

此时,如果 client 上的 APP 调用 getpeername 之类的接口查问 peer address,这个时候获取到的地址和 connect 发动的地址是不统一的,如果 app对于 peeraddr 有判断或者非凡用处,可能会有意外状况。

针对这种状况,咱们同样能够通过 eBPF 在 socket 层面来修改:

1、在guest kernel 上新增 bpf_attach_type,能够对 getpeername 和 getsockname 减少 hook 解决。

2、发动连贯的时候,在相应的 socket hook 解决中,定义 map 记录响应的VIP:VPort 和 RSIP:RSPort 的对用关系。

3、当 APP 要调用 getpeername/getsockname 接口的时候,利用 eBPF 程序修改返回的数据:批改上下文中的远端的 IP+port为vip+vport。

总结

和TC-EBPF/IPVS性能比照

测试环境:4vcpu + 8G mem 的平安容器实例,单 client + 单 clusterip + 12 backend。socket BPF:基于 socket ebpf 的 service 实现。tc eBPF:基于 cls-bpf 的 service 实现,目前曾经在 ack 服务中利用。IPVS-raw:去掉所有平安组规定和 veth 之类开销,只有 IPVS 转发逻辑的 service 实现。socket BPF 在所有性能指标上,均有不同水平晋升。

大量并发的短连贯,基本上吞吐晋升 15%,时延升高 20%。

转发性能比拟(QPS)

90%转发延时比拟(ms)

持续演进

eBPF does to Linux what JavaScript does to HTML.– Brendan Gregg

基于 socket eBPF 实现的 service,大大简化了负载平衡的逻辑实现,充分体现了 eBPF 灵便、玲珑的特点。eBPF 的这些特点也很符合云原生场景,目前,该技术已在阿里云开展实际,减速了 kubernetes 服务网络。咱们会持续摸索和欠缺更多的 eBPF 的利用案例,比方 IPv6、network policy 等。

原文链接
本文为阿里云原创内容,未经容许不得转载。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理