腾讯云Service Mesh生产实践及架构演进

\u003ch2\u003e背景介绍\u003c/h2\u003e\n\u003cp\u003eService Mesh(服务网格)是一个基础设施层,让服务之间的通信更安全、快速和可靠,是云原生技术栈的关键组建之一。2018年是Service Mesh 高歌猛进的一年,Service Mesh数据面板百花齐放,遍地开花,业界几乎所有大厂都在推出自己的Service Mesh 产品。2018年Service Mesh大事件如下:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e2018年7月31日,Istio 1.0版本发布,标志着Istio可用于生产环境。\u003c/li\u003e\n\u003cli\u003e2018年9月19日,Conduit,这个史上唯一一个主打rust语言的Mesh,宣布合入到Linkerd,后续作为linkerd2.x版本继续演进。\u003c/li\u003e\n\u003cli\u003e2018年11月28日,Istio的官配sidecar,高性能边缘代理Envoy,成为了继k8s以及prometheus之后,第三个从CNCF毕业的项目。\u003c/li\u003e\n\u003cli\u003e2018年12月5日,AWS推出服务网状网络App Mesh公开预览版,供用户轻松的监视与控制AWS上,构成应用程序微服务之间的通信。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e早在2017年腾讯云中间件团队就选定Istio为技术路线,开始Service Mesh的相关研发工作,作为腾讯云TSF(微服务平台)的无侵入式服务框架的核心实现,并在18年初在腾讯广告平台投入,打磨稳定后陆续开始对外输出,目前在银行、电商、零售、汽车等行业都有落地案例。\u003c/p\u003e\n\u003cp\u003e落地过程并非一帆风顺,本文将对腾讯云Service Mesh 在生产实践过程中遇到的典型问题以及解决方案进行总结分享,同时对腾讯云Service Mesh后续重点探索的技术方案进行简要阐述。\u003c/p\u003e\n\u003ch2\u003e腾讯云 Service Mesh 核心技术实现\u003c/h2\u003e\n\u003cp\u003e腾讯云Service Mesh,遵循Service Mesh的理念进行设计,为应用提供服务自动注册发现、灰度路由、限流、熔断等服务治理能力,且应用无需对源码进行侵入式变更即可与该服务框架进行集成。\u003c/p\u003e\n\u003cp\u003e在实现上,基于业界达到商用标准的开源软件Istio、envoy进行构建。整体架构上,从功能逻辑上分为数据面和控制面:\u003c/p\u003e\n\u003cp\u003e控制面主要提供配置及控制指令支撑sidecar的正常运行,以及对服务运行过程中的相关数据进行采集。\u003c/p\u003e\n\u003cp\u003e数据面主要是提供通信代理(sidecar)来进行透明的服务调用,支撑正常的业务流程。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/81/e2/817031b90c684b54e7286a27723a72e2.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e接下来,让我们对腾讯云Service Mesh各关键优化点做详细的描述。\u003c/p\u003e\n\u003ch3\u003e解耦k8s,拥抱其他计算平台\u003c/h3\u003e\n\u003cp\u003e众所周知,Istio强依赖于Kubernetes ,大部分功能都依托于Kubernetes 平台进行构建。下面列举几个核心的功能:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e服务配置管理\u003c/strong\u003e:Istio配置通过Kubernetes Crd (custom resources definition) 以及configmap进行存取。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/1a/fb/1a279350becdfe94f1d4008f0f31fbfb.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e\u003cstrong\u003e服务发现及健康检查\u003c/strong\u003e:Istio全功能的服务注册发现能力是基于Kubernetes 的PodServices能力以及Endpoints机制实现的,节点健康检查能力基于ReadinessProbe机制实现 (当前社区上面也有基于Consul的服务发现机制实现,但是缺失健康检查机制)。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/05/f8/0540dbc8792568a86c3bfe8e68db76f8.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e但实际落地过程中,TSF的用户并非全部是Kubernetes用户,例如公司内部的一个业务因历史遗留问题,不能完全容器化部署,同时存在VM和容器环境,架构如下:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/93/e3/93b1bcf8db1644aaf0ae64273cef3be3.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e从业务架构图可以看出,业务要求TSF可以支持其部署在自研PAAS以及Kubernetes 的容器、虚拟机以及裸金属的服务都可以通过Service Mesh进行相互访问。\u003c/p\u003e\n\u003cp\u003e因此,为了实现多平台的部署,必须与Kubernetes 进行解耦。经过分析发现,脱离Kubernetes 后,Istio存在以下三个问题:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003ePilot/Mixer的远程动态配置能力不可用(只能用本地配置);\u003c/li\u003e\n\u003cli\u003ePilot无法获取服务节点健康信息;\u003c/li\u003e\n\u003cli\u003e无法通过Istioctl(Istio小工具)进行服务注册/反注册以及写配置能力。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e针对这3个问题,TSF团队对Istio的能力进行了扩展和增强,增强后的架构如下:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/ed/a0/ed03a28423da00f41ee29e257cb39fa0.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e下表更详细的描述了存在的问题、解决方案以及所得到的目的,同时TSF 团队实现了Istio 对Consul的完整适配。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/6a/8d/6a8839c4e8bc54a893fb80e27f95438d.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e经过改造后,Service Mesh成功与Kubernetes 平台解耦,组网变得更加简洁,通过REST API可以对数据面进行全方位的控制,可从容适配任何的底层部署环境,对于私有云客户可以提供更好的体验。\u003c/p\u003e\n\u003ch3\u003e服务寻址模式的演进\u003c/h3\u003e\n\u003cp\u003e解决了跨平台部署问题后,第二个面临的问题就是服务的寻址互通问题。\u003c/p\u003e\n\u003cp\u003eIstio下的应用使用FQDN(fully qualified domain name)进行相互调用,基于FQDN的寻址依赖DNS服务器,Istio官方对DNS服务器的说明如下:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/b9/b0/b9ee6c35a8e4ceea0598c2c4f9e258b0.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://Istio.io/docs/examples/bookinfo/\"\u003eIstio的官方demo\u003c/a\u003e中,Reviews与Ratings之间的完整的服务调用会经过以下过程:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/e4/15/e4ff49fe231d5a37d843232e7801ec15.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e从图上可以看出,Reviews和Ratings的互通,kube-dns主要实现2个功能:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e应用程序的DNS请求被kube-dns接管;\u003c/li\u003e\n\u003cli\u003ekube-dns可以将服务名解析成可被iptables接管的虚拟IP(clusterIP)。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e在私有云的实际交付中,客户的生产环境不一定包含Kubernetes 或者kube-dns,我们需要另外寻找一种机制来实现上面的两个功能。\u003c/p\u003e\n\u003cp\u003e在DNS选型中,有集中式和分布式两种方案,分别如下:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e集中式DNS:代表有ConsulDNS, CoreDNS等,通过内置机制或者插件的方式,实现与服务注册中心进行数据同步。其架构组网如下:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/05/7f/0537174d3ce2e9c3251d3795d89ada7f.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003ekube-dns也属于集中式DNS的一种,集中式DNS存在以下问题:组网中额外增加一套DNS集群,并且一旦DNS Server集群不可服务,所有数据面节点在DNS缓存失效后都无法工作,因此需要为DNS Server考虑高可用甚至容灾等一系列后续需求,会导致后期运维成本增加。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e分布式DNS:就是将服务DNS的能力下沉到数据平面中,其架构组网如下:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/10/a3/10b4641787b56674ccbc327b5d1af1a3.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e分布式DNS运行在数据面节点上,DNS无单点故障,无需考虑集群容灾等要素,只需要有机制可以在其down掉后重新拉起即可。但是,由于其与业务进程运行在同一节点,因此其资源占用率必须控制得足够低,才不会对业务进程产生影响。\u003c/p\u003e\n\u003cp\u003e综合考虑,最终选用了分布式DNS的方案,最开始团队采用独立进程作为DNS Server的方案,如下图:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/48/ce/4899b2894a73973be9a0f3497c7553ce.png\" alt=\"\" /\u003e\u003cbr /\u003e\n该方案新增监听在127.0.0.1:53 上的mesh-dns进程,该进程实时从Pilot同步服务列表。Mesh-dns在节点启动时将127.0.0.1写入到/etc/resolv.conf首行中,同时接管/etc/resolv.conf的其他nameserver。这样,当app发起DNS查询时,DNS请求首先会到达mesh-dns,遇到匹配服务名的查询则直接返回,而当遇到不是针对服务名的DNS查询时,就把DNS请求转发给其他nameserver进行处理。\u003c/p\u003e\n\u003cp\u003e该方案看起来简单可行,但是经测试验证后发现存在以下问题:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eresolv.conf修改时间差问题:该方案需要对/etc/resolv.conf进行修改,在linux环境,域名解析机制是通过glibc提供的。而glibc 2.26之前的版本有个BUG,导致假如在进程启动后,对resolv.conf就行修改,则该修改无法被该进程感知,直到进程重启。而由于在容器部署的场景中,mesh-dns和应用分别部署在同一个POD的不同容器中,容器的启动是相互独立的,所以无法保证对resolv.conf的修改一定在应用启动前。即使改成通过InitContainer进行修改,当容器异常重启后,resolv.conf也同样会被还原导致服务不可用。\u003c/li\u003e\n\u003cli\u003e端口监听冲突问题:由于mesh-dns必须监听53端口,假如客户节点环境已经安装了dnsmasq等同样需要占用53的进程,则可能会出现端口冲突导致启动失败。\u003c/li\u003e\n\u003cli\u003enameserver选择策略问题:假如存在多个nameserver,部分操作系统,默认会使用rotate(随机选取一个作为首选查询的nameserver)作为nameserver的选择策略。此时会出现一定概率下会选不到127.0.0.1的nameserver,从而导致服务域名解释失败。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e针对上述问题,对方案进行了进一步的优化,优化后的方案如下图:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/0c/52/0c8eb81f3f2207d958c7ffdb380d0b52.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003emesh-dns不再监听53端口,而是监听在5353端口(可配置),启动时无需修改resolv.conf。通过增加iptables规则,将所有发往nameserver的流量导入到mesh-dns,从而解决了上文中的“端口监听冲突”以及“nameserver选择策略”的问题。\u003c/p\u003e\n\u003cp\u003emesh-dns通过inotify监听/etc/resolv.conf,可以随时获取环境中dns配置的更改,从而解决了上文中的“resolv.conf修改时间差”的问题。\u003c/p\u003e\n\u003ch3\u003e与非Service Mesh服务的互通\u003c/h3\u003e\n\u003cp\u003e现实总是复杂的,前面解决mesh服务之间相互访问的问题,如何解决用户Service Mesh应用和其他非Mesh 应用的相互访问呢? 用户内部有不同技术栈,一部分服务基于service mesh进行实现服务,另外一部分服务基于spring cloud框架进行实现。同时,客户的微服务组网中,存在大量第三方服务如支付网关、分布式存储、设备等,微服务需要与这些第三方服务也存在交互。用户期望支持的架构如下图所示:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/d0/4e/d024794602b0ddcd3e56722322e7ba4e.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e这个架构中,最大的挑战在于涉及了两个不同的微服务框架之间的互通。但是,这两个微服务框架从架构模式、概念模型、功能逻辑上,都存在较大的差异。唯一相通的点,就是他们都是微服务框架,可以将应用的能力通过服务的形式提供出来,给消费者调用,消费者实际上并不感知服务的具体实现。\u003c/p\u003e\n\u003cp\u003e基于这个共通点,为了使得不同框架开发的服务能够正常工作,TSF团队做了大量的开发工作,将两个微服务框架,从部署模式、服务及功能模型上进行了拉通,主要包括如下几点:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e服务模型的互通:基于统一的服务元数据模型,针对pilot registry及spring cloud registry的服务注册发现机制进行拉通。\u003c/li\u003e\n\u003cli\u003e服务API的互通:基于标准API模型(OpenAPI v3),针对两边框架的API级别服务治理能力进行拉通。\u003c/li\u003e\n\u003cli\u003e服务路由能力互通:基于标准权重算法以及标签模型,针对pilot virtual-service以及spring cloud ribbon能力进行拉通。\u003c/li\u003e\n\u003cli\u003e服务限流能力互通:基于标准令牌桶架构和模型,以及条件匹配规则,对mixer及spring cloud ratelimiter能力进行拉通。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3\u003e代理单节点多服务\u003c/h3\u003e\n\u003cp\u003e用户的需求是多种多样的,在交付过程中存在如下多服务场景:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e客户机器资源不足,且没有做容器化,因此需要把多个服务部署到一个节点上。\u003c/li\u003e\n\u003cli\u003e客户的传统应用使用OSGI(一种Java模块化技术)实现,一个进程中包含多个服务,监听在同一个端口。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/4f/2f/4f509b9b68443e12a607d16563c6c42f.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e为了支持多服务场景,简化用户的使用流程,TSF提供了服务描述文件,可支持多服务场景,服务配置文件与Kubernetes 标准格式一致:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/9f/16/9f83685e3097482b577dff08445db516.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003epilot-agent会根据服务配置,按照\u003cservice-name\u003e-\u003cIP\u003e-\u003cport\u003e的格式将配置中services注册成多个独立的服务实例。\u003c/p\u003e\n\u003cp\u003e在OutBound服务路由时,可以通过LDS-\u0026gt;RDS-\u0026gt;CDS-\u0026gt;EDS的方式进行路由,和独立部署的服务没有区别:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/49/f4/49125c142ab294940c861a4886a269f4.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e然而,在InBound服务路由过程中,通过开源Istio生成的listener会遇到一些坑。\u003c/p\u003e\n\u003cp\u003e对于多服务监听同一端口的场景,开源Istio在生成inbound的时候,会将同IP+Port的其中一个服务给reject掉。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/9c/81/9cc8e20ec70b0b6a42fee660eb4a5181.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e因此,生成的LDS中,只有其中一个服务的相关路由信息:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/bd/8e/bd1b09dad18cac465d224fc1b84f948e.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e这样一来,普通消息投递,不会有什么问题(目标端点信息是一致的),但是假如需要与mixer结合,做api鉴权或者限流等操作,则会出现两个服务的mixer_attribute互相混淆的情况,导致功能不可用。\u003c/p\u003e\n\u003cp\u003e为了解决这个问题,团队分析了\u003ca href=\"https://www.envoyproxy.io/docs/envoy/v1.8.0/api-v2/api/v2/listener/listener.proto.html?highlight=filter_chain_match#envoy-api-msg-listener-filterchainmatch\"\u003eenvoy的filter_chain_match能力\u003c/a\u003e,对pilot进行改造,扩展了listener能力,通过server_name来分流数据包到不同的filter中。\u003c/p\u003e\n\u003cp\u003e最终生成的LDS如下:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/57/27/57bdffc562f7b321f74324aeed205d27.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e经过这样的改造,同一端口上,不同的服务的filter配置不再冲突,两个服务的mixer_attribute也能相互隔离,顺利支持同端口多服务的场景。\u003c/p\u003e\n\u003ch3\u003e二进制协议的支持\u003c/h3\u003e\n\u003cp\u003e在当前业界的开源Service Mesh产品中,主打的协议都是标准协议(HTTP1/2, GRPC),标准协议都有一个特点,那就是协议头中包含了目的端相关的所有信息,Service Mesh会根据这些信息进行路由。如下表所示:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/66/3b/66366baf9281bc871066c0296f49dd3b.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e对于其他二进制协议,则分为2大类:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e第一种是协议中带有目标端信息的二进制协议,如thrift,dubbo等;\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e第二种是协议中不带有目标端信息的二进制协议,这种就比较多了,一般常见于私有云中的各种私有通信协议。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e开源Istio中,对于二进制协议的支持,仅仅局限于四层的端口转发,一般用于集成外部服务(mysql, mongodb等),典型场景是对不同入口的流量做转发,如下图所示:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/68/39/68f042aba94f0c451e149160edefe939.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e单纯的四层转发,无法满足复杂的微服务路由的需求。当前TSF交付的客户中,多个客户都提出了需要支持私有协议路由的需求,因此,针对这个问题,TSF团队提供了两种解决方案。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e用户将私有协议转换成GRPC协议,接入到Service Mesh:\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/ca/76/caddde535d635d56e3b72ecf8574f076.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e由于GRPC的Data Frame本身传输的就可以是TCP协议,因此用户可以直接把自己的二进制协议通过GRPC的bytes类型编码,然后通过Data Frame传输过来.\u003cbr /\u003e\n该方案适用于本身有一定的技术积累,愿意去做GRPC改造的用户。\u003c/p\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e根据用户定义的协议头描述文件,进行私有协议七层路由中间件团队对envoy的filter进行了扩展,用户提供一个protobuf格式的描述文件,指定协议头的字段顺序,proxy根据描述文件的定义,进行消息头的接收及解析,然后根据解析后的消息头内容,进行七层路由和转发。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg src=\"https://static001.infoq.cn/resource/image/08/37/08d7ab59e4941cc04241495ad9463e37.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e该方案适用于自身带有目标端信息的二进制协议,可以让私有协议的用户无需任何的改造,即可接入Service Mesh。\u003c/p\u003e\n\u003ch2\u003e总结\u003c/h2\u003e\n\u003cp\u003e腾讯云Service Mesh当前通过TSF平台在持续交付中,上文主要针对落地过程中遇到的典型功能性需求及技术方案演进进行了总结介绍,除此之外,中间件团队在Service Mesh性能方面也有很多优化和探索,主要包括减少envoy和mixer之间的网络交互、优化数据包在envoy节点内部从内核态到用户态的拷贝次数、envoy 到envoy之间数据的转发性能等,后续将针对性能优化进行专项分享。\u003c/p\u003e\n\u003chr /\u003e\n\u003cp\u003e作者简介:\u003c/p\u003e\n\u003cp\u003e单家骏,来自腾讯公司。腾讯云高级工程师,负责腾讯云中间件paas以及servicemesh的研发与架构,关注云原生与中间件技术。热爱开源、崇尚技术,希望能够使用技术使软件的应用变得简单、高效和美好。\u003c/p\u003e\n
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值