摘要:今年9月份,Istio社区宣布Ambient Mesh开源,由此引发国内外众多开发者的热烈讨论。
本文分享自华为云社区《深度剖析!Istio共享代理新模式Ambient Mesh》,作者:华为云云原生团队。
今年9月份,Istio社区宣布Ambient Mesh开源,由此引发国内外众多开发者的热烈讨论。实际上,通过与Istio TOC成员linsun(https://github.com/linsun)的交流,我们得知早在2021年,http://Solo.io已经开始共享代理的研究和设计,同样也是在2021年Google内部也在探索共享代理模式。因此,两家公司一拍即合,今年4~5月份开始以协作开发的方式,加速共享代理模式的发展。
目前Ambient Mesh已经发布预览版本,感兴趣的读者可以按需体验。由于篇幅限制,本文主要针对Ambient Mesh架构及四层流量治理的流程进行深度剖析,关于七层流量治理的详解,请关注后续文章。
简单来讲,Ambient Mesh是Istio服务网格的一种共享代理的新模式。它是由 Google 和 http://Solo.io 联合开发的一种新的数据面模式,旨在简化操作、提升应用兼容性并降低基础设施成本。Ambient模式可在放弃引入 Sidecar 的情况下,保持 Istio 的零信任安全、流量管理和遥测等核心功能。
开始Ambient的架构之前,我们先简单回顾一下Istio 的架构。其主要由两部分组成,分别是控制面和数据面。控制面Istiod进行基本的配置生成和推送,管理着所有的数据面;数据面引入 Sidecar 代理,接管应用的入口和出口流量。
图1 Istio架构
相比Sidecar,Ambient Mesh提供一种侵入性更低,升级管理更简单的选择。Ambient 将 Istio 的功能分成两个不同的层次,安全覆盖层(四层治理)和 七层 处理层(七层治理):
图2 Ambient mesh分a层
• 安全覆盖层:处理TCP路由、监控指标、访问日志,mTLS 隧道,简单的授权
• 七层处理层:除安全覆盖层的功能外,提供HTTP协议的路由、监控、访问日志、调用链、负载均衡、熔断、限流、重试等流量管理功能以及丰富的七层授权策略
Ambient Mesh下的负载可与 Sidecar 模式下的负载无缝互通,允许用户根据自己的需要来混合搭配使用两种模式。
• 四层治理架构:在 Sidecar 模式下,Istio 通过 InitContainer 或 Istio-CNI 实现流量拦截。Ambient Mesh下 Istio-CNI 是必选组件,下图展示了基本的 Ambient Mesh四层治理架构:
图3 Ambient mesh四层治理架构
Istio-CNI 中,新增 Ambient 的处理模块 ,该模块会监听 Namespace 以及 Pod 的变化,为所在节点的应用设置路由和iptables规则:
• 路由:设置路由表,将本节点应用发出的流量路由到 ztunnel,以及将本节点接收的流量路由到ztunnel。
• iptables:在ztunnel容器中设置iptables规则,将流量透明拦截至 ztunnel 对应的端口上。
ztunnel 是 Ambient 新引入的组件,以 Daemonset 的方式部署在每个节点上。ztunnel 为网格中的应用通信提供 mTLS、遥测、身份验证和 L4授权功能,不执行任何七层协议相关的处理。只有当ztunnel运行在工作负载相同的节点上时,控制面才会将工作负载证书颁发给该 ztunnel。因此 当ztunnel 被攻击时,只有运行在该节点上的负载的证书可能被盗用,安全风险相对可控,这和其他实现良好的节点共享基础设施类似。
• 七层流量治理架构:目前 Ambient Mesh需要通过定义一个 Gateway API 资源显式对某个服务启用七层处理。下图展示了Ambient七层治理的架构:
图4 Ambient mesh七层治理架构
与Ambient四层治理相比,七层治理架构中新增了waypoint组件。Pilot 中新增了waypoint 的处理模块 , 它监听 ServiceAccount、Deployment、Gateway API对象的变化,然后调协相关的 waypoint对象:
• ServiceAccount发生变化时,Pilot会尝试更新当前命名空间下所有的 waypoint
• Deployment发生变化时,通过其OwnerReference 关联的 Gateway对象,触发waypoint的维护
• Gateway发生变化时,更新关联的 waypoint 代理
当前 Ambient创建 waypoint 代理需要依赖类似下面的Gateway API 资源:
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
name: productpage
annotations:
istio.io/service-account: bookinfo-productpage
spec:
gatewayClassName: istio-mesh
gatewayClassName 值必须设置为 istio-mesh,否则可能会被忽略。每个 ServiceAccount 都有自己专用的 waypoint 代理,这点与 Sidecar 模型非常相似。建议每个服务使用自己单独的身份,避免带来额外的安全风险。
Pilot会将 七层七层 流量规则通过 xDS 更新至 waypoint 代理,实现 七层 相关流量治理能力。waypoint 代理不一定保证与其所服务的工作负载位于同一节点,这似乎会带来一定的性能问题。但是对于 Istio 来说,延迟更多来自于复杂的 七层 处理,预计最终Ambient模式的七层治理延迟与 Sidecar 模式接近。waypoint 代理通过单独Deployment部署,因此可为其单独配置所需要的 CPU、内存,设置相关 的HPA弹性伸缩策略,不再与应用耦合,提供更加灵活的扩展性并可以在一定程度上提升资源的使用率。
我们知道ztunnel只能进行四层的流量治理,四层负载均衡以及TLS流量加密、基本的认证和鉴权等,而不能进行更高级的七层路由和认证鉴权。这里我们通过sleep应用访问bookinfo的例子,深入理解Ambient Mesh的四层流量是如何路由的。本例子的实际环境背景如下,sleep和productpage应用分别运行在两个不同的节点。
图5 Ambient mesh四层流量代理流程
• 在sleep容器内访问productpage服务,首先请求被拦截到同节点的ztunnel中,ztunnel做基本的四层负载均衡和TLS加解密,最后选择一个目标实例(productpage容器的IP)将本次请求转发出去。
• 本次请求进入productpage容器所在的节点,首先被拦截到ztunnel, ztunnel负责TLS流量的解密,然后执行用户指定的鉴权策略,最后将请求发往productpage容器。
以上就是Ambient mesh流量转发的一个基本流程,下面我们结合具体的xDS配置深入理解完整的通信流程。
(1)sleep访问productpage的流量被同节点的tunnel以TPROXY(透明代理)方式拦截转发到ztunnel(监听127.0.0.1:15001),使用TPROXY的好处是保留原始的目的地址,ztunnel做转发时必须依赖原始目的地址。
-A PREROUTING -i pistioout -p tcp -j TPROXY --on-port 15001 --on-ip 127.0.0.1 --tproxy-mark 0x400/0xfff
(2)ztunnel通过"ztunnel_outbound"监听器,监听在15001端口。ztunnel_outbound监听器与Istio Sidecar模式的监听器完全不同,它包含所有本节点上的服务到整个网格其他服务的过滤器链。

图6 ztunnel_outbound监听器
可以看到所有的过滤器链都没有设置匹配条件(默认全部匹配),那么这时ztunnel怎么根据流量特征选择目标过滤器链的呢?原来在监听器根上还有一种设置过滤器匹配条件的方式,通过下面匹配到源地址为10.244.1.4,目的地址为10.96.179.71,目的端口为9080的流量,交由"spiffe://cluster.local/ns/default/sa/sleep_to_http_productpage.default.svc.cluster.local_outbound_internal"过滤器处理,
图7 ztunnel_outbound过滤器链匹配
(3)"spiffe://cluster.local/ns/default/sa/sleep_to_http_productpage.default.svc.cluster.local_outbound_internal"过滤器关联到同名的Cluster。该Cluster一共包含两个Endpoint实例,根据负载均衡算法选择某一个Endpoint,并且最重要的是将metadata(tunnel的destination和address)传递给名为"outbound_tunnel_lis_spiffe://cluster.local/ns/default/sa/bookinfo-productpage" 的监听器处理。
图8 outbound_internal内部Cluster配置
图9 outbound_internal内部Cluster Endpoint
(4)“outbound_tunnel_lis_spiffe://cluster.local/ns/default/sa/sleep”监听器通过“set_dst_address”过滤器根据上一步的选择的Endpoint的metadata设置数据的目的地址。假如前面outbound_internal Cluster选择的是10.244.2.8:9080这个Endpoint,那么这里的tunnel监听器将把10.244.2.8:15008设置为目的地址。另外该监听器,只有一个TcpProxy,关联到名为“outbound_tunnel_clus_spiffe://cluster.local/ns/default/sa/ sleep”的Cluster,那么流量自然交由该Cluster处理。TCP过滤器上还设置了HTTP Connect隧道(承载发送到10.244.2.8:9080的流量),供后面productpage所在节点的ztunnel使用。HTTP隧道是Ambient Mesh 组件之间安全通信的承载协议。
图10 outbound_tunnel监听器配置
outbound_tunnel Cluster的类型为“ORIGINAL_DST”,并且配置有UpstreamTlsContext, 因此它负责流量TLS加密,然后直接发送到目的地址,即10.244.2.8:15008。
图11 outbound_tunnel cluster配置
(1)sleep访问productpage的流量(目的地址是“10.244.2.8:15008”)到达productpage所在的节点,以TPROXY(透明代理)方式被拦截到ztunnel(监听127.0.0.1:15008),使用TPROXY的好处是保留原始的目的地址,ztunnel做转发时必须依赖原始目的地址。
10.244.2.8 via 192.168.126.2 dev istioin table 100 src 10.244.2.1
-A PREROUTING -i pistioin -p tcp -m tcp --dport 15008 -j TPROXY --on-port 15008 --on-ip 127.0.0.1 --tproxy-mark 0x400/0xfff
(2)ztunnel上面“ztunnel_inbound”监听器监听在15008端口,因此流量首先经过ztunnel_inbound监听器的处理。ztunnel_inbound监听器上面设置了TLS,根据其配置与下游进行TLS握手,从而所有的ztunnel之间基于双向TLS加密通信。另外,从下面配置中可以看到,CONNECT 升级已经设置,那么Envoy就会代理HTTP的Connect请求。除此之外,RouteMatch中设置了connectMatcher,意味着将HTTP Connect请求交由“virtual_inbound”Cluster处理。
图12 ztunnel_inbound监听器配置
(3)virtual_inbound Cluster类型为ORIGINAL_DST,并且设置使用x-envoy-original-dst-host HTTP Header重写原始目的地址,而此Header恰恰由发送侧的“outbound_tunnel_lis_spiffe://cluster.local/ns/default/sa/sleep”监听器设置,值为10.244.2.8:9080。因此本次请求通过virtual_inbound最终成功发送给productpage容器。
图13 virtual_inbound cluster配置
图14 完整的服务访问四层代理
sleep访问productpage的实例中,虽然我们使用的是HTTP协议,但是从Ambient所有的组件视角来看,其代理的为TCP流量。前面我们深入分析了ztunnel中每一个监听器、每一个Cluster的工作原理,看起来可能会很复杂。故在此通过图14进行一个概要的总结,我们发现在通信的过程中,实际参加工作的模块并不多:
Sidecar是 Istio 的特色,利用 Sidecar,对应用进行非常小的修改就可以享受服务网格带来的好处,减少运维负担;但是 Sidecar 模式也有一些限制:
当前 Ambient Mesh已经较好地解决了 Sidecar 模式下应用和 Sidecar 的部署依赖问题,不再需要注入Sidecar;服务网格的能力是通过 ztunnel 和 waypoint proxy 提供的,应用和网格组件的部署和升级不再相互依赖。
另外,Ambient共享模式可以大大减少网格组件本身的资源开销,这一点对资源敏感的用户来说是一个巨大的福音。
Ambient仍然处于预览状态,许多特性仍然在开发中,在官方文档中已经列出不少限制,此外,社区用户在使用过程中也有新的发现:
• 不支持IPV6
• 与Calico CNI不兼容,因为Ambient创建的iptables与Calico冲突。
同时,目前基于 envoy 的 ztunnel 在 xDS 效率、多租户、遥测方面可能存在性能问题,未来可能会基于rust重写一个更加轻量、高性能的ztunnel。
长期来看,Sidecar模式依然会是Istio的主流模式。Ambient共享模式为Istio社区或者服务网格业界带来了足够的刺激,相信基于社区所有开发者的共同努力,Ambient共享模式将会成为Istio的第二选择。
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
有人知道如何将capybarapoltergeist的用户代理覆盖到移动用户代理以进行测试吗?我发现了一些有关为seleniumwebdriver配置它的信息:http://blog.plataformatec.com.br/2011/03/configuring-user-agents-with-capybara-selenium-webdriver/这在capybara闹鬼中怎么可能? 最佳答案 请参阅poltergeistgithub页面上的链接:https://github.com/teampoltergeist/polte
我正在使用Ruby/Mechanize编写一个“自动填写表格”应用程序。它几乎可以工作。我可以使用精彩CharlesWeb代理以查看服务器和我的Firefox浏览器之间的交换。现在我想使用Charles查看服务器和我的应用程序之间的交换。Charles在端口8888上代理。假设服务器位于https://my.host.com。.一件不起作用的事情是:@agent||=Mechanize.newdo|agent|agent.set_proxy("my.host.com",8888)end这会导致Net::HTTP::Persistent::Error:...lib/net/http/pe