乐趣区

关于云原生: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 等。

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

退出移动版