在腾讯,已经有很多产品已使用或者正在尝试使用istio来作为其微服务治理的基础平台。不过在使用istio时,也有一些对通信性能要求较高的业务会对istio的性能有一些担忧。由于envoy sidecar的引入,使两个微服务之间的通信路径变长,导致服务延时受到了一些影响,istio社区一直以来也有这方面的声音。基于这类抱怨,我们希望能够对这一通信过程进行优化,以更好的满足更多客户的需求。
首先,我们看一下istio数据面的通信模型,来分析一下为什么会对延时有这么大的影响。可以看到,相比于服务之间直接通信,在引入istio 之后,通信路径会有明显增加,主要包括多出了两次本地进程之间的tcp连接通信和用户态网络代理envoy对数据的处理。所以我们的优化也分为了两部分进行。
内核态转发优化
那么对于本地进程之间的通信优化,我们能做些什么呢?
其实在开源社区已经有了这方面的探索了。istio官方社区在2019年1月的时候已经有了这方面讨论,在文档里面提到了使用ebpf的技术来做socket转发的数据代理,使数据在socket层进行转发,而不需要再往下经过更底层的TCP/IP协议栈的一个处理,从而减少它在数据链路上的通信链路长度。
另外,网络开源项目cilium也在这方面有一个比较深入的实践,同样也是使用了ebpf的技术。不过在cilium中本地网络加速只是其中的一个模块,没有作为一个独立的服务进行开发实践,在腾讯云内部没法直接使用,这也促使了我们开发一个无依赖的解决方案。
当然在初期的时候,我们也对ebpf的技术进行了一个验证,从验证结果中可以看到,在使用了ebpf的技术之后,它的延时大概有20%到30%的提升,说明ebpf的技术应用在本地通讯上还是有一定优化能力的。
简单介绍一下ebpf,看一下它是怎么做到加速本地通讯的。首先ebpf可以看作是一个运行在内核里面的虚拟机,用户编写的ebpf程序可以被加载到内核里面进行执行。内核的一个verify组件会保证用户的ebpf程序不会引发内核的crash,也就是可以保证用户的ebpf程序是安全的。目前ebpf程序主要用来追踪内核的函数调用以及安全等方面。下图可以看到,ebpf可以用在很多内核子系统当中做很多的调用追踪。
另外一个比较重要的功能,就是我们在性能优化的时候使用到的在网络上的一个能力,也就是下面提到的sockhash。sockhash本身是一个ebpf特殊的一个kv存储结构,主要被用作内核的一个socket层的代理。它的key是用户自定义的,而value是比较特殊的,它存储的value是内核里面一个socket对象。存储在sockhash中的socket在发送数据的时候,如果能够通过我们挂在sockhash当中的一个ebpf当中的程序找到接收方的socket,那么内核就可以帮助我们把发送端的数据直接拷贝到接收端socket的一个接收队列当中,从而可以跳过数据在更底层的处理,比如TCP/IP协议栈的处理。
在sidecar中,socket是怎样被识别并存储在sockhash当中来完成一个数据拷贝的呢?我们需要分析一下数据链的本地通讯的流量特征。
首先从ingress来讲,ingress端的通信会比较简单一点,都是一个本地地址的通信。ingress端的envoy进程和用户服务进程之间通信,它的原地址和目的地址刚好是一一对应的。所以我们可以利用这个地址的四元组构造它的key,把它存储到sockhash当中。在发送数据的时候,根据这个地址信息反向构造这个key,从sockhash当中拿到接收端的socket返回给内核,这样内核就可以帮我们将这个数据直接拷贝给接收端的socket。
egress会稍微复杂一点,在一个egress端服务程序对外发出的