Istio中的Sidecar注入、透明流量劫持及流量路由过程详解
本文基于Istio1.13版本,介绍了sidecar模式及其优势sidecar如何注入到数据平面,Envoy如何做流量劫持和路由转发的,包括Inbound流量和Outbound流量。
为了理解本文希望你先阅读以下内容:
本文基于Istio1.13版本,将为大家介绍以下内容:
请大家结合下图理解本文中的内容,本图基于Istio官方提供的Bookinfo示例绘制,展示的是reviewsPod的内部结构,包括LinuxKernel空间中的iptables规则、Sidecar容器、应用容器。
productpage访问reviewsPod,入站流量处理过程对应于图示上的步骤:1、2、3、4、EnvoyInboundHandler、5、6、7、8、应用容器。
reviewsPod访问rating服务的出站流量处理过程对应于图示上的步骤是:9、10、11、12、EnvoyOutboundHandler、13、14、15。
注意:图中的路径16近用于路由规则说明,它不不出现在当前示例中。实际上仅当Pod内发出的对当前Pod内的服务访问的时候才会途径它。
上图中关于流量路由部分,包含:
在阅读下文时,请大家确立以下已知点:
将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如Kubernetes中的Pod)可以被视为sidecar模式。如下图所示,sidecar模式允许您在应用程序旁边添加更多功能,而无需额外第三方组件配置或修改应用程序代码。
就像连接了Sidecar的三轮摩托车一样,在软件架构中,Sidecar连接到父应用并且为其添加扩展或者增强功能。Sidecar应用与主应用程序松散耦合。它可以屏蔽不同编程语言的差异,统一实现微服务的可观测性、监控、日志记录、配置、断路器等功能。
使用sidecar模式部署服务网格时,无需在节点上运行代理,但是集群中将运行多个相同的sidecar副本。在sidecar部署方式中,每个应用的容器旁都会部署一个伴生容器(如Envoy或MOSN),这个容器称之为sidecar容器。Sidecar接管进出应用容器的所有流量。在Kubernetes的Pod中,在原有的应用容器旁边注入一个Sidecar容器,两个容器共享存储、网络等资源,可以广义的将这个包含了sidecar容器的Pod理解为一台主机,两个容器共享主机资源。
因其独特的部署结构,使得sidecar模式具有以下优势:
下文将从以下几个方面讲解:
为了查看iptables配置,我们需要登陆到sidecar容器中使用root用户来查看,因为kubectl无法使用特权模式来远程操作docker容器,所以我们需要登陆到productpagepod所在的主机上使用docker命令登陆容器中查看。
我们仅查看与productpage有关的iptables规则如下,因为这些规则是运行在该容器特定的网络空间下,因此需要使用nsenter命令进入其网络空间。进入的时候需要指定进程ID(PID),因此首先我们需要找到productpage容器的PID。对于在不同平台上安装的Kubernetes,查找容器的方式会略有不同,例如在GKE上,执行dockerps-a命令是查看不到任何容器进程的。下面已minikube和GKE两个典型的平台为例,指导你如何进入容器的网络空间。
经过上面的步骤,你已经可以查看到init容器向Pod中注入的iptables规则,如下所示。
下图展示了ISTIO_ROUTE规则的详细流程。
规则1
规则2、5
规则3、6
规则4、7
规则8
规则9
以上规则避免了Envoy代理到应用程序的路由在iptables规则中的死循环,保障了流量可以被正确的路由到Envoy代理上,也可以发出真正的出站请求。
关于RETURNtarget
你可能留意到上述规则中有很多RETURNtarget,它的意思是,指定到这条规则时,跳出该规则链,返回iptables的调用点(在我们的例子中即OUTPUT)后继续执行其余路由规则,在我们的例子中即POSTROUTING规则,把流量发送到任意目的地址,你可以把它直观的理解为透传。
关于127.0.0.6IP地址
通过上文,你已经了解了Istio是如何在Pod中做透明流量劫持的,那么流量被劫持到Envoy代理中之后是如何被处理的呢?流量路由分为Inbound和Outbound两个过程,下面将根据上文中的示例及sidecar的配置为读者详细分析此过程。
InboundHandler的作用是将iptables拦截到的downstream的流量转发给Pod内的应用程序容器。在我们的实例中,假设其中一个Pod的名字是reviews-v1-545db77b95-jkgv2,运行istioctlproxy-configlistenerreviews-v1-545db77b95-jkgv2--port15006查看该Pod中15006端口上的监听器情况,你将看到下面的输出。
reviewsPod中的Iptables将入站流量劫持到15006端口上,从上面的输出我们可以看到Envoy的InboundHandler在15006端口上监听,对目的地为任何IP的9080端口的请求将路由到inbound|9080||Cluster上。
从该Pod的Listener列表的最后两行中可以看到,0.0.0.0:15006/TCP的Listener(其实际名字是virtualInbound)监听所有的Inbound流量,其中包含了匹配规则,来自任意IP的对9080端口的访问流量,将会路由到inbound|9080||Cluster,如果你想以Json格式查看该Listener的详细配置,可以执行istioctlproxy-configlistenersreviews-v1-545db77b95-jkgv2--port15006-ojson命令,你将获得类似下面的输出。
Envoy监听在15001端口上监听所有Outbound流量,OutboundHandler处理,然后经过virtualOutboundListener、0.0.0.0_9080Listener,然后通过Route9080找到上游的cluster,进而通过EDS找到Endpoint执行路由动作。
ratings.default.svc.cluster.local:9080路由
运行istioctlproxy-configroutesreviews-v1-545db77b95-jkgv2--name9080-ojson查看route配置,因为sidecar会根据HTTPheader中的domains来匹配VirtualHost,所以下面只列举了ratings.default.svc.cluster.local:9080这一个VirtualHost。
outbound|9080||ratings.default.svc.cluster.local集群的端点
运行istioctlproxy-configendpointreviews-v1-545db77b95-jkgv2--port9080-ojson--cluster"outbound|9080||ratings.default.svc.cluster.local"查看集群的Endpoint配置,结果如下。
本文使用了Istio官方提供的bookinfo示例,按图索骥得带领读者了解了sidecar注入、iptables透明流量劫持及sidecar中流量路由背后的实现细节。Sidecar模式和流量透明劫持是Istio服务网格的特色和基础功能,理解该功能的背后过程及实现细节,将有助于大家理解ServiceMesh的原理,因此希望读者可以在自己的环境中从头来试验一遍以加深理解。
目前Istio使用iptables实现透明劫持,主要存在以下三个问题:
上述几个问题并非在所有场景中都存在,比方说某些场景下,连接数并不多,且NAT表未被使用到的情况下,iptables是一个满足要求的简单方案。为了适配更加广泛的场景,透明劫持需要解决上述三个问题。
为了优化Istio中的透明流量劫持的性能,业界提出了以下方案。
使用Merbridge开源项目利用eBPF劫持流量
Merbridge利用eBPF的sockops和redir能力,可以直接将数据包从inboundsocket传输到outboundsocket。eBPF提供了bpf_msg_redirect_hash函数可以直接转发应用程序的数据包。
使用tproxy处理inbound流量
tproxy可以用于inbound流量的重定向,且无需改变报文中的目的IP/端口,不需要执行连接跟踪,不会出现conntrack模块创建大量连接的问题。受限于内核版本,tproxy应用于outbound存在一定缺陷。目前Istio支持通过tproxy处理inbound流量。
使用hookconnect处理outbound流量
为了适配更多应用场景,outbound方向通过hookconnect来实现,实现原理如下:
无论采用哪种透明劫持方案,均需要解决获取真实目的IP/端口的问题,使用iptables方案通过getsockopt方式获取,tproxy可以直接读取目的地址,通过修改调用接口,hookconnect方案读取方式类似于tproxy。
实现透明劫持后,在内核版本满足要求(4.16以上)的前提下,通过sockmap可以缩短报文穿越路径,进而改善outbound方向的转发性能。
下面是本文的几次更新说明。
2020年4月27日,第一版,基于Istio1.5
2022年1月17日,第二版,基于Istio1.11
Istio1.11与Istio1.1中的sidecar注入和流量劫持环节最大的变化是:
2022年4月24,第三版,基于Istio1.13
Istio1.13相比Istio1.11的变化是istioctlproxy-config命令的输出有了较大变化。