返回 登录
0

将已有应用程序迁移到Mesos上(Part 2)

作者简介:David Greenberg是Two Sigma公司的首席架构师,他负责公司交易战略的分布式计算环境。他也是Cook的设计师,Cook是一个开源的Mesos框架,用来做抢占式作业的调度。

译者简介:崔婧雯,现就职于IBM,高级软件工程师,负责IBM业务流程管理软件的系统测试工作。曾就职于VMware从事桌面虚拟化产品的质量保证工作。对分布式集群管理,虚拟化,业务流程管理都有浓厚的兴趣。

本文节选自《用Mesos框架构建分布式应用》第三章,以下内容承接“将已有应用程序迁移到Mesos上(Part 1)”。

搭建Marathon上的HAProxy

我们在上文中已经学习了Marathon,并且一起体会到为什么说Marathon是应用程序的绝佳平台。至此,应用程序已经能够在Marathon上运行了,但是还有一个问题:外部世界是如何真正连接到应用程序上去的呢?一些类型的应用程序,比如Celery或者Resque worker,不需要任何额外的配置:它们通过共享数据库通信。其他类型的应用程序,比如HTTP服务器和Redis实例,需要确保其很容易被发现。在Mesos集群里解决这个问题的最流行方式是在静态,非Mesos管理的主机上运行代理,并且自动更新这些代理,将其指向应用程序运行着的实例。这样,应用程序在已知主机和端口(也就是代理)上持续保持可用,同时仍然可以在后台动态扩展其能力。另外,每个代理主机都能够服务很多应用程序,每个应用程序放在不同的端口上,这样少量的代理主机就能够运行上百个后台程序。通常来说,如果代理提供SSL终止功能,它会受所有活跃连接的总带宽,或者CPU使用总量的限制。(SSL终止功能是当客户端通过安全SSL连接和代理通信时,代理会处理SSL,这样后台无须在加解密上浪费CPU资源,甚至无须支持加解密。)

有两种代理无疑是现代应用程序栈使用最多的方案:HAProxy和Nginx。HAProxy是功能很有限的代理:它能够代理HTTP和TCP连接,执行基本的健康检查,并且提供SSL终止功能。它是为稳定性和性能而构建的:HAProxy在13年里都没有出现任何崩溃或死锁问题。这也正是现在HAProxy在Mesos用户中如此受欢迎的原因。

与之对比,Nginx拥有很多特性。除了作为代理,它还能够运行自定义的,用Lua、JavaScript,或者基于JVM的语言编写的用户代码。这些代码可能会影响代理的操作,甚至能够直接作出响应。很多系统实际上使用这两种代理:客户端连接到HAProxy实例上,随后HAProxy将请求转发给Nginx实例。Nginx实例随后要么直接回复,要么发送到后台处理请求的服务器上。

本节仅仅讨论HAProxy,但是如果需要使用Nginx所提供的额外功能,那么要知道Nginx也完全可以按照本节介绍的方式使用。

haproxy-marathon-bridge:在Marathon存储库里,有一个名为bin/haproxymarathon-bridge的脚本。该脚本意图为Marathon生成一份HAProxy的配置文件。不幸的是,它并不允许用户指定Marathon上服务的哪个端口会绑定到HAProxy主机上。因此,该脚本基本没什么用。

下文介绍搭建Mesos代理的最为流行的配置。

Bamboo

Bamboo是一个带有web接口的HAProxy配置后台程序。它提供UI来查看以及管理HAProxy规则的当前状态。Bamboo的核心功能是监控Marathon上应用程序的更改,并且保证HAProxy实例的后台程序使用的是最新版本。Bamboo还提供一些有用的特性,所有这些都通过web接口和HTTP REST API暴露出来。

最重要的是,Bamboo支持为每个Marathon应用程序使用HAProxy ACL规则。ACL规则能够基于其URL、cookie、头信息和其他因素来路由请求。比如,可以使用ACL规则将路径由/static开头的请求路由到处理静态内容的后台程序,同时将所有其他请求路由到应用程序服务器。ACL规则相当强大,因为它们允许用户将自己的应用程序分隔成独立的组件或者服务,从而分别扩展这些组件,同时暴露给客户端的URL仍然是唯一的。使用ACL规则,用户可以选择将匿名访客路由到某个服务器池,登录用户路由到另一个服务器池,并且将静态数据请求路由到第三个服务器池,从而可以单独扩展这些服务器池的能力。

Bamboo还有一个强大的模板系统,基于GO语言的text/template包。该模板系统很灵活,但是和更简单更流行的系统,比如mustache模板相比,其学习曲线更陡峭一些。

所有代理配置信息都存储在ZooKeeper里,这样可以通过保证Bamboo所有实例的同步来简化配置管理。

Bamboo使用Marathon事件总线来发现后台配置的变更,这意味着HAProxy配置在Marathon上的变更通常仅仅会延时数百微秒。

另外,Bamboo能够和StatsD集成,从而报告配置事件发生的时间。

Bamboo为Marathon应用程序的精确HAProxy配置提供了完备的方案。

微服务的HAProxy

如今,微服务是一种非常流行的架构模式。Mesos和Marathon是构建基于微服务的应用程序的坚实基础:用户可以在Marathon上托管所有的服务。不幸的是,服务发现是微服务部署时必须要解决的一个重要问题。下文介绍如何使用Mesos和Marathon创建类似于SmartStack的系统。

这里需要考虑两个问题:如何做服务发现?标准方案的问题是什么?标准方案包括:

  • DNS
    可以选择使用轮询DNS来给所有后台服务器相同的DNS名称。但是这里会遇到两个问题:通常需要花费至少几秒钟才能在整个基础架构上完成DNS的变更,而且一些应用程序会永远缓存DNS解析(意味着这些应用程序永远不会知道DNS的变更)。另一个问题是,除非用户编写自定义的使用SRV记录的客户端库函数,否则DNS并不能提供简单的方案在同一个服务器上运行多个应用程序,并且让这些应用程序使用不同或者随机分配的端口。

  • 中央化的负载均衡
    这一点类似于“Bamboo”小节所讨论的问题。对于一些应用程序而言,这的确是个伟大的设计,但是,该方案要求全局可访问的负载均衡器池。这样的中央化让应用程序的安全隔离工作显得很枯燥:为了隔离应用程序,用户必须为特定的隔离需求配置Nginx或者HAProxy规则。(另一种方案,DNS和应用内发现,将通信直接委托给每个应用程序,这样就无须为隔离而配置任何中央系统。相反,每个应用程序能够在和其他应用程序完全隔离的情况下,处理认证授权。)

  • 应用内发现
    Twitter和Google这样的公司使用特别定制的服务请求和发现层,使用的分别是Finagile和Stubby。这些RPC层将服务发现直接集成进应用程序内,在需要找到可以发送请求的服务器时,查询ZooKeeper或Chubby(Google内部的类似ZooKeeper的系统)。如果可以在环境里这么实现,那么使用特别定制的服务发现层能够给路由、负载均衡和可靠性方面提供最大的灵活度,因为用户可以控制该系统所有方面的细节。不幸的是,如果需要和用多种语言编写的应用程序集成,就会变得很麻烦并且很费时间,因为需要为每种语言开发平行的发现系统。此外,如果需要和已有的,没有遵守内建服务发现机制开发的应用程序集成的话,该方案则完全不可行。

上述这些方案的缺点导致很多工程师选择了第四种模式:联合中央化负载均衡的易于集成的特点和去中央化方案的容错特点的方案。使用该方案,用户会在数据中心里的每台单独机器上运行一个HAProxy的副本,然后给每个应用程序分配一个端口(不是主机名)。最后,将所有HAProxy实例都分别指向其后台应用程序的端口,无论这些后台程序运行在集群的什么位置。

这一方案有两大优势:

  • 易于搭建
    用户可以在集群内每台机器上使用Bamboo或者一个简单的cron作业来保持HAProxy本地实例和Marathon的同步。即使基础架构的某些部分暂时不可用,本地HAProxy也会继续将路由流量直接发送给后台程序。也就是说,即使Marathon崩溃了,也不会影响到应用程序的运营。

  • 易于使用
    每个应用程序都可以简单地连接到localhost:$PORT和其他微服务通信,这里$PORT是分配给微服务的端口。不需要为应用程序编写任何特别的或者自定义的代码,因为所有需要做的事情就是记住哪个端口对应哪个应用程序而已。

该方案也有一些缺陷。其中之一是用户需要仔细维护服务到端口的映射。如果用户尝试全局存储这样的映射,让应用程序在启动时读取映射信息,那么就必须再次为每种语言编写自定义代码。另一方面,如果本身硬编码了端口,就等于自挖陷阱,出现问题时调试会非常困难。

另一个缺陷是代理间缺少协调。即使发生局部故障,系统还会继续工作,因为每台机器都是独立操作的。也正是由于每台机器的独立操作,可能某些HAProxy会路由到相同的后台程序上,从而加重这个后台程序的负载,造成层级故障。随着集群内节点数的增加,经常会看到请求时间的方差也会随之增加。

如今,服务映射到端口,并且每个客户端都连接到localhost的HAProxy上的方案似乎已经成为管理Mesos集群上服务发现最为流行的方式了。

在Marathon上运行Mesos框架

Mesos集群里的常见方案是在Marathon上运行集群的Mesos框架。但是Marathon本身就是一种Mesos的框架!那么在Marathon上运行Mesos框架意味着什么呢?不用考虑如何将每种框架的调度器部署到特定的主机上并且处理这些主机的故障,Marathon能够确保框架的调度器总是在集群里的某处运行着。这样大幅简化了在高可用配置里部署新框架的复杂度。

如何给由Marathon运行的框架分配资源?Mesos的新用户通常都会问,“如果我在Marathon上运行一个框架,这会如何影响资源的分配呢?”实际上,虽然框架的调度器是由Marathon运行的,但是调度器仍然必须直接连接到Mesos上,并且调度器会以和在特定机器上直接运行它们完全一样的方式接受资源。当然,在Marathon上运行调度器的优势是任意Mesos slave上都能够运行调度器,并且当slave发生故障时,Marathon能够自动重启调度器。

Chronos是什么

Chronos是一种Mesos框架,提供高可用性,分布式基于时间的作业调度器,比如cron。借助于Chronos,用户能够启动命令行程序(可能是从URI处下载的)或者Docker容器。和Marathon一样,Chronos有web UI,以及易于编程的REST API。虽然Chronos端点和Marathon端点的接口有细微的不同,但是本书不会加以详细介绍,因为在线文档非常详尽。Chronos提供了调度作业的四大核心特性:

  • 间隔调度
    Chronos使用标准ISO 8601注释指定重复间隔。通过web UI或者REST API,用户可以指定重新运行作业的频率。这使得Chronos很适合每隔几分钟运行一次快速作业,或者运行夜间的数据生成作业。

  • 依赖跟踪
    理论上,Chronos支持依赖性作业。依赖性作业仅仅在其前提条件成功运行之后才会运行。不幸的是,Chronos依赖性作业的实现实际上有些出入。在Chronos里,如果父作业最近一次的调用成功了,那么就认为满足了该依赖性作业的前提条件。只要依赖性作业的所有前提条件都至少运行过一次,那么这个依赖性作业就会运行。这意味着除非每个父作业的运行间隔严格一致,否则这个依赖性作业可能就会在非预期时间运行。本书“Chronos运维注意事项”部分会详细介绍这个问题的常规处理策略。

  • 延迟启动
    Chronos也能够用来运行一次性的或者在未来的某个时间点重复间隔地执行作业。当指定重复执行作业的ISO 8601间隔时,还可以指定希望什么时候首次运行该作业。这意味着用户可以使用Chronos在未来的每天、每周或者每月调度作业的执行,这很有助于做计划。

  • worker可扩展性
    Chronos和大多数其他作业调度器的关键区别在于Chronos在Mesos上的容器内部运行其作业。这意味着作业是互相隔离的,worker是可扩展的,因此无论Chronos生成什么样的负载,用户都能够添加新的Mesos slave来处理额外的工作,并且确保作业能够接收到所需资源。

和所有可以用于生产环境的Mesos框架类似,Chronos通过热备支持高可用性——大多数Chronos的部署都会运行至少两个调度器的实例,来确保即使调度器或者其主机发生故障时,Chronos也永远不会掉线超过几秒钟。Chronos还带有一个同步工具,chronos-sync.rb,用户可以通过将文件提交到版本控制系统里,将要在集群里运行的作业版本化。这是确保可重复性而使用最多的方案,因为这样做允许用户在存储库里保存Chronos配置。

在Marathon上运行Chronos

Mesos框架部署里最常见的技术之一是在Marathon上运行框架调度器。这么做是因为这些调度器可以依赖Marathon来确保它们一直在某处运行着。这给运维人员减轻了大量负担,否则运维人员需要确保每个调度器都有良好监控,并且部署在多台机器上。

为了可以在Marathon上部署,调度器必须实现一定程度上的选主。在第4章里会介绍,调度器是从单台主机连接到Mesos上的。也就是说,如果想在两个地方运行调度器(比如,有一个热备),用户需要给出某种类型的信号确保某个时间点只有一个调度器在运行。最常见的方式是使用ZooKeeper来解决这个问题,因为ZooKeeper带有一个简单集成的选主组件(细节见“加入高可用”部分)。Marathon和Chronos都是采用的这种方案。

让我们一起看看Marathon上启动Chronos的样例JSON表达式(示例3-16)。为了最大化端口分配的效率,允许Chronos绑定任意端口。要连接到Chronos上,要么在Marathon的UI上点击,要么使用之前介绍的HAProxy服务发现机制之一。

示例3-16 Chronos JSON描述符

{
    "cmd": "java -jar chronos.jar -Xmx350m -Xms350m ---port $PORT0",
    "uris": [
        "http://fetchable/uri/with/chronos.jar"
    ],
    "cpus": 1, 
    "mem": 384, 
    "instances": 2, 
    "id": "chronos",
    "ports": [0], 
    "constraints": [["hostname", "UNIQUE"]]
}
  • 假定所有Mesos slave上都已经安装了Java。因此只需要将动态分配的端口传递给Chronos,它就能成功启动了。
  • 与其在每台Mesos slave上都安装Chronos,不如将其.jar文件存储到某个已知位置。这样.jar文件会被动态下载到被分配运行Chronos调度器的任意slave上,从而简化部署的复杂度。
  • 选择匹配允许JVM分配的内存总量(为JVM本身预留了34MB的headroom)的容器大小。
  • 运行Chronos两个实例就可以实现几乎即时的故障转移,因为备用Chronos调度器也会一直运行着。
  • Chronos仅仅需要分配一个端口。
  • 使用UNIQUE主机约束,确保备用Chronos实例运行在不同的机器上。如果slave有属性指定每个slave所在的机架,最好将调度器及其热备运行在不同的机架上。

验证Chronos在运行的最简单方式是从Marathon应用程序视图里点击分配的端口数量。每个任务的端口分配都是一个主机和端口的组合链接,点击会加载Chronos UI。Chronos UI有些已知的问题,因此推荐使用curl或者chronos-sync.rb来配置Chronos实例。

Chronos运维注意事项

Chronos是Mesos生态系统的重要一环,但是,要注意不能过度依赖它。在生产环境里运行Chronos时需要注意以下事项:

  • 依赖性作业的特性并没有什么用——一定要使用工作流管理器。
  • Chronos仍然是一个复杂的分布式系统——只有当需要在容器里可扩展地运行作业时才有必要使用。

几乎没有人使用Chronos依赖性作业的特性。相反,大多数人使用Chronos来调度流行的数据工作流工具的调用。对于简单工作流(比如顺序运行一些流程)而言,Bash脚本是常用的方案,但是,如果超过20行的话,Bash脚本就会很笨重,对于复杂循环和条件的语法支持也很弱。make是另一种表达依赖关系的流行方式,因为其支持工作流的高级语法,并且支持基本的并行机制。Luigi是功能更为丰富也更为强大的工作流管理器。它支持HDFS和数据库里的检查点,可以改进中间节点失败作业重试的效率,因为不需要重做检查点之前的工作。无论你的特别需求和用例是什么,都必须使用一些其他工具来管理Chronos负责调度的作业的执行顺序。

如果已经完成了所有上述搭建,就几乎已经可以在生产环境使用Chronos了!最后需要考虑的是,是否真的需要Chronos从Mesos得到的可扩展隔离性。即使已经运行着一个Mesos的集群,也并不一定需要使用Chronos来做任务管理。有很多其他作业调度服务器在Marathon上能够更容易地运行,仅仅在每个作业都运行在自己的容器里时才需要使用Chronos。

Marathon上的Chronos:小结

Chronos是一个强大的工具,帮助Mesos集群实现可靠性,可扩展性以及基于间隔的作业调度。它在很多公司(包括Airbnb)里帮助强化了数据生成流程。要记住本书介绍的一些略显怪异的模式、变通方法以及建议!

Marathon上的Chronos仅仅是一个例子,展示了Marathon如何提供高可用以及如何简化其他Mesos调度器的部署。一个商业化的Mesos产品——DCOS,在该领域更进一步,要求所有DCOS上的框架都由Marathon托管。不管部署的是Chronos、Kafka还是Cotton、Marathon,都是托管其他Mesos框架调度器的绝佳选择。

Marathon+Chronos的备选方案

当然,Marathon(和Chronos)无法满足所有的集群需求。在本节中简单看看其他两种流行的Mesos PaaS框架:Aurora和Singularity。这两种框架学习曲线都更为陡峭,但是它们也提供了更多额外的特性。

Singularity

Singularity是HubSpot开发的一个Mesos框架。它提供了像Marathon一样的部署服务的能力(包括Docker化的应用程序)。在这之上,如果某个服务在部署后无法通过其健康检查,Singularity会自动将部署回滚。和Chronos类似,Singularity也支持重复性作业,以及一次性作业,可以通过REST API触发。这样的设计简化了给任何服务或者重复性作业添加容器化,异步后台的处理过程。在管理服务发现和代理方面,Singularity和另一个称为Baragon的工具集成,Baragon管理并且集成HAProxy、Nginx以及Amazon的Elastic Load Balancer。

Aurora

Aurora是Twitter开发的Mesos框架。Aurora带有由Python编写的特别先进的作业描述API。这个API,称为Thermos,允许用户指定如何构建并且安装在Mesos执行器里运行的应用程序,以及工作流和流程序列。Aurora有高性能的分布式服务发现API,支持C++、Java和Python的绑定。能够运行cron风格语法的重复性作业。和Singularity类似,Aurora还能够自动检测什么时候服务的健康检查开始失败,然后回滚到之前工作的版本。Aurora最为强大的特性是多租户控制:Aurora允许一些任务使用空闲的集群能力,并且随后在需要调度高优先级生产任务时将这些任务取代。还可以在用户间强制使用quotas。这些特性都来自于在大规模Mesos集群上运行由上百个开发人员编写的海量应用程序的企业需求。如果你为之构建Mesos集群的企业听上去属于这一类型,那么便值得深入研究Aurora。

本章小结

本章学习了如何使用Mesos构建标准、无状态、基于服务器的应用程序。并且构建出了自修复、自重启且十分简单的服务发现——在Mesos构建了一个基本的PaaS!这些都是通过使用Marathon,一种流行的Mesos框架而实现的。

Marathon跨Mesos集群启动、监控,并且重启进程。它简化了这一切,让用户点击按钮就可以实现扩展伸缩,并且提供了很容易实现集成的JSON API。

本章首先介绍了如何启动Marathon,如何保护它,以及如何确保其是高可用的。随后,介绍了如何编写Marathon服务描述符,允许用户启动无处不在的SimpleHTTPServer。这仅仅是一个示例应用程序,任何其他应用程序都可以通过简单变更命令行而启动。还介绍了如何编程查询Marathon上应用程序的状态。

接下来,本书深入探讨Marathon上应用程序的扩展。最重要的是,讨论了位置约束,以及如何轻松确保应用程序运行在正确的slave机器组上,并且在机架间均匀分布。

Docker是最流行的应用程序容器化技术。本章介绍了如何在Marathon上花最小的代价启动Docker化的应用程序,以及如何配置各种Docker特定的参数,比如加载主机磁盘。

如果不包含健康检查、动推送和回滚,应用程序就不足够健壮。本章讨论了如何配置TCP、HTTP和基于命令行的监控检查,以及在升级中如何指定应用程序的行为。还探讨了从Marathon里如何构建自定义集成来订阅某个事件流。

然后,学习Marathon和服务发现代理的集成。Bamboo是一种开源HAProxy配置管理器,是同步HAProxy和Marathon任务的强大工具。本章还介绍了使用HAProxy和Marathon构建可扩展、多语言微服务架构的流行方案。

最后,介绍如何使用Marathon托管其他Mesos框架的调度器,使用一种分布式,类似于cron的框架Chronos作为示例。还概览了Marathon的可替代PaaS框架,这些框架提供了很多额外的特性(代价是学习曲线更陡)。

可能Marathon及其备选方案都不能满足你的应用程序的需求,还需要对如何启动进程,或者如何处理失败等这些有更多的控制。这样的话,就需要编写自己的框架。下一章会介绍构建Marathon的技术,这样大家就可以构建出属于自己的自定义Mesos框架。

图片描述

《用Mesos框架构建分布式应用》


由CSDN主办的中国云计算技术大会(CCTC 2017)将于5月18-19日在北京召开,Spark、Container、区块链、大数据四大主题峰会震撼袭来,包括Mesosphere CTO Tobi Knaup,Rancher labs 创始人梁胜、Databricks 工程师 Spark commiter 范文臣等近60位技术大牛齐聚京城,为云计算、大数据以及人工智能领域开发者带来一场技术的盛大Party。现在报名,只需599元就可以聆听近60场的顶级技术专家分享,还等什么,登陆官网,赶快报名吧!

图片描述

评论