互联网
上的服务网格
上有很多
文章 ,而这是另一篇。 万岁! 但是为什么呢? 然后,我想表达的观点是,如果服务网格出现在10年前(在Docker和Kubernetes等容器平台出现之前)会更好。 我并不是说我的观点比其他观点更好或更糟,但是由于服务网格是非常复杂的动物,因此多种观点将有助于更好地理解它们。
我将讨论dotCloud平台,该平台基于一百多个微服务而构建,并支持容器中的数千个应用程序。 我将解释我们在其开发和启动过程中遇到的问题,以及服务网格如何提供帮助(或不能提供帮助)。
dotCloud的历史
我已经写过dotCloud的历史以及该平台的架构选择,但还谈到了网络级别。 如果您不想阅读
上一篇有关dotCloud的
文章 ,这里是一个简短的摘要:它是一种PaaS平台即服务,它允许客户端启动各种应用程序(Java,PHP,Python等),并支持各种数据服务(MongoDB, MySQL,Redis ...)和Heroku之类的工作流程:将代码上传到平台,它会构建容器映像并进行部署。
我将告诉您流量是如何定向到dotCloud平台的。 并不是因为它特别酷(尽管该系统在一段时间内运行良好!),而是主要是因为借助现代工具,如果需要一种方法在一群微服务或多个微服务之间路由流量的方式,那么适度的团队可以轻松地在短时间内实现这样的设计。一堆应用程序。 因此,您可以比较以下选项:如果您自己开发一切或使用现有的服务网格,会发生什么。 标准选择:自己动手或购买。
托管应用程序的流量路由
DotCloud应用程序可以提供HTTP和TCP端点。
HTTP端点被动态添加到
Hipache负载平衡器群集配置中。 这类似于Kubernetes
Ingress资源和类似
Traefik的负载均衡器
如今所做的 。
客户端通过各自的域连接到HTTP端点,前提是该域名指向dotCloud负载均衡器。 没什么特别的
TCP端点与端口号关联,然后通过环境变量将其传递到此堆栈的所有容器。
客户端可以使用适当的主机名(例如gateway-X.dotcloud.com)和端口号连接到TCP端点。
该主机名解析为“ nats”服务器群集(与
NATS不相关),该服务器群集会将传入的TCP连接路由到正确的容器(或在负载平衡服务的情况下,路由到正确的容器)。
如果您熟悉Kubernetes,这可能会使您想起
NodePort服务。
dotCloud平台上没有等效的
ClusterIP服务:为简单起见,从平台内部和外部对服务的访问都是相同的。
一切的组织都非常简单:HTTP和TCP路由网络的初始实现,可能只有几百行Python。 随着平台的发展和其他要求的出现,最终确定了简单(天真)的算法。
不需要大量重构现有代码。 特别是
12因子应用程序可以直接使用通过环境变量获得的地址。
这与现代服务网格有何不同?
能见度有限。 我们通常没有TCP路由网格的度量标准。 至于HTTP路由,在更高版本中,出现了详细的HTTP度量标准以及错误代码和响应时间,但是现代服务网格则更进一步,例如,与Prometheus等度量标准收集系统集成。
可见性不仅在操作上很重要(以帮助解决问题),而且在发布新功能时也很重要。 这是关于安全的
蓝绿色部署和
金丝雀的部署 。
路由效率也受到限制。 在dotCloud路由网格中,所有流量都必须经过一组专用路由节点。 这意味着可能会跨越几个可用区边界(可及性区域),并且延误时间会大大增加。 我记得如何解决每页进行100多个SQL查询的代码问题,并为每个查询打开了到SQL Server的新连接。 在本地启动时,页面会立即加载,但在dotCloud中,加载会花费几秒钟,因为每个TCP连接(以及后续的SQL查询)都需要数十毫秒的时间。 在这种特殊情况下,持久连接解决了该问题。
现代服务网格可以更好地解决此类问题。 首先,他们验证连接是否
在源处路由。 逻辑流程是相同的:
→ →
,但是现在mesh在本地工作,而不是在远程节点上,因此
→
连接是本地的并且非常快(微秒而不是毫秒)。
现代服务网格还实现了更智能的负载平衡算法。 通过控制后端的性能,它们可以将更多的流量发送到更快的后端,从而提高整体性能。
安全性也更好。 dotCloud路由网格完全可以在EC2 Classic上运行,并且不对流量进行加密(假设如果有人设法对EC2网络流量进行嗅探,那么您已经遇到了很大的问题)。 现代服务网格例如通过相互TLS身份验证和后续加密透明地保护我们的所有流量。
平台服务的流量路由
好的,我们讨论了应用程序之间的流量,但是dotCloud平台本身如何?
该平台本身包含约一百种微服务,负责各种功能。 一些收到了其他人的请求,还有一些是后台工作人员,他们连接到其他服务但不接受连接。 无论如何,每个服务必须知道必须连接到的地址的端点。
许多高级服务可以使用上述路由网格。 实际上,许多数百个dotCloud微服务已作为常规应用程序部署在dotCloud平台本身上。 但是少量的低层服务(尤其是实现此路由网格的服务)需要更简单,依赖性更小的东西(因为它们无法依靠自己工作,这是一个很好的老鸡和鸡蛋问题)。
通过直接在多个关键节点上运行容器来部署这些低级别的重要服务。 同时,不涉及标准平台服务:链接程序,调度程序和运行程序。 如果要与现代容器平台进行比较,就好比使用直接在节点上
docker run
的docker启动控制平面,而不是委派Kubernetes任务。 这非常类似于
kubeadm或
bootkube在加载独立集群时
使用的
静态模块(炉 床 )的概念。
这些服务以简单粗略的方式公开:它们的名称和地址在YAML文件中列出; 并且每个客户端都必须复制此YAML文件以进行部署。
一方面,它非常可靠,因为它不需要诸如Zookeeper之类的外部键/值存储的支持(不要忘记,那时etcd或Consul还不存在)。 另一方面,这使移动服务变得困难。 每次移动时,所有客户端都应收到更新的YAML文件(并可能重新启动)。 不太方便!
随后,我们开始引入一种新方案,其中每个客户端都连接到本地代理服务器。 对于地址和端口,他只知道服务的端口号并通过
localhost
连接就足够了。 本地代理服务器处理此连接,并将其路由到实际服务器。 现在,将后端移动到另一台计算机或进行扩展而不是更新所有客户端时,只需更新所有这些本地代理即可; 并且不再需要重新启动。
(还计划将流量封装在TLS连接中,并在接收方放置另一个代理服务器,以及在没有接收服务参与的情况下检查TLS证书,接收服务被配置为仅在
localhost
上接受连接。稍后将详细介绍)。
这与Airbnb的
SmartStack非常相似,但是最大的不同是SmartStack是在生产中实现和部署的,而当dotCloud转变为Docker时,内部的dotCloud路由系统就放在了盒子里。
我个人认为SmartStack是Istio,Linkerd和Consul Connect等系统的前身之一,因为它们都遵循相同的模式:
- 在每个节点上运行代理。
- 客户端连接到代理。
- 更改后端时,管理平面会更新代理配置。
- ...利润!
服务网格的现代实现
如果今天我们需要实现类似的网格,则可以使用类似的原则。 例如,通过将服务名称映射到
127.0.0.0/8
地址来配置内部DNS区域。 然后,在群集的每个节点上运行HAProxy,接受与每个服务地址(此子网中的
127.0.0.0/8
)的连接,并将负载重定向/平衡到相应的后端。 HAProxy配置可由
confd控制,您可以将后端信息存储在etcd或Consul中,并在必要时将更新后的配置自动推送到HAProxy。
这就是Istio的工作方式! 但是有一些区别:
- 使用Envoy代理而不是HAProxy。
- 通过Kubernetes API而不是etcd或Consul保存后端配置。
- 服务是在内部子网上分配的地址(Kubernetes ClusterIP地址)而不是127.0.0.0/8。
- 它具有一个可选组件(Citadel),用于在客户端和服务器之间添加双向TLS身份验证。
- 支持新功能,例如断路,分布式跟踪,部署金丝雀等。
让我们快速看一下其中的一些区别。
特使代理
Enftoy Proxy由Lyft [在出租车市场的优步竞争对手-大约 跨]。 它在许多方面与其他代理非常相似(例如,HAProxy,Nginx,Traefik ...),但是Lyft编写了自己的代理,因为它们需要其他代理中没有的功能,并且制作一个新代理比扩展现有代理似乎更合理。
特使可以单独使用。 如果我具有应连接到其他服务的特定服务,则可以将其配置为连接到Envoy,然后使用其他服务的位置动态配置和重新配置Envoy,同时获得许多出色的附加功能,例如可见性。 我们将流量引向Envoy,而不是自定义客户端库或将调用跟踪嵌入代码中,并为我们收集指标。
但是Envoy还可以充当服务网格的数据平面。 这意味着对于该服务网格,现在
由控制平面配置Envoy。
控制平面
在管理平面中,Istio依赖Kubernetes API。
这与使用confd并没有什么不同,confd依靠etcd或Consul来查看数据仓库中的一组键。 Istio通过Kubernetes API查看Kubernetes资源集。
在两种情况之间 :我个人认为此
Kubernetes API描述有用 ,内容为:
Kubernetes API Server是一个“哑服务器”,提供API资源的存储,版本控制,验证,更新和语义。
Istio旨在与Kubernetes合作; 如果要在Kubernetes之外使用它,则需要运行Kubernetes API服务器(和辅助服务etcd)的实例。
服务地址
Istio依赖Kubernetes分配的ClusterIP地址,因此Istio服务获得一个内部地址(不在
127.0.0.0/8
范围内)。
不带Istio的Kubernetes集群中针对特定服务的ClusterIP地址的流量会被kube-proxy拦截,并发送到此代理的服务器部分。 如果您对技术细节感兴趣,那么kube-proxy可以设置iptables规则(或IPVS负载平衡器,具体取决于您如何配置它)来重写连接到ClusterIP地址的连接的目标IP地址。
在Kubernetes集群中安装Istio之后,除非通过将
sidecar
容器引入自定义壁炉中而为给定的使用者甚至整个命名空间显式打开它,否则一切都不会改变。 此容器将启动Envoy的实例,并设置一系列iptables规则以拦截到其他服务的流量并将该流量重定向到Envoy。
与Kubernetes DNS集成后,这意味着我们的代码可以按服务名称进行连接,并且一切“正常”。 换句话说,我们的代码发出诸如
http://api/v1/users/4242
之类的请求,然后
api
将请求解析为
10.97.105.48
,iptables规则拦截来自10.97.105.48的连接并将其重定向到本地Envoy代理,而该本地代理将直接请求实际的后端API。 !
多余的东西
Istio还通过mTLS(相互TLS)提供端到端的加密和身份验证。 名为
Citadel的组件对此负责。
Envoy还可以为
每个请求请求一个
Mixer组件,以便根据各种因素(例如标头,后端加载等)做出对此请求的特殊决定。(不用担心:有很多方法可以确保Mixer正常工作,甚至如果崩溃,则Envoy将继续作为代理正常工作)。
当然,我们提到了可见性:Envoy在提供分布式跟踪的同时收集了大量指标。 在微服务的体系结构中,如果一个API请求必须通过微服务A,B,C和D,则当您登录系统时,分布式跟踪将为该请求添加一个唯一的标识符,并通过子查询将这些标识符保存到所有这些微服务中,从而允许您记录所有相关的调用,它们的延误等
开发或购买
Istio以复杂的系统而闻名。 相比之下,使用现有工具相对简单地构建路由网格(我在本文开头描述了这一点)。 那么,创建您自己的服务网格是否有意义?
如果我们有适度的需求(您不需要可见性,断路器和其他细节),那么您就会想到开发自己的工具。 但是,如果我们使用Kubernetes,甚至没有必要,因为Kubernetes已经提供了用于服务发现和负载平衡的基本工具。
但是,如果我们有高级要求,那么“购买”服务网格似乎是一个更好的选择。 (由于Istio附带了开放源代码,因此这并不总是“购买”,但我们仍然需要投入工程时间来了解其工作,进行部署和管理)。
选择什么:Istio,Linkerd或Consul Connect?
到目前为止,我们仅讨论了Istio,但这不是唯一的服务网格。 一个流行的替代方法是
Linkerd ,还有
Consul Connect 。
选择什么?
老实说,我不知道。 目前,我认为自己没有足够的能力回答这个问题。 有一些
有趣的 文章比较了这些工具甚至
基准 。
一种有前途的方法是使用诸如
SuperGloo之类的工具。 它实现了一个抽象层,以简化和统一由服务网格提供的API。 我们可以研究更简单的SuperGloo构造,而不必研究各种服务网格的特定(而且,我认为是相对复杂的)API,并且可以轻松地从一种切换到另一种,就像我们有描述HTTP接口和接口的中间配置格式一样。后端能够生成Nginx,HAProxy,Traefik,Apache的实际配置...
我沉迷于Istio和SuperGloo,在下一篇文章中,我想展示如何使用SuperGloo将Istio或Linkerd添加到现有集群中,以及后者将如何处理其工作,也就是说,它允许您从一个服务网格切换到另一个服务网格而无需重写配置。