共计 4206 个字符,预计需要花费 11 分钟才能阅读完成。
作者
刘旭,腾讯云高级工程师,专一容器云原生畛域,有多年大规模 Kubernetes 集群治理及微服务治理教训,现负责腾讯云服务网格 TCM 数据面产品架构设计和研发工作。
引言
目前以 Istio[1] 为代表的服务网格广泛应用 Sidecar 架构,并应用 iptables 将流量劫持到 Sidecar 代理,长处是对应用程序无侵入,然而 Sidecar 代理会减少申请时延和资源占用。
性能始终是用户十分关心的一个点,也是用户评估是否应用服务网格产品的关键因素,腾讯云 TCM 团队始终致力于优化服务网格性能,上周咱们在 KubeCon 分享了应用 eBPF 代替 iptables 优化服务网格数据面性能的计划。
iptables 实现流量劫持
首先看一下以后社区应用的基于 iptables 的流量劫持计划,下图是一个 Pod 的创立过程,sidecar injector 会向 Pod 中注入两个容器,istio-init 和 istio-proxy
- istio-init 是一个 init container,负责创立流量劫持相干的 iptables 规定,在创立实现后会退出
istio-proxy 中运行着 envoy,负责代理 Pod 的网络流量,iptables 会将申请劫持到 istio-proxy 解决
下图展现了 iptables 实现流量劫持的整个过程,这里简略阐明下,感兴趣的同学能够查看 [2]
- Inbound iptables 将入流量重定向到 15006 端口,也就是 envoy 的 VirtualInboundListener,envoy 会依据申请的原始目标地址转发到应用程序的指定端口
Outbound iptables 将出流量重定向到 15001 端口,也就是 envoy 的 VirtualOutboundListener,envoy 会依据申请的原始目标地址以及 Host URL 等信息路由到指定后端
eBPF 实现流量劫持
eBPF(extended Berkeley Packet Filter) 是一种能够在 Linux 内核中运行用户编写的程序,而不须要批改内核代码或加载内核模块的技术,目前被宽泛用于网络、平安、监控等畛域。在 Kubernetes 社区最早也是最有影响的基于 eBPF 我的项目是 Cilium[4],Cilium 应用 eBPF 代替 iptables 优化 Service 性能。
Inbound
首先来看一下对入流量的劫持,对入流量的劫持次要应用 eBPF 程序 hook bind 零碎调用实现。
eBPF 程序会劫持 bind 零碎调用并批改地址,例如应用程序 bind 0.0.0.0:80 会被批改为 127.0.0.1:80,应用程序还有可能 bind ipv6 的地址,所以这里有两个 eBPF 程序别离解决 ipv4 和 ipv6 的 bind。
和 iptables 不同,iptables 能够针对每个 netns 独自设置规定,eBPF 程序 attach 到指定 hook 点后,会对整个零碎都失效,例如 attach 到 bind 零碎调用后,所有 Pod 内以及节点上过程调用 bind 都会触发 eBPF 程序,咱们须要辨别哪些调用是来自须要由 eBPF 实现流量劫持的 Pod。
在 K8s 中,除了 hostnetwork 的状况,每个 Pod 都有独立的 netns,而每个 netns 都有惟一的 cookie,因而咱们将须要应用 eBPF 实现流量劫持的 Pod 对应的 netns cookie 保留在 cookie_map
中,eBPF 程序通过判断以后 socket 的 netns cookie 是否在 cookie_map
中来决定是否批改 bind 地址。
批改应用程序的 bind 地址后,还须要下发 pod_ip:80 listener 配置到 envoy,pod_ip:80 listener 会将申请转发到 127.0.0.1:80 也就是应用程序监听的地址,这样就实现了对入流量的劫持。然而这里有一个问题,因为 istio 应用 istio-proxy 用户启动 envoy,默认状况下非 root 用户不能 bind 1024 以下的特权端口,咱们通过 istio-init 批改内核参数 sysctl net.ipv4.ip_unprivileged_port_start=0
解决了这个问题。
比照 iptables 和 eBPF 对入流量的劫持,iptables 计划每个包都须要 conntrack 解决,而 eBPF 计划只有在应用程序调用 bind 时执行一次,之后不会再执行,缩小了性能开销。
Outbound
再来看一下对出流量的劫持,对出流量的劫持比较复杂,依据协定分为 TCP 和 UDP 两种状况。
TCP 流量劫持
对 TCP 的出流量劫持过程:
_coonect4
通过劫持 connect 零碎调用将目标地址批改为 127.0.0.1:15001,也就是 envoy 的 VirtualOutboundListerer,同时将连贯的原始目标地址保留在sk_storage_map
- 在 TCP 连贯建设实现后,
sockops
会读取sk_storage_map
中的数据,并以四元组(源 IP、目标 IP、源端口、目标端口)为 key 将原始目标地址保留在origin_dst_map
_getsockopt
通过劫持 getsockopt 零碎调用,读取origin_dst_map
中的数据将原始目标地址返回给 envoy
UDP 流量劫持
istio 在 1.8 版本反对了智能 DNS 代理 [5],开启后 iptables 会将 DNS 申请劫持到 Sidecar 解决,咱们也须要用 eBPF 实现雷同逻辑,对于 TCP DNS 的劫持和下面相似,对 UDP DNS 的劫持见下图
对 UDP 的出流量劫持过程:
_connect4
和_sendmsg4
都是负责批改 UDP 的目标地址为 127.0.0.1:15053 并保留原始的目标地址到sk_storage_map
,因为 Linux 提供两种发送 UDP 数据的形式- 先调用 connect 再调用 send,这种状况由
_connect4
解决 - 间接调用 sendto,这种状况由
_sendmsg4
解决
- 先调用 connect 再调用 send,这种状况由
recvmsg4
通过读取sk_storage_map
将回包的源地址改为原始的目标地址,这是因为有些应用程序,例如 nslookup 会校验回包的源地址。
对于 TCP 和 connected UDP,iptables 计划每个包都须要 conntrack 解决,而 eBPF 计划的开销是一次性的,只须要在 socket 建设时执行一次,升高了性能开销。
Sockmap
应用 sockmap 优化服务网格性能的计划最早由 cilium 提出,咱们的计划也参考了 cilium,这里借用 cilium 的两张图来阐明下优化成果
优化前 Sidecar 代理与应用程序间的网络通信都须要通过 TCP/IP 协定栈解决
优化后 Sidecar 代理与应用程序间的网络通信绕过了 TCP/IP 协定栈,如果两个 Pod 在同一节点上,两个 Pod 间的网络通信也能够被优化。这里简略阐明下 sockmap 的优化原理,感兴趣的同学能够查看 6。
sock_hash
是一个存储 socket 信息的 eBPF map,key 是四元组(源 IP、目标 IP、源端口、目标端口)_sockops
负责监听 socket 事件,并将 socket 信息保留在sock_hash
_sk_msg
会拦挡 sendmsg 零碎调用,而后到sock_hash
中查找对端 socket,如果找到会调用bpf_msg_redirect_hash
间接将数据发送给对端 socket
问题
然而用四元组做为 key 可能会存在抵触的问题,例如在同一节点上的两个 Pod 中,envoy 应用同一源端口 50000 申请应用程序的 80 端口。
为了解决这个问题,咱们在 key 中增加了 netns cookie,同时对于非 localhost 的申请将 cookie 设置为 0,这样既保证了 key 不会抵触,又能够减速同一节点上两个 Pod 间的网络通信。
然而之前版本的内核不反对在 sockops
和 sk_msg
这两种 eBPF 程序中获取 netns cookie 信息,因而咱们提交了两个 patch 8 到内核社区,目前已合入 5.15 版本。
架构
整个计划的架构如图所示,istio-ebpf 以 DaemonSet 的模式运行在节点上,负责 load/attach eBPF 程序和创立 eBPF map。istio-init 容器依然保留,然而不再创立 iptables 规定,而是更新 eBPF map,istio-init 会将 Pod 的 netns cookie 保留在 cookie_map 中。同时咱们也批改了 istiod,istiod 会依据 Pod 的流量劫持模式(iptables/eBPF) 下发不同的 xDS 配置。
性能比照
测试环境:Ubuntu 21.04 5.15.7
- 同等条件下,应用 eBPF 可缩小 20% 的 System CPU 占用
- 同等条件下,应用 eBPF 可进步 20% QPS
- 同等条件下,应用 eBPF 可升高申请时延
总结
服务网格的 Sidecar 架构不可避免的会减少申请时延和资源占用,咱们通过应用 eBPF 代替 iptables 实现流量劫持,同时应用 sockmap 减速 Sidecar 代理和应用程序间的网络通信,在肯定水平上升高了申请时延和资源开销,因为内核版本等限度这一计划预计会在明年初上线,TCM 团队将继续摸索新的性能优化方向。
Reference
[1] https://istio.io
[2] https://jimmysong.io/blog/sid…
[3] https://ebpf.io
[4] https://cilium.io
[5] https://istio.io/latest/blog/…
[6] https://arthurchiao.art/blog/…
[7] https://github.com/cilium/cil…
[8] https://git.kernel.org/pub/sc…
[9] https://git.kernel.org/pub/sc…
对于咱们
更多对于云原生的案例和常识,可关注同名【腾讯云原生】公众号~
福利:
①公众号后盾回复【手册】,可取得《腾讯云原生路线图手册》&《腾讯云原生最佳实际》~
②公众号后盾回复【系列】,可取得《15 个系列 100+ 篇超实用云原生原创干货合集》,蕴含 Kubernetes 降本增效、K8s 性能优化实际、最佳实际等系列。
③公众号后盾回复【白皮书】,可取得《腾讯云容器平安白皮书》&《降本之源 - 云原生老本治理白皮书 v1.0》
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!