Istio优化方案
Istio优化方案
此istio的优化方案使用的软件版本信息为:
Kubernetes:1.18.1
Istio:1.8.1
我们在订购的公司kcs集群里部署Bookinfo服务,通过在kubernetes集群里创建70个namespace,并且在其中部署信息如下表:
服务数 | pod数 | VirtualService数 | DestinationRule数 |
280 | 420 | 140 | 280 |
内存优化
使用Sidecar限制下发到Envoy信息数量
我们知道istio是通过在pod中注入sidecar实现进出pod流量的拦截,在istio中是通过envoy实现sidecar功能,istio网格中,pilot会与envoy建立长连接,并向其推送最新更新的网络策略。Envoy采用xDS接口从pilot中获取到Listener、Cluster、Route等动态配置信息,Envoy根据Listener的配置,结合Route和Cluster对流量进行处理。这里Envoy从pilot中获取全量下发的配置信息,当集群中使用istio的服务增多时,每个envoy里的配置信息量会快速增加。这里我们使用Sidecar配置优化下发到Envoy中的配置信息。
部署完上述服务以后,我们进入某个namespace下的pod中查看Envoy资源占用情况

从图中我们可以看到Envoy实际占用内存大概为86M,TCMalloc预留的系统内存为138M,pod中占用的Envoy竟然占用86M的内存,随着产品数量的增加,部署服务的增多,Envoy占用的总内存数也会急剧增加,那么怎么解决这个问题呢?
我们查看下reviews中是哪些文件占用了Envoy的内存

我们知道reviews服务只需要访问ratings服务,因此在reviews的sidecar中只需要ratings服务相关的outbound配置就可以了。但是因为Envoy配置信息是全量下发,通过上图查询reviews pod中proxy的配置,可以看到Pilot下发的配置信息中包含了reviews、productpage、details这些它并不需要的outbound cluster信息,并且还包含了其他namespace的outbound cluster信息,这些outbound cluster没有任何作用,并且会导致额外的内存消耗。
下面我们通过sidecar来对reviews服务的sidecar进行配置,只创建reviews相关ratings服务的outbound cluster。
创建如下reviews-sidecar.yaml文件,对reviews服务进行配置。
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: reviews
namespace: bookinfo20
spec:
workloadSelector:
labels:
app: reviews
egress:
- hosts:
- "./ratings.bookinfo20.svc.cluster.local"
运行完上述yaml文件后,我们在查下Envoy的内存占用情况

我们看到Envoy实际占用内存大概为降为15M,Envoy实际占用的内存量已经急剧下降从下图中我们可以看到保留下来的outbound cluster都是在当前namespace里的ratings。

但是TCMalloc预留的系统内存仍然为138M,通过查阅相关资料得知TCMalloc是通过三层缓存实现系统内存的申请。在申请内存时,会从上层到下层依次申请,释放内存同理,层与层之间内存的申请和释放都是批量进行的,只有在本层的空闲内存满足一定的条件时,才会批量归还给下层,最终TCMalloc预留的系统内存满足一定条件时批量归还给操作系统,所以内存没有释放是因为没有达到释放的条件。 但是如果条件暂时达不到,操作系统中的资源又紧张岂不是白白造成资源浪费,其他产品还无法使用,因此这里就有了第二种内存优化方法
限制Container的内存大小
通过上面我们可以看到TCMalloc预留的内存为138M左右,远远大于了Envoy实际所需的内存数量,但是系统内存又没满足条件导致预留的内存无法归还给操作系统,因此在这里我们手动限制Container的内存大小,在Deployment里通过在annotations里配置sidecar.istio.io/proxyMemoryLimit:xx可以限制Container最大占用的内存,但是这里也需注意,如果设置的不合理,设置的内存满足不了启动的要求,会导致服务无法起来,报OOM的错误,如下图

我们设置reviews-v1的Container最大内存大小为60M,作为对比我们不修改reviews-v2的Container最大内存大小。

通过前面的优化我们知道,如果全量下发,Envoy占用的内存实际大小是86M,因此我们在当前的namespace里配置好Sidecar,保证配置不全量下发,配置完之后我们查询新的Envoy占用情况如下图

从图中对比我们看到reviews-v1的Container里的TCMalloc预留的内存只有11M左右,预留的内存释放了,而没有做优化的reviews-v2的Container占用的系统内存有94M
性能优化
通过eBPF进行性能优化
我们知道istio利用istio-init初始化容器initContainer中的iptables指令,对所在pod的流量进行劫持,Envoy在注入到pod之后,将原有服务调用从源容器 ->目标容器的通信方式改变为源容器 -> Envoy-> Envoy-> 目标容器,如下图所示

网络连接本质上是 socket 之间的通讯,eBPF提供了一个函数,用来将应用发出的包直接转发到对端的 socket,如下图所示,这样可以加速包在内核中的处理流程。
其中设置三个主要的函数:
SOCK_MAP
记录socket规则的关键部分,根据当前的数据包信息,从SOCK_MAP中挑选一个存在的 socket连接来转发请求
SOCK_OPS
attach到操作系统中的某个cgroup文件系统上,来监听这个cgroup当中所有进程中的socket状态,筛选进入某些进程中的socket,然后可以把他加入SOCK_MAP里面,方便进行下一步的操作
SK_SKB
监听每一个进入某个socket中的sockerbuffer包,对每个包进行操作,可以把他导流到其他socket上,或者选择放过不进行操作或者拒绝掉

此处的性能通过fortio进行测试,通过fortio访问某个namespace里的服务,分别通过设置25万条、100万条、400万条请求获取对应的平均响应毫秒时长,结果如下表所示,通过eBPF优化后,性能大概提升了6%。
LibreOffice/7.0.5.2$Linux_X86_64 LibreOffice_project/64390860c6cd0aca4beafafcfd84613dd9dfb63a
eBPF优化
具体使用的eBPF方案可参考https://github.com/intel/istio-tcpip-bypass
后面会继续参考其他成熟的istio优化方案对istio的性能及内存继续进行调优,也会参考其他代码修改方案进行代码修改获取更好的优化结果。
