SmartStack 介绍 —— 云端的服务发现

  categories:资料  author:

英文原文:Introducing SmartStack: Service Discovery in the Cloud

SmartStack是一个服务自动发现和注册的框架。它通过透明地处理你组织中运行代码的创建、删除、异常及维修工作使工程师的日常工作更便利。我们相信这是处理这类问题最好的方案之一:概念简单,易于操作,相比同类工具提供更多的可配置性。SmartStack过去一年中一直在Airbnb内部测试,并已在许多大大小小的组织中广泛地应用。

SmartStack的组件——Nerve和Synapse——在GitHub上可以获取!继续阅读获得更多内容。

SOA中的服务问题

类似Airbnb的公司往往创立初期是以一个独立应用的形式——像瑞士军刀一样能完成有组织内的所有功能。随着流量(以及产品开发工程师数量)的增长,这种比喻并没有随之变得更贴切。代码库变得越来越复杂,问题出在分离不彻底上,众多工程师所负责代码库的不同部分合在一起时发生了变化,性能是由应用中表现最差的部分决定的。

解决这个问题的办法是服务:独立的、具有更小代码代码的、运行在独立服务器上拥有独立迭代周期的,这种服务能更明确更具目标性的定位问题区域。这被称为面向服务的架构:SOA。

当你在你的架构中增加了一些服务时,你会注意到你现在维护着许多小型的服务池,而不是维护一个单一的通常意义上的应用服务池。这带来了几个问题。你如何把阻塞引导到池中的每一台机器?你如何增加新机器或者移除那些坏掉的或者退休的机器?单个机器的损坏对该应用的其它机器会产生什么影响?

跨越几个服务集合来处理这些问题,将需要很快投入多个全职的工程师,致力于这个费时费力的错误监测活动,而且充满着风险和宕机的可能。

理想解决方案的特征

解决服务问题的答案是自动化-让计算机完成后端池的维护清理工作。然而实现这样的自动化有许多方法。首先让我们总结一下理想实现的特征:

  • 能够处理特定服务请求的后端可以自动地接收这样的请求
  • 负载将智能地分布到所有的后端上,因此没有任何一个后端服务器比其余的后端做更多的工作,这样请求就会自动的路由到最不忙碌的后端
  • 遇到问题的后端将自动地停止接受请求
  • 同一系统下,应当可能在不影响其他后端的情况下从一个后端卸下负载。这样才可能进行调试。
  • 应当具有非常完美的内省功能,这样你通常可以知道哪些后端可用,它们接收哪种类型的负载以及这些负载来自于哪些客户。
  • 对后端列表的更改应该对客户的影响最小;理想情况下,这样的更改对客户应该是透明的。
  • 不应当存在单点故障-系统里的任何地方的任一机器出现故障都不会有任何影响。

我们可以使用这些标准去评估可能的解决方案。例如,手动更改哪些直接写在客户端的后端列表,然后再部署客户端,这立刻引起许多标准在很大程度上失效。其他方法结果会怎样呢?

不奏效的解决方案

许多常用方案应用到服务发现上在实践中并没有很好地工作。要理解为什么SmartStack这么好,了解一下其他方案不好的地方会有帮于理解。

DNS

注册和发现的最简单的方案就是把所有的后端放在同一个DNS后。需要定位服务时请求DNS便会得到一个随机的后端。

这个注册组件相当容易理解。在你自己的基础设施中,你可以使用像BIND-DLZ这样的动态域名解析服务用来注册服务。在云端,像Route53这样DNS托管服务,简单的API调用就足够了。在AWS上,如果你使用循环CNAME你会得到免费的水平拓展,而且同样的记录在AWS内外都起作用。

不过,使用DNS来进行服务发现也很冒险。首先,消费者不得不下载变更 – 因为没有方法来进行状态推送。而且,DNS收到传播延时的困扰;即使你的监控器检测到一次失败并且向DNS发送了一个取消注册的命令之后,仍然需要等待至少几秒钟消费者才能得到消息。更糟的是,由于DNS设备存在着多级缓存,传播延时的准确时间通常是无法确定的。

如果使用最简单的方式,使用名称来标记你的服务,你也无法确定哪个节点陷入了拥堵。使用随机路由你也会面临同样的情况,某些后端设备上的负载会混乱的堆积如山,而另一些设备却静止不懂。

最糟糕的是,许多应用程序只在启动时缓存DNS解析一次。例如,Nginx将缓存初始名称的解析结果,除非你使用配置文件监听,HAProxy也是这样。应用程序脆弱性表现出来的代价是昂贵的,解决问题更难。

请注意,我们这里是指本地通过客户端库使用DNS。这里有一个使用DNS来做服务发现的聪明做法——我们在SmartStack上使用Zookeeper,我们也使用DNS但没有失去太多的功能。但是只是简单使用HTTP库发起到myservice.mydomain.com的HTTP请求并不会受到好的成效。

集中式负载均衡

如果你确信DNS是发现服务的所采取的错误的方法的话,那么你可能决定采用集中式服务路由。在这中方法力,如果服务a想要和服务b会话,那么它应当同可能路由这个请求的负载均衡器会话。所有的服务都要配置查找负载均衡器的方法,而且负载器是所有后端都必须了解的唯一的东西。

这种方法听起来很有希望,不过现实面前它却不会给你很多。首先,服务是如何发现负载均衡器的呢?通常答案是DNS,然而目前DNS方法给你带来了比你要解决的问题更多地问题-如果负载均衡器停止运行,那么你仍让要面对使用DNS实现服务发现的所有问题。另外,集中式路由层是最大的故障点。如果这个层停止运行,那么么其他的一切都会因为它而停止运行。重新对新的后台配置负载均衡会带来很大的危险-而这是例行操作。

接下来,你会选择什么负载均衡方案?在传统的数据中心上,也许你会尝试两台硬件设备的方式,就像F5。但在云端这并不适用。在AWS上,你也许会尝试适用ELB,但是ELB在内网负载均衡上表现很糟,因为他们只有公网IP。从你的一台服务器到另一台服务器的阻塞,将会不得不退出你的私有网络并且重新进入。除了引入延时之外,这种情况也对安全分组产生很大损害。如果你决定在EC2实例上运行你自己的负载均衡层,那么最终的结果只是把服务发现中的问题推向了上层。你的负载均衡器现在不再是一个特殊的、无法替代的设备;它只是另外一个实例,一个失败探测器,但是对你的操作来说它确实至关重要的。

应用内部实现服务注册/服务发现

由于DNS和集中式负载均衡都存在问题,因此你可能决定仅通过代码解决这个问题。为了在你的应用内不使用DNS发现依赖,为何不使用一些不同的、更加专业的机制呢?

这是很流行的做法,在许多软件堆栈中都可以发现。例如,Airbnb最初在Twitter commons服务里使用了这个模型。我们运行在Twitter commons上的java服务是使用zookeeper自动注册自身的,用配置信息集中点替代了DNS。想要与这些服务会话的应用向zookeeper请求可用后端的列表,并且通过zookeeper watches定期地刷新或者订阅以了解列表的变化。

然而,这种方法也会受到许多限制。首先,如果你在统一的软件栈上运行的话,这种方法工作的非常良好。在Twitter上,大多数服务都运行在JVM上,那么一切就很容易实现。然而,在Airbnd上,为了与ZK注册服务通信,我们不得不用ruby编写自己的在zookeeper。最终的实现非常不可靠,以至于在Zookeeper停止的时候服务就会中断。

———————

更为糟糕的是,当我们在像Node.js这样的栈上开始开发更多服务的时候,我们预计到将迫使我们用各种各样的语言实现同样的注册和发现逻辑。有时,相关库的缺乏或者不成熟都会进一步阻止我们的努力。有时你希望运行那些不是你自己写的但仍然使用你基础架构的应用:Nginx,CI服务器,rsyslog或者其他软件。这种情况下,在应用内部实现服务发现是完全不可能的。

最终这种方法难于操作。在没有停止应用的前提下调试一个自身来注册服务的应用是不可能的-我们将不得不借助 iptables 来阻止我们正在研究的机器上的Zookeeper端口。必须采取特别的方式来对与特定的应用实例目前正在通信的后端列表和对。 另外再说明一下,智能负载均衡的实现也很复杂。

SmartStack方式

SmartStack是我们解决SOA问题的方式。事实上,SmartStack通过把你应用程序的问题暴露出来进行工作的。在相同的机器上运行两个独立的应用程序:Nerve,用于服务注册,Synapse,用于服务发现。

Nerve

Nerve相比Twitter做的并没有更加的复杂化。在Zookeeper中,它创建了短暂的节点,包含地址/端口组合等信息,这为一个后端可用的服务请求提供一个特定的服务。

为了知道一个特定的后端是否可以注册,Nerve运行进行健康状态检查。每个你想注册的服务都有一系列健康状态值。如果他们中的任何一个失败了都会导致注册失败。

尽管一个健康检查很简单,就像“该应用可以通过TCP或HTTP访问吗”一样,但是正确的和Nerve整合还是意味着要针对你的应用实现并暴露一个健康检查接口。比如,在Airbnb上,每一个使用HTTP协议通信的应用都暴露了一个/health的服务点,它会在应用健康时返回一个200 OK的状态码,否则返回一个其它状态码。在我们的基础框架中,就是如此;比如,你可以在浏览器里尝试访问https://www.airbnb.com/health!

对于非HTTP应用来说,我们已经写了一些检查,使用特定的协议来发出询问。一个redis检查,举例来说,会尝试写入并读取一个简单的key。而一个rabbitmq检查,会尝试发布并消费一条消息。

Synapse

Synapse在Airbnb中的后端发现服务中十分有魔力。它和你的服务一起运行,并且能使服务所要依赖的程序能正常使用,它对你的应用来说是透明的。Synapse在Zookeeper中读出有关可用的后端程序信息,然后用这些信息来配置本地HAProxy进程.当一个客户端想要和服务通信,它只需要和本地HAProxy通信,本地HAProxy只关心匹配请求适当的路径。

本质上,这就是我上面提及的将分散的请求负载均衡的解决方案。这里没有引导错误的问题,因为没有必要发现负载均衡层-通常在localhost来显现。

真正的工作是由HAProxy完成的,它已被证明非常稳定并经过实战考验。Synapse进程本身就是一个中介——如果它宕掉了,你只会收不到变更通知但其他一切仍会良好地运行。使用HAProxy有一大堆的优点。我们能用到强大logging和introspection。我们能够使用先进的负载平衡算法、队列控制、重试及超时。HAProxy还有内置的健康检查,我们可以用它来防范网络隔离(我们能够从Nerve了解到有一个可用的后端但因为网络问题却不能和它连接)。事实上,谈论HAProxy以及如何配置它需要另开一篇博客了,有太多关于它的内容了。

尽管每次Synapse收到后端变更后都更改整个HAProxy的配置文件,但我们依旧尝试用HAProxy状态套接字来做变更。已经存在于HAProxy中的后端在宕掉时会通过状态套接字进入维护模式,当它们恢复后再回到正常模式。我们只有在要添加一个全新的后端时才重启HAProxy让它重新读取配置文件。

SmartStack的好处

SmartStack在Airbnb这里工作的非常出色。回到我们之前的清单, To return to our previous checklist, here is how we stack up:

  • 后端修复后在不超过一次健康检查间隔的延迟之内,它就将在Zookeeper中成为可用的;通过Synapse的Zookeeper watches它也立刻会对用户也是可用的。
  • 我们在一个健康检查间隔内发现问题,并将后端移出轮询。为了使间隔更短,一种机制使服务向Nerve通知它们处在不健康状态。同时,部署人员在开始工作时可以停止Nerve,并在结束时重启Nerve。
  • Synapse在其被发布到Zookeeper上时开始工作,并且大部分情况下重新配置HAProxy是非常快的。因为对于多数更改我们都利用HAProxy的stats socket,所以我们甚至不必重启该进程,除非我们加入新的后端。
  • 因为我们的基础设施是分布式的,我们无法进行集中计划。但是HAProxy提供了高度可配置的队列语义。对于最大的客户,我们在HAProxy层建立智能队列;对于其他,我们至少保证循环。
  • 对一个后端进行调试和维护就像在机器上停止Nerve进程一样简单,其他部分没有受到任何影响。
  • 通过HAProxy状态页,你可以准确地知道哪些后端是可用的。得益于HAProxy出色的日志输出,你可以得到非常好的统计信息和每个请求的详细信息,包括请求动作的数量的统计在rsyslog中。
  • 基础设施是完全分布式的。最为关键的节点为Zookeeper节点,并且Zookeeper被特别设计为分布式的,并具有对于失败的鲁棒性。

在Airbnb上使用SmartStack几年后,我们仍然非常喜欢它。它易于使用,将我们从许多问题中解放出来,并且在出现问题时也非常容易调试。

基于SmartStack的构件

除了SmartStack的基本功能 – 允许服务器和其它服务器进行通讯 – 之外,我们在其之上创建了一些额外工具。

Charon

Charon是Airbnb的面向前端的负载均衡器。在Charon之前,我们使用过Amazon的ELB。不过,ELB并没有给我们的生产拥堵带来改善。而且向ELB中添加和移除实例的过程非常笨拙 – 我们拥有一个强大的服务发现框架,随后我们有了一个只是用于处理前端阻塞的框架。最终,由于ELB是基于EBS的,我们也担心一个EBS的故障可能带来的影响。

在ELB之后我们已经将Nginx放置在第一位了。这是因为我的网络流量已经被分为两个部分,其包括一些用户相关的应用-https://www.arebnb.com的服务器和另一个https://www.airbnb.com/help服务器。比如,在Charon里,从Akamai的一个请求先是直接到达一个Nginx服务器。每一个服务器都运行着Synapse,并且都启用了用户相关服务。当我们根据位置匹配了URI后,信息流发送,通过正确的HAPoxy来选择最佳的后端程序来处理请求。

事实上,Charon从我们用户到我们的用户相关服务选择路径就像是在后端程序之间选择一样。我们得到了所有的好处-健康检查,添加、删除实例,通过HAProxy来修正队列行为。

Dyno

我们用用户相关服务来做什么,我们也用它作为外部服务就如仪表盘或是监控器。大量DNS发送所有的.dyno请求到一些dyno盒子中。Nginx从服务的主机请求头部得到信息然后同Synapse和HAProxy指向所请求的服务。

未来的工作

SmartStack的瓶颈是Zookeeper.目前,我们失败的Zookeper集群将拖垮我们所有的基础设施。同时,因为我们我用zk 库的边界情况,我们将不能及时处理单个失败的ZK节点属性。

目前我们大量的工作是围绕着构建一个更好的SmartStack测试基础设施。现在我们不能用我们的Chef cookbook和Vagrant来为整个平台简单的加速整体化测试。我们希望将有更好的测试,我们可以在SmarkStack上做出更有弹性的进步。

我们也考虑过在服务器上增加动态的注册服务。目前,我们发现Nerve很难通过配置文件在Nerve中编码。事实上,因为我们用配置文档来管理(Chef)我们的基础设施,这个问题对我们来说不是最严重的。如果我们在机器上部署了新的服务,那么在Chef上部署服务也要在Nerve配置上进行更新。

然而,在如Mesos的环境中,一个服务可以在任意时刻动态的在一个机器上执行,这样就很容易把服务注册在Nerve上以进行监控。这样在服务和SmarStack之间的耦合性要比目前的更强,所以我要慎重考虑这个方式以确保我们能构建正确的API。

—————

试一下吧!

我们期待Smartstack在其他组织中证明它的价值。尝试一下,反馈给我们你的使用体验!

可能上手最简单的方法是使用“厨师食谱”。这里的代码可以创建一个SmartStack的综合测试环境,通过运行它也可以完成自动化集成测试。这也是我们在生产环境运行的Smartstack代码,其中提供我们认为管理Smartstack配置的恰当方法。在这份食谱内还包含运行和调试Smartstack超棒的文档。

如果你想自己配置Nerve和Synapse,查看它们各自库里的文档。我们已经在其中包含了一个示例配置供你学习使用。

TL;DR
web基础设施的扩容需要通过服务,但搭建以服务组织的基础设施很难。简化一下它吧,使用Smartstack是全自动的,能够透明的服务发现和登记:全方位控制你的分布式基础设施。

来源:https://www.oschina.net/translate/smartstack-service-discovery-cloud?lang=chs&page=3#

 



快乐成长 每天进步一点点