在egress上,会首先对目的地址执行映射查找来缺点该报文是否被加密,如果被加密,则找出目的节点上可用的密钥。在选出的节点上挑选最近使用的可用的密钥,并将报文标记为加密。然后改报文会传递到Linux xfrm层执行加密。当接收到现在加密的报文时,它被传递到下一层,或通过发送到Linux 栈进行路由,或(如果正在使用overlay)直接执行尾部调用。
socket层强制(Enforcement):socket层强制会使用两个钩子,socket 操作钩子和socket 发送/接收钩子来监控并附加到所有与Cilium管理的endpoint相关的TCP socket,包括L7代理。socket操作钩子会识别要加速的候选套接字,这些候选套接字包括所有的本地节点连接(endpoint到endpoint)以及所有到Cilium代理的连接。这些标识的连接将会包含所有由socket 发送/接收钩子处理的消息,并且使用sockmap快速重定向进行加速。快速重定向保证Cilium中实现的所有策略对于关联的socket/endpoint映射均有效,并假设它们会直接向对端socket发送消息。sockmap send/recv钩子确保消息不会被上面提到的任何对象处理。
L7策略:L7策略对象将代理的流量重定向到一个Cilium用户空间代理实例中。Cilium使用一个Envoy作为它的用户空间代理。Envoy要么转发流量,要么会根据配置的L7策略生成拒绝消息。
Cilium通过连接这些组件实现了灵活高效的数据路径。下面将展示连接单个节点上的endpoint可能存在的数据流(进入一个endpoint以及endpoint到网络设备)。在每种情况下,都会通过一个额外的图显示启用socket层强制时的可用的TCP加速路径。
Endpoint到Endpoint首先展示的是本地endpoint到endpoint的数据流,在engress和ingress上使用了L7策略。紧跟着展示了启用socket层强制下相同endpoint到endpoint的数据流走向。通过对TCP流量启用socket层强制,发起连接的握手将遍历endpoint策略对象,直到TCP的状态变为ESTABLISHED。最后只有L7策略对象允许之后,连接状态才能变为ESTABLISHED。
从Endpoint出站(Egress from Endpoint)下面展示了本地endpoint使用overlay网络进行出站的场景。overlay网络流量通过与overlay对应的Linux网络接口进行转发。默认的overlay接口称为cilium_vxlan。与上面类似,通过启用socket层强制并使用L7代理可以避免在TCP通信的endpoint和L7策略之间运行endpoint策略块。L3加密块可以在启用时加密报文。
入站到Endpoint(Ingress to Endpoin)最后展示了使用overlay网络时入站到endpoint的场景。与上面类似,通过启用socket层强制可以避免在代理和endpoint socket之间遍历策略集。如果接收到的报文被加密,则首先需要进行解密,并使用正常流程处理。
基于veth的数据路径和基于ipvlan的数据路径的对比|基于ipvlan的数据路径目前仅在技术预览中,用于实验目的。该限制会在后续的Cilium发布中移除。
默认的Cilium CNI运行在基于veth的数据路径模式下,由于所有的BPF程序都由Cilium在主机网络命名空间之外进行管理,因此使用该模式可以获得更大的灵活性,这样容器就可以被授予其命名空间(如CAP_NET_ADMIN)的特权,而不会影响安全性(因为容器无法访问主机中的BPF强制点)。鉴于BPF程序是从主机的网络名称空间附加的,BPF也能够接管并有效地管理本地容器到主机之间的转发逻辑。但由于veth模式下,网络栈内部在处理从一个veth设备到位于另一个网络命名空间中的对端设备时需要重新遍历网络栈,因此会造成延迟。当在本地Cilium endpoint间进行通信时,这种出站到入站的转变需要执行两次,对于正在到达或从主机发送出去的数据包,则为一次。
对于更具延迟优化的数据路径,Cilium CNI还支持ipvlan L3/L3S模式,但存在大量限制。为了支持老的且不存在ipvlan发夹模式的内核,Cilium会在tc egress层将BPF程序附加到位于容器的网络命名空间内的slave设备上,意味着这种数据路径模式只能用于使用非CAP_NET_ADMIN 和CAP_NET_RAW特权运行的容器。ipvlan使用内部转发逻辑直接进行slave-to-slave或slave-to-master的重定向,因此BPF程序本身不会执行到设备的转发。由于网络栈不需要像基于veth的数据路径一样在处理外部报文时重新遍历,因此能够更有效地切换命名空间。主机到容器网络命名空间的切换直接发生在L3层,在后续的ingress处理中无需排队和重新调度。在本地endpoint之间通信的情况下,会执行一次egress-to-ingress的且华北,而不必执行两次。