背景知识:

  • 在软件开发中复杂度是不灭的,只是在不同的部分之间做转移。将服务治理抽离为单独的一层就要面对流量链路的增长以及运维难度的提升,且服务网格需要在云原生的环境中使用,这对于运维的专业能力及工程实践经验有了更高的要求。所以说技术只是用于解决问题的工具,服务网格能带来的价值还是得从应用的实际情况出发。
  • 网络是 Kubernetes 的核心部分,涉及了 Pod 间通信、Pod 和服务间通信以及服务与外部系统间的通信等。Kubernetes 集群中使用 CNI 插件(Calico、Flannel、Weave Net来管理其容器网络功能,使用 Kube-proxy 维护节点上的网络规则,譬如使发往 Service 的流量(通过ClusterIP 和端口)负载均衡到正确的后端 Pod。
  • 为了支持网络协议栈的多个实例,Linux在网络协议栈中引入了网络名称空间(NetworkNamespace),这些独立的协议栈被隔离到不同的命名空间中。处于不同的命名空间的网络协议栈是完全隔离的,彼此之间无法进行网络通信,就好像两个“平行宇宙”。通过这种对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境,而Docker正是利用这种网络名称空间的特性,实现了不同容器之间的网络隔离。在Linux的网络命名空间内可以有自己独立的Iptables来转发、NAT及IP包过滤等功能
  • kube-proxy 设置是全局的,无法针对每个服务进行细粒度控制;而且 kube-proxy 只是专在网络数据包级别上运行。它无法满足现代应用程序的需求,如应用层流量管理、跟踪、身份验证等。
  • CNI 网络插件使用封装网络模型(例如 Virtual Extensible Lan,缩写是 VXLAN)或非封装网络模型(例如 Border Gateway Protocol,缩写是 BGP)来实现网络结构。Calico使用封装(VXLAN,IPIP)或未封装。

服务网格Sidecar/Sidecarless _sidecar  sidecarless

  • Geneve 相较于 VXLAN 封装,具有更加灵活、安全、扩展和运维的特点,适用于更加复杂和安全性要求高的虚拟化网络环境。Geneve(Generic Network Virtualization Encapsulation)是一种网络虚拟化封装Encapsulation(隧道tunnel)协议,它的设计的初衷是为了解决当前数据传输缺乏灵活性和安全性的问题。
  • Sidecarless 模式中,由 Mesh CNI 网络插件在每个节点上,设置了两个虚拟网络接口(Geneve Device)- istioin 和 istioout;在每个 ztunnel Pod 上,设置了两个虚拟网络接口(Geneve Device - pistioin 和 pistioout。节点与 ztunnel 通过 Geneve 隧道(Geneve tunnel )连接。节点网络空间的Geneve设备(istioin)<--->Geneve隧道(Geneve tunnel )<---> ztunnel pod网络空间的Geneve设备(pistioin)
  • veth 设备与 netlink 机制之间存在关联。netlink 是 Linux 内核中的一种通信机制,用于内核与用户空间之间的通信。通过 netlink 机制,用户空间可以向内核发送请求,获取网络设备、路由表、套接字等信息,实现网络配置和管理等功能。在容器化技术中,veth 设备的创建和配置通常是通过 netlink 机制实现的。使用以下类似命令创建一个名为 istioin 的 Geneve 设备
ip link add name istioin type geneve id 1000 remote "${ZTUNNEL_IP}"
  • 主机上的虚拟网络接口 vethX/cali2552a28489a,位于主机的根网络命名空间,一方面与容器的以太接口 eth0 间通过隧道相连(容器的eth0与主机网络命名空间的vethX/caliX组成通道),发送到任何一端的网络包都会直达对端;另一方面应该与主机命名空间上的网桥cni0/virbr0相连;
  • veth pair 全称是 Virtual Ethernet Pair,是一对设备,所有从这对端口一端进入的数据包都将从另一端出来,反之也是一样。在一个宿主机操作系统(网络协议栈可以定义多个Network Namespace),不同的 Network Namespace(网络协议栈的多个实例:linux可以创建多个NS) 直接进行通信;不同节点使用隧道技术。
  • 作为虚拟网线用于连接两个虚拟网络设备,veth pair是根据数据链路层的MAC地址对网络数据包进行转发的过程来实现的,本质是反转通讯数据的方向,需要发送的数据会被转换成需要收到的数据重新送入内核网络层进行处理,从而间接的完成数据的注入veth pair在虚拟网络设备中是作为“网线“的存在,将tap(是二层设备)之间,tap与Bridge之间连接起来(Linux tap interfaces created with ip tuntap命令 cannot be used to attach network namespaces to linuxbridges or the openvswitch.——tap不能、tun可以)。

服务网格Sidecar/Sidecarless _sidecar  sidecarless_02

  • 引入veth pair 是为了在不同的 Network Namespace 直接进行通信。veth pair只能实现两个网络接口之间的通信;想实现多个网络接口之间的通信,使用下面网桥(Bridge):Linux 内核是通过一个虚拟的网桥设备(Net Device)来实现桥接的。veth桥接在cni0的网桥上
  • 连接bridge(也需有veth设备)和veth pair。以下命令将系统空间中的veth10添加到网桥中。
  • k8s主机内组网模型是veth pair+bridge的方式:k8s使用veth pair将容器主机的网络协议栈连接起来,从而使数据包可以进出pod。容器放在主机根network namespace中,veth pair的一端连接到linux网桥,可以让同一节点上的各pod之间相互通信。
# ip link add <p1-name> type veth peer name <p2-name> 
//创建veth pair,然后将veth pair一端的虚拟网卡加入到namespace,再将另一端通过brctl addif命令加入到网桥上。
# brctl addif br0 veth20
[root@worker2 ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.4a6812e35d87       no              veth20
  • veth和tap/tun类似,也是linux提供的一种虚拟网络设备;但与tap/tun不同的是,veth总是成对出现的,从一端进入的数据包将会在另一端出现,因此又常常称为veth pair。我们可以把veth pair看成一条网线两端连接的两张以太网卡。
  • tun/tap 设备与物理网卡的区别:
  1. 对于硬件网络设备而言,一端连接的是物理网络,一端连接的是网络协议栈
  2. 对于 tun/tap 设备而言,一端连接的是应用程序(通过字符设备文件 /net/dev/tun),一端连接的是网络协议栈。
  3. tap/tun 是Linux内核 2.4.x 版本之后使用软件实现的虚拟网络设备,这类接口仅能工作在内核中。不同于普通的网络接口,没有物理硬件(因此也没有物理线路连接到这类接口)。
  4. tap接口和tun接口的区别是,tap接口会会输出完整的以太帧,而tun接口会输出IP报文(不含以太头)。可以在创建接口时指定该接口是tun接口还是tap接口。
  5. tun/tap 的最主要应用场景,就是vpn ,tunnel。
  • Apache APISIX 是一个动态、实时、高性能的云原生 API 网关,Amesh是 Apache APISIX的服务网格库。它适配了 xDS 协议,可以从诸如 Istio 的控制平面中接收数据,并生成 APISIX 所需的数据结构,可以让 APISIX 替代 Istio 所使用的 Envoy 组件来接管集群流量。而网格内部的所有流量都将由 APISIX 接管。其中,APISIX 的配置中心被设置为 Amesh,这使得 APISIX 脱离 etcd 的依赖。在最近发布的的 v0.2 版本中,Amesh 新增了可选的控制面 amesh-controller 组件,它为用户提供了配置 APISIX 插件的能力,使 APISIX 众多的插件在服务网格场景下也能开箱即用,而无需用户进行自定义的开发。而常规的 Istio 部署,用户需通过 Lua 或 Wasm 来对 Envoy 进行功能扩展。
  • 为什么使用 TPROXY 方式呢?TPROXY 是 Linux 内核的一个功能,允许在传输层透明地拦截和重定向网络流量,相比之下,使用 REDIRECT 目标修改数据包以更改目标地址。使用 TPROXY,这样请求从pod通过隧道到 ztunnel 代理的所有跳跃中,流量的原始源 IP 和端口被保留,允许目标服务器看到原始客户端的 IP 地址和端口。


1、 Sidecar 代理模式服务网格通过 Sidecar 代理将流量控制从 Kubernetes 的服务层中解耦,将代理注入到每个 Pod;并通过控制平面操纵管理这些分布式代理,从而可以更加精细地控制这些服务之间的流量。

  • sidecar 模式可以让应用程序在不对代码进行重大修改的前提下获得 Istio 提供的各种好处(流量治理,应用安全,可观察性等)。
  • Istio 在单一的架构组件 sidecar 中实现了从基本的加密到高级的 L7 策略的所有数据平面功能。在实践中,这使得 sidecar 成为一个要么全选,要么没有的功能组件
  • 服务网格通过 Sidecar 代理将流量控制从 Kubernetes 的服务层中解耦,将代理注入到每个 Pod;并通过控制平面操纵管理这些分布式代理,从而可以更加精细地控制这些服务之间的流量
  • 当前 Istio 实现中涉及了 TCP/IP 堆栈的开销,它使用了 Linux 内核的 netfilter 通过配置 iptables 来拦截流量,并根据配置的规则对进出 sidecar 代理的流量进行路由。
  • 客户端 pod 到服务器端 pod 之间的典型路径(即使在同一主机内)至少要遍历 TCP/IP 堆栈 3 次(出站、客户端 Sidecar Proxy 到服务器端 Sidecar Proxy、入站)。为了解决这个网络数据路径问题,业界通过引入 eBPF 绕过 Linux 内核中的 TCP/IP 网络堆栈来实现网络加速,这不但降低了延迟,同时也提升了吞吐量。当然,eBPF 并不会替换 Envoy 的七层代理能力,在七层流量管理的场景下,仍然是 4 层 eBPF + 7 层 Envoy 代理的融合模式。只有针对 4 层流量网络的情况下,跳过代理 pod 直接进入网络接口(下面右图)

服务网格Sidecar/Sidecarless _sidecar  sidecarless_03

2、Sidecarless 模式   Ambient Mesh

  • Sidecar 并非唯一的一种服务网格实现模式,除此之外还有 DaemonSet 模式及 Ambient mesh 模式。Istio 支持 sidecar 与 ambient mesh 无缝互通。
  • 传统的 Sidecar 代理模式之外,业界开始在探索一种新型的数据平面模式。它的设计理念是:将数据平面分层,4 层用于基础处理,特点是低资源、高效率;7 层用于高级流量处理,特点是功能丰富,当然需要更多的资源。用户可以根据所需功能的范围,以渐进增量的方式采用服务网格技术。数据面 L4 与 L7 代理的解耦模式下,Service Mesh 的网络拓扑将是什么形式?
  • 一方面,将 L4 Proxy 能力下移到 CNI 组件中,L4 Proxy 组件以 DaemonSet 的形式运行,分别运行在每个节点上。这意味着它是为一个节点上运行的所有 pod 提供服务的共享基础组件。
  • 另一方面,L7 代理不再以 Sidecar 模式存在,而是解耦出来,我们称之为 Waypoint Proxy, 它是为每一个 Service Account 创建的 7 层代理 pod。从Kubernetes 的角度来看,waypoint proxy 只是普通的 pod,可以像其他Kubernetes 工作负载一样进行自动伸缩。
  • 不影响应用程序是使 Ambient Mesh 比传统的 Sidecar 模式具备更少侵入性的原因之一。与采用 Sidecar 模式时必须将 Sidecar 代理注入到每个应用程序部署中相比,Ambient 模式下无需以任何方式重新部署或修改现有应用程序。通过不重新部署和直接修改应用程序,可以有效地降低落地风险并简化采用 Mesh 的落地曲线。
  • Ambient Mesh 的设计是非侵入式的,并且仅对存在特定标记的命名空间并使现有应用程序成为 Ambient Mesh 的一部分,可以逐步采用。一旦应用程序成为 Ambient Mesh 的一部分,它立即获得 mTLS 和 L4 可观察性功能。
  • 在 ambient 模式下,服务网格的能力是通过应用 pod 之外的 ztunnel 和 waypoint proxy 提供的,不再需要对应用 pod 进行 sidecar 注入,较好地解决了 Istio sidecar 模式下应用和 sidecar 的部署依赖问题。
  •  ztunnel 和 istio-cni 以 daemonset 方式部署在每个 node 上。与 Sidecar 模式下不同,CNI 插件所做的配置不会直接影响任何工作负载 pod。更改仅在节点网络命名空间和 ztunnel pod 的网络命名空间中进行,与流量重定向机制无关。——CNI 插件istio-cni 在每个节点上配置网络命名空间,以将进出节点的流量透明地路由到节点,并相应地将其路由到 ztunnel 代理(在 ztunnel pod 的网络命名空间中配置路由)。
  • Sidecarless 模式中 CNI 插件在两个位置进行网络命名空间的配置,其中一个就是在所处的节点上,另一个是在 ztunnel pod 上。。
  • 网格 CNI 插件为节点进行配置的内容可以分为 4 个部分:
  • 1. 创建名为 ztunnel-pods-ips 的 IP 集(ipset),每次添加一个 pod 时,CNI 插件将 pod 的 IP 地址添加到节点上的 ztunnel-pods-ips IP 集(ipset)中,CNI 插件会让节点上的 ipset 保持最新。
  • 2. 由 Mesh CNI 网络插件创建网络接口(Geneve Device),在每个节点上,设置了两个虚拟网络接口(Geneve Device) - istioin 和 istioout,与其他网络设备进行通信和交换数据。结合节点上的 iptables 规则和路由表,确保来自 ambient pods 的流量被拦截,并根据方向(入站或出站)分别发送到 istioout 或 istioin,发送到这些接口的数据包最终会到达在同一节点上运行的 ztunnel pod 的 pistioout 或 pistioin。
  • 3. 设置网络接口的参数: rp_filter 参数设置为 0,关闭反向路径过滤,绕过数据包源 IP 地址的校验; accept_local 参数设置为 1,以接受本地生成的数据包
  • 4. 设置 iptables、路由表和路由规则:将数据包从现有的表(NAT 和 MANGLE)中的标准链(例如 PREROUTING,OUTPUT 等)重定向到自定义的 ztunnel 链。任何标记为 0x100/0x100 的数据包(即来自 Ambient pod)根据对应的路由表中的规则进行路由。
  • 网格 CNI 插件为 ztunnel Pod 零信任隧道代理简称为 ztunnel进行配置的内容可以分为 3 个部分:
  • 1. 由 Mesh CNI 网络插件创建网络接口(Geneve Device):在每个 ztunnel Pod 上,设置了两个虚拟网络接口(Geneve Device)- pistioin 和 pistioout。
  • 2. 设置网络接口的参数:网格 CNI 插件将为 ztunnel pod 设置网络接口,rp_filter 参数设置为 0,关闭反向路径过滤,绕过数据包源 IP 地址的校验。
  • 3. 为 ztunnel pod 设置 iptables、路由表和路由规则。iptables 规则使用 TPROXY 标记(0x400/0xfff)标记来自入站或出站隧道的数据包,并将其定向到相应的 ztunnel 入站和出站端口。在 ztunnel pod 上,pistioin 接口接收到的任何内容都会被转发到端口 15008(HBONE)和 15006(纯文本);同样,pistioout 接口接收到的数据包最终会到达端口 15001。
  • Geneve(Generic Network Virtualization Encapsulation)是一种网络虚拟化封装隧道)协议,它的设计的初衷是为了解决当前数据传输缺乏灵活性和安全性的问题。Geneve 相较于 VXLAN 封装,具有更加灵活、安全、扩展和运维的特点,适用于更加复杂和安全性要求高的虚拟化网络环境。

服务网格Sidecar/Sidecarless _sidecar  sidecarless_04

istioin 和 pistioin 之间的隧道使数据包落在 ztunnel pod 上。

ip link add name istioin type geneve id 1000 remote 192.168.126.2

节点网络空间的Geneve设备(istioout)<--->Geneve隧道(Geneve tunnel )<---> ztunnel pod网络空间的Geneve设备(pistioout)

*:虚拟接口 istioin的 IP 地址为 192.168.126.1;type geneve 表示创建一个 Geneve 设备,id 1000 表示设备的虚拟网络标识符,remote IP 地址表示设备的远程目的地址192.168.126.2。

  • veth 设备与 netlink 机制之间存在关联。netlink 是 Linux 内核中的一种通信机制,用于内核与用户空间之间的通信。通过 netlink 机制,用户空间可以向内核发送请求,获取网络设备、路由表、套接字等信息,实现网络配置和管理等功能。在容器化技术中,veth 设备的创建和配置通常是通过 netlink 机制实现的
  • 为何不在本地节点上进行 L7 处理?Ambient mesh 采用一个部署在本地节点上的共享 ztunnel 代理来处理 mesh 的零信任方面,而 L7 的处理则交给了独立部署 的 waypoint proxy pod。 为何要这么麻烦地将流量从 ztunnel 转接到 waypoint proxy,而不是直接在节点上使用一个共享的完整 L7 代理呢?与 waypoint proxy 所需的 L7 处理相比,ztunnel 所提供的 mTLS 和 L4 功能需要的 CPU 和内存占用要小得多。通过将 waypoint proxy 作为一个共享的 namespace 基本的资源来运行,我们可以根据该 namespace 的需求来对它们进独立伸缩,其成本不会不公平地分配给不相关的租户。