早在像Docker和Kubernetes这样的容器平台兴起之前的10年,有一个dotCloud平台,基于100多个微服务构建的平台,支持数千个以容器运行的生产应用程序,作者将分享构建和运行它时面临的挑战与经验,并讨论服务网格到底有没有用?
dotCloud
它是一个PaaS,允许客户运行各种各样的应用程序(Java,PHP,Python ......)。数据服务(MongoDB,MySQL,Redis ......)以及类似于Heroku的工作流程:您可以将代码推送到平台,平台将构建容器映像,并部署这些容器映像。
如何路由流量呢?部署在dotCloud上的应用程序可能会暴露HTTP和TCP端点。
HTTP端点被动态添加到 Hipache 负载均衡器集群的配置中。这类似于我们今天可以通过Kubernetes Ingress 资源和Traefik 等负载均衡器实现的 目标 。客户端可以使用其关联的域名连接到HTTP端点,前提是域名将指向dotCloud的负载均衡器。
TCP端点与端口号相关联,然后通过环境变量传递给该堆栈的所有容器。客户端可以使用指定的主机名(例如gateway-X.dotcloud.com)和端口号连接到TCP端点。
如果您熟悉Kubernetes,这可能会提醒您想起 NodePort 服务。dotCloud平台没有相应的 ClusterIP 服务:为简单起见,服务从平台的内部和外部以相同的方式访问。
HTTP和TCP路由网格的初始实现可能只有几百行Python,使用相当简单(我敢说,天真)的算法,但随着时间的推移演变以处理平台的增长和其他要求。它不需要对现有应用程序代码进行大量重构。特别是 十二因子应用程序 可以直接使用通过环境变量提供的地址信息。
与现代服务网格有什么不同?
1. 可观察性有限。TCP路由网格根本没有度量标准。至于HTTP路由网格,后来的版本提供了详细的HTTP指标,显示了错误代码和响应时间; 但是现代服务网格已经超越,并提供与Prometheus等指标收集系统的集成。可观察性不仅从运维角度(帮助我们解决问题)很重要,而且还提供安全 蓝/绿部署 或 金丝雀部署等功能 。
2.路由效率也受到限制。在dotCloud路由网格中,所有流量都必须通过一组专用路由节点。这意味着可能跨越几个AZ(可用区)边界,并显着增加延迟。我记得一些代码的疑难解答问题,这些代码在显示给定页面时发出了100多个SQL请求,并为每个请求打开了与SQL服务器的新连接。在本地运行时,页面会立即加载,但在dotCloud上运行时,需要几秒钟,因为每个TCP连接(以及后续的SQL请求)都需要几十毫秒才能完成。在这种特定情况下,使用持久连接就可以了。
现代服务网格比这更好。首先,确保在源处路由连接。逻辑流仍然是client → mesh → service,但现代网格是在本地运行,而不是在远程节点上运行,因此client → mesh连接是本地连接,因此非常快(微秒而不是毫秒)。
现代服务网格还实现了更智能的负载平衡算法。通过监视后端的运行状况,他们可以在更快的后端上发送更多流量,从而提高整体性能。
现代服务网格的安全性也更强。dotCloud路由网格完全在EC2 Classic上运行,并没有对流量进行加密。现代服务网格可以透明地保护我们的所有流量,例如通过相互TLS身份验证和后续加密。
平台服务的流量路由
我们已经讨论了应用程序如何沟通,但是dotCloud平台本身又如何呢?
平台本身由大约100个微服务组成,负责各种功能。其中一些服务接受了其他人的请求,其中一些是后台工作者,可以连接到其他服务,但不能自己接收连接。无论哪种方式,每个服务都需要知道连接所需的地址的端点。
这些服务以非常简单粗暴的方式公开:有一个YAML文件列出了这些服务,将它们的名字映射到它们的地址; 并且这些服务的每个使用者都需要该YAML文件的副本作为其部署的一部分。
一方面,这非常强大,因为它不涉及维护像Zookeeper这样的外部键/值存储(记住,当时不存在etcd或Consul)。另一方面,它使得难以移动服务。每次移动服务时,其所有使用者都需要接收更新的YAML文件(并且可能会重新启动)。不太方便!
我们开始实施的解决方案是让每个消费者都连接到本地代理。消费者不需要知道服务的完整地址+端口,而只需知道其端口号,然后连接即可localhost。本地代理将处理该连接,并将其路由到实际后端。现在,当后端需要移动到另一台机器,或者按比例放大或缩小,而不是更新所有消费者时,我们只需要更新所有这些本地代理; 我们不再需要重新启动消费者了。
这与AirBNB的 SmartStack 非常相似;
我个人认为SmartStack是Istio,Linkerd,Consul Connect等系统的先驱之一......因为所有这些系统都遵循这种模式:
今天的服务网格
如果我们今天必须实现类似的网格,我们可以使用类似的原则。例如,我们可以设置内部DNS区域,将服务名称映射到127.0.0.0/8 空间中的地址。然后在我们集群的每个节点上运行HAProxy,接受每个服务地址(在该127.0.0.0/8子网中)的连接,并将它们转发/负载平衡到适当的后端。HAProxy配置可以通过 confd 进行管理,允许在etcd或Consul中存储后端信息,并在需要时自动将更新的配置推送到HAProxy。
这或多或少是Istio如何运作的!但有一些差异:
Istio vs. Linkerd vs. Consul Connect
到目前为止,我们只讨论过Istio,但它并不是唯一的服务网格。 Linkerd 是另一个受欢迎的选择,还有 Consul Connect 。
我们应该选哪一个?
实说,我不知道,在这一点上,我认为自己没有足够的知识来帮助任何人做出这个决定。有一些有趣的 文章 比较 它们,甚至是 基准 。
一种具有很大潜力的方法是使用像 SuperGloo 这样的工具。SuperGloo提供了一个抽象层,用于简化和统一服务网格公开的API。我们可以使用SuperGloo提供的更简单的构造,并从一个服务网格无缝切换到另一个服务网格,而不是了解各种服务网格的特定(以及我认为相对复杂的)API。有点像我们有一个描述HTTP前端和后端的中间配置格式,并且能够为NGINX,HAProxy,Traefik,Apache生成实际配置......