精通-Mesos-全-
精通 Mesos(全)
原文:
annas-archive.org/md5/02afb619d35d623ceeb88a3a89f4324f
译者:飞龙
序言
Apache Mesos 抽象化了 CPU、内存、存储和其他计算资源,将它们从机器(物理或虚拟)中分离出来,从而使得可以轻松构建和高效运行容错且弹性的分布式系统。它提高了资源利用率,简化了系统管理,并支持多种分布式应用程序,能够通过其可插拔架构轻松部署。
本书将提供一个详细的逐步指南,使用所有标准的 DevOps 工具部署 Mesos 集群,帮助有效地迁移 Mesos 框架,并总体上揭示 Mesos 的概念。
本书将首先建立 Mesos 的存在理由,并有效地解释其架构。随后,书中将带领读者逐步进入 Mesos 的复杂世界,从简单的单机设置逐步过渡到高度复杂的多节点集群设置,并沿途引入新的概念。旅程的最后,读者将掌握管理当今现代数据中心需求复杂性的所有资源。
本书内容
第一章,介绍 Mesos,介绍了 Mesos,深入探讨了其架构,并介绍了一些重要的主题,如框架、资源分配和资源隔离。还讨论了 Mesos 采用的两级调度方法,提供了其 API 的详细概述,并给出了 Mesos 在生产环境中使用的一些示例。
第二章,Mesos 内部机制,全面概述了 Mesos 的特性,并引导读者了解几个关于高可用性、容错性、扩展性和效率的关键主题,如资源分配、资源预留和恢复等。
第三章,开始使用 Mesos,讲解了如何在公共云(AWS、GCE 和 Azure)和私有数据中心(本地)上手动设置并运行 Mesos 集群。还讨论了各种调试方法,并深入探讨了如何排除 Mesos 设置中的故障。
第四章,服务调度与管理框架,介绍了几个基于 Mesos 的调度和管理框架或应用程序,这些框架或应用程序对于长时间运行服务的轻松部署、发现、负载均衡和故障处理至关重要。
第五章,Mesos 集群部署,解释了如何使用系统管理员和 DevOps 工程师常用的标准部署和配置管理工具,轻松地设置和监控 Mesos 集群。还讨论了在部署 Mesos 集群时常见的一些问题及其解决方案。
第六章,Mesos 框架,详细介绍了 Mesos 框架的概念和特性。它还提供了 Mesos API 的详细概述,包括新的 HTTP 调度器 API,并提供了在 Mesos 上构建自定义框架的方案。
第七章,Mesos 容器化器,介绍了容器的概念,并简单讲解了 Docker,可能是目前最流行的容器技术。它还提供了 Mesos 中不同“容器化器”选项的详细概述,并介绍了诸如 Mesos 管理的容器网络和获取缓存等其他主题。最后,提供了一个关于如何在 Mesos 中部署容器化应用的示例,以帮助更好地理解。
第八章,Mesos 大数据框架,是一本指南,介绍如何在 Mesos 上部署重要的大数据处理框架,如 Hadoop、Spark、Storm 和 Samza。
第九章,Mesos 大数据框架 2,引导读者通过在 Mesos 上部署重要的大数据存储框架,如 Cassandra、Elasticsearch-Logstash-Kibana(ELK)堆栈和 Kafka。
本书所需
为了最大限度地发挥本书的作用,你需要具备基本的 Mesos 和集群管理知识,同时熟悉 Linux 操作系统。你还需要能够访问 AWS、GCE 和 Azure 等云服务,最好是在 Ubuntu 或 CentOS 操作系统上运行,具有 15GB 内存和四个核心的配置。
本书适合谁阅读
本书旨在服务那些熟悉 Linux 系统及其工具管理基础的 DevOps 工程师和系统管理员。
约定
在本书中,你会看到一些不同的文本样式,以区分不同种类的信息。以下是这些样式的示例以及它们的含义解释。
文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名以如下方式显示:“为了简化,我们将直接运行sleep
命令。”
代码块如下所示:
{
"args": [
"--zk=zk://Zookeeper.service.consul:2181/Mesos"
],
"container": {
"type": "DOCKER",
"Docker": {
"network": "BRIDGE",
"image": "{{ Mesos_consul_image }}:{{ Mesos_consul_image_tag }}"
}
},
"id": "Mesos-consul",
"instances": 1,
"cpus": 0.1,
"mem": 256
}
当我们希望特别引起你对某个代码块部分的注意时,相关行或项目会以粗体显示:
# Tasks for Master, Slave, and ZooKeeper nodes
- name: Install mesos package
apt: pkg={{item}} state=present update_cache=yes
with_items:
- mesos={{ mesos_pkg_version }}
sudo: yes
任何命令行输入或输出都以如下方式编写:
# Update the packages.
$ sudo apt-get update
# Install the latest OpenJDK.
$ sudo apt-get install -y openjdk-7-jdk
# Install autotools (Only necessary if building from git repository).
$ sudo apt-get install -y autoconf libtool
# Install other Mesos dependencies.
$ sudo apt-get -y install build-essential python-dev python-boto libcurl4-nss-dev libsasl2-dev maven libapr1-dev libsvn-dev
新术语和重要词汇以粗体显示。你在屏幕上看到的词汇,例如菜单或对话框中的词语,会以这样的方式出现在文本中:“现在按下ADD按钮以添加特定端口。”
注意
警告或重要提示会以这样的框框显示。
提示
提示和技巧如下所示。
读者反馈
我们始终欢迎读者的反馈。让我们知道你对本书的看法——你喜欢或不喜欢什么。读者反馈对我们非常重要,它帮助我们开发出你真正能从中获益的书籍。
如果您想向我们提供一般反馈,请通过电子邮件 <feedback@packtpub.com>
与我们联系,并在邮件主题中注明书名。
如果您有某个领域的专业知识,并且对编写或为书籍贡献内容感兴趣,请查看我们的作者指南 www.packtpub.com/authors。
客户支持
现在,作为一位骄傲的 Packt 图书拥有者,我们为您提供了许多帮助您充分利用购买的资源。
下载示例代码
您可以从您的帐户在 www.packtpub.com
下载本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问 www.packtpub.com/support
并注册,让文件直接通过电子邮件发送给您。
您可以通过以下步骤下载代码文件:
-
使用您的电子邮件地址和密码登录或注册我们的网站。
-
将鼠标悬停在顶部的 SUPPORT 标签上。
-
点击 Code Downloads & Errata。
-
在 Search 框中输入书名。
-
选择您想要下载代码文件的书籍。
-
从下拉菜单中选择您购买这本书的地点。
-
点击 Code Download。
下载文件后,请确保使用最新版本的以下软件解压或提取文件夹:
-
适用于 Windows 的 WinRAR / 7-Zip
-
适用于 Mac 的 Zipeg / iZip / UnRarX
-
适用于 Linux 的 7-Zip / PeaZip
本书的代码包也托管在 GitHub 上,地址为 github.com/PacktPublishing/Mastering-Mesos
。我们还有来自丰富图书和视频目录的其他代码包,您可以在 github.com/PacktPublishing/
查看!快来看看吧!
勘误
虽然我们已经尽力确保内容的准确性,但错误确实会发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——我们将非常感激您能将其报告给我们。通过这样做,您可以帮助其他读者避免困惑,并帮助我们改进该书的后续版本。如果您发现任何勘误,请访问 www.packtpub.com/submit-errata
提交,选择您的书籍,点击 勘误提交表单 链接,并输入勘误的详细信息。您的勘误一旦验证通过,您的提交将被接受,勘误将上传至我们的网站或添加至该书籍的勘误列表中。
要查看先前提交的勘误,请访问 www.packtpub.com/books/content/support
,并在搜索框中输入书名。所需信息将显示在勘误部分。
盗版
网络上的版权盗版问题在所有媒体中都持续存在。在 Packt,我们非常重视版权和许可的保护。如果您在互联网上发现任何形式的非法复制我们的作品,请立即提供该位置地址或网站名称,以便我们采取补救措施。
请通过 <copyright@packtpub.com>
与我们联系,并提供涉嫌盗版内容的链接。
感谢您帮助我们保护作者权益,以及确保我们能够为您提供有价值的内容。
问题
如果您对本书的任何内容有疑问,您可以通过 <questions@packtpub.com>
与我们联系,我们将尽力解决问题。
第一章:介绍 Mesos
Apache Mesos 是一款开源的分布式集群管理软件,起源于 2011 年 UC Berkeley AMPLab。它将 CPU、内存、存储和其他计算资源从物理或虚拟机器中抽象出来,使得容错和弹性分布式系统可以轻松构建并高效运行。它被称为元调度器(调度器的调度器)和“分布式系统内核/分布式数据中心操作系统”。
它提高了资源利用率,简化了系统管理,并支持多种可以通过利用其可插拔架构部署的分布式应用程序。它具有可扩展性和高效性,并提供诸如资源隔离和高可用性等功能,这些特点,加上一个强大且充满活力的开源社区,使其成为最令人兴奋的项目之一。
本章将涵盖以下主题:
-
Mesos 的数据中心操作系统与架构介绍
-
框架介绍
-
属性、资源以及资源调度、分配和隔离
-
Mesos 提供的监控与 API
-
Mesos 在生产环境中的应用
Mesos 的数据中心操作系统与架构介绍
在过去十年中,数据中心已经从将多个应用程序打包到单台服务器中,发展到拥有大型数据中心,聚合数千台服务器作为大规模分布式计算基础设施。随着虚拟化、微服务、集群计算和超大规模基础设施的到来,当前的需求是创建一个以应用为中心的企业,遵循软件定义的数据中心战略。
目前,服务器集群通常是单独管理的,这可以类比于在 PC 上有多个操作系统,每个操作系统分别管理处理器、磁盘驱动器等。采用这种将机器视为独立实体并孤立管理的抽象模型,会大大降低数据中心有效构建和运行分布式应用的能力。
另一种看待这一情况的方式是将数据中心中运行应用程序与在笔记本电脑上运行应用程序进行比较。一个主要的区别是,在启动文本编辑器或网页浏览器时,我们不需要检查哪些内存模块是空闲的,也不需要选择符合需求的内存模块。这里正是平台像主操作系统一样发挥作用的意义,它允许多个用户同时运行多个应用程序,并利用共享的资源集。
当前,数据中心运行着各种分布式应用工作负载,如 Spark、Hadoop 等,并需要具备智能匹配资源与应用程序的能力。如今,数据中心生态系统必须具备管理和监控资源的能力,并能够高效地在统一的资源池中分配工作负载,灵活且轻松地满足多样化的用户需求(包括非基础设施团队)。数据中心操作系统为资源管理和监控提供了一种全面且可持续的方法,这不仅降低了拥有成本,还允许以隔离的数据中心基础设施无法支持的方式灵活处理资源需求。
数据中心操作系统的理念是智能软件,它位于数据中心的所有硬件之上,确保高效和动态的资源共享。除此之外,它还具备持续监控资源使用情况并改善工作负载和基础设施管理的能力,这一切都是以一种与特定应用需求无关、无缝的方式进行的。如果没有这种操作系统,数据中心就会呈现出孤岛式的情形,这迫使开发人员构建针对机器特定特性的专用软件,使得应用程序的迁移和调整变得极为繁琐。
数据中心操作系统充当着一个软件层,将数据中心中的所有服务器聚合为一个巨大的超级计算机,提供跨所有微服务应用程序的多延迟、隔离和资源控制的优势。另一个主要的优势是消除了在持续分配和重新分配虚拟资源过程中人为错误的发生。
从开发者的角度来看,这将使他们能够轻松且安全地构建分布式应用程序,而不必将其局限于一堆专门的工具,这些工具各自针对特定的需求。例如,考虑到数据科学团队的情况,他们开发的分析应用程序通常是资源密集型的。一个能够简化资源访问、共享和分配的操作系统,成功地解决了他们每次工作负载变化时必须重新分配硬件的难题。
数据中心操作系统对 DevOps 的相关性至关重要,DevOps 本质上是一种软件开发方法,强调自动化、集成、协作以及传统软件开发人员与其他 IT 专业人员之间的沟通。借助能够有效将单独服务器转化为资源池的操作系统,DevOps 团队可以专注于加速开发,而无需不断担心基础设施问题。
在分布式计算成为常态的世界里,数据中心操作系统是一个伪装的福音。通过免去手动配置和维护单独机器与应用程序的麻烦,系统工程师无需为特定应用配置特定的机器,因为所有应用都可以在任何可用的资源上运行,即使这些机器上已经有其他应用在运行。使用数据中心操作系统可以实现集中的控制和智能的资源利用,消除硬件和软件的孤岛,确保即便是非基础设施专业人员也能更好地访问和使用资源。
一些组织通过数据中心操作系统管理其超大规模数据中心的例子包括 Google 的 Borg(以及下一代 Omega)系统。数据中心操作系统的优点是显而易见的,优势包括计算资源的可扩展性、支持应用程序之间数据共享的灵活性,以及在启动和管理可互操作的集群应用程序时节省团队的精力、时间和资金。
正是通过将数据中心转变为单一超级计算机的愿景,Apache Mesos 希望实现这一目标。Mesos 起源于 2011 年伯克利 AMPLab 研究论文,至今已取得长足进展,许多领先公司(如 Apple、Twitter、Netflix、AirBnB 等)已在生产环境中使用它。Mesosphere 是一家初创公司,正在开发以 Mesos 为核心的分布式操作系统产品。
Mesos 的架构
Mesos 是一个开源平台,用于在不同的分布式应用程序(或框架)之间共享商品服务器的集群,如 Hadoop、Spark、Kafka 等。其核心思想是作为一个集中的集群管理器,通过汇聚集群的所有物理资源,并将其作为一个高度可用的资源池提供给所有不同的框架使用。例如,如果一个组织有一个 10 节点集群(16 个 CPU 和 64 GB 内存)和一个 5 节点集群(4 个 CPU 和 16 GB 内存),那么可以利用 Mesos 将它们整合成一个虚拟集群,具有 720 GB 内存和 180 个 CPU,在这个集群上可以运行多个分布式应用程序。以这种方式共享资源大大提高了集群利用率,并消除了每个框架所需的昂贵数据复制过程。
Mesos 的一些重要特性包括:
-
可扩展性:可以弹性扩展到超过 50,000 个节点
-
资源隔离:通过 Linux/Docker 容器实现
-
高效性:通过在多个框架之间进行 CPU 和内存感知的资源调度来实现
-
高可用性:通过 Apache ZooKeeper 实现
-
监控接口:用于监控集群状态的 Web UI
Mesos 基于与 Linux 内核相同的原则,旨在提供一个高可用、可扩展和容错的基础,使得各种框架能够有效且隔离地共享集群资源。分布式应用程序种类繁多且持续发展,这一事实使得 Mesos 的设计理念趋向于提供一个精简的接口,允许不同框架之间高效地分配资源,并将任务调度和作业执行的工作委托给框架本身。这样做的两个优点是:
-
不同的框架数据复制工作可以独立地设计方法来解决它们的数据局部性、容错性及其他需求
-
它简化了 Mesos 的代码库,并使其具备可扩展性、灵活性、健壮性和敏捷性
Mesos 的架构通过采用资源报价抽象将任务调度的责任交给各自的框架,它将一组资源打包并向每个框架发出报价。Mesos 主节点决定向每个框架提供多少资源,而每个框架则决定接受哪些资源报价,并在这些接受的资源上执行哪些任务。这种资源分配方法显示出可以为共享同一集群的每个框架实现良好的数据局部性。
另一种架构将实现一个全局调度器,该调度器将框架需求、组织优先级和资源可用性作为输入,并提供按框架和资源划分的任务调度输出,本质上充当了作业与资源之间的媒介,优先级作为约束条件。这个架构的挑战,如开发一个能捕捉不同框架各种需求的健壮 API、预见新框架的出现,以及为数百万个作业解决复杂的调度问题,使得前一种方法对于创作者来说更具吸引力。
框架介绍
Mesos 框架位于 Mesos 和应用程序之间,充当管理任务调度和执行的层。由于其实现是特定于应用程序的,因此这个术语通常用来指代应用程序本身。早期,Mesos 框架只能通过 libmesos C++ 库与 Mesos API 交互,因此开发了包括 Java、Scala、Python 和 Go 等语言的绑定,这些语言都大量利用了 libmesos。从 v0.19.0 版本开始,对基于 HTTP 协议所做的更改使得开发者可以使用他们想要的语言开发框架,而不必依赖 C++ 代码。一个框架由两个组件组成:a) 调度器和 b) 执行器。
调度器负责决定它收到的资源报价,并跟踪集群的当前状态。与 Mesos 主节点的通信由 SchedulerDriver 模块处理,该模块将框架注册到主节点,启动任务,并向其他组件传递消息。
第二个组件,Executor,顾名思义,负责在从节点上执行任务。与从节点的通信由 ExecutorDriver 模块处理,该模块还负责将状态更新发送到调度程序。
本章稍后讨论的 Mesos API 使程序员能够开发自己的自定义框架,这些框架可以在 Mesos 上运行。框架的其他一些特性,如身份验证、授权和用户管理,将在 第六章,Mesos 框架 中详细讨论。
构建在 Mesos 上的框架
下面列出了一些构建在 Mesos 上的服务和框架。此列表并不详尽,几乎每天都有新框架得到支持。除了下面的列表,你还可以参考 mesos.apache.org/documentation/latest/frameworks/
:
长时间运行的服务
-
Aurora:这是一个运行在 Mesos 上的服务调度程序,允许你运行长时间运行的服务,利用 Mesos 的可扩展性、容错性和资源隔离性。
-
Marathon:这是一个基于 Mesos 的私有 PaaS。它自动处理硬件或软件故障,并确保应用始终在线。
-
Singularity:这是一个调度程序(HTTP API 和 Web 界面),用于运行 Mesos 任务,例如长时间运行的进程、一次性任务和定时任务。
-
SSSP:这是一个简单的 Web 应用程序,提供一个“Megaupload”白标用于存储和共享 S3 中的文件。
大数据处理
-
Cray Chapel 是一种高效的并行编程语言。Chapel Mesos 调度器让你可以在 Mesos 上运行 Chapel 程序。
-
Dark 是一个 Python 版的 Spark,是一个类似 MapReduce 的框架,使用 Python 编写并在 Mesos 上运行。
-
Exelixi 是一个分布式框架,用于大规模运行遗传算法。
-
Hadoop:在 Mesos 上运行 Hadoop 会高效地将 MapReduce 作业分配到整个集群中。
-
Hama 是一个分布式计算框架,基于大规模科学计算的批同步并行(Bulk Synchronous Parallel)技术,例如矩阵、图和网络算法。
-
MPI 是一个消息传递系统,旨在适用于各种并行计算机。
-
Spark 是一个快速且通用的集群计算系统,使并行作业的编写变得简单。
-
Storm 是一个分布式实时计算系统。Storm 使得可靠地处理无界数据流变得简单,它为实时处理做的事情类似于 Hadoop 为批处理所做的工作。
批量调度
-
Chronos 是一个分布式作业调度器,支持复杂的作业拓扑结构。它可以作为更具容错性的 cron 替代品。
-
Jenkins 是一个持续集成服务器。Mesos-Jenkins 插件使其能够根据工作负载动态地在 Mesos 集群上启动工作节点。
-
JobServer 是一个分布式作业调度和处理器,允许开发人员使用点选式 Web UI 构建自定义批处理任务。
数据存储
-
Cassandra 是一个高性能且高度可用的分布式数据库。在线性可扩展性和在商用硬件或云基础设施上经受住了故障容忍性验证,使其成为处理关键数据的理想平台。
-
Elasticsearch 是一个分布式搜索引擎。Mesos 使其能够轻松运行和扩展。
Mesos 的属性和资源
Mesos 通过以下两种方法描述集群中存在的从节点:
属性
属性用于描述有关从节点的某些附加信息,例如其操作系统版本、是否具有某种硬件类型等。它们以键值对的形式表达,支持三种不同的值类型——标量、范围和文本——并与报价一起发送给框架。请查看以下代码:
attributes : attribute ( ";" attribute )*
attribute : text ":" ( scalar | range | text )
资源
Mesos 可以管理三种不同类型的资源:标量、范围和集合。这些资源用于表示 Mesos 从节点所能提供的不同资源。例如,标量资源类型可以用来表示从节点的 CPU 数量。每个资源都通过一个键字符串来标识,如下所示:
resources : resource ( ";" resource )*
resource : key ":" ( scalar | range | set )
key : text ( "(" resourceRole ")" )?
resourceRole : text | "*"
预定义用途和惯例
Mesos 主节点预定义了它如何处理以下资源列表:
-
cpus
-
mem
-
disk
-
ports
特别地,一个没有cpu
和mem
资源的从节点将永远不会将其资源广告给任何框架。此外,主节点的用户界面将mem
和disk
中的标量值解释为 MB。例如,值15000
会显示为14.65GB
。
示例
这里是一些配置 Mesos 从节点的示例:
-
resources='cpus:24;mem:24576;disk:409600;ports:[21000-24000];bugs:{a,b,c}'
-
attributes='rack:abc;zone:west;os:centos5;level:10;keys:[1000-1500]'
在这种情况下,我们有三种不同类型的资源:标量、范围和集合。它们分别被称为cpus
、mem
和disk
,范围类型为ports
。
-
一个名为
cpus
的标量,值为24
-
一个名为
mem
的标量,值为24576
-
一个名为
disk
的标量,值为409600
-
一个名为
ports
的范围,值为21000
到24000
(包含端点) -
一个名为
bugs
的集合,包含值a
、b
和c
在属性的情况下,我们将得到三个属性:
-
一个名为
rack
的属性,文本值为abc
-
一个名为
zone
的属性,文本值为west
-
一个名为
os
的属性,文本值为centos5
-
一个名为
level
的属性,标量值为10
-
一个名为
keys
的属性,范围值为1000
到1500
(包含端点)
两级调度
Mesos 具有一个两级调度机制,用于为不同框架分配资源并启动任务。在第一层级中,管理 Mesos 集群中每个节点上从节点进程的主控进程会确定每个节点上可用的空闲资源,将其分组,并根据组织政策(如优先级或公平共享)将这些资源提供给不同的框架。组织还可以通过自定义分配模块定义自己的共享政策。
在第二级,每个框架的调度组件(作为客户端注册到主控)根据框架的需求接受或拒绝所提供的资源。如果接受该资源提供,框架的调度器会向 Mesos 主控发送需要执行的任务信息,以及每个任务所需的资源数量。主控将任务转交给相应的从节点,从节点为框架的执行器组件分配必要的资源,执行器管理容器中所有任务的执行。当任务完成后,容器被拆除,资源被释放供其他任务使用。
以下图示和来自 Apache Mesos 文档的解释(mesos.apache.org/documentation/latest/architecture/
)更详细地解释了这一概念:
让我们来看一下前面图示中提到的要点:
-
1: 从节点 1 向主控报告其有四个 CPU 和 4 GB 内存空闲。然后,主控调用分配模块,告知它应该将所有可用资源提供给框架 1。
-
2: 主控发送描述这些资源的资源提供信息给框架 1。
-
3: 框架的调度器向主控回复有关在从节点上运行的两个任务的信息,第一个任务使用两个 CPU 和 1 GB 内存,第二个任务使用一个 CPU 和 2 GB 内存。
-
4: 主控进程将任务发送到从节点,从节点为框架的执行器分配适当的资源,执行器进而启动这两个任务。由于一个 CPU 和 1 GB 内存仍然空闲,分配模块现在可能会将这些资源提供给框架 2。此外,当任务完成并且有新的资源释放时,该资源还会提供给进程重试。
Mesos 还为框架提供了拒绝资源提供的能力。框架可以拒绝那些不符合其要求的资源提供。这使得框架能够支持各种复杂的资源约束,同时保持 Mesos 的简单性。一种叫做 延迟调度 的策略允许框架等待有限时间以访问存储其输入数据的节点,尽管会有轻微的延迟,但能提供公平的数据本地性。
如果框架约束复杂,可能会出现一个框架在收到符合其需求的合适资源报价之前需要等待的情况。为了解决这个问题,Mesos 允许框架设置过滤器,指定它们将用于始终拒绝某些资源的标准。例如,框架可以设置一个过滤器,表示它只能在至少有 32 GB 空闲内存的节点上运行。这样可以绕过拒绝过程,减少通信开销,从而降低整体延迟。
资源分配
资源分配模块包含了 Mesos 主节点用来确定需要向每个框架提供的资源类型和数量的策略。组织可以自定义该模块以实现自己的分配策略——例如,公平分配、优先级等——从而实现细粒度的资源共享。可以开发自定义分配模块来解决特定需求。
资源分配模块负责确保资源在竞争框架之间公平共享。用于确定共享策略的算法选择对集群管理器的效率有着重要影响。最流行的分配算法之一是最大最小公平分配及其加权派生算法,接下来的部分将进行描述。
最大最小公平分配算法
假设有一组源(1,2,...,m),其资源需求分别为 x[1],x[2],...,x[m]。假设资源总数为 R。我们将首先把 R/m 的资源分配给每个 m 个源。然后,从需求最少的源开始,我们将分配的资源与实际需求进行比较。如果初始分配(R/m)超过了源 1 的需求要求,我们将把多余的资源在其余源之间平均重新分配。然后,我们将把新的分配与第二个需求最少的源的实际需求进行比较,继续重复这一过程。直到每个源的分配资源小于或等于其实际需求。如果有任何源的分配资源少于其实际需要,算法确保没有其他源可以获得比该源更多的资源。这种分配被称为最大最小公平分配,因为它最大化了需求没有得到满足的源的最小份额。
请参考以下示例:
如何计算一个四个源的最大最小公平分配,S1,S2,S3,S4,它们的需求分别为 2,2.5,4 和 5,当资源总容量为 10 时。
根据之前描述的方法,为了解决这个问题,我们将资源初步划分为四个每个大小为 2.5 的部分。接下来,我们将比较这种分配与需求最小的源(在本例中为S1)的实际需求。由于分配大于实际需求,剩余的 0.5 将均等地分配给其余的三个源,即S2、S3和S4,每个获得 2.666。继续这一过程,我们会发现新的分配大于源S2的实际需求。多余的 0.166 再次均匀地分配给剩下的两个源S3和S4,每个变为2.666 + 0.084 = 2.75。现在每个源的分配量都小于或等于实际需求,因此这个过程停止。最终的分配结果是,S1 – 2,S2 – 2.5,S3 – 2.75,和S4 – 2.75。
这一策略在同质环境中效果很好——也就是资源需求在不同竞争用户之间大致成比例的环境,比如 Hadoop 集群。然而,在具有异构资源需求的框架之间调度资源则构成了更复杂的挑战。如果用户 A 运行的任务每个需要两个 CPU 和 8GB 的内存,而用户 B 运行的任务每个需要四个 CPU 和 2GB 内存,那么适合的公平分配政策是什么?如所示,用户 A 的任务是内存密集型的,而用户 B 的任务是 CPU 密集型的。那么,如何在这两种资源(RAM 和 CPU)之间为两位用户分配资源呢?
后者场景是 Mesos 常遇到的情况,因为 Mesos 的设计目的是主要在异构环境中管理资源。为了解决这个问题,Mesos 将主导资源公平性算法(DRF)作为其默认资源分配策略,这在异构环境中更为适用。该算法及其在高效资源分配中的作用将在下一章中详细讨论。
资源隔离
集群管理器的关键要求之一是确保将资源分配给特定框架时不会影响其他框架上任何正在运行的作业。因此,Mesos 提供了在从节点上实施隔离机制以区分不同任务的功能。容器被用于资源隔离,并采用可插拔架构。Mesos 的从节点使用 Containerizer API 来提供一个隔离环境,运行框架的执行器及其相应的任务。Containerizer API 的目标是支持广泛的实现,这意味着可以开发定制的容器化工具和隔离器。当从节点进程启动时,可以指定用于启动容器的容器化工具以及用于执行资源约束的隔离器。
Mesos 容器化 API 使用 Linux 特有的功能(如控制组和命名空间)提供框架执行器的资源隔离。它还为 POSIX 系统提供基本支持(仅限资源使用报告,而非实际隔离)。这一重要话题将在后续章节中详细探讨。
Mesos 还在容器级别提供网络隔离,以防止单个框架占用所有可用的网络带宽或端口。然而,默认情况下不支持此功能,需要安装和配置额外的依赖项来启用此功能。
Mesos 中的监控
在本节中,我们将查看 Mesos 提供的用于监控各种组件的不同度量指标。
Mesos 提供的监控
Mesos 主节点和从属节点提供丰富的数据,支持资源利用率监控和异常检测。信息包括可用资源、已用资源、注册框架、活跃从属节点和任务状态的详细信息。这些数据可用于创建自动化警报和开发集群健康监控仪表板。更多细节请参考:
mesos.apache.org/documentation/latest/monitoring/
。
每个活跃容器的网络统计数据通过从属节点上的/monitor/statistics.json
端点发布。
度量指标类型
Mesos 提供两种不同类型的度量指标:计数器和仪表。它们的解释如下:
-
计数器:用于衡量离散事件,如已完成的任务数或无效的状态更新。其值始终为整数。
-
仪表:用于检查特定度量指标的快照,如某一时刻活跃框架的数量或正在运行的任务数量。
Mesos API
Mesos 提供一个 API,允许开发人员构建可以在底层分布式基础架构上运行的自定义框架。利用此 API 和新的 HTTP API 开发定制框架的详细步骤将在第六章,Mesos 框架中详细探讨。
消息
Mesos 实现了一种基于演员模型的消息传递编程模型,以实现不同 Mesos 组件之间的非阻塞通信,并使用协议缓冲区进行传输。例如,调度程序需要告诉执行器使用一定数量的资源,执行器需要向调度程序提供有关执行的任务的状态更新,等等。协议缓冲区提供了所需的灵活消息传递机制,允许开发人员定义可以跨不同语言使用的自定义格式和协议。有关不同 Mesos 组件之间传递的消息的更多详细信息,请参阅github.com/apache/mesos/blob/master/include/mesos/mesos.proto
API 详情
以下部分简要介绍了 Mesos 提供的不同 API 和方法:
执行器 API
以下是执行器 API 的简要描述。欲了解更多详情,请访问mesos.apache.org/api/latest/java/org/apache/mesos/Executor.html
。
-
registered
:可以通过以下代码注册:void registered(ExecutorDriver driver, ExecutorInfo executorInfo, FrameworkInfo frameworkInfo, SlaveInfo slaveInfo)
这段代码会在执行器驱动程序成功连接到 Mesos 后调用。特别地,调度程序可以通过
ExecutorInfo.getData()
字段将一些数据传递给其执行器。以下是参数:
-
driver
:这是已注册并连接到 Mesos 集群的执行器驱动程序。 -
executorInfo
:这是描述已注册的执行器信息。 -
frameworkInfo
:这是描述已注册框架的信息。 -
slaveInfo
:这是描述将用于启动该执行器任务的从节点。
-
-
reregistered
:可以重新注册,具体如下:void reregistered(ExecutorDriver driver, SlaveInfo slaveInfo)
这段代码会在执行器与重新启动的从节点重新注册时调用。
以下是参数:
-
driver
:这是已与 Mesos 主节点重新注册的执行器驱动程序。 -
slaveInfo
:这是描述将用于启动该执行器任务的从节点。
-
-
disconnected
:可以通过以下代码断开连接:void disconnected(ExecutorDriver driver)
上述代码会在执行器与从节点“断开连接”时调用,例如在从节点由于升级而重新启动时。
以下是参数:
driver
:这是已断开连接的执行器驱动程序。
-
launchTask
:请查看以下代码:void launchTask(ExecutorDriver driver, TaskInfo task)
上述代码会在该执行器上启动任务时调用(通过
SchedulerDriver.launchTasks(java.util.Collection<OfferID>, java.util.Collection<TaskInfo>, Filters)
发起)。请注意,此任务可以通过线程、进程或一些简单的计算来实现;但是,在该回调返回之前,不会在此执行器上调用其他回调。以下是参数:
-
driver
:这是启动任务的执行器驱动程序。 -
task
:描述已启动的任务
-
-
killTask
:运行以下代码:void killTask(ExecutorDriver driver, TaskID taskId)
当此执行器中的任务通过
SchedulerDriver.killTask
(TaskID)被终止时,将调用此方法。请注意,执行器不会代表任务发送状态更新,执行器负责创建新的TaskStatus
protobuf 消息(即,TASK_KILLED
),并调用ExecutorDriver.sendStatusUpdate
(TaskStatus
)。以下是参数:
-
driver
:这是拥有被终止任务的执行器驱动程序 -
taskId
:这是已终止任务的 ID
-
-
frameworkMessage
:运行以下代码:void frameworkMessage(ExecutorDriver driver, byte[] data)
当执行器接收到框架消息时,将调用此方法。这些消息是尽力而为的;不要期望框架消息以任何可靠的方式被重新传输。
以下是参数:
-
driver
:这是接收到消息的执行器驱动程序 -
data
:这是消息负载
-
-
shutdown
:执行以下代码:void shutdown(ExecutorDriver driver)
当执行器终止其所有当前运行的任务时,将调用此方法。请注意,在 Mesos 确定执行器已终止后,任何执行器没有发送终止状态更新的任务(例如,
TASK_KILLED
、TASK_FINISHED
、TASK_FAILED
等)将创建一个TASK_LOST
状态更新。以下是参数:
driver
:这是应该终止的执行器驱动程序。
-
error
:运行以下:void error(ExecutorDriver driver, java.lang.String message)
当执行器和/或执行器驱动程序发生致命错误时,前述代码会被调用。在调用此回调之前,驱动程序将被终止。
以下是参数:
-
driver
:这是由于此错误而被中止的执行器驱动程序 -
message
:这是错误信息
-
执行器驱动程序 API
以下是执行器驱动程序 API 的简要说明。如需更多详细信息,请访问 mesos.apache.org/api/latest/java/org/apache/mesos/ExecutorDriver.html
。
-
start
:运行以下一行:Status start()
上述代码启动了执行器驱动程序。必须在进行其他驱动程序调用之前调用此方法。
调用后的驱动程序状态已返回。
-
stop
:运行以下一行:Status stop()
这将停止执行器驱动程序。
调用后的驱动程序状态就是返回值。
-
abort
:运行以下一行:Status abort()
这将中止驱动程序,以便不再向执行器发送回调。中止和停止的语义被故意分开,以便代码能够检测到中止的驱动程序(通过
join()
的返回状态;参见以下部分),并根据需要实例化并启动另一个驱动程序(在同一进程内,尽管目前执行器不支持此功能)。调用后的驱动程序状态就是返回值。
-
join
:运行以下:Status join()
这会等待驱动程序停止或中止,可能会无限期地阻塞当前线程。此函数的返回状态可用于确定驱动程序是否被中止(查看
mesos.proto
以了解状态描述)。调用后的驱动程序状态就是返回值。
-
run
:查看以下代码行:Status run()
这将启动并立即加入(即阻塞)驱动程序。
调用后的驱动程序状态就是返回值。
-
sendStatusUpdate
:执行以下代码:Status sendStatusUpdate(TaskStatus status)
这会向框架调度器发送状态更新,必要时会重试,直到收到确认或执行器被终止(在这种情况下,将发送
TASK_LOST
状态更新)。有关状态更新确认的更多信息,请查看Scheduler.statusUpdate(org.apache.mesos.SchedulerDriver, TaskStatus)
。以下是参数:
status
:这是需要发送的状态更新。
-
调用后的驱动程序状态就是返回值。
-
sendFrameworkMessage
:运行以下代码:Status sendFrameworkMessage(byte[] data)
这会向框架调度器发送一条消息。这些消息以尽力而为的方式发送,不能期望它们以任何可靠的方式重新传输。
参数如下:
data
:这是消息负载。
调用后的驱动程序状态就是返回值。
调度器 API
以下是调度器 API 的简要描述。更多细节,请访问 mesos.apache.org/api/latest/java/org/apache/mesos/Scheduler.html
。
-
registered
:可以通过以下代码进行注册:void registered(SchedulerDriver driver, FrameworkID frameworkId, MasterInfo masterInfo)
前述代码会在调度器成功注册到 Mesos 主节点时调用。一个由主节点生成的唯一 ID 被用来区分该框架与其他框架,并且包含当前主节点 IP 和端口的
MasterInfo
被作为参数提供。以下是参数:
-
driver
:这是已注册的调度器驱动程序。 -
FrameworkID
:这是由主节点生成的FrameworkID
。 -
MasterInfo
:这是关于当前主节点的信息,包括 IP 和端口。
-
-
reregistered
:前述代码可以如下重新注册:void reregistered(SchedulerDriver driver, MasterInfo masterInfo)
前述代码会在调度器重新注册到新选举出的 Mesos 主节点时调用。只有在调度器之前已注册时才会调用。包含选举出的主节点更新信息的
MasterInfo
被作为参数提供。参数如下:
-
driver
:这是已重新注册的驱动程序。 -
MasterInfo
:这是关于选举出的主节点的更新信息。
-
-
resourceOffers
:执行以下代码:void resourceOffers(SchedulerDriver driver, java.util.List<Offer> offers)
当资源提供给该框架时,会调用前面的代码。单个提议仅包含来自单个从属节点的资源。与提议相关的资源不会重新提供给该框架,直到以下情况之一发生:(a)该框架拒绝这些资源(参考
SchedulerDriver.launchTasks(java.util.Collection<OfferID>, java.util.Collection<TaskInfo>, Filters)
),或(b)这些资源被撤销(参考offerRescinded(org.apache.mesos.SchedulerDriver, OfferID)
)。请注意,资源可能会同时提供给多个框架,具体取决于所使用的分配器。在这种情况下,第一个使用这些资源启动任务的框架将能够使用这些资源,而其他框架将会失去这些资源。(或者,如果一个框架已经使用这些资源启动了任务,这些任务将失败,并显示TASK_LOST
状态,附带一条说明信息。)以下是参数:
-
driver
:这是用于运行该调度器的驱动程序 -
offers
:这是提供给该框架的资源
-
-
offerRescinded
:运行以下代码:void offerRescinded(SchedulerDriver driver, OfferID offerId)
当提议不再有效时会调用此代码(例如,从属节点丢失或另一个框架使用了提议中的资源)。如果由于某种原因,提议从未被撤销(例如,丢失的消息,框架故障转移等),那么尝试使用无效提议启动任务的框架将收到
TASK_LOST
状态更新(请查看resourceOffers(org.apache.mesos.SchedulerDriver, java.util.List<Offer>)
)。以下是参数:
-
driver
:这是用于运行该调度器的驱动程序 -
offerID
:这是被撤销的提议的 ID
-
-
statusUpdate
:请查看以下代码:void statusUpdate(SchedulerDriver driver, TaskStatus status)
当任务状态发生变化时,会调用前面的代码(例如,某个从属节点丢失,任务也随之丢失;任务完成,执行器发送状态更新;等等)。如果由于某种原因,在这个回调过程中调度器被中止或进程退出,那么会再传递一次状态更新。(不过请注意,如果在此期间发送状态更新的从属节点丢失或失败,当前这种情况并不适用。)
以下是参数:
-
driver
:这是用于运行该调度器的驱动程序 -
status
:这是状态更新,包含任务 ID 和状态
-
-
frameworkMessage
:请查看以下代码:void frameworkMessage(SchedulerDriver driver, ExecutorID executorId, SlaveID slaveId, byte[] data)
当执行器发送消息时,会调用前面的代码。这些消息是尽最大努力发送的,不能期望它们会以可靠的方式重新传输。
参数如下:
-
driver
:这是接收消息的驱动程序 -
ExecutorID
:这是发送消息的执行器 ID -
SlaveID
:这是启动执行器的从属节点 ID -
data
:这是消息的有效载荷
-
-
disconnected
:运行以下代码:void disconnected(SchedulerDriver driver)
当调度器与主节点断开连接时会触发此操作(例如,主节点失败并由另一个主节点接管)。
以下是参数:
driver
: 这是用来运行此调度器的驱动程序
-
slaveLost
: 执行以下代码:void slaveLost(SchedulerDriver driver, SlaveID slaveId)
当从节点被确定为无法访问时(例如,机器故障或网络分区),会调用此操作。大多数框架需要在新的从节点上重新调度所有在该从节点上启动的任务。
以下是参数:
-
driver
: 这是用来运行此调度器的驱动程序 -
SlaveID
: 这是丢失的从节点 ID
-
-
executorLost
: 执行以下操作:void executorLost(SchedulerDriver driver, ExecutorID executorId, SlaveID slaveId, int status)
上述操作会在执行器退出或终止时触发。请注意,任何正在运行的任务都会自动生成
TASK_LOST
状态更新。以下是参数:
-
driver
: 这是用来运行此调度器的驱动程序 -
ExecutorID
: 这是丢失的执行器的 ID -
slaveID
: 这是启动执行器的从节点 ID -
status
: 这是执行器的退出状态
-
-
error
: 执行以下代码:void error(SchedulerDriver driver, java.lang.String message)
上述操作会在调度器或驱动程序发生不可恢复错误时触发。驱动程序将在调用此回调之前被中止 before。
以下是参数:
-
driver
: 这是用来运行此调度器的驱动程序 -
message
: 这是错误消息
-
调度器驱动程序 API
以下是调度器驱动程序 API 的简要描述。更多细节,请访问 mesos.apache.org/api/latest/java/org/apache/mesos/SchedulerDriver.html
-
start
: 执行以下代码:Status start()
这会启动调度器驱动程序。它需要在任何其他驱动程序调用之前调用。
上述操作会在调用后返回驱动程序的状态。
-
stop
: 执行以下代码:Status stop(boolean failover)
这会停止调度器驱动程序。如果
failover
标志设置为 false,则预计该框架永远不会重新连接到 Mesos。因此,Mesos 将注销该框架并关闭所有任务和执行器。如果failover
为 true,所有执行器和任务将保持运行(在某些特定的故障转移超时内),允许调度器重新连接(可能是在同一进程中或来自不同进程——例如,在不同的机器上)。以下是参数:
failover
: 这是是否预计会发生框架故障转移
这会返回调用后驱动程序的状态。
-
Stop
: 执行以下一行:Status stop()
这会停止调度器驱动程序,假设没有故障转移。这样会导致 Mesos 注销该框架并关闭所有任务和执行器。
这会返回调用后驱动程序的状态。
-
abort
: 执行以下代码:Status abort()
这将中止驱动程序,从而不再对调度器进行任何回调。中止和停止的语义被故意分开,以便代码可以检测到已中止的驱动程序(通过
join()
的返回状态;请参阅以下部分),并根据需要在同一进程内实例化并启动另一个驱动程序。它返回调用后的驱动程序状态。
-
join
: 运行以下代码:Status join()
这将等待驱动程序停止或被中止,可能会阻塞当前线程,直到操作完成。该函数的返回状态可用于确定驱动程序是否被中止(请查看
mesos.proto
以了解Status
的描述)。它返回调用后的驱动程序状态。
-
run
: 执行以下操作:Status run()
这将启动并立即加入(即阻塞)驱动程序。
它返回调用后的驱动程序状态。
-
requestResources
: 请查看以下内容:Status requestResources(java.util.Collection<Request> requests)
这将从 Mesos 请求资源(请查看
mesos.proto
以了解 Request 的描述,以及如何例如从特定的 slaves 请求资源)。任何可用的资源将通过Scheduler.resourceOffers(org.apache.mesos.SchedulerDriver, java.util.List<Offer>)
回调异步地提供给框架。以下是参数:
requests
: 这些是资源请求。
它返回调用后的驱动程序状态。
-
launchTasks
: 使用以下代码:Status launchTasks(java.util.Collection<OfferID> offerIds, java.util.Collection<TaskInfo> tasks, Filters filters)
上述代码将在一组 offer 上启动给定的任务集。资源来自多个 offer 时将会被聚合。请注意,所有的 offer 必须属于同一个 slave。任何剩余的资源(即未被任务或其执行器使用的资源)将被视为已拒绝。指定的过滤器将应用于所有未使用的资源(请查看
mesos.proto
以了解 Filters 的描述)。如果使用空的任务集合调用此函数,则会完全拒绝所有的 offer(参见declineOffer(OfferID, Filters)
)。以下是参数:
-
offerIds
: 这是 offer ID 的集合。 -
tasks
: 这是要启动的任务集合。 -
filters
: 这是用于设置剩余资源的过滤器。
它返回调用后的驱动程序状态。
-
-
killTask
: 执行以下代码:Status killTask(TaskID taskId)
这将终止指定的任务。请注意,尝试终止任务目前并不可靠。例如,如果调度器在尝试终止任务时发生故障,它将在将来需要重新尝试。同样,如果未注册或断开连接,请求将被丢弃(这些语义可能会在未来发生变化)。
以下是参数:
taskId
: 这是要终止的任务的 ID。
它返回调用后的驱动程序状态。
-
declineOffer
: 运行以下代码:Status declineOffer(OfferID offerId, Filters filters)
这将完全拒绝一个请求,并对资源应用指定的过滤器(可以查看
mesos.proto
以了解过滤器的描述)。请注意,这可以随时进行,并不需要在Scheduler.resourceOffers(org.apache.mesos.SchedulerDriver, java.util.List<Offer>)
回调中完成。以下是参数:
-
offerId
:这是要拒绝的请求的 ID -
filters
:这是要为任何剩余资源设置的过滤器
它返回调用后的驱动程序状态。
-
-
reviveOffers
:执行以下操作:Status reviveOffers()
这将删除框架之前设置的所有过滤器(通过
launchTasks(java.util.Collection<OfferID>, java.util.Collection<TaskInfo>, Filters)
)。这使得框架可以从这些被过滤的从节点接收请求。它返回调用后的驱动程序状态。
-
sendFrameworkMessage
:请查看以下内容:Status sendFrameworkMessage(ExecutorID executorId, SlaveID slaveId, byte[] data)
这会从框架向其一个执行器发送消息。消息是尽最大努力发送的,不应期望以任何可靠的方式进行重传。
参数如下:
-
executorId
:这是发送消息的执行器 ID -
slaveId
:这是运行执行器的从节点的 ID -
data
:这是消息
它返回调用后的驱动程序状态。
-
-
reconcileTasks
:请查看以下代码:Status reconcileTasks(java.util.Collection<TaskStatus> statuses)
这允许框架查询非终结任务的状态。如果可能,主节点会返回
statuses
中每个任务的最新状态。那些不再存在的任务会导致TASK_LOST
更新。如果statuses
为空,主节点将发送当前已知的每个任务的最新状态。以下是参数:
statuses
:这是待对账的非终结TaskStatus
protobuf 消息集合。
它返回调用后的驱动程序状态。
Mesos 在生产环境中
Mesos 已在多家企业中投入生产使用,例如 Apple、Twitter 和 HubSpot,并且甚至被 Mattermark 和 Sigmoid 等初创公司使用过。这种广泛的吸引力验证了 Mesos 强大的实用性。例如,Apple 通过一个大型 Mesos 集群(据称跨越数万个节点)支持其面向消费者的、任务关键型的流行 Siri 应用程序。这里讨论了一个案例研究(发布在 Mesosphere 网站上)。
HubSpot 案例研究
关于 HubSpot 的案例研究可以在此处找到 mesosphere.com/mesos-case-study-hubspot/
。下面给出了该链接的摘录:
HubSpot 使用 Apache Mesos 运行包括 Web 服务、长时间运行的进程和计划任务在内的混合服务,组成了他们的 SaaS 应用程序。Mesos 允许 HubSpot 动态部署服务,从而减少了开发人员的摩擦和部署时间,提高了可靠性,实现了更好的资源利用率,并降低了硬件成本。
Mesos 提供了构建下一代部署系统的核心基础设施,这类似于 Heroku 提供的产品。在 Mesos 之上,HubSpot 构建了自己的调度器,能够执行长时间运行的服务和定时任务,并且是开发团队查看其应用程序在云中状态的接口。构建调度器框架使 HubSpot 更好地理解 Mesos 内部的核心概念,适应故障模式,并定制用户体验。
集群环境
HubSpot 内运行着超过 150 个服务。HubSpot 在 Amazon EC2 中使用了数百台服务器,Mesos 集群占据了大约 30% 的资源,并且随着越来越多服务迁移到 Mesos,集群的规模正在迅速扩大。由于 Mesos 能轻松处理大规模或小规模的服务器配置,数百台小型服务器被替换为数十台更大的服务器。
好处
Mesos 为开发团队和公司提供了众多好处。在 HubSpot,开发者拥有他们应用程序的操作权。借助 Mesos,开发者可以更快地部署服务,并减少维护工作。以下是一些其他的好处:
-
开发者可以立即访问集群资源,无论是为了扩展还是引入新服务。
-
开发者不再需要理解硬件或服务器申请的过程,且在 Mesos 中扩大资源需求比重建具有更多或更少 CPU 和内存的服务器要容易得多。
-
硬件故障对开发者而言更加透明,因为当任务丢失或失败时,服务会自动替换。换句话说,开发者不再因为简单的硬件故障而接到紧急通知。
-
定时任务(cron 任务)现在通过 Web 界面暴露出来,并且不再绑定到单一服务器,避免了服务器故障时 cron 任务也随之丢失的情况。
Mesos 还简化了从操作角度请求和管理硬件所需的技术栈。HubSpot 可以标准化服务器配置并简化 Mesos slave 执行的基础镜像。
最后,资源利用率得到了提高,这直接对应于降低成本。之前在过度配置的硬件上运行的服务现在仅使用所需的精确资源。
此外,QA 环境的容量已降至之前的 50%,因为 HubSpot 调度器确保当服务失败时会重新启动。这意味着不再需要在 QA 中运行多个副本来保证服务的高可用性。
挑战
采用过程中的核心挑战是向 100 位负责日常管理应用程序的工程师群体引入一种新的部署技术。HubSpot 通过围绕 Mesos 构建 UI,并利用 Mesos 使部署过程尽可能简单和高效,从而解决了这一挑战。
展望未来
HubSpot 将 Mesos 视为未来迁移到其他数据中心的核心技术。作为一种虚拟化和部署技术,Mesos 已证明是通往成功的有效路径。此外,HubSpot 希望最终利用 Mesos 根据负载动态扩展进程,缩小或扩大集群规模以满足需求,并帮助开发人员进行资源估算。
提示
下载代码包的详细步骤在本书的前言中有提到,请查阅。本书的代码包也托管在 GitHub 上,地址为 github.com/PacktPublishing/Mastering-Mesos
。我们还提供了来自我们丰富书籍和视频目录的其他代码包,地址为 github.com/PacktPublishing/
。赶紧看看吧!
总结
在本章中,我们介绍了 Mesos,深入探讨了其架构,并讨论了一些重要主题,如框架、资源分配和资源隔离。我们还讨论了 Mesos 使用的两级调度方法,并提供了其 API 的详细概述。最后的 HubSpot 案例研究展示了它在生产中的应用,并表明它已经准备好投入实际使用。本章的目标是解释 Mesos 是什么、为什么需要它,并提供一个高层次的工作原理概述。
在下一章中,我们将深入了解其重要特性,并理解它如何有助于扩展、效率、高可用性和可扩展性。
第二章. Mesos 内部机制
本章提供了 Mesos 特性概述,并引导读者了解有关高可用性、容错性、扩展性和效率等多个重要主题。本章将涵盖以下主题:
-
扩展性和效率
-
资源分配(主导资源公平算法)
-
预留(静态和动态)
-
超额订阅
-
可扩展性
-
-
高可用性和容错性
-
从属恢复
-
对账
-
持久化卷
-
扩展性与效率
Mesos 旨在提供一种高度可扩展且高效的机制,使各种框架能够有效共享集群资源。分布式应用种类繁多,可能在不同上下文中有不同的优先级,并且不断发展,这一事实促使 Mesos 的设计理念朝着提供可定制的资源分配策略发展,用户可以根据自己的需求定义和设置这些策略。
资源分配
Mesos 资源分配模块包含 Mesos 主节点用来确定每个框架所需资源数量和类型的策略。组织可以根据自己的需求定制该策略,例如公平共享、优先级等,从而实现精细化的资源共享。可以开发自定义的分配模块,以满足特定需求。
资源分配模块负责确保资源在竞争框架之间公平地共享。选择用来确定共享策略的算法对于集群管理器的效率有着重要影响。
最大最小公平性(max-min fairness)是最受欢迎的分配算法之一,在同质环境中表现良好;这是指不同竞争用户之间的资源需求在较大程度上是成比例的,比如 Hadoop 集群。然而,在异构资源需求的框架之间调度资源则构成了更为复杂的挑战。如果用户 A 运行的任务每个需要两个 CPU 和 8 GB RAM,而用户 B 运行的任务每个需要四个 CPU 和 2 GB RAM,该采用什么样的公平分配策略?显然,用户 A 的任务内存需求较大,而用户 B 的任务 CPU 需求较大。那么,如何在这两者之间分配一组组合的 RAM + CPU 资源呢?
后者场景是 Mesos 经常遇到的常见问题,因为 Mesos 主要设计用于管理异构环境中的资源。为了解决这个问题,Mesos 采用了 Dominant Resource Fairness 算法 (DRF) 作为其默认资源分配策略,这对于异构环境更为合适。该算法在以下章节中有详细描述。
主导资源公平算法(DRF)
数据中心中的作业调度不仅限于 CPU,还扩展到其他资源,如内存和磁盘。在资源需求变化的场景中,有些任务是 CPU 密集型的,而有些任务是内存或磁盘密集型的;这正是最小-最大公平算法不足之处。这里需要一个资源调度机制,能够为异构环境中的每个用户提供其最需要的公平资源。简单来说,DRF 是最大-最小公平算法的一个变体,用于在用户之间公平地分配异构资源。
让我们考虑以下示例,来理解算法是如何工作的。
我们假设资源是按照需求向量的倍数提供的,并且是可分割的。
假设可用的总资源是八个 CPU 和 10 GB 内存。用户 1 运行的任务需要一个 CPU 和 3 GB 内存,用户 2 运行的任务需要三个 CPU 和 1 GB 内存。在我们继续分析 DRF 算法如何分配任务之前,先来理解主导资源和份额的概念:
-
主导资源:指的是用户最需要的资源(CPU 或内存)。在此案例中,用户 1 运行对内存有较高需求的任务(每个任务 3 GB 内存),因此用户 1 的主导资源是内存。另一方面,用户 2 运行计算密集型任务(每个任务使用三个 CPU),因此其主导资源是 CPU。
-
主导份额:指的是用户被分配的主导资源的比例。以我们的例子为例,用户 1 的主导份额是 30%(3/10),而用户 2 的主导份额是 37.5%(3/8)。
DRF 分配模块跟踪每个用户的主导份额,并记录分配给每个用户的资源。DRF 从向所有竞争用户中主导份额最低的用户提供资源(CPU 或内存)开始分配。然后,用户可以选择接受该提议,前提是它满足其需求。
现在,让我们来看一下 DRF 算法为用户 1 和用户 2 分配资源的每个步骤。为了简单起见,我们将忽略在小任务完成后释放回资源池的资源,并假设每次资源提供都被接受,且用户运行的是资源需求无限的任务。每个用户 1 的任务将消耗总 CPU 的八分之一和总内存的三分之一,从而使内存成为用户 1 的主导资源。每个用户 2 的任务将消耗总 CPU 的三分之一和总内存的十分之一,从而使CPU成为用户 2 的主导资源。
每一行提供以下信息:
-
用户选择:由算法提供资源的用户。
-
资源份额:在分配轮次中,分配给用户的每种资源类型的总可用资源的比例。
-
主导份额:主导资源的资源份额
-
主导份额百分比:主导份额以百分比(%)表示
-
CPU 总分配:在当前资源分配轮次中,分配给所有用户的 CPU 资源总和
-
内存总分配:在当前资源分配轮次中,分配给所有用户的内存资源总和
注意
注意:每一行中最低的主导份额用黄色突出显示。
首先,两个用户的主导份额都是 0%(因为还没有分配任何资源)。我们假设 DRF 选择用户 1 来首先提供资源,尽管如果我们假设选择用户 2,最终结果也会相同。以下是它将遵循的步骤:
-
用户 1 将获得运行任务所需的资源集。其主导资源(内存)的主导份额将增加到 30%。
-
用户 2 的主导份额为 0%,因此它将在下一轮获得资源。其主导资源(CPU)的主导份额将增加到 37.5%。
-
由于用户 1 现在的主导份额较低(30%),它将获得下一组资源。其主导份额上升至 60%。
-
现在,主导份额较低(37.5%)的用户 2 将获得资源。
-
该过程将继续,直到没有更多的资源可分配以运行用户任务为止。在这种情况下,步骤 4 之后,CPU 资源将达到饱和(以红色突出显示)。
-
如果有任何资源被释放或资源需求发生变化,过程将继续。
首要目标,DRF 是旨在最大化所有用户之间的最低主导份额。正如在这个例子中,DRF 与用户一起工作以分配以下资源:
-
为用户 1 分配两个任务,总分配为两个 CPU,6 GB 内存,主导份额为 60%(内存)。
-
为用户 2 分配两个任务,总分配为六个 CPU,2 GB 内存,主导份额为 75%(CPU)。
这可以通过以下图示表示:
加权 DRF
目前我们假设用户有相等的机会获得资源。算法中也可能进行修改,使得某个用户或一组用户在资源分配上优先于其他用户。这被称为加权 DRF,其中资源不会在用户之间平等共享。共享可以按每个用户和每个资源的权重进行,前者更为常见。
让我们考虑之前示例的每个用户加权计算。对于每个用户 i 和每个资源 j,权重表示为 w[1,j] = 3 和 w[2,j] = 1。这意味着用户 1 相比用户 2 在系统中将占有三倍的资源份额。如果两个权重值均为 1,则按照正常的 DRF 算法进行分配(如前所述)。
现在,我们来看 DRF 算法为用户 1 和用户 2 分配资源的每个步骤。
一开始,两个用户的主导份额都是 0%(因为尚未分配资源)。我们假设加权 DRF 首先选择给用户 1 分配资源,虽然如果我们假设是用户 2,最终结果也是一样的。以下是它将遵循的步骤:
-
用户 1 将收到运行任务所需的资源集。其主导资源(内存)的主导份额将增加至 10%(30%除以 3)。
-
用户 2 的主导份额为 0%,它将在下一轮接收资源。其主导资源(CPU)的主导份额将增加至 37.5%。
-
由于用户 1 现在拥有较低的主导份额(10%),它将接收下一批资源。其主导份额将上升到 20%(60%除以 3)。
-
用户 1 仍然拥有较低的主导份额(20%),现在再次分配资源,将其提升至 30%(90%除以 3)。
-
进程将继续,直到没有更多的资源可以分配给用户任务。在这种情况下,第四步后,内存资源将达到饱和(红色高亮显示)。
-
如果有资源被释放或资源需求发生变化,进程将继续。
加权 DRF 旨在根据每个用户分配的权重优先进行资源共享。在本例中,加权 DRF 与用户一起工作,分配了以下资源:
-
三个任务分配给用户 1,总共分配了三个 CPU 和 9GB 内存。
-
仅有一个任务分配给用户 2,总共分配了三个 CPU 和 1GB 内存。
这可以通过图示方式表示如下:
除此之外,还可以创建定制模块,以满足某个组织或特定资源分配的需求。这个部分将在同一章节后续进行讲解。
现在让我们看看 DRF 遵循/满足的一些重要属性:
-
渐进填充:在 DRF 中,通过渐进填充分配的资源会以相同的速度增加所有用户的主导份额,而其他用户的资源分配会根据需求按比例增加。这一过程会持续到至少有一个资源被饱和为止,此时需要饱和资源的用户的分配将被停止,这些用户会被淘汰。其他用户的渐进填充将以递归方式继续,直到没有用户的主导份额可以再增加为止。
-
份额保障:DRF 算法通过“渐进填充”向用户分配资源,确保每个用户的主导份额分配以相同的速度增加,并持续到一个资源被饱和并且资源分配被冻结为止。这间接保证了所有用户都被平等对待,并且至少能保证每个用户 1/n 的某项资源。
-
策略无关性:DRF 的这个特性确保了任何时刻,用户都无法通过伪造资源需求来从增加的资源分配中获益。如果用户确实尝试通过要求额外资源来欺骗系统,DRF 算法会以一种方式分配资源,使得这种行为对用户产生威慑作用。
-
帕累托效率:DRF 的这个特性意味着增加某个用户的主导份额会相应地减少其他用户对该特定资源的主导份额。得益于逐步填充算法,给一个特定用户分配更多资源会对其他用户造成影响,这一点是自然而然的。
-
无嫉妒性:DRF 是无嫉妒的,因为没有用户会偏好或嫉妒其他用户的资源分配。只有当例如用户 1 嫉妒用户 2,后者在某个特定资源上的主导份额更高时,才会产生嫉妒的情况。然而,考虑到资源分配是通过逐步填充进行的,到资源饱和时,用户 1 和用户 2 的主导份额将是相同的。这种嫉妒既没有好处,也不是必需的。
配置 Mesos 上的资源提供
常见的问题是,有时由于 slave 上资源配置设置不当,框架不会接受任何资源提供。例如,Elasticsearch 框架需要端口9200
和9300
,但 Mesos slave 的默认端口范围配置为31000
到32000
。
slave 必须正确配置,以便将正确的资源提供给框架,框架随后可以接受这些资源。可以按以下方式完成:
-
在
mesos-slave
命令中,添加必要的资源参数。以下是一个示例:--resources='ports:[9200-9200,9300-9300]' ...
-
在
/etc/mesos-slave
目录下创建一个名为resources
的文件,其内容是必要的资源字符串。运行以下命令:$ cat /etc/mesos-slave/resources ports:[9200-9200,9300-9300] $
预留
Mesos 还提供在指定的 slave 上预留资源的功能。这在确保重要服务从特定 slave 获取保证资源提供时特别有用(例如,数据库可能只需要从包含必要数据的特定 slave 获取资源提供)。如果没有预留机制,可能会出现重要服务或任务需要等待很长时间才能获得符合其过滤标准的资源提供,从而对性能产生不利影响。
另一方面,错误使用预留功能可能会导致相同类型的问题,例如 Mesos 最初旨在解决的资源利用率不足问题。因此,必须谨慎使用此功能。Mesos 访问控制机制确保请求预留资源的框架拥有适当的授权。
Mesos 提供两种资源预留方法:
-
静态预留
-
动态预留
静态预留
在这种类型的预留中,可以为特定框架或框架组在特定的从节点上保留指定的资源。为了为一个框架保留资源,必须将其分配给一个角色。如果需要,可以将多个框架分配给同一个角色。只有分配给特定角色(例如,角色 X)的框架才有权获得为角色 X 预留的资源的资源提供。首先需要定义角色,然后将框架分配给所需的角色,最后为这些角色设置资源策略。
角色定义
可以通过使用以下标志启动主节点来定义角色:
--roles = "name1, name2, name3"
例如,如果我们想定义一个名为hdfs
的角色,那么我们可以使用以下命令启动主节点:
--roles = "hdfs"
另外,你也可以通过执行以下命令来做到这一点:
echo hdfs > /etc/mesos-master/role
现在,需要通过执行以下命令重启主节点:
sudo service mesos-master restart
框架分配
现在,我们需要将框架映射到特定的角色。执行此操作的方法因框架而异。例如,像 Marathon 这样的框架可以通过–mesos_role
标志进行配置。在 HDFS 的情况下,可以通过将mesos-site.xml
中的mesos.hdfs.role
更改为之前定义的hdfs
值来实现。
<property>
<name>mesos.hdfs.role</name>
<value>hdfs</value>
</property>
可以通过在FrameworkInfo
中设置role
选项为所需的值来为框架指定自定义角色(默认值是*
)。
角色资源策略设置
可以通过利用从节点的–resources
标志为特定角色保留每个从节点上的资源。由于随着集群规模和运行的框架数量的增加,管理开销会迅速变得令人难以承受,从节点级别的资源策略设置有其缺点。
如果我们在特定从节点上有八个核心和 24 GB(在 Mesos 中以 MB 为单位指定)RAM 可用,并希望为hdfs
角色保留 2 个核心和 6 GB RAM,那么我们可以在从节点上进行以下更改:
--resources="cpus:6;mem:18432;cpus(hdfs):2;mem(hdfs):6144"
完成此操作后,可以通过执行以下命令停止具有这些更改设置的mesos-slave
:
sudo service mesos-slave stop
可以通过以下命令移除这些从节点上的旧状态。任何正在运行的任务可以手动终止,因为任务状态也将被移除:
rm -f /tmp/mesos/meta/slaves/latest
现在,可以通过以下命令重启从节点:
sudo service mesos-slave start
动态预留
静态资源预留的主要缺点是,在停机期间,预留的资源不能被其他角色使用,也不能取消预留并作为更广泛资源池的一部分进行使用。这导致了资源利用率低下。为了解决这个问题,在 0.23.0 版本中加入了动态资源预留支持,这使得用户能够根据工作负载要求更动态地预留和取消预留资源。
对于资源提供,框架可以通过acceptOffers
API 发送以下两条消息作为响应:
-
Offer::Operation::Reserve
-
Offer::Operation::Unreserve
这些将在以下章节中详细描述。请注意,框架的主体用于授权,具体内容将在第六章中详细讨论,Mesos 框架。
Offer::Operation::Reserve
每个框架可以作为报价周期的一部分预留资源。例如,假设一个资源报价中包含八个核心和 12 GB 的 RAM,且这些资源未被预留,框架收到了这个报价。请查看以下代码:
{
"id": <offer_id>,
"framework_id": <framework_id>,
"slave_id": <slave_id>,
"hostname": <hostname>,
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": { "value": 8 },
"role": "*",
},
{
"name": "mem",
"type": "SCALAR",
"scalar": { "value": 12288 },
"role": "*",
}
]
}
我们可以通过在以下消息中指定需要预留的每种资源类型的数量以及框架的角色和主体,来为框架预留四个核心和 6 GB 的 RAM:
{
"type": Offer::Operation::RESERVE,
"reserve": {
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": { "value": 4 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
{
"name": "mem",
"type": "SCALAR",
"scalar": { "value": 6144 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
}
]
}
}
下一次资源报价将包括前述的预留资源,如下所示:
{
"id": <offer_id>,
"framework_id": <framework_id>,
"slave_id": <slave_id>,
"hostname": <hostname>,
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": { "value": 4 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
{
"name": "mem",
"type": "SCALAR",
"scalar": { "value": 6144 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
]
}
Offer::Operation::Unreserve
每个框架还可以作为报价周期的一部分取消预留资源。在之前的示例中,我们为框架/角色预留了四个核心和 6 GB 的 RAM,直到明确取消预留之前,这些资源会一直提供。这里解释了取消预留的方法。
首先,我们将收到预留资源的报价,如下所示:
{
"id": <offer_id>,
"framework_id": <framework_id>,
"slave_id": <slave_id>,
"hostname": <hostname>,
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": { "value": 4 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
{
"name": "mem",
"type": "SCALAR",
"scalar": { "value": 6144 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
]
}
我们现在可以通过在以下消息中指定需要取消预留的每种资源类型的数量以及框架的角色和主体,来为框架取消预留四个核心和 6 GB 的 RAM:
{
"type": Offer::Operation::UNRESERVE,
"unreserve": {
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": { "value": 4 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
},
{
"name": "mem",
"type": "SCALAR",
"scalar": { "value": 6144 },
"role": <framework_role>,
"reservation": {
"principal": <framework_principal>
}
}
]
}
}
在随后的资源报价中,这些取消预留的资源将成为更广泛的未预留资源池的一部分,并开始被提供给其他框架。
/reserve
和 /unreserve
HTTP 端点也在 v0.25.0 中引入,可以用于从主节点进行动态资源预留管理。
/reserve
假设我们有兴趣为位于从节点 <slave_id>
上的某个角色预留四个核心和 6 GB 的 RAM。可以向 /reserve
HTTP 端点发送 HTTP POST
请求,如下所示:
$ curl -i \
-u <operator_principal>:<password> \
-d slaveId=<slave_id> \
-d resources='[ \
{ \
"name": "cpus", \
"type": "SCALAR", \
"scalar": { "value": 4 }, \
"role": <framework_role>, \
"reservation": { \
"principal": <operator_principal> \
} \
}, \
{ \
"name": "mem", \
"type": "SCALAR", \
"scalar": { "value": 6144 }, \
"role": <framework_role>,\
"reservation": { \
"principal": <operator_principal> \
} \
} \
]' \
-X POST http://<ip>:<port>/master/reserve
响应可以是以下之一:
-
200 OK
:成功 -
400 BadRequest
:无效的参数(例如,缺少参数) -
401 Unauthorized
:未经授权的请求 -
409 Conflict
:资源不足,无法满足预留操作
/unreserve
现在,如果我们有兴趣取消之前预留的资源,可以向 /unreserve
HTTP 端点发送 HTTP POST
请求,如下所示:
$ curl -i \
-u <operator_principal>:<password> \
-d slaveId=<slave_id> \
-d resources='[ \
{ \
"name": "cpus", \
"type": "SCALAR", \
"scalar": { "value": 4 }, \
"role": <framework_role>, \
"reservation": { \
"principal": <operator_principal> \
} \
}, \
{ \
"name": "mem", \
"type": "SCALAR", \
"scalar": { "value": 6144 }, \
"role": <framework_role>\
"reservation": { \
"principal": <operator_principal> \
} \
} \
]' \
-X POST http://<ip>:<port>/master/unreserve
响应可以是以下之一:
-
200 OK
:成功 -
400 BadRequest
:无效的参数(例如,缺少参数) -
401 Unauthorized
:未经授权的请求 -
409 Conflict
:资源不足,无法满足取消预留操作
资源过度分配
通常,用户会为框架提供足够的缓冲资源,以应对突发的工作负载激增。这导致整个集群的资源总体利用率较低,因为相当一部分资源处于空闲状态。将这种情况加到多个框架中,就会导致显著的资源浪费。v0.23.0 引入的超额订阅概念旨在通过在这些空闲资源上执行低优先级任务(例如后台进程或临时的非关键分析)来解决这个问题。
为了启用此功能,引入了两个额外的组件:
-
资源估算器:用于确定可供最佳努力进程使用的空闲资源数量。
-
服务质量(QoS)控制器:用于在观察到工作负载激增或原始任务性能下降时终止这些最佳努力任务。
虽然提供了基本的默认估算器和控制器,但 Mesos 允许用户创建自定义的估算器和控制器。
此外,现有的资源分配器、资源监控器和 Mesos 工作节点也通过新标志和选项进行了扩展。下图说明了超额订阅概念的工作原理(来源:mesos.apache.org/documentation/latest/oversubscription/
):
可撤销资源报价
以下步骤按顺序执行:
-
主要步骤包括收集使用统计数据并估算超额订阅的资源数量,这些资源可供低优先级任务使用。资源监控器通过传递
ResourceStatistics
消息将这些统计数据发送给被称为资源估算器的模块。 -
估算器通过利用计算缓冲区量的算法来识别资源的超额订阅情况。Mesos 提供了基于用户指定逻辑开发自定义资源估算器的能力。
-
每个工作节点都会轮询资源估算器以获取最新的估算数据。
-
然后,工作节点会定期(每当估算值发生变化时)将此信息传输给主节点的分配器模块。
-
分配器将这些超额订阅的资源标记为“可撤销”资源,并单独监控这些资源。
-
在
FrameworkInfo
方法中注册了REVOCABLE_RESOURCES
的框架会接收到这些可撤销资源的报价,并可以使用launchTasks()
API 在这些资源上调度任务。请注意,这些资源不能动态保留。
注册可撤销资源能力
运行以下代码:
FrameworkInfo framework;
framework.set_name("Revocable framework");
framework.add_capabilities()->set_type(
FrameworkInfo::Capability::REVOCABLE_RESOURCES);
一个包含可撤销和标准资源的示例报价
请看下面的代码:
{
"id": <offer_id>,
"framework_id": <framework_id>,
"slave_id": <slave_id>,
"hostname": <hostname>,
"resources": [
{
"name": "cpus",
"type": "SCALAR",
"scalar": {
"value": 4
},
"role": "*"
}, {
"name": "mem",
"type": "SCALAR",
"scalar": {
"value": 6144
},
"role": "*"
},
{
"name": "cpus",
"type": "SCALAR",
"scalar": {
"value": 1
},
"role": "*",
"revocable": {}
}
]
}
-
当收到
runTask
请求时,任务将在工作节点上启动。即使是包含单个可撤销资源的容器也可以被 QoS 控制器终止,因为它被视为可撤销容器。 -
原始任务也会持续监控,如果观察到性能下降或工作负载激增,任何可撤销的资源将被归还给它。这被称为干扰检测。
目前,Mesos 资源估算器相当基础,提供两种默认估算器,分别是固定和noop资源估算器。在第一个估算器中,一组固定资源可以标记为过度订阅,而后者在 slave 节点被查询时提供空估算值,实际上意味着没有可用资源用于过度订阅。
正在积极研究引入复杂和动态的过度订阅资源估算模型(例如 Mesosphere 和 Intel 的一个模块,称为Project Serenity),旨在最大化资源利用率,同时确保不会影响服务质量。
资源估算器
运行以下代码:
class ResourceEstimator
{
public:
virtual Try initialize(const lambda::function<process::Future()>& usage) = 0;
virtual process::Future oversubscribable() = 0;
};
QoS 控制器
执行以下代码:
class QoSController
{
public:
virtual Try initialize(const lambda::function<process::Future()>& usage) = 0;
virtual process::Future<std::list> corrections() = 0;
};
配置过度订阅
现在,slave 节点有四个新的与过度订阅相关的标志可用,如下表所示:
标志 | 说明 |
---|---|
--oversubscribed_resources_interval=VALUE |
slave 节点定期将过度订阅的资源估算值发送给 master。可以通过此标志指定这些更新的间隔(默认:15 秒) |
--qos_controller=VALUE |
这是需要使用的 QoS 控制器名称 |
--qos_correction_interval_min=VALUE |
slave 节点会轮询并执行 QoS 修正,由 slave 从控制器根据原始任务的性能下降/恶化水平执行。这一标志控制这些修正的间隔(默认:0 纳秒) |
--resource_estimator=VALUE |
这是用于确定过度订阅资源的资源估算器名称 |
可扩展性
不同的组织有不同的需求。此外,在同一个组织内部,不同的用户以不同的方式运行集群,具有不同的规模和延迟要求。用户需要处理特定应用程序的行为,确保符合行业特定的安全合规性等等。这一切意味着,如果 Mesos 要实现作为整个数据中心操作系统服务于所有组织的目标,它需要具备极强的可定制性和可扩展性。它需要一个功能,能够保持 Mesos 核心小巧而轻量,同时又足够强大,以允许根据需要进行尽可能多的定制/扩展。
许多软件系统,如浏览器,支持库以:
-
扩展功能支持
-
抽象复杂性
-
使开发配置驱动
Mesos 模块
Mesos 模块在 v0.21.0 中引入,基于这一概念,允许用户通过库扩展 Mesos 的功能,这些库可以创建并共享,而无需不断重新编译。在 Mesos 的上下文中,模块是一个完整的组件,任何用户都可以添加或替换。所有外部依赖项都被视为单独的库,可以按需加载。现在,所有用户都可以在 Mesos 之上开发他们的实验功能,而无需了解所有详细的内部工作或影响其他用户。可以实现自定义分配逻辑、自定义超额资源估算算法等各种用例特定的定制功能。不同的子系统,如负载均衡器、隔离机制和服务发现机制,也可以以模块化的方式配置。
模块调用
--modules
CLI 标志可用于主从节点,提供需要使用的模块列表。
模块列表可以通过带有 JSON 格式字符串的文件来提供,使用--modules=filepath
。filepath
可以是/path/to/file
或file:///path/to/file
类型。
若要内联提供模块列表,请使用--modules="{...}"
。
有两个参数,name
和file
;每个库必须提供其中之一。file
参数可以是绝对路径(例如,/User/mesos/lib/example.so
)、文件名(例如,example.so
)或相对路径(例如,lib/example.so
)。name
参数是库的名称(例如,example
)。如果提供了该参数,Mesos 会自动将其扩展为适合当前操作系统的库名称(例如,Linux 上将example
扩展为example.so
,在 OS X 上扩展为example.dylib
)。如果同时提供了两个参数,则忽略name
参数。
下面给出了一个 JSON 字符串示例:
-
通过以下方式加载库
example.so
,包括两个模块org_apache_mesos_X
和org_apache_mesos_Y
:{ "libraries": [ { "file": "/path/to/example.so", "modules": [ { "name": "org_apache_mesos_X", }, { "name": "org_apache_mesos_Y" } ] } ] }
从库示例中,加载
org_apache_mesos_X
模块并传递参数 A,值为 B(加载另一个模块org_apache_mesos_Y
,没有任何参数)通过以下代码:{ "libraries": [ { "name": "example", "modules": [ { "name": "org_apache_mesos_X" "parameters": [ { "key": "A", "value": "B", } ] }, { "name": "org_apache_mesos_Y" } ] } ] }
-
要内联指定模块,请使用以下代码:
--modules='{"libraries":[{"file":"/path/to/example.so", "modules":[{"name":"org_apache_mesos_X"}]}]}'
这里提供了一个Hello World
模块实现示例:mesos.apache.org/documentation/latest/modules/
。
构建模块
以下命令假设 Mesos 已安装在标准位置——即,Mesos 动态库和头文件是可用的:
g++ -lmesos -fpic -o test_module.o test_module.cpp
$ gcc -shared -o libtest_module.so test_module.o
钩子
Mesos 提供了另一种扩展其功能的方式,这种方式无需从零开始创建整个组件,称为钩子。钩子不会干扰请求的处理;相反,它们允许用户在 Mesos 生命周期中添加功能。一些钩子可以在对象移动过程中更改其内容,这些被称为装饰器。
当前支持的模块
以下是目前在 Mesos 上支持的模块:
-
分配器:在后续章节中将对此进行更详细的描述。
-
认证器:该模块允许用户创建和集成新的自定义认证方法。
-
隔离器:通过此接口,用户可以开发定制的隔离器,解决各种用例问题,例如网络隔离(例如,Project Calico)。
-
QoS 控制器:通过此模块,可以实现用于撤销在超额订阅资源上启动的最佳努力任务的复杂逻辑。
-
资源估算器:该模块允许第三方开发者尝试自己的可撤销资源估算算法,以最大化集群利用率。像 Project Serenity 这样的努力正在利用此模块,尝试提出一个生产级的动态资源估算逻辑。
分配器模块
Mesos 资源分配模块包含 Mesos 主节点用来确定需要向每个框架提供哪种类型和数量的资源报价的策略。组织可以定制此模块以实现自己的分配策略——例如,公平共享、优先级等——从而实现精细化的资源共享。可以开发自定义分配模块来应对特定的需求。例如,超额订阅模块允许提供可撤销资源,这是默认的 DRF 分配器不支持的功能。
以下步骤是将自定义分配模块加载到主节点所需的:
-
在
--modules
配置中列出它 -
使用
--allocator
标志选择它
例如,要运行一个名为 ExternalAllocatorModule
的自定义分配器,可以运行以下命令:
./bin/mesos-master.sh --work_dir=m/work --modules="file://<modules-including-allocator>.json" --allocator=ExternalAllocatorModule
现在,我们将看看如何实现一个自定义分配器,并将其打包为模块,以便像之前所示那样加载到主节点中。
实现自定义分配器模块
分配器模块是用 C++ 实现的,需要实现 mesos/master/allocator.hpp
中定义的接口(方法列在下面的表格中)。它们也可以通过 C++ 代理开发,代理将调用重定向到由其他语言实现的逻辑:
方法 | 描述 |
---|---|
initialize(flags, offerCallback, roles) |
分配器初始化 |
addFramework(frameworkId, frameworkInfo, usedResources)``removeFramework(frameworkId, frameworkInfo, usedResources) |
框架添加/移除 |
activateFramework(frameworkId)``deactivateFramework(frameworkId) |
框架启用/停用 |
addSlave(slaveId, slaveInfo, totalResources, usedResources)``removeSlave(slaveId, slaveInfo, totalResources, usedResources) |
从属机添加/移除 |
activateSlave(slaveId)``deactivateSlave(slaveId) |
从属机启用/停用 |
requestResources(frameworkId, requests) |
资源请求回调 |
updateAllocation(frameworkId, slaveId, operations) |
资源分配更新 |
recoverResources(frameworkId, slaveId, resources, filters) |
资源回收回调 |
reviveOffers(frameworkId) |
提供复活回调 |
updateWhitelist(whitelist) |
更新从节点白名单 |
默认的分层 DRF 分配器采用非阻塞的基于演员的实现。通过扩展src/master/allocator/mesos/allocator.hpp
中定义的MesosAllocatorProcess
类,可以在自定义分配器中利用此实现。通过使用排序器抽象,默认分配器可以进行扩展,从而避免了从头构建一个新的分配器。排序器 API 定义在src/master/allocator/sorter/sorter.hpp
中,以下表列出了其中的一些方法:
方法 | 描述 |
---|---|
void add(client, weight=1)``void remove(client) |
该方法用于将客户端添加/移除分配过程 |
void deactivate(client)``void activate(client) |
这用于激活/停用客户端 |
void add(slaveId, resources)``void remove(slaveId, resources)``void update(slaveId, resources) |
该方法用于添加/移除/更新分配的资源数量 |
List<string> sort() |
该方法返回一个按指定策略排序的客户端列表,指明如何分配资源 |
void allocated(client, slaveId, resources)``void update(client, slaveId, oldResources, newResources)``void unallocated(client, slaveId, resources) |
该方法决定将资源分配/更新/取消分配给指定客户端 |
Map<SlaveId, Resources> allocation(client) |
该方法返回分配给指定客户端的资源 |
bool contains(client) |
如果排序器包含指定的客户端,则返回 true,否则返回 false |
int count() |
该方法返回客户端数量 |
一旦开发完成,自定义分配器需要设置为资源分配时使用的分配器,而不是默认的分配器。这涉及将自定义分配器封装在一个分配器模块中,并将其加载到主节点中。
将自定义分配器(如在external_allocator.hpp
中实现)封装为一个名为ExternalAllocatorModule
的模块的过程在此处详细描述:mesos.apache.org/documentation/latest/allocation-module/
。
高可用性与容错性
高可用性,简单来说,就是通过确保没有单点故障来实现接近 100%的系统正常运行时间。这通常是通过引入冗余机制来实现的,例如备用进程可以在失败后立即接管等。
精通高可用性
在 Mesos 中,这是通过使用 Apache ZooKeeper(一种集中式协调服务)来实现的。设置多个主节点(一个活跃的领导者和其他备份节点),ZooKeeper 负责协调领导者选举,并通过其他 Mesos 组件,如从节点和框架,来处理主节点的检测。
为了维持高可用性设置,至少需要三个主节点以保证法定人数。然而,对于生产系统,建议至少使用五个主节点。领导者选举过程的详细描述请参见zookeeper.apache.org/doc/trunk/recipes.html#sc_leaderElection
。
失败的主节点状态可以通过利用存储在从节点和框架调度器中的信息,在下一个被选举的主节点上重建。在新主节点选举后,其他组件会通过 ZooKeeper 被通知此变动,使它们能够注册到新主节点并向其传递状态更新信息。根据这些数据,新的主节点能够重建失败的主节点状态。
框架调度器容错
这是通过将每个框架的多个调度器注册到当前主节点来实现的。如果调度器发生故障,主节点会要求次级调度器接管。然而,每个框架的多个调度器之间的状态共享实现需要由各自的框架来处理。
从节点容错
Mesos 提供了一种从节点恢复机制用于容错,这将在后续章节中详细讨论。主节点会监控所有从节点的状态。如果某个从节点没有响应主节点发送的心跳,即使经过多次通信尝试,主节点也会移除该从节点并尝试终止它。
执行器/任务
在任务或执行器失败的情况下,主节点会通知启动该任务的相应框架调度器。根据调度器逻辑中指定的策略,调度器将处理失败任务的执行,通常通过在满足资源要求的新的从节点上重新启动该任务。
从节点恢复
从 v0.14.0 版本开始引入了“从节点恢复”容错机制,通过该机制,即使从节点进程宕机,任务也能继续运行,并且在从节点重新启动后,可以重新与正在运行的任务建立连接。无论是计划中的升级还是突发的崩溃,都可能导致从节点进程宕机并需要重启。
为了实现这一点,从节点将当前正在执行的任务信息保存到本地磁盘(也称为检查点)。它们写入的数据包括任务、执行器和状态信息。要启用此功能,从节点及其所在的框架需要进行适当配置。如果启用检查点功能,那么从节点在故障事件发生后重新启动并能够从最近的检查点恢复数据,重新与执行器建立连接,并继续执行任务。当从节点宕机时,主节点和执行器会等待它重新启动并重新连接。由于检查点涉及多次写入本地磁盘,需要在高可用性和这些频繁写入带来的延迟开销之间做出权衡。
此外,执行器驱动程序也进行了改进,使其在面临故障事件时更加健壮和容错。例如,在从节点宕机期间,驱动程序会缓存执行器传递给它的更新,并在从节点重新与执行器建立连接后重新发送这些更新。这确保了执行器能够继续运行任务并传输消息,同时不必担心从节点进程。
检查点机制通过确保任务更新消息能够传递给框架,即使发生故障,也能提高系统的可靠性。例如,如果一个从节点和主节点同时发生故障,框架将无法收到所需的TASK_LOST
状态更新消息。通过检查点机制,从节点现在可以从最后的检查点状态恢复任务信息,并在重新连接后向框架发送所需的消息。
从节点的可恢复性非常重要,原因包括确保有状态进程或长期运行的任务能够从最后记录的状态重新启动,进行无缝的集群升级,以及减少维护和管理的开销。
启用从节点检查点功能
可以通过以下方式启用从节点检查点功能。
注意
注意,自 v0.22.0 版本起,所有从节点的检查点功能默认启用。
相关的标志如下:
-
checkpoint
:此选项允许用户指定从节点是否需要执行检查点以支持恢复[默认值为true
]。- 重启后的从节点可以恢复更新并重新建立与执行器的连接(
--recover
=reconnect
)或终止连接(--recover
=cleanup
)。
注意
注意,从 v0.22.0 版本开始,该标志将被移除,并默认启用所有从节点的检查点功能。
- 重启后的从节点可以恢复更新并重新建立与执行器的连接(
-
strict
:此选项决定是否在严格模式下执行恢复[默认值为true
]。-
如果
strict
=true
,则所有与恢复相关的错误都将被视为致命错误。 -
如果
strict
=false
,则在恢复过程中出现任何错误(如数据损坏等)时,状态恢复会尽力进行。
-
-
recover
:这决定了从节点是否应重新连接旧的执行器或终止它们[默认值为reconnect
]。-
如果
recover
=reconnect
,从节点可以重新建立与活动执行器的连接。 -
如果
recover
=cleanup
,从节点将终止旧的执行器。此选项通常用于进行不兼容的升级。
注意
请注意,如果没有检查点信息,则不会执行恢复操作。重启时,从节点会作为新节点注册到主节点。
-
-
recovery_timeout
:这是从节点必须在其中恢复的时间[默认为15 分钟
]。- 如果从节点在指定的
recovery_timeout
时间内未能恢复,主节点将关闭该从节点,从而导致所有执行器也被终止。
注意
请注意,只有在启用
--checkpoint
时,这种机制才适用和可用。 - 如果从节点在指定的
启用框架检查点
框架可以通过在注册前将FrameworkInfo
中包含的可选检查点字段的值设置为 true(FrameworkInfo.checkpoint
=True
)来启用检查点。如果启用了此选项,则只有来自检查点从节点的 offers 才会被该框架接收。
对账
Mesos 实现了一个基于演员模型的消息传递编程模型,以便在不同的 Mesos 组件之间实现非阻塞通信,并利用协议缓冲区来完成这一点。例如,调度器需要告诉执行器使用一定数量的资源,执行器需要向调度器提供有关正在执行任务的状态更新,等等。协议缓冲区提供了灵活的消息传递机制,使得通过定义自定义格式和协议,开发人员可以在不同语言间进行通信。
为此目的,采用了最多一次的消息传递模型,但某些消息(例如状态更新)会采用至少一次的消息传递模型,通过使用确认机制来保证。在发生故障时,主节点和从节点之间的消息可能会丢失,从而导致状态不一致。
例如,在多个场景中,当框架发出启动任务的请求时,任务可能会丢失。主节点在框架发送请求后但在接收请求之前可能会失败,或者它可能在接收到消息后但在将其发送给从节点之前失败。框架可能在表达启动任务的愿望后,但在发送所需消息之前失败,依此类推。为了应对这些情况带来的不一致性,Mesos 需要与框架之间进行对账机制。Mesos 必须确保框架意识到可能发生的失败事件,并了解这些事件何时得到解决。此外,恢复发生后,Mesos 还必须确保所有组件的状态彼此同步并保持一致性。
任务对账
框架需要在任务失败后显式地进行对账,因为调度器不会维护与任务相关的信息。Mesos 中提供了两种对账方式:
-
第一个是“显式”对账,其中调度器发送它希望了解状态的任务的详细信息,主节点返回这些任务的状态。
-
第二个是“隐式”对账,其中调度器不指定任务,而是向主节点发送一个空列表,主节点返回所有已知任务的状态。
实现此功能的方法如下:
message Reconcile {
repeated TaskStatus statuses = 1; // Should be non-terminal only.
}
主节点仅检查必需的 TaskID
字段和可选的 SlaveID
字段。
资源提供对账
资源提供会自动进行对账。它们不会超出主节点的生命周期,如果发生故障,则不再有效。每次框架重新注册时,资源提供会被重新发放。
有关对账的更多信息,请参见 mesos.apache.org/documentation/latest/reconciliation/
。
持久化卷
从 v0.23.0 版本开始,Mesos 引入了对一种名为 持久化卷 的新特性的实验性支持。Mesos 面临的一个关键挑战是为有状态服务(如数据库)提供一个可靠的机制,使其能够在 Mesos 内存储数据,而不是依赖外部文件系统。
例如,如果正在运行一个数据库任务,那么必须将该任务调度到包含其所需数据的从节点上。之前,没有办法保证任务只会从包含所需数据的从节点上获得资源提供。解决此问题的常用方法是使用本地文件系统或外部分布式文件系统。这些方法涉及网络延迟或资源利用率低的问题(因为包含特定数据的节点需要被静态划分并只对需要该数据的框架可用)。
解决此问题的两个新特性是:
-
动态预留:除了本章“预留”部分讨论的特性外,动态预留的另一个优点是框架可以预留一个持久化存储,确保当另一个任务需要启动时,它将始终被重新提供给该框架。
-
持久化卷:Mesos 现在提供了从磁盘资源创建持久化卷的能力。当启动新任务时,可以创建一个卷,它位于任务的沙盒之外。即使任务完成后,这个卷也会保持持久化,并将再次提供给相同的框架,以便其可以在相同的磁盘资源上启动另一个任务。
请注意,持久化卷只能从静态或动态预留的磁盘资源中生成。如果持久化卷是从动态预留的磁盘资源中创建的,则在不销毁卷的情况下无法取消预留。这提供了一种安全机制,防止敏感数据意外暴露给其他框架。垃圾回收机制用于删除残留数据,正在开发中。
持久化卷创建接口描述如下:
-
框架可以通过
acceptOffers
API 发送两条消息作为提供响应:Offer::Operation::Create
和Offer::Operation::Destroy
-
主控可以通过
/create
和/destroy HTTP
端点管理持久化卷,这些端点目前处于 Alpha 阶段。
请注意,框架的主体需要进行授权,具体内容将在第六章中进一步讨论,Mesos 框架。
Offer::Operation::Create
卷可以作为常规提供周期的一部分由框架创建。例如,假设接收到如下的 6GB 动态预留磁盘资源提供:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 6144 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
}
}
]
}
现在可以通过发送以下消息从这些磁盘资源中创建持久化卷。在消息中,需要指定以下内容:
-
一个唯一的角色特定持久化卷 ID
-
容器内需要存储卷的相对路径
-
卷权限
现在可以通过发送以下消息从这些磁盘资源中创建持久化卷:
{
"type" : Offer::Operation::CREATE,
"create": {
"volumes" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 6144 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
},
"disk": {
"persistence": {
"id" : <persistent_volume_id>
},
"volume" : {
"container_path" : <container_path>,
"mode" : <mode>
}
}
}
]
}
}
下一个资源提供将包含之前创建的持久化卷:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 6144 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
},
"disk": {
"persistence": {
"id" : <persistent_volume_id>
},
"volume" : {
"container_path" : <container_path>,
"mode" : <mode>
}
}
}
]
}
Offer::Operation::Destroy
当前,持久化卷需要显式删除。这可以作为常规提供周期的一部分完成。首先,系统将接收到包含持久化卷的资源提供。以之前的示例为例,资源提供将是:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 2048 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
},
"disk": {
"persistence": {
"id" : <persistent_volume_id>
},
"volume" : {
"container_path" : <container_path>,
"mode" : <mode>
}
}
}
]
}
接下来,持久化卷通过Offer::Operation::Destroy
消息被销毁,具体如下:
{
"type" : Offer::Operation::DESTROY,
"destroy" : {
"volumes" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 6144 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
},
"disk": {
"persistence": {
"id" : <persistent_volume_id>
},
"volume" : {
"container_path" : <container_path>,
"mode" : <mode>
}
}
}
]
}
}
请注意,删除持久化卷不会导致磁盘资源被取消预留。因此,以下的资源提供仍会包含它们:
{
"id" : <offer_id>,
"framework_id" : <framework_id>,
"slave_id" : <slave_id>,
"hostname" : <hostname>,
"resources" : [
{
"name" : "disk",
"type" : "SCALAR",
"scalar" : { "value" : 6144 },
"role" : <framework_role>,
"reservation" : {
"principal" : <framework_principal>
}
}
]
}
概述
在本章中,我们深入探讨了 Mesos 的一些最重要特性,这些特性使它高效、可扩展并具有容错能力。我们详细解释了 Mesos 的资源分配选项和生产级容错能力等高级话题。通过这一强大的背景,读者将在接下来的章节中了解 Mesos 的安装、管理和框架设置等更实际的方面。
在下一章中,我们将讨论如何在公共云服务和私有数据中心中设置一个多节点的 Mesos 集群,并讨论常见问题及其调试和故障排除方法。
第三章:入门 Mesos
本章介绍如何在公共云(AWS、GCE 和 Azure)以及私有数据中心(本地)上手动设置并运行 Mesos 集群,还讨论了各种调试方法,并详细探讨了如何排查 Mesos 设置中的问题。
在本章中,我们将探讨以下主题:
-
如何在公共云平台(如 AWS、GCE 和 Azure)上启动实例(虚拟机),我们还将介绍如何在其上设置 Mesos。
-
如何在私有数据中心安装 Mesos 集群
-
如何排查和调试在设置过程中常见的问题
虚拟机(VM)实例
实例是 虚拟机(VM),托管在云服务提供商的基础设施上。实例可以运行云服务提供商提供的 Linux 和 Windows Server 镜像,或者任何定制版的镜像。你还可以构建并运行其他操作系统的镜像。大多数云服务提供商,如 Google Compute Engine (GCE)、Amazon Web Services (AWS)、Microsoft Azure 等,也允许你指定实例的机器属性,例如所需的内存和 CPU 数量,这些都取决于你使用的机器类型。在接下来的部分,我们将探讨如何在公共云平台上设置 Mesos。
在 Amazon Web Services(AWS)上设置多节点 Mesos 集群
请参考 docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html
。
正如其名,Amazon Web Services(AWS)是亚马逊提供的云计算平台。它包括了从 11 个物理区域提供的广泛网络服务,可以远程访问。这些服务按使用量定价,可以在一个账户下使用,包括 EC2(计算或处理)、S3(存储)、CloudWatch、RDS、DynamoDB、EBS 等。
AWS 为你提供了一个免费套餐,让你可以开始使用 Amazon EC2 服务。注册后,你将能够启动一台 微型机器(一台内存为 700 MB 的小型机器),并几乎可以免费运行一年。你还需要支付其他服务的费用,例如启动更大机器,或使用存储服务,如 Amazon S3。你可以在官网查找最新的定价信息。
账户注册与创建:前往 aws.amazon.com
,点击 Sign Up,并按照指示完成 AWS 账户的创建过程。过程中有一个步骤需要进行手机验证。注册完成后,你将收到一封确认邮件,其中包含了账户编号。请记下此编号,因为后续步骤中会用到。
密钥对:默认情况下,AWS 使用公钥认证登录到您的 Linux 实例,因为这种方式更加安全。您可以在启动实例时从下拉列表中选择密钥对,或者在启动实例时创建新的密钥对。如果您还没有创建密钥对,可以通过亚马逊 EC2 控制台创建一个。请注意,每个区域需要单独创建密钥对。
安全组:安全组充当关联实例的防火墙。它们可以控制实例的进出流量。您必须启用规则,以允许从您的IP地址到SSH端口的连接,以便您能够登录到机器。您还可以根据需要添加或删除任意数量的规则;例如,HTTP和HTTPS可以从任何地方访问,但 SSH 连接应仅从您的 IP 地址访问。
实例类型
在 AWS 上,虚拟服务器或机器也被称为实例。亚马逊 EC2 提供了多种优化的实例类型,针对不同的需求。实例类型提供了各种资源(存储、内存、CPU 和网络容量)选项,为用户提供灵活性,可以根据特定的使用案例选择合适的资源组合。每种实例类型包括一个或多个实例大小,允许根据目标工作负载的需求扩展资源。
实例类型根据应用程序最需要利用的资源类型进行分组;例如,通用型(用于需要均衡资源的应用程序)、计算优化型(用于 CPU 密集型工作负载)、GPU 实例(用于需要 GPU 功能的应用程序)、内存优化型(用于需要大量内存的任务)、存储优化型(用于处理大量数据的应用程序)和微型实例(用于快速试验或轻量级应用程序)。实例是从预配置的模板创建的,这些模板包括已经安装操作系统和其他常见软件的亚马逊机器镜像(AMI)。AMI 可以是 AWS 提供的,也可以通过 AWS 市场获取。用户还可以创建并与更广泛的社区共享自己的 AMI。
启动实例
在 AWS 上启动实例有多种方式。在这里,我们将使用通过亚马逊 EC2 控制台使用选定的亚马逊机器镜像启动实例的常见方式。步骤如下:
-
导航到亚马逊 EC2 控制台。
-
从控制台点击启动实例按钮,如下图所示:
-
从选择一个亚马逊机器镜像(AMI)页面中选择64 位 Ubuntu 14.04 LTS AMI,如下所示:
-
在选择实例类型页面上,选择所需的实例,基于资源需求(CPU、内存等)。对于我们的 Mesos 集群,我们将使用每个节点具有 4 个核心和 16 GB 内存的 m4.xlarge 实例:
-
在配置实例详细信息页面上,更改以下设置;将实例数选择为
4
(因为我们正在启动一个四节点集群): -
单击 配置安全组,添加 Mesos Web UI 端口 5050 作为 自定义 TCP 规则,确保您将 My IP 设置为 来源 地址。此选项将限制除您 IP 地址以外的任何其他连接尝试连接到您的网络:
-
单击 Review Instance Launch。我们现在可以跳过添加存储和标记实例的步骤:
-
单击 Launch。
-
现在,您将被提示选择登录机器的私钥。您可以选择 创建新的密钥对 或使用现有的密钥对,然后将启动机器:
-
单击 Download Key Pair 按钮,将下载名为
mesos-cluster.pem
的密钥对文件到您的计算机。您可以使用此文件来 SSH 连接到您在 AWS 中启动的机器。
安装 Mesos
现在登录到所有机器:
ssh -i mesos-cluster.pem ec2-54-221-197-122.compute-1.amazonaws.com (master)
ssh -i mesos-cluster.pem ec2-54-221-196-123.compute-1.amazonaws.com (slave1)
ssh -i mesos-cluster.pem ec2-54-221-198-125.compute-1.amazonaws.com (slave2)
ssh -i mesos-cluster.pem ec2-54-221-198-130.compute-1.amazonaws.com (slave3)
让我们称前面列表中的第一台机器为我们的主节点,其余的为 slave1
、slave2
、slave3
在本章的其余部分。
现在,我们可以在所有四台机器上安装依赖库和软件包,输入以下命令:
# Following command is used to update the packages.
$ sudo apt-get update
# We will require JDK for deploying java projects over mesos, use the following command to install java. Here we are using Java version 7.
$ sudo apt-get install -y openjdk-7-jdk
# This step is necessary only if you are building from the git repository. It will install the autotools.
$ sudo apt-get install -y autoconf libtool
# Now we can install the dependencies for the mesos project.
$ sudo apt-get -y install build-essential python-dev python-boto libcurl4-nss-dev libsasl2-dev maven libapr1-dev libsvn-dev
现在,我们的环境已准备就绪,可以开始构建 Mesos 二进制文件。
您可以从主节点构建 Mesos 二进制文件,然后将构建内容复制到从机器,而不是从所有机器构建。
下载 Mesos
有两种不同的方式可以获取 Mesos:
-
从 Apache 的最新稳定版本下载(推荐):
mesos.apache.org/downloads/
。在撰写本书时,Mesos 的最新版本是 0.25.0:ubuntu@ip-10-155-18-106:~$ wget http://www.apache.org/dist/mesos/0.25.0/mesos-0.25.0.tar.gz ubuntu@ip-10-155-18-106:~$ tar -zxf mesos-0.25.0.tar.gz ubuntu@ip-10-155-18-106:~$ mv mesos-0.25.0 mesos
-
克隆 Mesos 的 git 仓库(仅限高级用户):
ubuntu@ip-10-155-18-106:~$ git clone https://git-wip-us.apache.o rg/repos/asf/mesos.git
构建 Mesos
我们可以按照以下步骤构建 Mesos:
# Change working directory.
ubuntu@ip-10-155-18-106:~$ cd mesos
# This step is only required if you are building from the gitrepository, or else you can skip this step.
ubuntu@ip-10-155-18-106:~$ ./bootstrap
# Create a build directory. This will contain the compiled Mesosbinaries. It is a good practice to create the build directory.
ubuntu@ip-10-155-18-106:~$ mkdir build
ubuntu@ip-10-155-18-106:~$ cd build
# Now we can trigger the "configure build" command and "make" commandfollowed by it. This step is time consuming and can take some time to get executed
ubuntu@ip-10-155-18-106:~$ ../configure
ubuntu@ip-10-155-18-106:~$ make
In order to speed up the building and reduce verbosity of the logs, you can append-j <number of cores> V=0 to make:
# Once the make command is executed, you can test the make by issuing the following command.
ubuntu@ip-10-155-18-106:~$ make check
# The following step is optional, you can use it if you are installing it system wide.
ubuntu@ip-10-155-18-106:~$ make install
现在,我们可以将 Mesos 主节点上的构建目录复制到 slave1
、slave2
和 slave3
机器上:
ubuntu@ip-10-155-18-106:~$ rsync -za mesos ip-10-155-18-107:
ubuntu@ip-10-155-18-106:~$ rsync -za mesos ip-10-155-18-109:
ubuntu@ip-10-155-18-106:~$ rsync -za mesos ip-10-155-18-110:
启动 Mesos master,如下所示:
ubuntu@ip-10-155-18-106:~/mesos/build$ ./bin/mesos-master.sh --work_dir=/var/lib/mesos
启动 Mesos slaves:
ubuntu@ip-10-155-18-107:~/mesos/build$ ./bin/mesos-slave.sh --master=mesos-master:5050
ubuntu@ip-10-155-18-109:~/mesos/build$ ./bin/mesos-slave.sh --master=mesos-master:5050
ubuntu@ip-10-155-18-110:~/mesos/build$ ./bin/mesos-slave.sh --master=mesos-master:5050
Mesos Web UI 在主节点上的端口 5050
上运行,这是可以检查安装完成状态的地方。
要执行此操作,请在您的 web 浏览器中输入以下 URL:
http://ec2-54-221-197-122.compute-1.amazonaws.com:5050
Mesos UI 将显示如下:
使用 mesos-ec2 脚本一次启动多台机器
Mesos 提供了脚本,用于在 EC2 上创建各种配置的 Mesos 集群。位于ec2
目录中的mesos-ec2
脚本可以用来启动、运行任务和销毁 Mesos 集群。请注意,即使不构建 Mesos,我们也可以使用该脚本,但需要安装 Python(>=2.6)。我们可以通过不同的名称管理多个集群。
我们需要一个 AWS 密钥对来使用 ec2 脚本,并且需要在前面的步骤中创建的访问密钥和私密密钥:
ubuntu@local:~ $ export AWS_ACCESS_KEY_ID=<your-access-key>
ubuntu@local:~ $ export AWS_SECRET_ACCESS_KEY=<your-secret-key>
现在,我们可以使用 Mesos 提供的 EC2 脚本,通过以下命令启动一个新的集群:
ubuntu@local:~/mesos/ec2 $ ./mesos-ec2 -k <your-key-pair> -i <your-identity-file> -s 10 launch mesos-cluster
这将启动一个名为mesos-cluster
的集群,包含十个从节点。一旦脚本执行完成,它还会打印出以<master-hostname>:8080
形式展示的 Mesos Web UI 链接。我们可以通过访问该网页界面来确认集群是否已启动。
该脚本提供了多个选项,以下是其中的一些。我们可以通过运行mesos-ec2 --help
列出所有可用的选项:
命令 | 用途 |
---|---|
--slave 或 –s |
这是集群中的从节点数量 |
--key-pair 或 -k |
这是用于认证的 SSH 密钥对 |
--identity-file 或 –i |
这是用于登录实例的 SSH 身份文件 |
--instance-type 或 –t |
这是一个从节点实例类型,必须是 64 位 |
--ebs-vol-size |
这是 EBS 卷的大小,用于存储持久的 HDFS 数据 |
--master-instance-type 或 –m |
这是主节点实例类型,必须是 64 位 |
--zone 或 -z |
这是启动实例的 Amazon 可用区 |
--resume |
此标志从上次运行的地方恢复安装 |
我们可以使用登录操作,通过提供集群名称登录已启动的集群,如下所示:
ubuntu@local:~/mesos/ec2 $ ./mesos-ec2 -k <your-key-pair> -i <your-identity-file> login mesos-cluster
该脚本还设置了一个 HDFS 实例,可以通过/root/ephemeral-hdfs/
目录中的命令使用。
最后,我们可以使用以下命令终止集群。在终止集群之前,请确保复制任何重要数据:
ubuntu@local:~/mesos/ec2 $ ./mesos-ec2 destroy ec2-test
该脚本还支持高级功能,如暂停和重新启动具有 EBS 支持的集群。Mesos 文档是获取任何澄清信息的极好来源。值得一提的是,Mesosphere(mesosphere.com
)还为你提供了一种便捷的方式,在 Amazon EC2、Google Cloud 和其他平台上创建弹性 Mesos 集群,并为 Mesos 提供商业支持。
在 Google Compute Engine(GCE)上设置多节点 Mesos 集群
Google Compute Engine(GCE)是 Google 的基础设施即服务(IaaS)产品,允许用户在虚拟服务器上运行计算工作负载,这些服务器属于与 Gmail、YouTube 和 Google 搜索引擎等服务相同的基础设施。
实例类型介绍
机器类型决定了虚拟化硬件资源的分配,包括实例所拥有的内存、虚拟 CPU 和持久磁盘的限制。一个虚拟 CPU 对应于主机 CPU 上的一个硬件超线程,该主机正在运行您的实例。
机器类型被分为不同的类别,由 Google Compute Engine 管理。每种机器类型都有自己的定价,并且会单独计费。有关定价的信息,请查看价格表。
可用的机器类型包括以下几种:
-
标准机器类型
-
高 CPU 机器类型
-
高内存机器类型
-
共享核心机器类型
启动机器
启动 Google Compute Engine 的最简便方法是通过 Google Cloud Platform 提供的浏览器工具——Google 开发者控制台,在这里可以创建虚拟机实例,访问地址为 console.developers.google.com
。
设置 Google Cloud Platform 项目
在我们开始启动机器之前,需要先设置一个 Google Cloud Platform 项目:
-
前往 Google 开发者控制台。当提示时,选择一个已有项目或创建一个新项目。
提示
您使用的名称必须介于 1 到 63 个字符之间,并且首字符必须是小写字母。剩余字符可以使用短横线、小写字母或数字,但最后一个字符不能是短横线。此外,您还应当注意,一些资源标识符(如项目 ID)可能会在项目生命周期结束后被保留。因此,请避免将敏感信息存储在资源标识符中。
-
按照提示设置账单。如果您是 Google Cloud Platform 的新用户,您将获得免费的试用信用额度,用于支付您的实例费用。
创建网络和防火墙规则
在您设置好项目和账单后,前往开发者控制台中的 网络 部分:
-
点击 创建网络 来创建您的第一个网络,并将其命名为
mesos-network
: -
一旦网络添加完成,点击该网络,再点击 添加防火墙规则 按钮。
-
添加规则以打开 TCP 22 和 5050 端口,这通常是一个以分号分隔的列表,格式为
协议:端口
: -
点击 创建。
创建实例
在您设置好项目和账单后,前往开发者控制台中的 VM 实例 部分:
-
点击 创建新实例 来创建您的第一个实例。
-
将实例的 名称 字段设置为
mesos-master
。 -
在 启动磁盘 下,点击 更改,并选择 Ubuntu 14.04 LTS 启动磁盘镜像(如果默认没有指定)。Compute Engine 提供了其他操作系统供您选择,但在本示例中我们将使用 Ubuntu。保存更改:
-
点击 网络,然后选择我们在之前步骤中创建的 mesos-network。
-
点击 创建 来创建你的实例。实例启动后,页面会自动刷新。
一旦我们启动了 mesos-master
机器,现在重复前面的步骤三次,启动我们的 mesos-slave1
、mesos-slave2
,以及我们的集群:
安装 Mesos
到此为止,你将在 Google Compute Cloud 上启动三台机器,分别命名为 mesos-master
、mesos-slave1
和 mesos-slave2
:
现在,登录所有三台机器:
mesos-master: ssh 146.148.62.84
mesos-slave1: ssh 104.197.92.182
mesos-slave2: ssh 104.197.92.145
现在,我们可以通过输入以下命令,在三台机器上安装依赖库和软件包:
# Update the packages.
$ sudo apt-get update
# Install the latest OpenJDK.
$ sudo apt-get install -y openjdk-7-jdk
# Install autotools (Only necessary if building from git repository).
$ sudo apt-get install -y autoconf libtool
# Install other Mesos dependencies.
$ sudo apt-get -y install build-essential python-dev python-boto libcurl4-nss-dev libsasl2-dev maven libapr1-dev libsvn-dev
现在,我们的环境已经准备好,可以开始构建 Mesos 二进制文件。
你可以从主机器构建 Mesos 二进制文件,然后将构建好的文件复制到从机,而不是在所有机器上都进行构建。
下载 Mesos
按照 在 Amazon Web Services (AWS) 上设置多节点 Mesos 集群 部分下相应的 下载 Mesos 子章节中的步骤,在主机器上下载并解压 Mesos。
构建 Mesos
构建 Mesos 的过程已在相应的 AWS 部分中讨论。你可以按照那里列出的指示在主机器上构建 Mesos。
现在,我们可以通过查看运行在主机器端口 5050
上的 Mesos Web UI 来确保安装完成。
打开浏览器,前往以下网址:
http://146.148.62.84:5050
然后,Mesos Web UI 将如下所示:
在 Microsoft Azure 上设置多节点 Mesos 集群
Microsoft Azure 是一个云计算平台和基础设施,由微软创建,用于通过微软管理的和微软合作伙伴托管的数据中心的全球网络来构建、部署和管理应用程序和服务。欲了解更多信息,请访问 azure.microsoft.com
。
实例类型介绍
虚拟机有两种级别:基本版和标准版。两种类型都提供多种大小选项,但基本版不提供一些功能,如负载均衡和自动扩展,而这些功能在标准版中可用。标准版有不同的系列:A、D、DS、G 和 GS。
启动机器
要开始进行开发或在云中进行测试,你需要一个有效的 Microsoft Azure 订阅。如果你当前没有订阅,可以获得一个免费的一个月试用,并且获得 $200 的额度,可以用于任何 Azure 服务:
一旦你注册了 Azure,访问管理门户以启动实例。Azure 管理门户的网址如下:manage.windowsazure.com
创建云服务
Azure 中的云服务为你提供一个公共端点,用于访问在云服务中运行的所有服务(例如虚拟机等)。一个端点可能如下所示:
the-name-you-give.cloudapp.net (e.g: mesos-cluster.cloudapp.net)
以下是创建云服务的步骤:
-
点击左侧菜单面板中的CLOUD SERVICES选项:
-
选择NEW,然后点击CLOUD SERVICE,接着点击QUICK CREATE:
我们使用了
mesos-cluster
这个名称,因此,访问此服务的公共端点将是mesos-cluster.cloudapp.net
。 -
点击Create Cloud Service。创建云服务后,你将能够看到它,如下所示:
创建实例
一旦云服务准备好,接下来的任务是启动用于 Mesos 集群的虚拟机:
-
点击NEW,然后选择COMPUTE,接着选择VIRTUAL MACHINE:
-
点击FROM GALLERY;这个选项为我们提供了更好的选择和机器控制。
-
选择镜像并点击UBUNTU,然后从列表中选择Ubuntu Server 14.04 LTS镜像,点击Next:
-
虚拟机命名:在此步骤中,你需要为虚拟机命名。这个名称将成为你即将启动的机器的主机名,因此我们将其命名为
mesos-master
。选择SIZE为D3(即 4 核 14GB 内存的机器),并输入你将用来登录该机器的密码。然后,点击Next: -
虚拟机配置:在此步骤中,你将选择我们之前创建的mesos-cluster云服务。因此,实际上你将为本次操作启动的虚拟机将属于
mesos-cluster
云服务: -
点击Next并点击Finish按钮:
现在,按照相同的步骤启动mesos-slave1
和mesos-slave2
。
需要注意的一点是,对于每一台新虚拟机,如果你希望从mesos-cluster
端点外部访问这些虚拟机,你必须配置 SSH 端口,如下所示:
-
在虚拟机配置步骤中,在ENDPOINTS下,将PUBLIC PORT值更改为另一个数字:
-
假设
mesos
-master
的公共端口是22
,这意味着假设我们执行如下操作:ssh mesos-cluster.cloudapp.net -p 22
-
这将登录到
mesos-master
机器。现在,如果你想登录到mesos-slave1
,你需要将公共端口配置为44
,这样你就可以登录到mesos-slave1
,如下所示:ssh mesos-cluster.cloudapp.net -p 44
配置网络
出于安全考虑,每当你在云中部署机器时,务必确保已将必要的端口开放给公众。
除了 SSH 端口,我们还需要开放 Mesos UI 端口,该端口运行在 5050
上。为此,执行以下步骤:
-
在 Azure 控制台中,点击已配置的虚拟机中的 mesos-master 机器,然后点击 End Points。
-
现在点击 添加 按钮来添加特定端口:
-
选择 添加独立端点 选项并点击 下一步:
-
将此命名为
mesos-ui
,从协议列表中选择 TCP,并使用5050
作为 公共端口。这样,你就可以通过http://mesos-cluster.cloudapp.net:5050
访问它,并将端口5050
分配为 私有端口。点击 完成 按钮:
安装 Mesos
到这时,你将在 Azure 云中启动并运行三台机器,分别名为 mesos-master
、mesos-slave1
和 mesos-slave2
:
现在,登录到所有三台机器:
mesos-master: ssh mesos-cluster.cloudapp.net
mesos-slave1: ssh mesos-cluster.cloudapp.net -p 23
mesos-slave2: ssh mesos-cluster.cloudapp.net -p 24
理想情况下,使用高级 Linux 终端,如 terminator 或 xterm,可以在多台机器上工作,因为这些终端支持广播命令。你可以在一个窗口中输入命令,然后它会在所有三台机器上同时执行,从而减少配置工作量:
现在,我们可以通过输入以下命令,在所有三台机器上安装依赖库和软件包:
# Update the packages.
$ sudo apt-get update
# Install the latest OpenJDK.
$ sudo apt-get install -y openjdk-7-jdk
# Install autotools (Only necessary if building from git repository).
$ sudo apt-get install -y autoconf libtool
# Install other Mesos dependencies.
$ sudo apt-get -y install build-essential python-dev python-botolibcurl4-nss-dev libsasl2-dev maven libapr1-dev libsvn-dev
现在,我们的环境已准备就绪,可以开始构建 Mesos 二进制文件。你可以在主机上构建 Mesos 二进制文件,然后将构建文件复制到从机上,而不是在所有机器上都进行构建。
下载 Mesos
请按照 在 Amazon Web Services (AWS) 上设置多节点 Mesos 集群 部分下的 下载 Mesos 小节中的步骤,在主机上下载并解压 Mesos。
构建 Mesos
Mesos 的构建可以在 构建 Mesos 小节中找到,位于 在 Amazon Web Services (AWS) 上设置多节点 Mesos 集群 部分下。你可以按照那里列出的说明在主机上构建 Mesos。
一旦我们构建完成,就可以将构建目录从 mesos-master
机器复制到 mesos-slave1
和 mesos-slave2
机器:
mesos-master:~$ rsync -za mesos mesos-slave1:
mesos-master:~$ rsync -za mesos mesos-slave2:
启动 mesos-master
在主机上执行以下命令来启动 mesos-master
:
mesos-master:~/mesos/build$ ./bin/mesos-master.sh --work_dir=/var/lib/mesos
执行该命令后,你可以在终端中看到以下日志:
I1108 08:26:52.525831 13306 main.cpp:229] Build: 2015-11-08 07:26:59 by akhld
I1108 08:26:52.526072 13306 main.cpp:231] Version: 0.25.0
I1108 08:26:52.526406 13306 main.cpp:252] Using 'HierarchicalDRF' allocator
I1108 08:26:52.623775 13306 leveldb.cpp:176] Opened db in 97.107324ms
I1108 08:26:52.712013 13306 leveldb.cpp:183] Compacted db in 88.081084ms
I1108 08:26:52.712218 13306 leveldb.cpp:198] Created db iterator in 72800ns
I1108 08:26:52.712327 13306 leveldb.cpp:204] Seeked to beginning of db in 25401ns
I1108 08:26:52.712745 13306 leveldb.cpp:273] Iterated through 3 keys in the db in 342201ns
I1108 08:26:52.713101 13306 replica.cpp:744] Replica recovered with log positions 5 -> 6 with 0 holes and 0 unlearned
I1108 08:26:52.716048 13321 recover.cpp:449] Starting replica recovery
I1108 08:26:52.716660 13306 main.cpp:465] Starting Mesos master
……….
I1108 08:26:52.776047 13306 master.cpp:376] Master 090c9618-090f-49bd-aa95-265ec5f423d5 (100.73.76.103) started on 100.73.76.103:5050
这里的输出列出了构建版本、主机使用的各种配置以及集群的主节点 ID。从属进程应该能够连接到主节点。从属进程可以通过 --master
选项指定主节点的 IP 地址或主机名。
启动 mesos-slaves
在从节点机器上执行以下命令以启动从节点服务:
mesos-slave1:~/mesos/build$ ./bin/mesos-slave.sh --master=mesos-master:5050
mesos-slave1:~/mesos/build$ ./bin/mesos-slave.sh --master=mesos-master:5050
I1108 08:31:18.733666 26975 main.cpp:185] Build: 2015-11-08 07:26:59 by akhld
I1108 08:31:18.734257 26975 main.cpp:187] Version: 0.25.0
I1108 08:31:18.734724 26975 containerizer.cpp:143] Using isolation: posix/cpu,posix/mem,filesystem/posix
I1108 08:31:18.747735 26975 main.cpp:272] Starting Mesos slave
I1108 08:31:18.748929 26975 slave.cpp:190] Slave started on 1)@100.73.76.129:5051
此输出确认了与主节点的连接,并列出了从节点的资源。现在,集群已经启动,包含两个从节点,并准备运行框架。
我们现在可以通过查看在主节点上运行的 Mesos Web UI(端口为 5050
)来确认安装是否完成。
打开浏览器,访问以下 URL:http://mesos-cluster.cloudapp.net:5050
:
Mesos 命令
如果查看 Mesos 的 bin
目录,你会发现以下可执行文件,可以用于进行不同操作,如下所列:
命令 | 用途 |
---|---|
mesos-local.sh |
此命令在单一进程内启动一个内存集群。 |
mesos-tests.sh |
此命令运行 Mesos 测试用例套件。 |
mesos.sh |
这是一个封装脚本,用于启动 Mesos 命令。运行时不带任何参数会显示所有可用的命令。 |
gdb-mesos-* |
此命令使用 gdb 启动相应的进程进行调试模式。 |
lldb-mesos-* |
此命令使用 lldb 启动相应的进程进行调试模式。 |
valgrind-mesos-* |
此命令启动相应的 Valgrind 仪表框架。 |
mesos-daemon.sh |
此命令启动/停止 Mesos 守护进程。 |
mesos-start-cluster.sh |
此命令启动在 [install-prefix]/var/mesos/deploy/masters 和 [install-prefix]/var/mesos/deploy/slaves 文件中列出的节点上的 Mesos 集群。 |
mesos-stop-cluster.sh |
此命令停止在 [install-prefix]/var/mesos/deploy/masters 和 [install-prefix]/var/mesos/deploy/slaves 文件中列出的节点上的 Mesos 集群。 |
mesos-start-masters.sh |
此命令启动在 masters 文件中列出的节点上的 Mesos 主节点。 |
mesos-stop-masters.sh |
此命令停止在 masters 文件中列出的节点上的 Mesos 主节点。 |
mesos-start-slaves.sh |
此命令启动在 slaves 文件中列出的节点上的 Mesos 从节点。 |
mesos-stop-slaves.sh |
此命令停止在 slaves 文件中列出的节点上的 Mesos 从节点。 |
测试安装
我们现在可以通过运行随 Mesos 一起提供的简单示例(C++、Java 和 Python)来测试集群的安装:
# Run C++ framework (Exits after successfully running some tasks.).
mesos-master:~/mesos/build$ ./src/examples/test-framework --master=mesos-master:5050
输出结果如下:
I1108 09:26:45.512217 23136 sched.cpp:164] Version: 0.25.0
I1108 09:26:45.524862 23156 sched.cpp:262] New master detected at master@100.73.76.103:5050
I1108 09:26:45.527117 23156 sched.cpp:272] No credentials provided. Attempting to register without authentication
I1108 09:26:45.531708 23157 sched.cpp:641] Framework registered with 5f18729d-c83a-4264-a50b-bd972b2d10f3-0006
Registered!
Received offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O11 withcpus(*):4; mem(*):13000; disk(*):23976; ports(*):[31000-32000]
Launching task 0 using offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O11
Launching task 1 using offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O11
Launching task 2 using offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O11
Launching task 3 using offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O11
Received offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O12 withcpus(*):4; mem(*):13000; disk(*):23976; ports(*):[31000-32000]
Launching task 4 using offer 5f18729d-c83a-4264-a50b-bd972b2d10f3-O12
Task 4 is in state TASK_RUNNING
Task 0 is in state TASK_RUNNING
Task 1 is in state TASK_RUNNING
Task 4 is in state TASK_FINISHED
Task 2 is in state TASK_RUNNING
Task 3 is in state TASK_RUNNING
Task 0 is in state TASK_FINISHED
Task 1 is in state TASK_FINISHED
Task 2 is in state TASK_FINISHED
Task 3 is in state TASK_FINISHED
I1108 09:26:45.719132 23154 sched.cpp:1771] Asked to stop the driver
I1108 09:26:45.719321 23154 sched.cpp:1040] Stopping framework '5f18729d-c83a-4264-a50b-bd972b2d10f3-0006'
I1108 09:26:45.720949 23136 sched.cpp:1771] Asked to stop the driver
你也可以从 Web 界面查看任务的状态:
# Run Java framework (Exits after successfully running some tasks)mesos-master:~/mesos/build$ ./src/examples/java/test-framework mesos-master:5050
# Run Python framework (Exits after successfully running some tasks)
mesos-master:~/mesos/build$ ./src/examples/python/test-framework mesos-master:5050
注意
为了构建示例框架,确保通过以下步骤构建测试套件:
make check
在您的私有数据中心设置多节点 Mesos 集群
在本节中,我们将解释如何在您完全托管的数据中心上启动并运行 Mesos。为了简单起见,假设您的数据中心有三台机器,您将基于这些机器设置 Mesos。我们还假设您的数据中心虚拟机正在运行 CentOS 6.6 Linux 发行版。
假设以下机器已选择安装 Mesos:
machine-a : 192.168.1.10
machine-b : 192.168.1.11
machine-c : 192.168.1.12
我们将选择 machine-a
作为集群的 Mesos 主节点,并且 machine-b
、machine-c
将运行从节点进程。
安装 Mesos
以下是如何在您的私有数据中心安装多节点 Mesos 集群的步骤。
准备环境
我们需要安装 Mesos 在 CentOS 机器上运行所需的所有依赖项和库。为此,我们需要登录到所有三台机器并输入以下命令:
注意
请注意,以下是针对标准 CentOS 6.6 的指令。如果您使用的是其他操作系统,请相应地安装相应的包。
# Issue the following command to install the wget command for your centos machine
$ sudo yum install -y tar wget
# We will need c++11 support which is available in the devtoolset-2\. For that you will have to add it to the repo and then issue theinstallation command as follows:
$ sudo wget -O /etc/yum.repos.d/slc6-devtoolset.repohttp://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo# Import the CERN GPG key.
$ sudo rpm --importhttp://linuxsoft.cern.ch/cern/centos/7/os/x86_64/RPM-GPG-KEY-cern
# Fetch the Apache Maven repo file.
$ sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
# 'Mesos > 0.21.0' requires 'subversion > 1.8' devel package,
# which is not available in the default repositories.
# Add the WANdisco SVN repo file: '/etc/yum.repos.d/wandisco-svn.repo' with content:
[WANdiscoSVN]
name=WANdisco SVN Repo 1.8
enabled=1
baseurl=http://opensource.wandisco.com/centos/6/svn-1.8/RPMS/$basearch/
gpgcheck=1
gpgkey=http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco
# Now we can install the development tools which will contain the utilities like make for your our installation
$ sudo yum groupinstall -y "Development Tools"
# Install 'devtoolset-2-toolchain' which includes GCC 4.8.2 and related packages.
$ sudo yum install -y devtoolset-2-toolchain
# Now we can install the mesos related dependency for centos.
$ sudo yum install -y apache-maven python-devel java-1.7.0-openjdk-devel zlib-devel libcurl-devel openssl-develcyrus-sasl-devel cyrus-sasl-md5 apr-devel subversion-devel apr-util-devel
# Now we can enable the devtoolset for the shell.
$ scl enable devtoolset-2 bash
$ g++ --version
# At this point make sure we get the gcc+ version > 4.8.
下载 Mesos
按照 在 Amazon Web Services (AWS) 上设置多节点 Mesos 集群 部分中的 下载 Mesos 子部分的步骤,下载并提取主机上的 Mesos。
构建 Mesos
我们可以按照上一节中提到的步骤,在主机上构建 Mesos。
现在,我们可以从主机将构建目录从 machine-a
复制到 machine-b
和 machine-c
:
machine-a:~$ rsync -za mesos machine-b:
machine-a:~$ rsync -za mesos machine-c:
启动 mesos-master
从主机上执行以下命令来启动 mesos-master
:
machine-a:~/mesos/build$ ./bin/mesos-master.sh --work_dir=/var/lib/mesos --ip=192.168.1.10
启动 mesos-slaves
现在,我们可以从从节点机器上执行以下命令来启动从节点服务:
machine-b:~/mesos/build$ ./bin/mesos-slave.sh -- master=192.168.1.10:5050
machine-c:~/mesos/build$ ./bin/mesos-slave.sh -- master=192.168.1.10:5050
我们现在可以通过查看主机上运行在 5050
端口的 Mesos Web UI 来确认安装已完成。
打开浏览器并访问以下 URL:http://192.168.1.10:5050
:
当有多台机器时,自动化该过程
我们可以重复之前的步骤,手动在每个从节点上启动 mesos-slave
来设置集群,但对于大型集群而言,这种方式劳动强度大且容易出错。Mesos 包含一组位于 deploy
文件夹中的脚本,这些脚本可用于在集群上部署 Mesos。这些脚本依赖 SSH 来执行部署。
我们将设置一个包含 10 个从节点(slave1
、slave2
、slave10
)和一个主节点(master)的集群。
让我们配置集群,确保在安装所有前提条件后,各节点之间可以互相连接。以下命令将生成一个 SSH 密钥并将其复制到所有从节点:
master:~ $ ssh-keygen -f ~/.ssh/id_rsa -P ""
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave1
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave2
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave3
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave4
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave5
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave6
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave7
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave8
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave9
master:~ $ ssh-copy-id -i ~/.ssh/id_rsa.pub slave10
我们需要将 Mesos 构建复制到所有节点的相同位置,就像在主节点上那样:
master:~ $ scp –R build slave1:[install-prefix]
master:~ $ scp –R build slave2:[install-prefix]
master:~ $ scp –R build slave3:[install-prefix]
master:~ $ scp –R build slave4:[install-prefix]
master:~ $ scp –R build slave5:[install-prefix]
master:~ $ scp –R build slave6:[install-prefix]
master:~ $ scp –R build slave7:[install-prefix]
master:~ $ scp –R build slave8:[install-prefix]
master:~ $ scp –R build slave9:[install-prefix]
master:~ $ scp –R build slave10:[install-prefix]
使用您选择的编辑器,在 [install-prefix]/var/mesos/deploy/masters
目录中创建一个 masters 文件,按行列出主节点,在我们的情况下,将只有以下一个:
master:~ $ cat [install-prefix]/var/mesos/deploy/masters
master
类似地,slaves 文件将列出我们想要作为 Mesos 从机的所有节点:
master:~ $ cat [install-prefix]/var/mesos/deploy/slaves
slave1
slave2
slave3
slave4
slave5
slave6
slave7
slave8
slave9
slave10
现在,我们可以使用 mesos-start-cluster
脚本启动集群,并使用 mesos-stop-cluster
停止它:
master:~ $ mesos-start-cluster.sh
这将调用 mesos-start-masters
和 mesos-start-slaves
,它们将在主节点和从节点上启动相应的进程。该脚本会查找 [install-prefix]/var/mesos/deploy/mesos-deploy-env.sh
中的环境配置。为了更好的配置管理,主节点和从节点的配置选项可以分别指定在 [install-prefix]/var/mesos/deploy/mesos-master-env.sh
和 [install-prefix]/var/mesos/deploy/mesos-slave-env.sh
文件中。
调试与故障排除
在本节中,我们将讨论如何排查和调试在设置过程中遇到的常见问题。
处理缺失的库依赖
有时,当你在一个全新的 Linux 虚拟机上构建 Mesos 时,配置步骤可能会出现错误。比如缺少 libz-dev
软件包:
configure: error: cannot find libz
-------------------------------------------------------------------
libz is required for Mesos to build.
-------------------------------------------------------------------
每当你遇到像上面这样的错误,或者类似的缺少软件包的错误时,你需要做的下一件事是安装这些缺失的软件包,然后再次执行 configure
命令。
在这里,为了解决缺失的 libz
库,你需要在 Ubuntu 系统中输入以下命令:
$ sudo apt-get install libz-dev
对于 CentOS 版本,命令如下:
$ yum install zlib-devel
目录权限问题
如果 Mesos 尝试写入 /var/lib/mesos
,但你没有正确设置该目录的权限,那么将会出现以下错误:
mkdir: cannot create directory '/var/lib/mesos': Permission denied
为了克服这些问题,你需要通过以下命令为该目录分配正确的权限:
$ sudo chown `whoami` /var/lib/mesos
缺失 Mesos 库(未找到 libmesos*.so)
每当你遇到 libmesos*.so 文件未找到 时,你能做的最好的事情就是 复制 libmesos*.so
文件从 Mesos 安装目录到 /lib
目录。
这样的错误示例如下:
/home/akhld/mesos/build/src/.libs/test-executor: error while loading shared libraries: libmesos-0.25.0.so: cannot open shared object file: No such file or directory
调试失败的框架
有时,如果你的集群配置不正确或者有一个有问题的框架,那么它将无法成功执行。在这种情况下,你可以打开 Mesos 的 web 界面,然后点击 框架 选项卡下的失败 框架,如下所示:
在这里,你可以看到一些任务被标记为 KILLED,接下来你需要找出原因。
理解 Mesos 目录结构
在每个从机上,默认情况下,Mesos 的工作目录位于 /tmp/mesos
,并且在 /tmp/mesos/slaves/
下会有 slave id
,它会跟踪在其下运行的框架,并保存在 frameworks
目录下。该框架会记录它尝试执行给定任务的次数,每次运行的详细信息将记录在 runs
目录中。你可以从 stderr
和 stdout
文件中找到错误和标准输出,这些文件位于 runs 目录下。
这是一个包含单个执行器实例和多个框架的示例目录结构:
现在,为了追踪我们的问题,你需要转到相应的框架 ID,并查看 stderr
文件,它将记录该框架所面临的确切问题。
让我们来看一下以下示例:
$ cat /tmp/mesos/slaves/63088700-3d5d-4490-a3df-a3f85fe62a01-S1/frameworks/ed4b8897-6c4b-4b14-8575-4a828731785d-0001/executors/default/runs/latest/stderr
/var/lib/mesos/: Permission denied
现在你知道问题出在目录权限上,你可以按照上一节中描述的步骤来修复它。
Mesos 从属节点未能连接到 Mesos 主节点
诸如从属节点无法连接主节点的问题可以通过以下两种方式解决:
-
查看从属节点的控制台日志。
你可以查看控制台日志,当从属节点尝试连接到主节点时,这将显示连接失败的消息,如操作超时、连接被拒绝、网络无法访问等。
-
确保主节点和从节点绑定到正确的网络接口。
确保你的主节点和从节点绑定到正确的网络接口;使用
--ip
选项来确保这一点总是更安全。
在同一台机器上启动多个从属实例
如果你尝试在同一台机器上启动多个从属进程,最终会遇到以下异常:
Failed to initialize, bind: Address already in use [98]
如果你需要在单台机器上启动更多的从属实例,可以通过以下方式实现。
你可以再启动一个从属节点,但必须指定一个端口和一个不同的 workdir
,方法如下:
./mesos-slave.sh --master=<ipaddr>:<port> --ip=<ip of slave> --work_dir=<work_dir other than that of a running slave> --port=<another_port>
注意
Mesos 的 master
和 slave
节点可以部署在同一台机器上吗?
你绝对可以在同一节点上运行 master
和多个 slave
进程。你甚至可以在同一节点上运行多个 master
和 slave
进程,只要为它们指定唯一的端口,但这最好仅适用于测试集群。
总结
在本章中,我们展示了如何手动在公共云(AWS、GCE 和 Azure)以及私有数据中心(本地部署)上设置和运行 Mesos 集群。我们还讨论了在设置过程中常见的错误以及如何高效地进行调试和解决。
在下一章中,我们将探索重要的调度和管理框架,如 Marathon 和 Chronos,这些框架通常与 Mesos 密切配合,并且是 Mesos 生态系统的核心组件。
第四章:服务调度和管理框架
本章介绍了几个基于 Mesos 的调度和管理框架或应用程序,这些框架或应用程序对于长期运行服务的轻松部署、发现、负载均衡和故障处理至关重要。这些所谓的元框架负责处理其他框架和应用程序的家务工作,例如服务发现(即跟踪某个特定服务运行的实例)和负载均衡(确保在各个实例之间公平分配工作负载),此外还包括配置管理、自动化****任务调度、应用程序****扩展和故障处理。我们将在这里探讨的框架包括:
-
Marathon:用于在 Mesos 上启动和管理长期运行的应用程序
-
Chronos:这是一个集群调度器
-
Apache Aurora:这是一个用于长期运行服务和定时任务的框架
-
Singularity:这是一个平台即服务(PaaS),用于运行服务
-
Marathoner:这是用于 Marathon 的服务发现工具
-
Consul:执行服务发现和协调
-
HAProxy:用于负载均衡
-
Bamboo:用于自动配置 Mesos 和 Marathon 的 HAProxy
此外,我们还将简要介绍两个非常新的开源框架,即Netflix Fenzo(一个任务调度器)和Yelp 的 PaaSTA(一个运行服务的 PaaS)。
使用 Marathon 在 Mesos 上启动和管理长期运行的应用程序
Marathon 是一个常用的 Mesos 框架,用于长期运行的应用程序。它可以被视为传统系统中 init
或 upstart
的替代品,或者是您系统的 init.d
。
Marathon 拥有许多功能,例如控制高可用环境、检查应用程序的健康状态等。它还支持表现层状态转移(REST),如端点,您可以使用它来启动、停止和扩展您的应用程序。它可以根据负载自动扩展和缩减集群,这意味着它应能在某个可用实例宕机时启动新的实例。Marathon 还设计用来运行其他框架,例如Hadoop、Kafka、Storm、Chronos等。Marathon 确保通过它启动的每个应用程序即使在某个从节点宕机的情况下也能继续运行。
Marathon 以高度可用的方式运行,这意味着集群中可以运行多个调度器,但在任何时候,只有一个领导者。当应用程序请求非领导者时,请求将被代理到活跃的领导者。您还可以使用 HAProxy(本章稍后将解释)进行服务发现和负载均衡。
Marathon 还支持基本的认证机制,并使用 SSL 加密连接。
安装 Marathon
访问 mesosphere.github.io/marathon/
下载最新的 Marathon 版本。编写本书时,最新版本是 0.13.0。
可以按照如下方式下载 Marathon:
$ wget http://downloads.mesosphere.com/marathon/v0.13.0/marathon-0.13.0.tgz
下载后,按如下方式解压文件:
$ tar xf marathon-0.13.0.tgz
解压 Marathon 后,你将看到以下文件:
Marathon 有一个开发模式,在该模式下你不需要分布式 Mesos 设置。这被称为 Marathon 本地模式。本地模式仅用于实验目的,不建议在任何生产环境中运行。ZooKeeper 和 Marathon 一起使用,用于存储状态。
安装 ZooKeeper 以存储状态
Marathon 要求你运行一个 Apache ZooKeeper 实例,以便它能够保存状态。按照以下步骤安装并使用 ZooKeeper:
-
访问
zookeeper.apache.org
下载 ZooKeeper 的最新版本。编写本书时,当前版本是 3.4.7。 -
如下所示下载 ZooKeeper:
$ wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.7/zookeeper-3.4.7.tar.gz
-
下载后,按如下方式解压归档文件:
$ tar xf zookeeper-3.4.7.tar.gz
-
下一步是配置 ZooKeeper。可以按如下方式完成:
用以下内容编辑
conf/zoo.cfg
文件:tickTime=2000 dataDir=/var/zookeeper clientPort=2181
-
然后,运行以下命令来启动 ZooKeeper:
$ bin/zkServer.sh start
启动成功后,你可以看到以下消息:
在本地模式下启动 Marathon
以下命令将启动 Marathon 本地模式:
$ ./bin/start --master local --zk zk://localhost:2181/marathon
一旦 ZooKeeper 启动并运行,你可以通过浏览器访问服务器上的 8080
端口来查看 Marathon UI。
多节点 Marathon 集群设置
要设置此环境,需要配置一个高可用性的 Mesos 集群,详细内容将在第五章中解释,Mesos 集群部署。目前我们假设你已经配置好了高可用性的 Mesos 集群。接下来,我们将看看如何在集群的所有主机上安装 Marathon。
登录到所有 Mesos 主机,并输入以下命令来设置 Marathon。
在 Debian/Ubuntu 系统上,运行以下命令:
# Update the repositories
# Setup
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv E56151BF
$ DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
$ CODENAME=$(lsb_release -cs)
# Add the repository
$ echo "deb http://repos.mesosphere.com/${DISTRO} ${CODENAME} main" | \
sudo tee /etc/apt/sources.list.d/mesosphere.list
$ sudo apt-get
update
# Install Marathon
$ sudo apt-get -y install marathon
在 RedHat/CentOS 系统上,执行以下命令:
$ sudo yum -y install marathon
现在,你可以在浏览器中访问任何一个主机的 8080
端口,并查看 Marathon UI:
从 UI 启动测试应用程序
在 Mesos 中,应用程序通常是一个长时间运行的服务,可以扩展到多个实例。现在,我们将查看从用户界面启动测试应用程序的步骤:
-
点击左上角的 + 创建 按钮。
-
ID 可以用来标识任务。我们将其命名为
marathon-test
。 -
提供作业所需的 CPU 数量——比如说,
1
。 -
内存以 MB 为单位,因此我们将分配
16
MB(这也是默认值)。 -
实例数可以为我们的测试应用程序设置为 1。
-
在命令框下的文本框中编写以下 bash 脚本:
while [ true ] ; do echo 'Hello Marathon' ; sleep 5 ; done
如果一切正确,你会首先看到 marathon-test 测试应用程序,状态为 已部署,最终会变为 运行中。
扩展应用程序
在创建时,我们给出了 1
作为实例数。我们可以通过点击 UI 上的“扩展应用程序”按钮来修改实例数量。应用程序将按指定的实例数量启动。
终止应用程序
现在我们可以通过点击应用程序列表中的应用程序名称,然后点击 销毁 按钮来终止我们的 marathon-test 应用程序。
销毁应用程序是一个不可逆的过程,无法撤销。
Chronos 作为集群调度器
可以将 Chronos 看作一个基于时间的作业调度器,例如典型 Unix 环境中的 cron。Chronos 是分布式的,完全容错,并且运行在 Apache Mesos 之上。
与 cron 类似,Chronos 默认执行 shell 脚本(结合 Linux 命令),并且支持 Mesos 执行器。
即使 Mesos 工作节点(实际执行作业的机器)上没有安装该系统,Chronos 也可以与像 Hadoop 或 Kafka 这样的系统进行交互。你可以使用 Chronos 在远程机器的后台启动服务或运行脚本。包装脚本可以包含异步回调,提醒 Chronos 作业状态,如是否完成或失败等。大多数情况下,人们使用 Chronos 来运行 Docker 化的应用程序。关于 Docker 化应用程序 的详细解释,请参见 第七章,Mesos 容器化器。
Chronos 附带一个 Web UI,你可以在其中查看作业状态、作业历史统计、作业配置以及重试信息。
安装 Chronos
登录到任何一台机器(比如说其中一台 Mesos 主机),然后输入以下命令来设置 Chronos。
在 Debian/Ubuntu 机器上,运行以下命令:
# Install chronos
$ sudo apt-get -y install chronos
# Start the chronos server
$ sudo service chronos start
在 RedHat/CentOS 机器上,执行以下代码:
# Install chronos
$ sudo yum -y install chronos
# Start the chronos server
$ sudo service chronos start
安装完成后,你可以将浏览器指向机器的 4400
端口来查看 Chronos 的 UI,如下所示:
调度一个新作业
按照这里提到的步骤调度新作业:
-
点击 + 新建作业 按钮,如前一截图所示。
-
现在,填写 名称 和 描述 字段。
-
COMMAND 是将实际调度在执行器上运行的任务。为了简化起见,我们将仅运行
sleep
命令。 -
在 OWNER(S) 字段中,我们可以填写名称和电子邮件地址,以便 Chronos 在任务失败时向其发送警报邮件。
-
在 SCHEDULE 下,我们可以设置任务应该运行的调度频率。默认情况下,它是空的并且是无限的。我们可以将其设置为任何数字值。例如,当值设置为 0 时,重复次数仅为一次。
一旦任务创建成功,我们可以通过 UI 查看任务的摘要,如下图所示:
任务的状态也可以在以下截图中查看。在这种情况下,我们可以看到 chronos-test 任务处于 运行中 状态:
我们可以前往 Mesos UI(运行在 5050
端口上),实际查看由 Chronos 启动的任务。
Chronos 加 Marathon
Chronos 和 Marathon 的结合可以作为构建生产级分布式应用程序的基础。你已经知道 Chronos 可以在预定的时间间隔触发任务;cron 和 Marathon 让你的任务能够持续运行,例如在典型的 Linux 环境中的 init
或 upstart
。如前所述,两个调度器都提供 REST 端点,允许用户管理任务。你可以使用这个端点来启动、管理和终止运行中的任务。接下来我们将看看这是如何实现的。
Chronos REST API 端点
如前所述,您可以通过 HTTP 使用 REST JSON API 与 Chronos 进行通信。默认情况下,运行 Chronos 的节点会在 8080
端口监听 API 请求。本节将介绍如何使用 REST 端点执行以下任务:
-
列出正在运行的任务
-
手动启动任务
-
添加一个调度任务
-
删除任务
欲了解更多信息,请访问 mesos.github.io/chronos/docs/api.html
。
列出正在运行的任务
使用 HTTP GET
方法请求 /scheduler/jobs
将返回当前正在运行的任务列表,格式为 JSON。
这是一个示例:
$ curl -L -X GET localhost:8080/scheduler/jobs
响应中包含以下数据:
-
successCount
-
errorCount
-
lastSuccess
-
lastError
-
executor
-
parents
手动启动任务
要手动启动任务,请向 /scheduler/job
发送 HTTP PUT
请求,并在命令的末尾添加可选参数。
看一下以下示例:
$ curl -L -X PUT localhost:8080/scheduler/job/job_a?arguments=-debug
添加一个调度任务
你可以向 /scheduler/iso8601
发送 HTTP POST
请求,并附带 JSON 数据来调度任务。发送给 Chronos 的 JSON 数据必须包含以下字段:
-
名称
-
命令
-
schedule
-
任务的重复次数;
-
任务开始时间,采用 ISO 8601 格式;
-
标准 ISO 8601 日期时间格式
-
scheduleTimeZone
-
epsilon
-
拥有者
-
异步
请看以下示例:
$ curl -L -H 'Content-Type: application/json' -X POST -d '{ "schedule": "<some_value>", "name": "jab_a", "epsilon": "….", "command": "echo 'FOO' >> /tmp/job_a_OUT", "owner": "akhil@sigmoid.com", "async": false }' localhost:8080/scheduler/iso8601
删除一个作业
要删除作业,你可以在/scheduler/job/<jobName>
上使用HTTP DELETE
请求,其中jobName
可以从正在运行的作业列表中获取。
这是一个示例:
$ curl -L -X DELETE localhost:8080/scheduler/job/job_a
删除作业的所有任务
要删除某个作业的所有任务,你可以在/scheduler/task/kill/<jobName>
上使用HTTP DELETE
请求。
请看以下示例:
$ curl -L -X DELETE localhost:8080/scheduler/task/kill/job_a
Marathon REST API 端点
本节将介绍 Marathon 的 REST 端点。可以执行以下任务:
-
列出正在运行的应用程序。
-
添加一个应用程序。
-
更改配置。
-
删除一个应用程序。
列出正在运行的应用程序
你可以通过向/v2/apps
端点发送HTTP GET
请求,列出在 Marathon 上部署的正在运行的应用程序。该端点还支持一个过滤器,可以帮助你将列出内容限制到特定的应用程序。
以下是该端点所需的参数:
-
cmd
:用于过滤包含给定命令的应用程序 -
embed
:可以多次指定该参数,用于嵌入与提供的路径匹配的嵌套资源
这是一个示例:
$ curl -L -X GET "localhost:8080/v2/apps?cmd=sleep 60"
你可以查看以下类似的 JSON 格式响应:
{
"apps": [
{
"id": "/product/us-east/service/myapp",
"cmd": "env && sleep 60",
"constraints": [
[
"hostname",
"UNIQUE",
""
]
],
"container": null,
"cpus": 0.1,
"env": {
"LD_LIBRARY_PATH": "/usr/local/lib/myLib"
},
"executor": "",
"instances": 3,
"mem": 5.0,
"ports": [
15092,
14566
],
"tasksRunning": 1,
"tasksStaged": 0,
"uris": [
"https://raw.github.com/Mesosphere/Marathon/master/README.md"
],
"version": "2014-03-01T23:42:20.938Z"
}
]
}
添加一个应用程序
要从 REST 端点创建并启动一个应用程序,你可以使用/v2/apps
端点并发送一个HTTP POST
请求。该请求需要传递包含应用程序信息的 JSON 数据。以下是此调用所需的参数:
-
id
:这是应用程序的名称 -
cmd
:这是需要执行的命令 -
args
:这是应用程序的可选参数 -
cpus
:这是为应用程序分配的 CPU 核心数量 -
mem
:这是为应用程序分配的内存大小 -
ports
:这是为应用程序预留的端口 -
instances
:这是部署应用程序的实例数量
以下示例展示了如何在 Marathon 中启动一个简单的 Python HTTP 服务器作为应用程序:
$ curl -L -H 'Content-Type: application/json' -X POST –d
'{
"args": null,
"backoffFactor": 1.15,
"backoffSeconds": 1,
"maxLaunchDelaySeconds": 3600,
"cmd": "env && python3 -m http.server $PORT0",
"constraints": [
[
"hostname",
"UNIQUE"
]
],
"container": {
"Docker": {
"image": "python:3"
},
"type": "DOCKER",
"volumes": []
},
"cpus": 0.25,
"dependencies": [],
"deployments": [
{
"id": "f44fd4fc-4330-4600-a68b-99c7bd33014a"
}
],
"disk": 0.0,
"env": {},
"executor": "",
"healthChecks": [
{
"command": null,
"gracePeriodSeconds": 3,
"intervalSeconds": 10,
"maxConsecutiveFailures": 3,
"path": "/",
"portIndex": 0,
"protocol": "HTTP",
"timeoutSeconds": 5
}
],
"id": "/my-app",
"instances": 2,
"mem": 50.0,
"ports": [
0
],
"requirePorts": false,
"storeUrls": [],
"upgradeStrategy": {
"minimumHealthCapacity": 0.5,
"maximumOverCapacity": 0.5
},
"uris": [],
"user": null,
"version": "2014-08-18T22:36:41.451Z"
}
' localhost:8080/v2/apps
另外,注意如果给定的应用程序 ID 在 Marathon 中已经存在,将会抛出重复错误,并且应用程序根本不会启动。
更改应用程序的配置
你可以向/v2/apps/<appId>
端点发送一个HTTP PUT
请求,来更改给定应用程序的配置。appId
值可以通过之前列出正在运行的应用程序的方法获取。一旦请求发送,当前运行的任务将会使用新的配置重启。
它接受force
参数,布尔值,默认为 false。将其设置为 true 会覆盖当前的部署,如果应用程序的状态受到影响的话。
考虑以下示例:
$ curl -L -X PUT localhost:8080/v2/apps/my_app -d '{
"cmd": "sleep 55",
"constraints": [
[
"hostname",
"UNIQUE",
""
]
],
"cpus": "0.3",
"instances": "2",
"mem": "9",
"ports": [
9000
]
}
'
一旦更新成功,它将返回一个包含以下内容的 JSON 响应:
{
"deploymentId": "6b2135a6-3326-4e44-9333-554eda6c3838",
"version": "2015-12-16T12:37:50.462Z"
}
删除应用程序
你可以使用HTTP DELETE
请求在/v2/apps/<appId>
端点销毁应用程序及其相关数据。
看一下以下示例:
$ curl -X DELETE localhost:8080/v2/apps/my_app
Apache Aurora 介绍
Apache Aurora 是一个强大的 Mesos 框架,适用于长期运行的服务、定时任务和临时任务。它最初在 Twitter 设计,后来根据 Apache 许可证开源。你可以使用 Aurora 将 Mesos 集群转变为私人云。与 Marathon 不同,Aurora 负责在共享资源池中长时间运行任务。如果池中的任何机器出现故障,Aurora 可以智能地将任务重新调度到其他健康的机器上。
如果你尝试构建一个具有特定调度需求的应用程序,或者如果任务本身就是一个调度器,那么 Aurora 将不适用。
管理长期运行的应用程序是 Aurora 的关键功能之一。除此之外,Aurora 可以为你的任务提供粗粒度(即固定)的资源,以确保任务始终具有指定数量的资源。它还支持多个用户,且配置使用 DSL(领域特定语言)模板化,以避免配置中的冗余。
安装 Aurora
Aurora 任务可以通过 Aurora Web UI 和 Aurora 命令行工具进行交互。要安装 Aurora,需要安装 vagrant
。你可以使用以下命令安装 vagrant
:
$ sudo apt-get install vagrant
登录到集群中的任何机器,使用以下命令克隆 Aurora 仓库:
$ git clone git://git.apache.org/aurora.git
将工作目录更改为 Aurora,如下所示:
$ cd aurora
然后,输入以下命令在此机器上安装 Aurora:
$ vagrant up
vagrant
命令将使用与 Aurora 分发版一起提供的配置,在虚拟机上安装并启动 Aurora 服务。它将:
-
下载相应的 Linux 虚拟机镜像
-
配置并启动虚拟机
-
在虚拟机上安装 Mesos 和 ZooKeeper 以及构建工具
-
在虚拟机上编译并构建 Aurora 源代码
-
在虚拟机上启动 Aurora 服务
这个过程可能需要几分钟才能完成。如果命令失败并提示机器上没有安装 VirtualBox,可以使用以下命令进行安装:
$ sudo apt-get install virtualbox
如果一切顺利,你将在终端看到以下输出:
Singularity 介绍
Singularity 最初在 HubSpot 设计,后来根据 Apache 许可证开源。Singularity 作为一个 API 和 Web 应用程序,可以用于启动和调度长期运行的 Mesos 进程、定时任务和任务。可以将 Singularity 及其组件视为一个PaaS(平台即服务)提供给最终用户。初学者可以使用 Singularity 在 Mesos 上部署任务,而无需详细了解 Mesos。
Singularity 利用 Apache Mesos 的容错性、可扩展性和资源分配等功能,并作为 Mesos 框架的任务调度器运行。
安装 Singularity
在安装 Singularity 之前,确保你的机器上已经安装了 Docker。如果还没有安装,你可以通过访问 docs.docker.com
上的官方文档来安装。
第一步是克隆 Singularity 仓库,操作如下:
$ git clone https://github.com/HubSpot/Singularity
现在,将工作目录切换到 Singularity,如下所示:
$ cd Singularity
一旦成功安装了 Docker 和 Docker Compose,你可以使用 Docker Compose 的 pull
和 up
命令来尝试 Singularity。这些命令将为你在容器中设置以下内容:
-
Mesos 主节点和从节点
-
ZooKeeper
-
奇点
-
Baragon 服务和代理
如果你希望在没有 Docker 的情况下安装 Singularity,以下步骤可以帮助你完成安装:
# Compile the source code
$ mvn clean package
完成后,你可以看到 Singularity jars 被创建在 SingularityService/target
目录下。
我们将使用 SingularityService-0.4.6-SNAPSHOT-shaded.jar 来运行 Singularity。
创建 Singularity 配置文件
Singularity 配置保存在 YAML 文件中。这里将解释一个示例的 YAML 配置文件。
端口 7099
用于运行 SingulartiyService
,日志将保存在 /var/log/singularity-access.log
中。请查看以下代码:
server:
type: simple
applicationContextPath: /singularity
connector:
type: http
port: 7099
requestLog:
appenders:
type: file
currentLogFilename: /var/log/singularity-access.log
archivedLogFilenamePattern: /var/log/singularity-access-%d.log.gz
#Mesos configuration, put the content from /etc/Mesos/zk as Mesos master
mesos:
master: zk://100.76.90.36:2181,100.76.126.34:2181,100.72.150.2:2181/Mesos
defaultCpus: 1 # number of core that will be used by the job
defaultMemory: 128 # default memory of the job, being 128MB
frameworkName: Singularity
frameworkId: Singularity
frameworkFailoverTimeout: 1000000
Zookeeper: # quorum should be a host:port separated by comma
quorum: 100.76.90.36:2181,100.76.126.34:2181,100.72.150.2:2181
zkNamespace: singularity
sessionTimeoutMillis: 60000
connectTimeoutMillis: 5000
retryBaseSleepTimeMilliseconds: 1000
retryMaxTries: 3
logging:
loggers:
"com.hubspot.singularity" : TRACE
enableCorsFilter: true
sandboxDefaultsToTaskId: false # enable if using SingularityExecutor
ui:
title: Singularity (local)
baseUrl: http://localhost:7099/singularity
将上述配置保存为 singularity_config.yaml
,并使用以下命令启动 Singularity:
java -jar SingularityService/target/SingularityService-*-shaded.jar server singularity_config.yaml
如果一切顺利,你将在 Mesos UI 的框架标签下看到 Singularity 框架,如下图所示:
你可以将浏览器指向以下 URL 来访问 Singularity 的 UI:http://ServerIPAddress:7099/singularity/
。
使用 Marathoner 的服务发现
现代分布式应用程序需要一种相互通信的方式,这意味着当它们在同一网络上时,一个应用程序应该知道另一个应用程序的存在。这就是服务发现。在本节中,我们将查看在 Marathon 上运行的 Web 服务的服务发现。你可以采用这种方法来处理大多数在 Marathon 上运行的无状态应用程序。
我们将结合流行的 HAProxy TCP/HTTP 负载均衡器和 Marathon 的 REST API 脚本(在之前的主题中已覆盖),重新生成 HAProxy 配置文件,以实现 Marathon 应用程序的服务发现。当任务在某个 Mesos 从节点上启动时,它们会配置为将端口绑定到默认范围 31,000-32,000 内的一个任意端口。
服务发现使得在 Marathon 上运行的应用程序可以通过它们配置的 Marathon 应用程序端口与其他应用程序进行通信。例如,你可以考虑一个运行在端口 80 上的 Python Web 应用程序,它可以通过连接到 localhost:8080
来与其在端口 8080
上运行的 Java 后端进行通信。
HAProxy 可以将它收到的请求路由到实际运行服务实例的端口和主机。如果由于某种原因,它未能连接到给定的主机和端口,它将尝试连接到下一个配置运行该服务的实例。
我们将使用 HAProxy-Marathon-bridge shell 脚本,该脚本是 Marathon 提供的,用于连接到 Marathon 并检索主机名、运行中的应用程序绑定的端口以及配置的应用程序端口。该脚本每 60 秒通过 cron 调度运行一次。该脚本基本上检查它在上次运行中生成的配置与当前配置是否有所不同,如果检测到变化,则重新加载新的配置到 HAProxy 中。请注意,我们不需要重新启动 HAProxy。
以下是两个服务 SVC1 和 SVC2 在集群中运行的图示,它们分别配置了在端口 1111 和 2222 上运行的应用程序。Mesos 分配的任务端口分别是 31100
和 31200
。请注意,由 HAProxy 负责在用户配置的应用程序端口和 Mesos 分配的任务端口之间路由请求。
例如,如果从从节点 2 上的 SVC2 尝试通过 localhost:2222
连接到 SVC1,HAProxy 将把请求路由到配置的 SVC1 实例——即运行在从节点 1 上的实例。
如果从节点 1 出现故障,则对 localhost:2222
的请求将被路由到从节点 2。
使用 Consul 进行服务发现
Mesos-consul 用于注册和注销作为 Mesos 任务运行的服务。
例如,如果你有一个名为 myapp
的 Mesos 任务,那么该程序将会在 Consul 中注册该应用程序,并将其 DNS 暴露为 myapp.service.consul
。Consul 还通过 leader.Mesos.service.consul
DNS 进行 Mesos 主节点发现,该 DNS 指向当前活动的主节点。
这与其他服务发现软件有何不同?
Mesos-dns 是一个类似于 Consul 的项目。在 Mesos-dns 中,它轮询 Mesos 以获取任务信息,而在 Consul 中,信息不是通过内置的 DNS 服务器暴露,而是通过 Consul 服务发现填充这些信息。然后,这些服务通过 DNS 和其 REST 端点由 Consul 暴露。
运行 Consul
如果你的 ZooKeeper 和 Marathon 服务没有在 Consul 中注册,你需要更改环境变量的值。你可以将 Consul 容器化,并通过 Marathon 运行它。
Consul 可以以以下方式运行:
curl -X POST -d@Mesos-consul.json -H "Content-Type: application/json" http://Marathon.service.consul:8080/v2/apps'
上述代码是一个 HTTP POST
请求,向 Consul API 端点发送以下 JSON 数据,位于 Mesos-consul.json
文件中:
{
"args": [
"--zk=zk://Zookeeper.service.consul:2181/Mesos"
],
"container": {
"type": "DOCKER",
"Docker": {
"network": "BRIDGE",
"image": "{{ Mesos_consul_image }}:{{ Mesos_consul_image_tag }}"
}
},
"id": "Mesos-consul",
"instances": 1,
"cpus": 0.1,
"mem": 256
}
以下表格列出了命令行工具 Mesos-consul 支持的选项:
选项 | 描述 |
---|---|
version |
这个命令会打印出 Mesos-consul 的版本。 |
refresh |
这指的是 Mesos 任务刷新之间的时间间隔。 |
Mesos-ip-order |
这是一个以逗号分隔的列表,控制 github.com/CiscoCloud/Mesos-consul 查找任务 IP 地址的顺序。有效选项为 netinfo 、Mesos 、Docker 和 host (默认值为 netinfo,Mesos,host )。 |
healthcheck |
这个选项用于启用 HTTP 端点的健康检查。当启用此标志时,它会在 127.0.0.1:24476 提供健康状态。 |
healthcheck-ip |
这是健康检查服务接口的 IP(默认值为 127.0.0.1 )。 |
healthcheck-port |
这是健康检查服务的端口(默认值为 24476 )。 |
consul-auth |
这是用于身份验证的用户名和密码(可选),由冒号分隔。 |
consul-ssl |
这会在与注册表通信时使用 HTTPS。 |
consul-ssl-verify |
在通过 SSL 连接时验证证书。 |
consul-ssl-cert |
提供 SSL 证书的路径,可用于身份验证注册表服务器。 |
consul-ssl-cacert |
这是 CA 证书文件的路径,包含一个或多个 CA 证书,可用于验证注册表服务器证书。 |
consul-token |
这是用于注册表 ACL 的令牌。 |
heartbeats-before-remove |
这是在任务从 Consul 中移除之前,注册失败的次数(默认值为 1 )。 |
zk* |
这是 ZooKeeper 中 Mesos 路径的位置,默认值为 zk://127.0.0.1:2181/Mesos 。 |
使用 HAProxy 进行负载均衡
HAProxy-Marathon-bridge 脚本随 Marathon 安装一起提供。你也可以使用 Marathon-lb 来实现相同的功能。这两个工具都会通过查看 Marathon 的 REST API 中正在运行的任务,来为 HAProxy 创建一个配置文件和一个轻量级的 TCP/HTTP 代理。
HAProxy-Marathon-bridge 是一个简单的脚本,提供最小的功能集,且对新手用户更易理解。后者,Marathon-lb,支持高级功能,如 SSL 卸载、基于 VHost 的负载均衡和粘性连接。
创建 HAProxy 和 Marathon 之间的桥接
首先,你需要从运行中的 Marathon 实例创建 HAProxy 配置,该实例默认运行在机器的8080
端口。你可以通过以下语法使用 HAProxy-Marathon-bridge 脚本来实现这一点:
$ ./bin/haproxy-Marathon-bridge localhost:8080 > /etc/haproxy/haproxy.cfg
请注意,这里我们指定了localhost:8080
,因为我们在同一台机器上运行了 Marathon 实例和 HAProxy。
一旦生成了 HAProxy 配置,你可以通过运行以下命令来简单地重新加载 HAProxy,而不会中断现有的连接:
$ haproxy -f haproxy.cfg -p haproxy.pid -sf $(cat haproxy.pid)
你可以使用典型的 cron 作业自动化配置生成和重新加载过程。如果在重新加载过程中由于某种原因,某个节点发生故障,HAProxy 的健康检查将检测到并停止向该节点发送进一步的流量。
你无需创建触发器来重新加载 HAProxy 配置。HAProxy-Marathon-bridge 脚本已经为你完成了这项工作。它包含 HAProxy 和一个每分钟触发的 cron 作业,用来从 Marathon 服务器拉取配置,并在检测到与上一个版本的变化时刷新 HAProxy。
你可以使用以下命令来实现:
$ ./bin/haproxy-Marathon-bridge install_haproxy_system localhost:8080
它将在/etc/haproxy-Marathon-bridge/Marathons
文件中为 Marathon 每一行添加 ping 命令,并将脚本安装在/usr/local/bin/haproxy-Marathon-bridge
。你可以在/etc/cron.d/haproxy-Marathon-bridge
目录下找到安装的 cron 作业,该作业将在根用户下触发。
Bamboo - 自动为 Mesos 加 Marathon 配置 HAProxy
Bamboo 作为 Web 守护进程运行,并自动为部署在 Mesos 和 Marathon 上的 Web 服务配置 HAProxy。
Bamboo 包含以下内容:
-
一个 Web UI,用于为每个 Marathon 应用程序配置 HAProxy 访问控制限制(ACL)规则。
-
一个 REST 端点来实现相同功能
-
基于你的模板的预配置 HAProxy 配置文件,你可以自定义自己的模板来启用 SSL 并设置 HAProxy 统计信息接口,或配置负载均衡策略。
-
如果 Marathon 应用配置了健康检查,则会有一个健康检查端点。
-
无状态守护进程,支持可扩展性和水平复制
-
无额外依赖(因为它是用 Golang 开发的)
-
与 StatsD 集成,监控配置重新加载事件
Bamboo 可以在每个 Mesos 从节点上与 HAProxy 一起部署。由于 Bamboo 主要用于部署在 Mesos 上的 Web 服务,因此服务发现就像连接到你为其分配了 ACL 规则的本地主机或域名一样简单。然而,你也可以将 HAProxy 和 Bamboo 部署在不同的机器上,这意味着你需要对 HAProxy 集群进行负载均衡。
以下截图显示了 Bamboo 和 HAProxy 通过 Marathon 与 Mesos 集群的交互:
你可以使用以下命令安装 Bamboo:
# Clone the github repository
$ git clone https://github.com/QubitProducts/bamboo
# Change the working directory
$ cd bamboo
# Install, (make sure you have installed go first)
$ go build bamboo.go; ./builder/build.sh
安装完成后,打开浏览器并访问你安装了 Bamboo 的任意一台机器上的端口 8000
,你将看到如下截图所示的 Web UI:
你可以通过点击 Marathon 应用程序右侧的编辑图标来配置 ACLs。
Bamboo 命令行接受一个 --config
选项,用于指定 JSON 应用配置文件的位置。你可以在 config 目录下找到示例配置文件模板;config/production.example.json
和 config/haproxy_template.cfg
就是其中的两个。现在,来看一下下面的代码:
{
// This is where you configure the Marathon instance
"Marathon": {
// Since we are running web applications, give the host:port to the applications
"Endpoint":"http://Marathon1:8080,http://Marathon2:8080,http://Marathon3:8080",
// Use the Marathon HTTP event streaming feature
"UseEventStream": true
},
"Bamboo": {
// Bamboo's HTTP address can be accessed by Marathon
// Used for Marathon HTTP callback and each Bamboo instance
// must provide a unique Endpoint that is directly addressable by Marathon
// (e.g., every server's IP address)
"Endpoint": "http://localhost:8000",
// Proxy setting information is stored in Zookeeper// This path is created by Bamboo, if it does not already exist
"Zookeeper": {
// Make sure that the same setting is used while running on the same ZK cluster
"Host": "zk01.example.com:2812,zk02.example.com:2812",
"Path": "/Marathon-haproxy/state",
"ReportingDelay": 5
}
}
// Make sure you are using absolute path on production
"HAProxy": {
"TemplatePath": "/var/bamboo/haproxy_template.cfg",
"OutputPath": "/etc/haproxy/haproxy.cfg",
"ReloadCommand": "haproxy -f /etc/haproxy/haproxy.cfg-p /var/run/haproxy.pid -D -sf $(cat /var/run/haproxy.pid)",
// A command that will validate the config before you run reload command.// '{{.}}' will be expanded to a temporary path that contains the config contents
"ReloadValidationCommand": "haproxy -c -f {{.}}",
// A command that will always be run after ReloadCommand, even if reload fails
"ReloadCleanupCommand": "exit 0"
},
// Enable or disable StatsD event tracking
"StatsD": {
"Enabled": false,
// StatsD or Graphite server host
"Host": "localhost:8125",
// StatsD namespace prefix -
// Label each node if you have multiple Bamboo instances
// by bamboo-server.production.n1.
"Prefix": "bamboo-server.production."
}
}
Bamboo 将以下环境变量映射到相应的 Bamboo 配置。你可以在 production.json
文件中使用这些配置:
环境变量 | 对应的配置项 |
---|---|
MARATHON_ENDPOINT |
Marathon.Endpoint |
MARATHON_USER |
Marathon.User |
MARATHON_PASSWORD |
Marathon.Password |
BAMBOO_ENDPOINT |
Bamboo.Endpoint |
BAMBOO_ZK_HOST |
Bamboo.Zookeeper.Host |
BAMBOO_ZK_PATH |
Bamboo.Zookeeper.Path |
HAPROXY_TEMPLATE_PATH |
HAProxy.TemplatePath |
HAPROXY_OUTPUT_PATH |
HAProxy.OutputPath |
HAPROXY_RELOAD_CMD |
HAProxy.ReloadCommand |
BAMBOO_DOCKER_AUTO_HOST |
当 Bamboo 容器启动时,这将把 BAMBOO_ENDPOINT 设置为 $HOST ,并且可以是任何值 |
STATSD_ENABLED |
StatsD.Enabled |
STATSD_PREFIX |
StatsD.Prefix |
STATSD_HOST |
StatsD.Host |
Netflix Fenzo 介绍
Netflix 最近开源了他们为 Apache Mesos 框架编写的调度器库,该库使用 Java 编写,支持调度优化和集群自动扩展。在写书时,Fenzo 已经开源,并且可以在官方的 Netflix OSS 套件仓库中找到,网址为:github.com/Netflix/Fenzo
开发像 Fenzo 这样的框架基本上有两个动机。与前面讨论的其他调度器和框架不同,构建 Fenzo 的原因是为了调度优化和根据使用情况自动扩展集群。
当你的集群处理的数据量时常变化时,预配置集群以应对峰值使用看起来很浪费,因为大部分时间资源会处于闲置状态。这正是根据负载自动扩展应用程序的主要原因——即,在集群资源达到峰值时提供更多机器,并在机器闲置时关闭这些机器。
扩展集群是一个较为简单的任务。您可以使用监控工具来观察资源利用率,当它超过阈值时,可以继续增加集群资源。另一方面,在缩减集群时,您需要识别您将要终止的机器上是否有长期运行的任务,以及终止这些机器是否会影响正在运行的任务。
目前,Fenzo 中的自动扩展基于以下两种策略:
-
阈值
-
资源短缺分析
在基于阈值的自动扩展中,用户可以根据主机组指定规则,如 EC2 自动扩展、GCE 自动扩展等。这些可以视为为计算密集型、网络密集型及其他工作负载创建主机组。这些规则使得新的任务可以迅速启动在预配置的空闲主机上。
在资源短缺分析的情况下,它首先计算完成待处理工作负载所需的主机数量。也可以将其视为一种预测性自动扩展系统,可以分析工作负载并启动新的主机以满足待处理的工作负载。这样的系统的一个例子是 Netflix 网站的 Scryer。
以下是一个图示,展示了 Fenzo 如何被 Apache Mesos 框架使用。Fenzo 本身包含一个任务调度器,提供调度核心,但并不直接与 Mesos 交互。该框架与 Mesos 交互,以获取新的资源报价并拉取任务状态更新。
PaaSTA 简介
Yelp 的 PaaSTA(平台即服务)分布式系统具有高可用性,用于构建、部署和运行使用容器(如 Docker 和 Apache Mesos)的服务。PaaSTA 由 Yelp 设计和开发,并且最近已开源。您可以通过以下网址查看开源仓库:github.com/yelp/paasta
这是一个供开发人员指定如何从 Git 仓库构建、部署、路由和监控代码的工具集。Yelp 已使用 PaaSTA 超过一年,以支持其生产级别的服务。如果您有严格的生产环境,例如 Yelp,要求许多小型微服务,并且在推出新代码时应无缝进行且不打扰生产系统,PaaSTA 最为适用。PaaSTA 帮助自动化整个过程。
它包括以下现有的开源组件:
-
Docker:用于容器化代码
-
Apache Mesos:用于执行和调度
-
Marathon:用于管理长期运行的应用程序
-
Chronos:用于调度目的
-
SmartStack:用于服务发现和注册
-
Sensu:用于监控和警报
-
Jenkins:用于持续构建和部署(这是可选的)
将所有这些组件集中在一起的原因之一是可重用性。你可以重用这些组件中的任何一个来解决分布式环境中的不同问题。
不同调度/管理框架的比较分析
本节将简要比较和介绍我们在本章中讨论的不同调度框架及其应用场景。
Marathon 是一个构建在 Mesos 上的 PaaS,确保即使集群中的部分机器出现故障,作业仍然能够永远运行。它可以无缝处理硬件和软件故障,确保应用程序始终在运行。这类框架在生产环境中非常有用,特别是当你的应用程序需要 24/7 全天候运行并始终可用时——例如,托管网站的 Web 服务器。在这种情况下,你可以将其作为 Marathon 应用程序进行部署,后者会处理所有这些方面。
Chronos 可以被视为典型 Linux cron 作业的分布式容错替代品,cron 作业通常用于启动定时任务、定期备份、检查系统健康状况等。Chronos 和 Marathon 都提供了 Web UI 和 REST 端点来管理作业。我们可以围绕这些工具编写包装脚本,自动化应用程序部署和作业调度,而不仅仅依赖 Web UI。
Aurora 和 Marathon 本质上非常相似,它们都是服务调度器。你所需要做的就是告诉 Aurora 或 Marathon 如何部署应用程序,它们会保持应用程序持续运行,不会出现故障。另一方面,Aurora 对于初学者来说有点难以安装和使用。与 Marathon 不同,Aurora 并不正式支持 REST 端点,但很快会推出。在此之前,Aurora 通过暴露 Thrift API 来进行通信,这意味着你需要在服务器上额外安装 Thrift 库。
Apache Aurora 旨在处理大规模的基础设施,如数据中心。一个典型的例子是 Twitter 上运行的集群,这些集群由成千上万的机器组成,成百上千的工程师用来进行开发和生产。
总结
在本章中,我们深入探讨了一些 Mesos 的重要框架,这些框架使得作业调度和负载均衡变得更加容易和高效。我们介绍了如 Marathon 和 Chronos 等框架,它们的 REST 端点,以及其他一些工具,如 HAProxy、Consul、Marathoner、Bamboo、Fenzo 和 PaaSTA。
在下一章中,我们将讨论系统管理员和 DevOps 专业人员如何使用 Ansible、Chef、Puppet、Salt、Terraform 和 CloudFormation 等标准工具来部署 Mesos 集群,并利用 Nagios 和 Satellite 进行监控。
第五章:Mesos 集群部署
本章讲解了如何使用系统管理员和 DevOps 工程师常用的标准部署和配置管理工具,轻松地设置和监控 Mesos 集群。我们将解释如何通过 Ansible、Puppet、SaltStack、Chef、Terraform 或 Cloudformation 设置 Mesos 集群的步骤,并介绍如何使用 Playa Mesos 设置测试环境。我们还会讨论一些标准监控工具,例如 Nagios 和 Satellite,可以用来监控集群。我们还将讨论在部署 Mesos 集群时遇到的一些常见问题以及相应的解决方法。
本章将涵盖以下主题:
-
使用以下方法部署和配置 Mesos 集群:
-
Ansible
-
Puppet
-
SaltStack
-
Chef
-
Terraform
-
Cloudformation
-
-
使用 Playa Mesos 创建测试环境
-
常见部署问题及解决方案
-
使用以下工具监控 Mesos 集群:
-
Nagios
-
Satellite
-
使用 Ansible 部署和配置 Mesos 集群
Ansible 是一种流行的基础设施自动化工具,当前广泛应用于系统管理员中,并且最近被 Red Hat 收购。节点通过安全外壳(SSH)进行管理,只需要 Python 支持。Ansible 已开源了许多剧本,包括我们将在本节中讨论的 ansible-mesos
剧本。
ansible-mesos
剧本可以用于安装和配置 Mesos 集群,并支持自定义的主节点和从节点设置选项。目前,它支持基于 Ubuntu 和 CentOS/Red Hat 操作系统的机器。ansible-mesos
剧本还支持设置特定的从节点执行器,因此可以与原生 Docker 支持一起运行。
安装 Ansible
安装 Ansible 只需要在单台机器上进行。它不需要数据库,也不需要持续运行守护进程。它通过 SSH 来管理集群,并要求机器上安装 Python(版本 2.6 或 2.7)。你甚至可以在笔记本电脑或个人计算机上安装 Ansible,远程管理其他机器。安装了 Ansible 的机器被称为控制机器。在编写本书时,Windows 机器尚不受支持。受控制的机器称为受管节点,需要控制机器的 SSH 访问权限,并且上面也需要安装 Python(版本 2.4 或更高)。
安装控制机器
我们可以在没有 root 权限的情况下运行 Ansible,因为它不需要安装任何额外的软件或数据库服务器。执行以下代码:
# Install python pip
$ sudo easy_install pip
# Install the following python modules for ansible
$ sudo pip install paramiko PyYAML Jinja2 httplib2 six
# Clone the repository
$ git clone git://github.com/ansible/ansible.git --recursive
# Change the directory
$ cd ansible
# Installation
$ source ./hacking/env-setup
如果一切顺利,我们可以在终端中看到以下输出,表示安装成功。之后,我们将能够在终端中使用 Ansible 命令。
默认情况下,Ansible 使用/etc/ansible/hosts
中的清单文件,该文件采用类似 INI 格式,可能如下所示:
mail.xyz.com
[webservers]
foo.xyz.com
bar.xyz.com
[dbservers]
one.xyz.com
two.xyz.com
three.xyz.com
这里,组名用括号表示,可用于对将被管理的系统进行分类。
我们还可以使用命令行选项-i
,将其指向不同的文件,而不是/etc/ansible/hosts
中找到的文件。
创建 ansible-mesos 设置
Ansible 执行由角色组成的剧本,针对的是如之前在 hosts 文件中描述的,按组组织的一组主机。更多详情,请访问frankhinek.com/create-ansible-playbook-on-github-to-build-mesos-clusters/
。
首先,让我们通过以下方式创建一个指向 Mesos 主节点和从节点的 hosts 文件:
$ cat hosts
[mesos_masters]
ec2-….compute-1.amazonaws.com zoo_id=1 ec2-….compute-1.amazonaws.com zoo_id=2
ec2-….compute-1.amazonaws.com zoo_id=3
[mesos_workers]
ec2-….compute-1.amazonaws.com
ec2-….compute-1.amazonaws.com
在这里,我们创建了两个组,分别命名为mesos_masters
和mesos_workers
,并列出了主节点和从节点的 IP 地址。对于mesos_masters
组,我们还需要指定 ZooKeeper ID,因为集群将以高可用性运行。
在接下来的步骤中,我们将看看如何使用 Ansible 在 hosts 文件中列出的机器上部署 Mesos:
-
创建一个
site.yml
文件,内容如下:-- # This playbook deploys the entire Mesos cluster infrastructure. # RUN: ansible-playbook --ask-sudo-pass -i hosts site.yml - name: deploy and configure the mesos masters hosts: mesos_masters sudo: True roles: - {role: mesos, mesos_install_mode: "master", tags: ["mesos-master"]} - name: deploy and configure the mesos slaves hosts: mesos_workers sudo: True roles: - {role: mesos, mesos_install_mode: "slave", tags: ["mesos-slave"]}
-
现在,我们可以创建一个适用于集群中所有主机的组变量文件,内容如下:
$ mkdir group_vars $ vim all
-
接下来,我们将在所有文件中放入以下内容:
--- # Variables here are applicable to all host groups mesos_version: 0.20.0-1.0.ubuntu1404 mesos_local_address: "{{ansible_eth0.ipv4.address}}" mesos_cluster_name: "XYZ" mesos_quorum_count: "2" zookeeper_client_port: "2181" zookeeper_leader_port: "2888" zookeeper_election_port: "3888" zookeeper_url: "zk://{{ groups.mesos_masters | join(':' + zookeeper_client_port + ',') }}:{{ zookeeper_client_port }}/mesos"
-
现在,我们可以为 Mesos 集群创建角色。首先,通过以下命令创建一个 roles 目录:
$ mkdir roles; cd roles
-
我们现在可以使用
ansible-galaxy
命令初始化该角色的目录结构,内容如下:$ ansible-galaxy init mesos
-
这将创建以下目录结构:
-
现在,修改
mesos/handlers/main.yml
文件,内容如下:--- # handlers file for mesos - name: Start mesos-master shell: start mesos-master sudo: yes - name: Stop mesos-master shell: stop mesos-master sudo: yes - name: Start mesos-slave shell: start mesos-slave sudo: yes - name: Stop mesos-slave shell: stop mesos-slave sudo: yes - name: Restart zookeeper shell: restart zookeeper sudo: yes - name: Stop zookeeper shell: stop zookeeper sudo: yes
-
接下来,按以下方式修改
mesos/tasks/main.yml
文件中的任务:--- # tasks file for mesos # Common tasks for all Mesos nodes - name: Add key for Mesosphere repository apt_key: url=http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=0xE56151BF state=present sudo: yes - name: Determine Linux distribution distributor shell: lsb_release -is | tr '[:upper:]' '[:lower:]' register: release_distributor - name: Determine Linux distribution codename command: lsb_release -cs register: release_codename - name: Add Mesosphere repository to sources list copy: content: "deb http://repos.mesosphere.io/{{release_distributor.stdout}} {{release_codename.stdout}} main" dest: /etc/apt/sources.list.d/mesosphere.list mode: 0644 sudo: yes # Tasks for Master, Slave, and ZooKeeper nodes - name: Install mesos package apt: pkg={{item}} state=present update_cache=yes with_items: - mesos={{ mesos_pkg_version }} sudo: yes when: mesos_install_mode == "master" or mesos_install_mode == "slave" - name: Set ZooKeeper URL # used for leader election amongst masters copy: content: "{{zookeeper_url}}" dest: /etc/mesos/zk mode: 0644 sudo: yes when: mesos_install_mode == "master" or mesos_install_mode == "slave" # Tasks for Master nodes - name: Disable the Mesos Slave service copy: content: "manual" dest: /etc/init/mesos-slave.override mode: 0644 sudo: yes when: mesos_install_mode == "master" - name: Set Mesos Master hostname copy: content: "{{mesos_local_address}}" dest: /etc/mesos-master/hostname mode: 0644 sudo: yes when: mesos_install_mode == "master" - name: Set Mesos Master ip copy: content: "{{mesos_local_address}}" dest: /etc/mesos-master/ip mode: 0644 sudo: yes when: mesos_install_mode == "master" - name: Set Mesos Master Cluster name copy: content: "{{mesos_cluster_name}}" dest: /etc/mesos-master/cluster mode: 0644 sudo: yes when: mesos_install_mode == "master" - name: Set Mesos Master quorum count copy: content: "{{mesos_quorum_count}}" dest: /etc/mesos-master/quorum mode: 0644 sudo: yes when: mesos_install_mode == "master" # Tasks for Slave nodes - name: Disable the Mesos Master service copy: content: "manual" dest: /etc/init/mesos-master.override mode: 0644 sudo: yes when: mesos_install_mode == "slave" - name: Disable the ZooKeeper service copy: content: "manual" dest: /etc/init/zookeeper.override mode: 0644 sudo: yes notify: - Stop zookeeper when: mesos_install_mode == "slave" - name: Set Mesos Slave hostname copy: content: "{{mesos_local_address}}" dest: /etc/mesos-slave/hostname mode: 0644 sudo: yes when: mesos_install_mode == "slave" - name: Set Mesos Slave ip copy: content: "{{mesos_local_address}}" dest: /etc/mesos-slave/ip mode: 0644 sudo: yes when: mesos_install_mode == "slave" - name: Set Mesos Slave ip copy: content: "{{mesos_local_address}}" dest: /etc/mesos-slave/ip mode: 0644 sudo: yes when: mesos_install_mode == "slave" - name: Set Mesos Slave isolation copy: content: "cgroups/cpu,cgroups/mem" dest: /etc/mesos-slave/isolation mode: 0644 sudo: yes notify: - Start mesos-slave when: mesos_install_mode == "slave" # Tasks for ZooKeeper nodes only - name: Create zookeeper config file template: src=zoo.cfg.j2 dest=/etc/zookeeper/conf/zoo.cfg sudo: yes when: mesos_install_mode == "master" - name: Create zookeeper myid file template: src=zoo_id.j2 dest=/etc/zookeeper/conf/myid sudo: yes notify: - Restart zookeeper - Start mesos-master when: mesos_install_mode == "master"
这是配置集群中 Mesos 主从机器的标准模板。该文件还指定了安装 ZooKeeper 等组件所需的各种配置。步骤如下:
-
按如下方式创建 ZooKeeper 配置模板:
$ vim mesos/templates/zoo.cfg.j2
-
然后,添加以下内容:
tickTime=2000 dataDir=/var/lib/zookeeper/ clientPort={{ zookeeper_client_port }} initLimit=5 syncLimit=2 {% for host in groups['mesos_masters'] %} server.{{ hostvars[host].zoo_id }}={{ host }}:{{ zookeeper_leader_port }}:{{ zookeeper_election_port }} {% endfor %}
-
接下来,输入以下命令:
$ vim mesos/templates/zoo_id.j2
-
最后,添加以下内容:
{{ zoo_id }}
现在,我们可以运行这个剧本,将 Mesos 部署到 hosts 文件中列出的机器上。我们只需要更改 hosts 文件中的 IP 地址,就能在其他机器上部署。
使用 Puppet 部署和配置 Mesos 集群
这一部分将主要介绍如何使用 Puppet 配置管理工具,结合 ZooKeeper 和 Mesos 模块,从以下仓库部署 Mesos 集群:
Puppet 是一款开源的配置管理工具,支持在 Windows、Linux 和 Mac OS 上运行。Puppet Labs 由 Luke Kanies 于 2005 年创立,他也开发了 Puppet。Puppet 是用 Ruby 编写的,并在版本 2.7.0 之前作为 GNU 通用公共许可证(GPL)下的自由软件发布,从那时起采用 Apache License 2.0。通过使用 Puppet,系统管理员可以自动化他们需要定期执行的标准任务。有关 Puppet 的更多信息,请访问以下位置:
代码将按照配置文件和角色模式进行组织,节点数据将通过 Hiera 存储。Hiera 是一个 Puppet 工具,用于执行配置数据的键/值查找。它允许在 Puppet 中对数据进行层次化配置,而这在原生 Puppet 代码中是很难实现的。此外,它作为配置数据和代码的分隔器。
在本模块结束时,你将拥有一个高度可用的 Mesos 集群,包含三个主节点和三个从节点。此外,Marathon 和 Chronos 也将以相同的方式部署。
我们可以结合多个 Puppet 模块来管理 Mesos 和 ZooKeeper。让我们执行以下步骤:
-
首先,创建一个包含以下内容的
Puppetfile
:forge 'http://forge.puppetlabs.com' mod 'apt', :git => 'git://github.com/puppetlabs/puppetlabs-apt.git', :ref => '1.7.0' mod 'concat', :git => 'https://github.com/puppetlabs/puppetlabs-concat', :ref => '1.1.2' mod 'datacat', :git => 'https://github.com/richardc/puppet-datacat', :ref => '0.6.1' mod 'java', :git => 'https://github.com/puppetlabs/puppetlabs-java', :ref => '1.2.0' mod 'mesos', :git => 'https://github.com/deric/puppet-mesos', :ref => 'v0.5.2' mod 'stdlib', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib', :ref => '4.5.1' mod 'zookeeper', :git => 'https://github.com/deric/puppet-zookeeper', :ref => 'v0.3.5'
现在,我们可以为 Mesos 主节点和从节点编写配置文件和角色模式。在主节点上,还将包括管理 ZooKeeper、Marathon 和 Chronos。
-
为主节点创建以下角色:
class role::mesos::master { include profile::zookeeper include profile::mesos::master # Mesos frameworks include profile::mesos::master::chronos include profile::mesos::master::marathon }
-
接下来,为从节点创建以下角色:
class role::mesos::slave { include profile::mesos::slave }
现在,我们可以继续创建与之前在角色中列出的 include 语句匹配的可重用配置文件。这些配置文件将包含对 Mesos 和 ZooKeeper 模块的调用以及我们需要管理的任何其他资源。可以将角色视为业务逻辑,而将配置文件视为实际的实现。
-
为 ZooKeeper 创建以下配置文件:
class profile::zookeeper { include ::java class { '::zookeeper': require => Class['java'], } }
-
为 Mesos 主节点创建以下配置文件:
class profile::mesos::master { class { '::mesos': repo => 'mesosphere', } class { '::mesos::master': env_var => { 'MESOS_LOG_DIR' => '/var/log/mesos', }, require => Class['profile::zookeeper'], } }
-
接下来,为 Mesos 从节点创建以下配置文件:
class profile::mesos::slave { class { '::mesos': repo => 'mesosphere', } class { '::mesos::slave': env_var => { 'MESOS_LOG_DIR' => '/var/log/mesos', }, } }
这些是启动 Mesos 集群所需的基本内容。为了管理 Chronos 和 Marathon,还需要包含以下配置文件。
-
按照以下方式创建 Chronos 的配置文件:
class profile::mesos::master::chronos { package { 'chronos': ensure => '2.3.2-0.1.20150207000917.debian77', require => Class['profile::mesos::master'], } service { 'chronos': ensure => running, enable => true, require => Package['chronos'], } }
-
现在,通过以下代码创建 Marathon 的配置文件:
class profile::mesos::master::marathon { package { 'marathon': ensure => '0.7.6-1.0', require => Class['profile::mesos::master'], } service { 'marathon': ensure => running, enable => true, require => Package['marathon'], } }
到目前为止,角色和配置文件中并没有包含我们将用于设置集群的机器的信息。这些信息将通过 Hiera 提供。主节点的 Hiera 数据大致如下所示:
--- classes: - role::mesos::master mesos::master::options: quorum: '2' mesos::zookeeper: 'zk://master1:2181,master2:2181,master3:2181/mesos' zookeeper::id: 1 zookeeper::servers: ['master1:2888:3888', 'master2:2888:3888', 'master3:2888:3888']
由于我们正在设置一个高度可用的集群,因此主节点的名称分别为 master 1、master 2 和 master 3。
-
从节点的 Hiera 数据大致如下所示:
--- classes: - role::mesos::slave mesos::slave::checkpoint: true mesos::zookeeper: 'zk://master1:2181,master2:2181,master3:2181/mesos'
现在,我们可以在每台机器上启动 Puppet 运行,来安装和配置 Mesos、ZooKeeper、Chronos 和 Marathon。
模块的安装与任何 Puppet 模块相同,步骤如下:
$ puppet module install deric-mesos
一旦执行成功,我们可以预期 Mesos 包会被安装,并且 mesos-master
服务会在集群中配置好。
使用 SaltStack 部署和配置 Mesos 集群
SaltStack 平台,或称 Salt,是一个基于 Python 的开源配置管理软件和远程执行引擎。本模块解释了如何使用 SaltStack 在生产环境中安装一个包含 Marathon 和其他一些工具的 Mesos 集群。SaltStack 是 Puppet、Ansible、Chef 等的替代方案。与其他工具类似,它用于自动化在多个服务器上部署和配置软件。SaltStack 架构由一个节点作为 SaltStack 主节点,以及其他作为 minion(从节点)的节点组成。还有两种不同的角色:一个主节点角色用于执行集群操作,一个从节点角色用于运行 Docker 容器。
以下软件包将为主节点角色安装:
-
ZooKeeper
-
Mesos 主节点
-
Marathon
-
Consul
从节点角色将安装以下软件包:
-
Mesos 从节点
-
Docker
-
cAdvisor(用于将指标导出到 Prometheus)
-
Registrator(用于将服务注册到 Consul)
-
Weave(为容器之间提供覆盖网络)
现在,让我们看看这些组件在集群中的样子。下图显示了集群中所有这些组件的连接方式(来源:github.com/Marmelatze/saltstack-mesos-test
):
SaltStack 安装
我们需要安装 Salt-Master 来协调所有的 Salt-Minions。SaltStack 要求主节点数量为奇数。这些主节点中的一个可以作为 Salt-Master,其余的将成为 minions。让我们按照这里提到的步骤安装 SaltStack:
-
执行以下命令设置主节点和 minion:
$ curl -L https://bootstrap.saltstack.com -o install_salt.sh $ sudo sh install_salt.sh -U -M -P -A localhost #Clone the repository to /srv/salt this is where the configurations are kept. $ sudo git clone https://github.com/Marmelatze/saltstack-mesos-test /srv/salt
-
编辑
/etc/salt/master
文件并按如下方式更改配置:file_roots: base: - /srv/salt/salt # ... pillar_roots: base: - /srv/salt/pillar
现在重启主节点:
$ sudo service salt-master restart
-
通过以下代码编辑位于
/etc/salt/minion
的 minion 配置文件:# ... mine_interval: 5 mine_functions: network.ip_addrs: interface: eth0 zookeeper: - mine_function: pillar.get - zookeeper
-
现在,通过执行以下代码编辑位于
/etc/salt/grains
的salt-grains
文件:# /etc/salt/grains # Customer-Id this host is assigned to (numeric)- customer_id: 0 # ID of this host. host_id: ID # ID for zookeeper, only needed for masters. zk_id: ID # Available roles are master & slave. Node can use both. roles: - master - slave
-
然后,将 ID 替换为从 1 开始的数字值;这个 ID 类似于我们之前使用的 ZooKeeper ID。
现在,通过以下命令重启 minion:
$ sudo service salt-minion restart
-
公钥认证用于 minion 与主节点之间的认证。执行以下命令进行认证:
$ sudo salt-key -A
-
完成前面的步骤后,我们可以使用以下命令运行 SaltStack:
$ sudo salt '*' state.highstate
如果一切执行成功,则 Mesos 服务将在集群中启动并运行。
使用 Chef 部署和配置 Mesos 集群
Chef 既是一个公司的名字,也是一个配置管理工具的名称,它是用 Ruby 和 Erlang 编写的。Chef 使用纯 Ruby 领域特定语言(DSL)编写系统配置“配方”。本模块将解释如何使用 Chef cookbook 安装和配置 Apache Mesos 的主从节点。Chef 是一个配置管理工具,用于自动化大规模的服务器和软件应用部署。我们假设读者已经熟悉 Chef。以下代码库将作为参考:
github.com/everpeace/cookbook-mesos
本书编写时的 Chef cookbook 版本支持 Ubuntu 和 CentOS 操作系统。CentOS 版本为实验性版本,不建议在生产环境中使用。需要 Ubuntu 14.04 或更高版本才能使用 cgroups 隔离器或 Docker 容器功能。只有 Mesos 0.20.0 及更高版本支持 Docker 容器化。
这个 cookbook 支持两种安装方式——即,从源代码构建 Mesos 和从 Mesosphere 包构建。默认情况下,此 cookbook 是从源代码构建 Mesos 的。可以通过设置以下类型变量在源代码构建和 Mesosphere 之间切换:
node[:mesos][:type]
配方
以下是此 cookbook 用于安装和配置 Mesos 的配方:
-
mesos::default
:根据之前讨论的类型变量,这将使用源代码或 Mesosphere 配方安装 Mesos。 -
mesos::build_from_source
:这将以通常的方式安装 Mesos——即,从 GitHub 下载 zip 文件,配置,make,并安装。 -
mesos::mesosphere
:此变量使用 Mesosphere 的mesos
包安装 Mesos。与此同时,我们可以使用以下变量来安装 ZooKeeper 包。node[:mesos][:mesosphere][:with_zookeeper]
-
mesos::master
:此配置项用于配置 Mesos 主节点和集群部署的配置文件,并使用mesos-master
来启动服务。以下是与这些配置相关的变量:-
node[:mesos][:prefix]/var/mesos/deploy/masters
-
node[:mesos][:prefix]/var/mesos/deploy/slaves
-
node[:mesos][:prefix]/var/mesos/deploy/mesos-deploy-env.sh
-
node[:mesos][:prefix]/var/mesos/deploy/mesos-master-env.sh
-
如果我们选择mesosphere
作为构建类型,则默认的 ":" 前缀属性位置将是/usr/local
,因为来自 Mesosphere 的软件包将 Mesos 安装在这个目录下。此配方还在以下位置配置了 upstart 文件:
-
/etc/mesos/zk
-
/etc/defaults/mesos
-
/etc/defaults/mesos-master
配置 mesos-master
mesos-master
命令行参数可用于配置node[:mesos][:master]
属性。以下是一个示例:
node[:mesos][:master] = {
:port => "5050",
:log_dir => "/var/log/mesos",
:zk => "zk://localhost:2181/mesos",
:cluster => "MesosCluster",
:quorum => "1"
}
mesos-master
命令将使用配置中给定的选项进行调用,具体如下:
mesos-master --zk=zk://localhost:2181/mesos --port=5050 --log_dir=/var/log/mesos --cluster=MesosCluster
mesos::slave
命令为 Mesos 从节点提供配置并启动 mesos-slave
实例。我们可以使用以下变量来指向 mesos-slave-env.sh
文件:
node[:mesos][:prefix]/var/mesos/deploy/mesos-slave-env.sh
mesos-slave
的 upstart 配置文件如下:
-
/etc/mesos/zk
-
/etc/defaults/mesos
-
/etc/defaults/mesos-slave
配置 mesos-slave
mesos-slave
命令行选项可以通过 node[:mesos][:slave]
哈希值进行配置。下面是一个配置示例:
node[:mesos][:slave] = {
:master => "zk://localhost:2181/mesos",
:log_dir => "/var/log/mesos",
:containerizers => "docker,mesos",
:isolation => "cgroups/cpu,cgroups/mem",
:work_dir => "/var/run/work"
}
mesos-slave
命令的调用方式如下:
mesos-slave --master=zk://localhost:2181/mesos --log_dir=/var/log/mesos --containerizers=docker,mesos --isolation=cgroups/cpu,cgroups/mem --work_dir=/var/run/work
现在,让我们来看看如何将这些内容结合在一个 vagrant 文件中并启动一个独立的 Mesos 集群。创建一个包含以下内容的 Vagrantfile
:
# -*- mode: ruby -*-
# vi: set ft=ruby:
# vagrant plugins required:
# vagrant-berkshelf, vagrant-omnibus, vagrant-hosts
Vagrant.configure("2") do |config|
config.vm.box = "Official Ubuntu 14.04 daily Cloud Image amd64 (Development release, No Guest Additions)"
config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
# config.vm.box = "chef/centos-6.5"
# enable plugins
config.berkshelf.enabled = true
config.omnibus.chef_version = :latest
# if you want to use vagrant-cachier,
# please activate below.
config.cache.auto_detect = true
# please customize hostname and private ip configuration if you need it.
config.vm.hostname = "mesos"
private_ip = "192.168.1.10"
config.vm.network :private_network, ip: private_ip
config.vm.provision :hosts do |provisioner|
provisioner.add_host private_ip , [ config.vm.hostname ]
end
# for mesos web UI.
config.vm.network :forwarded_port, guest: 5050, host: 5050
config.vm.provider :virtualbox do |vb|
vb.name = 'cookbook-mesos-sample-source'
# Use VBoxManage to customize the VM. For example, to change memory:
vb.customize ["modifyvm", :id, "--memory", "#{1024*4}"]
vb.customize ["modifyvm", :id, "--cpus", "2"]
end
config.vm.provision :shell do |s|
s.path = "scripts/populate_sshkey.sh"
s.args = "/home/vagrant vagrant"
end
# mesos-master doesn't create its work_dir.
config.vm.provision :shell, :inline => "mkdir -p /tmp/mesos"
# Mesos master depends on zookeeper emsamble since 0.19.0
# for Ubuntu
config.vm.provision :shell, :inline => "apt-get update && apt-get install -y zookeeper zookeeperd zookeeper-bin"
# For CentOS
# config.vm.provision :shell, :inline => <<-EOH
# rpm -Uvh http://archive.cloudera.com/cdh4/one-click-install/redhat/6/x86_64/cloudera-cdh-4-0.x86_64.rpm
# yum install -y -q curl
# curl -sSfL http://archive.cloudera.com/cdh4/redhat/6/x86_64/cdh/RPM-GPG-KEY-cloudera --output /tmp/cdh.key
# rpm --import /tmp/cdh.key
# yum install -y -q java-1.7.0-openjdk zookeeper zookeeper-server
# service zookeeper-server init
# service zookeeper-server start
# EOH
config.vm.provision :chef_solo do |chef|
# chef.log_level = :debug
chef.add_recipe "mesos"
chef.add_recipe "mesos::master"
chef.add_recipe "mesos::slave"
# You may also specify custom JSON attributes:
chef.json = {
:java => {
'install_flavor' => "openjdk",
'jdk_version' => "7",
},
:maven => {
:version => "3",
"3" => {
:version => "3.0.5"
},
:mavenrc => {
:opts => "-Dmaven.repo.local=$HOME/.m2/repository -Xmx384m -XX:MaxPermSize=192m"
}
},
:mesos => {
:home => "/home/vagrant",
# command line options for mesos-master
:master => {
:zk => "zk://localhost:2181/mesos",
:log_dir => "/var/log/mesos",
:cluster => "MesosCluster",
:quorum => "1"
},
# command line options for mesos-slave
:slave =>{
:master => "zk://localhost:2181/mesos",
:isolation => "posix/cpu,posix/mem",
:log_dir => "/var/log/mesos",
:work_dir => "/var/run/work"
},
# below ip lists are for mesos-[start|stop]-cluster.sh
:master_ips => ["localhost"],
:slave_ips => ["localhost"]
}
}
end
end
现在,键入以下命令以启动一个完全功能的独立 Mesos 集群:
$ vagrant up
使用 Terraform 部署和配置 Mesos 集群
Terraform 是一个基础设施构建、变更和版本控制工具,用于安全高效地处理现有的流行服务以及定制的内部解决方案,属于 HashiCorp 公司并使用 Go 语言编写。在本模块中,我们将首先讨论如何安装 Terraform,然后再讨论如何使用 Terraform 启动一个 Mesos 集群。
安装 Terraform
前往 www.terraform.io/downloads.html
,下载适合您平台的版本,并解压,如下所示:
$ wget https://releases.hashicorp.com/terraform/0.6.9/terraform_0.6.9_linux_amd64.zip
$ unzip terraform_0.6.9_linux_amd64.zip
您会注意到,解压后,terraform
压缩包中的文件是一堆二进制文件,类似于以下内容:
现在,将目录路径添加到 PATH
变量中,这样您就可以从任何目录访问 terraform
命令。
如果一切顺利,当您在终端执行 terraform
命令时,您将看到 terraform
的使用:
在 Google Cloud 上使用 Terraform 启动 Mesos 集群
要在 Google Cloud Engine (GCE) 上使用 Terraform 启动 Mesos 集群,您需要一个 JSON 密钥文件进行身份验证。前往 console.developers.google.com
,然后通过导航到 Credentials | Service 账户生成一个新的 JSON 密钥。一个文件将被下载,稍后将用于启动虚拟机。
现在,我们可以为 Mesos 集群创建一个 terraform
配置文件。创建一个包含以下内容的 mesos.tf
文件:
module "mesos" {
source = "github.com/ContainerSolutions/terraform-mesos"
account_file = "/path/to/your/downloaded/key.json"
project = "your google project"
region = "europe-west1"
zone = "europe-west1-d"
gce_ssh_user = "user"
gce_ssh_private_key_file = "/path/to/private.key"
name = "mymesoscluster"
masters = "3"
slaves = "5"
network = "10.20.30.0/24"
domain = "example.com"
image = "ubuntu-1404-trusty-v20150316"
mesos_version = "0.22.1"
}
正如我们所看到的,其中一些配置可以用来控制版本,例如:
-
mesos_version
:这指定了 Mesos 的版本 -
image
:这是 Linux 系统镜像
现在,执行以下命令开始部署:
# Download the modules
$ terraform get
# Create a terraform plan and save it to a file
$ terraform plan -out my.plan -module-depth=1
# Create the cluster
$ terraform apply my.plan
销毁集群
我们可以执行以下命令来销毁集群:
$ terraform destroy
使用 Cloudformation 部署和配置 Mesos 集群
在本模块中,我们将讨论如何使用 Cloudformation 脚本在 Amazon AWS 上启动 Mesos 集群。在开始之前,请确保在希望启动集群的机器上安装并配置了 aws-cli。从以下存储库查看说明来设置 aws-cli:
在设置 aws-cli 后,我们需要的下一步是 cloudformation-zookeeper
模板用于管理由 Exhibitor 管理的 ZooKeeper 集群。
设置 cloudformation-zookeeper
我们首先需要克隆以下存储库,因为它包含了具有参数、描述符和配置值的 JSON 文件:
$ git clone https://github.com/mbabineau/cloudformation-zookeeper
登录 AWS 控制台,并为安全组打开以下端口:
-
SSH 端口:22
-
ZooKeeper 客户端端口:2181
-
Exhibitor HTTP 端口:8181
现在我们可以使用 aws-cli
命令来启动集群:
aws cloudformation create-stack \
--template-body file://cloudformation-zookeeper/zookeeper.json \
--stack-name <stack> \
--capabilities CAPABILITY_IAM \
--parameters \
ParameterKey=KeyName,ParameterValue=<key> \
ParameterKey=ExhibitorS3Bucket,ParameterValue=<bucket> \
ParameterKey=ExhibitorS3Region,ParameterValue=<region> \
ParameterKey=ExhibitorS3Prefix,ParameterValue=<cluster_name> \
ParameterKey=VpcId,ParameterValue=<vpc_id> \
ParameterKey=Subnets,ParameterValue='<subnet_id_1>\,<subnet_id_2>' \
ParameterKey=AdminSecurityGroup,ParameterValue=<sg_id>
使用 cloudformation-mesos
你可以从以下网址克隆项目存储库:
$ git clone https://github.com/mbabineau/cloudformation-mesos
该项目主要包括三个 JSON 格式的模板,定义了参数、配置和描述,如下所示:
-
mesos-master.json
:用于启动一组运行 Marathon 的 Mesos 主节点的模板,在自动扩展组中运行。 -
mesos-slave.json
:与前述相似,这会在自动扩展组中启动一组 Mesos 从节点。 -
mesos.json
:此文件从先前列出的对应模板创建mesos-master
和mesos-slave
两个堆栈。这是用于启动 Mesos 集群的通用模板。
master.json
中列出了一些可配置属性:
"MasterInstanceCount" : {
"Description" : "Number of master nodes to launch",
"Type" : "Number",
"Default" : "1"
},
"MasterQuorumCount" : {
"Description" : "Number of masters needed for Mesos replicated log registry quorum (should be ceiling(<MasterInstanceCount>/2))",
"Type" : "Number",
"Default" : "1"
},
MasterInstanceCount
和 MasterQuorumCount
控制集群中所需的主节点数量。查看以下代码:
"SlaveInstanceCount" : {
"Description" : "Number of slave nodes to launch",
"Type" : "Number",
"Default" : "1"
},
同样,SlaveInstanceCount
用于控制集群中从节点实例的数量。
Cloudformation 更新自动扩展组,Mesos 通过增加和减少节点来透明地处理扩展。详见:
"SlaveInstanceType" : {
"Description" : "EC2 instance type",
"Type" : "String",
"Default" : "t2.micro",
"AllowedValues" : ["t2.micro", "t2.small", "t2.medium",
"m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge","c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge", "c4.large", "c4.xlarge", "c4.2xlarge", "c4.4xlarge", "c4.8xlarge","r3.large", "r3.xlarge", "r3.2xlarge", "r3.4xlarge", "r3.8xlarge","i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge","hs1.8xlarge", "g2.2xlarge"],
"ConstraintDescription" : "must be a valid, HVM-compatible EC2 instance type."
},
我们还可以使用 InstanceType
配置属性来控制 AWS 云中主节点 (MasterInstanceType
) 和从节点 (SlaveInstanceType
) 的机器大小。
再次,在我们之前创建的安全组中,为 Mesos 通信打开以下端口:
-
Mesos 主节点端口:5050
-
Marathon 端口:8080
在 mesos-master.json
和 mesos-slave.json
文件中配置值后,我们可以使用以下命令将这些文件上传到 S3:
$ aws s3 cp mesos-master.json s3://cloudformationbucket/
$ aws s3 cp mesos-slave.json s3://cloudformationbucket/
现在我们可以使用 aws-cli 命令来启动我们的 Mesos 集群:
aws cloudformation create-stack \
--template-body file://mesos.json \
--stack-name <stack> \
--capabilities CAPABILITY_IAM \
--parameters \
ParameterKey=KeyName,ParameterValue=<key> \
ParameterKey=ExhibitorDiscoveryUrl,ParameterValue=<url> \
ParameterKey=ZkClientSecurityGroup,ParameterValue=<sg_id> \
ParameterKey=VpcId,ParameterValue=<vpc_id> \
ParameterKey=Subnets,ParameterValue='<subnet_id_1>\,<subnet_id_2>' \
ParameterKey=AdminSecurityGroup,ParameterValue=<sg_id> \
ParameterKey=MesosMasterTemplateUrl,ParameterValue=https://s3.amazonaws.com/cloudformationbucket/mesos-master.json \
ParameterKey=MesosSlaveTemplateUrl,ParameterValue=https://s3.amazonaws.com/cloudformationbucket/mesos-slave.json
使用 Playa Mesos 创建测试环境
使用 Playa Mesos 可快速创建 Apache Mesos 测试环境。你可以从以下网址查看官方存储库:
github.com/mesosphere/playa-mesos
。
在使用此项目之前,请确保在您的环境中安装并配置了 VirtualBox、Vagrant 和包含预装 Mesos 和 Marathon 的 Ubuntu 镜像。
安装
按照以下说明开始使用 Playa Mesos
-
安装 VirtualBox:您可以访问
www.virtualbox.org/wiki/Downloads
下载并安装适合您环境的版本。 -
安装 Vagrant:您可以参考 第四章 中 安装 Aurora 部分所描述的方法来开始使用 Vagrant。
-
Playa:您可以使用以下命令克隆仓库:
$ git clone https://github.com/mesosphere/playa-mesos # Make sure the tests are passed $ cd playa-mesos $ bin/test # Start the environment $ vagrant up
如果一切顺利,我们可以通过将浏览器指向 10.141.141.10:5050
来查看 Mesos master Web UI,并通过 10.141.141.10:8080
查看 Marathon Web UI。
一旦机器启动,我们可以使用 ssh
登录到机器,命令如下:
$ vagrant ssh
我们还可以使用以下命令来停止并终止测试环境:
# Halting the machine
$ vagrant halt
#Destroying the VM
$ vagrant destroy
除此之外,如果您希望稍微调整配置,您可以通过编辑位于 playa-mesos
仓库根目录的 config.json
文件来进行。
我们可以在 config.json
文件中使用以下配置属性:
-
platform
:这是虚拟化平台。我们将使用 VirtualBox,虽然 VMware Fusion 和 VMware Workstation 也可以使用。 -
box_name
:这是 Vagrant 实例的名称。 -
base_url
:这是 Vagrant 镜像存储的基本 URL。 -
ip_address
:这是虚拟机的私有网络 IP 地址。 -
mesos_release
:此参数是可选的,指定 Mesos 的版本。它应为apt-cache policy mesos
返回的完整字符串。例如:0.22.1-1.0.ubuntu1404
。 -
vm_ram
:这是分配给 Vagrant 虚拟机的内存。 -
vm_cpus
:这是分配给 Vagrant 虚拟机的核心数量。
我们可以通过将所有这些配置参数放在一起,创建一个示例 config.json
文件,文件内容如下:
{
"platform": "virtualbox",
"box_name": "playa_mesos_ubuntu_14.04_201601041324",
"base_url": "http://downloads.mesosphere.io/playa-mesos",
"ip_address": "10.141.141.10",
"vm_ram": "2048",
"vm_cpus": "2"
}
如您所见,我们分配了 2040 MB 的内存和两个核心,且机器将运行在 10.141.141.10
的 IP 地址上。
使用 Nagios 监控 Mesos 集群
监控是保持基础设施正常运行的关键部分。Mesos 与现有的监控解决方案集成良好,并且有适用于大多数监控解决方案的插件,例如 Nagios。本模块将指导您如何在集群上安装 Nagios,并启用监控功能,在集群出现故障时通过电子邮件向您发送警报。
安装 Nagios 4
在安装 Nagios 之前,我们需要做的第一件事是为 Nagios 进程添加一个 Nagios 用户,该进程可以运行并发送警报。我们可以通过执行以下命令来创建一个新用户和一个新用户组:
$ sudo useradd nagios
$ sudo groupadd nagcmd
$ sudo usermod -a -G nagcmd nagios
在这里,我们创建了一个用户 Nagios 和一个用户组nagcmd
,该组分配给 Nagios 用户,如前面列出的第三个命令所示。
现在,使用以下命令安装依赖包:
$ sudo apt-get install build-essential libgd2-xpm-dev openssl libssl-dev xinetd apache2-utils unzip
安装依赖项并添加用户后,我们可以开始通过执行以下命令下载并安装nagios
:
#Download the nagios archive.
$ wget https://assets.nagios.com/downloads/nagioscore/releases/nagios-4.1.1.tar.gz
# Extract the archive.
$ tar xvf nagios-*.tar.gz
# Change the working directory to nagios
$ cd nagios*
# Configure and build nagios
$ ./configure --with-nagios-group=nagios --with-command-group=nagcmd --with-mail=/usr/sbin/sendmail
$ make all
# Install nagios, init scripts and sample configuration file
$ sudo make install
$ sudo make install-commandmode
$ sudo make install-init
$ sudo make install-config
$ sudo /usr/bin/install -c -m 644 sample-config/httpd.conf /etc/apache2/sites-available/nagios.conf
一旦nagios
安装完成,我们可以通过下载并构建来安装nagios
插件,执行以下命令:
$ wget http://nagios-plugins.org/download/nagios-plugins-2.1.1.tar.gz
$ tar xvf nagios-plugins-*.tar.gz
$ cd nagios-plugins-*
$ ./configure --with-nagios-user=nagios --with-nagios-group=nagios --with-openssl
$ make
$ sudo make install
插件安装完成后,我们可以安装NRPE(Nagios 远程插件执行器)以从远程机器获取状态更新。可以通过执行以下命令进行安装:
$ wget http://downloads.sourceforge.net/project/nagios/nrpe-2.x/nrpe-2.15/nrpe-2.15.tar.gz
$ tar xf nrpe*.tar.gz
$ cd nrpe*
$ ./configure --enable-command-args --with-nagios-user=nagios --with-nagios-group=nagios --with-ssl=/usr/bin/openssl --with-ssl-lib=/usr/lib/x86_64-linux-gnu
$ make all
$ sudo make install
$ sudo make install-xinetd
$ sudo make install-daemon-config
出于安全原因,请编辑/etc/xinetd.d/nrpe
文件,内容如下:
only_from = 127.0.0.1 10.132.224.168
用我们的nagios
服务器 IP 地址替换文件中的 IP 地址,以确保只有我们的nagios
服务器可以进行远程调用。完成后,保存文件并退出,然后执行以下命令重启xintend
服务:
$ sudo service xinetd restart
现在nagios
已安装,我们可以通过编辑以下文件来配置接收通知的联系电子邮件地址:
$ sudo vi /usr/local/nagios/etc/objects/contacts.cfg
找到并将以下行替换为您自己的电子邮件地址:
email nagios@localhost ; << ** Change this to your email address **
通过执行以下命令,添加一个用户到nagios
,以便我们可以从浏览器登录并查看活动。在这里,我们使用nagiosadmin
作为用户名和密码,如下所示:
$ sudo htpasswd -c /usr/local/nagios/etc/htpasswd.users nagiosadmin
现在,通过执行以下命令重启nagios
服务:
$ sudo service nagios restart
现在,我们可以通过从浏览器访问以下 URL 登录nagios
管理面板:
http://MachineIP/nagios
MachineIP
是我们安装了nagios
的机器的 IP 地址,它会提示您输入认证表单,您可以在其中输入用户名和密码nagiosadmin
。
认证通过后,您将进入 Nagios 主页。要查看 Nagios 监控的主机,点击左侧的Hosts链接,如下图所示(来源:www.digitalocean.com/community/tutorials/how-to-install-nagios-4-and-monitor-your-servers-on-ubuntu-14-04
):
接下来,我们将讨论如何使用 NRPE 监控 Mesos 集群中的节点。
接下来的部分将添加一台机器到 Nagios 进行监控,我们可以重复相同的步骤添加需要的任意多台机器。目前,我们选择监控 Mesos 主节点,如果某个驱动器的磁盘使用量超过给定值,它将触发电子邮件。
现在,在主机上,通过以下命令安装 Nagios 插件和nrpe-server
:
$ sudo apt-get install nagios-plugins nagios-nrpe-server
如前所述,为了安全原因,请编辑/etc/nagios/nrpe.cfg
文件,并将nagios
服务器的 IP 地址放入allowed_hosts
属性下进行通信。
现在,使用以下命令编辑 nrpe
配置文件,设置监控磁盘使用情况:
$ sudo vi /etc/nagios/nrpe.cfg
然后,添加以下内容:
server_address=client_private_IP
allowed_hosts=nagios_server_private_IP
command[check_hda1]=/usr/lib/nagios/plugins/check_disk -w 20% -c 10% -p /dev/vda
在这里,server_address
是机器的 IP 地址,allowed_hosts
是 nagios
服务器的地址,命令是实际用于拉取磁盘使用情况的命令。我们使用了 nagios
自带的 check_disk
插件,并将参数传递给命令,分别为 -w 20%
和 -c 10%
。每当服务器的磁盘使用超过 20% 时,Nagios 会触发电子邮件警报。
编辑文件后,通过以下命令重启nrpe
服务器:
$ sudo service nagios-nrpe-server restart
既然我们已经配置了 Mesos 主节点来检查磁盘使用情况,我们还需要将这个 Mesos 主节点添加到 nagios
服务器,以便它可以持续检查磁盘使用情况,并在超过配额时提醒管理员。
在 nagios
服务器上添加一个新的配置文件进行监控,我们可以将文件添加到 /usr/local/nagios/etc/servers/
,如下所示:
$ sudo vi /usr/local/nagios/etc/servers/mesos-master.cfg
然后,添加以下内容:
define host {
use linux-server
host_name mesos-master
alias Mesos master server
address 10.132.234.52
max_check_attempts 5
check_period 24x7
notification_interval 30
notification_period 24x7
}
该配置将持续监控 Mesos 主机,检查其是否仍在运行。如果 Mesos 主机宕机,管理员(或邮件列表中指定的其他人员)将收到电子邮件通知。
我们还可以通过添加以下服务来启用网络使用情况检查:
define service {
use generic-service
host_name mesos-master
service_description PING
check_command check_ping!100.0,20%!500.0,60%
}
一旦我们为新主机设置了配置,就需要通过执行以下命令来重启nagios
:
$ sudo service nagios reload
我们还可以通过遵循之前列出的步骤,为从属节点创建新的配置文件。
使用 Satellite 监控 Mesos 集群
Satellite 是另一个用于监控 Mesos 的工具,Satellite 项目由 Two Sigma Investments 维护,使用 Clojure 编写。Satellite 主实例监控 Mesos 主节点,并通过 Satellite 从属节点接收来自 Mesos 从属节点的监控信息。对于每个 Mesos 主节点和从属节点,都会有一个 Satellite 主进程和从进程,satellite-slave
进程将向集群中的所有 satellite-master
发送一种类型的消息。
集群的汇总统计信息,如资源利用率、丢失的任务数量以及与主节点相关的事件(例如当前有多少个领导节点处于活动状态等),通常是被拉取的。Satellite 还提供了一个表现状态转移(REST)接口,以与 Mesos 主节点白名单进行交互。白名单是包含主节点将考虑发送任务的主机列表的文本文件。它还提供了一个 REST 接口,用于访问缓存的 Mesos 任务元数据。Satellite 本身从不缓存这些信息,只提供一个接口来检索缓存的信息(如果已缓存)。这是一个可选功能,但如果我们在 Mesos 内部持久化了任务元数据,它将非常有用。
Satellite 添加了两个额外的概念性白名单:
-
托管白名单:这些是自动输入的主机
-
手动白名单:如果某个主机出现在此白名单中,那么它的状态将覆盖前面讨论的受管白名单中的状态。这些是接受
PUT
和DELETE
请求的 REST 端点。
在间隔时间内,合并操作实际上会将这两者合并到白名单文件中。
卫星安装
卫星需要安装在所有 Mesos 集群中的机器上。我们需要在 Mesos 主机上安装 satellite-master
,并在 Mesos 从机上安装 satellite-slave
。运行以下代码:
# Install lein on all the machines
$ wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
$ chmod +x lein
$ export PATH=$PATH:/path/to/lein
# Clone the satellite repository
$ git clone https://github.com/twosigma/satellite
# Compile the satellite-master jar
$ cd satellite/satellite-master
$ lein release-jar
上述命令将在目标目录中创建一个 jar
文件,我们可以将其复制到所有 Mesos 主机上。
通过传递配置执行以下命令,将在机器上运行卫星进程:
$ java -jar ./target/satellite.jar ./config/satellite-config.clj
常见的部署问题及解决方案
本模块包含了一些在安装或设置本章中描述的工具和模块时常遇到的常见问题:
-
对于 Ansible python-setup 工具,查看以下截图:
如果你的 Ansible 安装显示上述消息,请执行以下命令来解决该问题:
$ sudo pip install setuptools
-
SSH 运行在不同的端口上,
nagios
显示连接被拒绝
错误。如果你将
ssh
服务器运行在不同的端口上,你将遇到以下异常:SERVICE ALERT: localhost;SSH;CRITICAL;HARD;4;Connection refused
通过编辑
/etc/nagios/conf.d/services_nagios.cfg
文件中的以下行可以解决此问题:# check that ssh services are running define service { hostgroup_name ssh-servers service_description SSH check_command check_ssh_port!6666!server use generic-service notification_interval 0 ; set > 0 if you want to be renotified
在这里,我们使用
6666
作为ssh
端口,而不是22
,以避免出现错误信息。 -
Chef 无法解压软件包。
有时,Chef 设置无法检索软件包并给出以下错误堆栈:
==> master1: [2015-12-25T22:28:39+00:00] INFO: Running queued delayed notifications before re-raising exception ==> master1: [2015-12-25T22:28:39+00:00] ERROR: Running exception handlers ==> master1: [2015-12-25T22:28:39+00:00] ERROR: Exception handlers complete ==> master1: [2015-12-25T22:28:39+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out ==> master1: [2015-12-25T22:28:39+00:00] ERROR: packageunzip had an error: Mixlib::ShellOut::ShellCommandFailed: Expected process to exit with [0], but received '100' ==> master1: ---- Begin output of apt-get -q -y install unzip=6.0-8ubuntu2 ---- ==> master1: STDOUT: Reading package lists... ==> master1: Building dependency tree... ==> master1: Reading state information... ==> master1: The following packages were automatically installed and are no longer required: ==> master1: erubis ohai ruby-bunny ruby-erubis ruby-highline ruby-i18n ruby-ipaddress ==> master1: ruby-mime-types ruby-mixlib-authentication ruby-mixlib-cli ==> master1: ruby-mixlib-config ruby-mixlib-log ruby-mixlib-shellout ruby-moneta ==> master1: ruby-net-ssh ruby-net-ssh-gateway ruby-net-ssh-multi ruby-polyglot ==> master1: ruby-rest-client ruby-sigar ruby-systemu ruby-treetop ruby-uuidtools ==> master1: ruby-yajl ==> master1: Use 'apt-get autoremove' to remove them. ==> master1: Suggested packages: ==> master1: zip ==> master1: The following NEW packages will be installed: ==> master1: unzip ==> master1: 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. ==> master1: Need to get 192 kB of archives. ==> master1: After this operation, 394 kB of additional disk space will be used. ==> master1: WARNING: The following packages cannot be authenticated! ==> master1: unzip ==> master1: STDERR: E: There are problems and -y was used without --force-yes ==> master1: ---- End output of apt-get -q -y install unzip=6.0-8ubuntu2 ---- ==> master1: Ran apt-get -q -y install unzip=6.0-8ubuntu2 returned 100 ==> master1: [2015-12-25T22:28:40+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1) Chef never successfully completed! Any errors should be visible in the output above. Please fix your recipes so that they properly complete.
当你的 apt 密钥过期时,可能会出现此错误。要解决此问题,你需要通过执行以下命令来更新密钥:
$ sudo apt-key update
-
ZooKeeper 抛出错误 "没有主节点当前正在领导"。
这是一个已知的 ZooKeeper 错误,源于 ZooKeeper 配置文件的错误配置。我们可以通过正确编辑位于
/etc/zookeeper/conf/zoo.cfg
的 ZooKeeper 配置文件来解决此错误。将以下属性添加到文件中列出的服务器 IP 旁边:tickTime=2000 dataDir=/var/zookeeper clientPort=2181
总结
阅读完本章后,你现在应该能够使用任何标准部署工具,在分布式基础设施上启动并配置 Mesos 集群。你还将能够理解 Mesos 支持的各种安全性、多租户性和维护功能,并学习如何将它们实现到生产级别的设置中。
在下一章,我们将更详细地探索 Mesos 框架。我们将讨论框架的各种特性、将现有框架移植到 Mesos 上的过程,并了解如何在 Mesos 上开发自定义框架来解决特定的应用需求。
第六章. Mesos 框架
本章详细讲解了 Mesos 框架的概念和功能。还提供了 Mesos API 的详细概述,包括新的 HTTP 调度器 API,并提供了在 Mesos 上构建自定义框架的具体方案。本章将涵盖以下主题:
-
Mesos 框架简介
-
框架认证
-
框架授权
-
访问控制列表(ACL)
-
调度器 HTTP API
-
在 Mesos 上构建自定义分布式框架
Mesos 框架简介
Mesos 框架位于 Mesos 与应用程序之间,充当管理任务调度和执行的层。由于其实现是应用程序特定的,因此该术语通常用于指代应用程序本身。早期,Mesos 框架只能通过 libmesos C++库与 Mesos API 进行交互,因此开发了其他语言绑定,例如 Java、Scala、Python 和 Go 等,这些语言绑定大量依赖 libmesos。从 v0.19.0 版本开始,对基于 HTTP 的协议进行了更改,开发者可以使用他们想要的编程语言来开发框架,而不必依赖 C++代码。一个框架由两个组件组成:
-
一个调度器
-
一个执行者
调度器负责对它所接收到的资源提供做出决策,并跟踪集群的当前状态。与 Mesos 主节点的通信由SchedulerDriver
模块处理,该模块负责将框架注册到主节点、启动任务并向其他组件传递消息。
第二个组件,执行者,顾名思义,负责在从属节点上执行任务。与从属节点的通信由ExecutorDriver
模块处理,该模块还负责向调度器发送状态更新。
框架 – 认证、授权和访问控制
从用户的角度来看,认证、授权和访问控制通常被认为是同一回事,因为它们通常一起实现。然而,它们之间有一些重要的区别。以下列出了一些区别,以帮助更清楚地理解这些概念的含义:
-
认证: 认证是一个过程,旨在验证某人或某物是否如其所声称的那样。它通常涉及实现一种或多种证明身份的方法。认证过程的结果通常是一个“是/否”答案。
-
授权: 授权用于确定应用程序或用户(已经通过身份验证)是否被允许执行请求的任务。它用于定义和确定经过身份验证的应用程序/用户可以做什么以及不能做什么。一个授权模块通常包括一个定义规则的机制(如角色、权限等)。
-
访问控制:访问控制是确保未认证或未授权的应用程序/用户无法绕过系统并执行受限操作的过程。它通常涉及实现多种安全功能,以确保系统中没有安全漏洞。
框架认证
从 Mesos v0.15 开始,添加了对框架认证的支持,v0.19 引入了从节点认证支持。这是一个非常有用的功能,可以防止未授权的应用程序在 Mesos 上运行。
要使框架能够注册到 Mesos 主节点,必须先进行认证。类似地,认证从节点也是必需的,否则它们无法注册到主节点,因为未授权的进程可能发起 分布式拒绝服务 (DDoS) 攻击、获取任务信息等。这还防止了恶意访问 /teardown
HTTP 端点,确保框架不会被非法终止。
Cyrus 简单认证与安全层 (SASL) 库是一个灵活的框架,支持多种认证机制,如 CRAM-MD5、Plain、GSSAPI 等,Mesos 利用该库实现认证功能。当前 Mesos 支持 CRAM-MD5 机制,但用户也可以实现自己的模块。CRAM-MD5 实现了一种共享密钥认证机制,在这种机制中,主体(在此案例中为框架)和认证者(Mesos)共享一个彼此已知的“秘密”密钥,用于加密和解密信息。每当框架与 Mesos 通信时,它都需要成功地使用该“秘密”密钥来解密或加密信息,以便 Mesos 确认其确实是授权的框架在与之通信。需要注意的是,这与框架用户不同,框架用户是执行任务的执行器所使用的身份。
配置选项
认证模块使用以下配置选项:
-
主节点:
-
--[no-]authenticate
:如果为true
,则允许已认证框架的注册。如果为false
,则其他框架也可以注册。 -
--[no-]authenticate_slaves
:如果为true
,则允许已认证从节点的注册。如果为false
,则其他从节点也可以注册。 -
--authenticators
:用于指定需要用于认证主节点的认证机制,默认使用 CRAM-MD5。–modules
选项也可以用来添加其他认证机制。 -
--credentials
:用于指定包含有效凭据列表的文件位置,具体是否需要取决于使用的认证机制。
-
-
从节点:
-
--authenticate
:用于指定需要用于认证从节点的认证机制,默认使用 CRAM-MD5。 -
--credential
:用于指定包含凭据的文件的位置,该凭据用于确定从属节点。
-
框架授权
授权指的是确定已经通过身份验证的用户是否具有执行请求任务或访问特定资源的所需权限的过程。从 Mesos 0.24 开始,授权 API 被模块化,系统管理员可以实现他们选择的授权后台/协议(例如轻量级目录访问协议或 LDAP)。
启动主节点时,可以通过指定--acls
标志来配置授权。
当前支持以下授权:
-
带有指定角色的框架注册
-
框架作为授权用户启动任务
-
授权主体关闭框架
-
授权主体设置配额
-
授权主体的资源预留和释放
-
授权主体创建和销毁持久化卷
访问控制列表(ACLs)
访问控制列表(ACLs)用于在 Mesos 中实现授权功能。每个之前提到的六种支持的授权都需要以 JSON 格式定义 ACL。在每个 ACL 中,必须定义一个主体列表,这些主体可以对一组对象执行操作。Mesos 主节点随后检查这些 ACL,以确认它接收到的请求是否是授权的。
支持的操作列表如下:
-
register_frameworks
:用于框架注册 -
run_tasks
:用于运行任务 -
shutdown_frameworks
:用于终止框架 -
set_quotas
:用于配额设置 -
reserve_resources
:用于资源预留 -
unreserve_resources
:用于释放资源 -
create_volumes
:用于创建持久化卷 -
destroy_volumes
:用于销毁持久化卷
支持的主体
列表如下:
-
principals
:这些可以分为两种类型:-
框架主体
-
用户名
-
支持的对象
列表如下:
-
roles
-
users
-
framework_principals
-
resources
-
reserver_principals
-
volume_types
-
creator_principals
例如,当一个框架尝试注册到主节点时,系统会检查 register_frameworks
ACL,验证尝试注册的框架是否被授权接收指定角色的资源报价。如果框架未授权,调度器将终止,并发送错误消息。
另一个例子是一个框架尝试启动一组任务的情况。在这种情况下,会检查 run_tasks
ACL 以验证框架是否被允许运行所需的任务,并且作为指定的用户。如果框架没有授权,系统会发送一个 TASK_LOST
消息,任务不会启动。
更多信息,请访问 mesos.apache.org/documentation/latest/authorization/
。
注意
ACL 按照设置顺序进行检查;即使用第一个相关的 ACL 来确定请求的授权状态。
ACLs.permissive
字段决定如何处理不匹配的请求。默认值为 true
,意味着如果没有匹配的 ACL,请求将被授权。
示例
实现 ACL 的几个示例如下:
-
A
和B
框架可以作为用户U
运行任务。请看以下代码:{ "run_tasks": [ { "principals": { "values": ["A", "B"] }, "users": { "values": ["U"] } } ] } Any framework can run tasks as user guest, as shown by the following code: { "run_tasks": [ { "principals": { "type": "ANY" }, "users": { "values": ["guest"] } } ] } No framework can run tasks as root, as follows: { "run_tasks": [ { "principals": { "type": "NONE" }, "users": { "values": ["root"] } } ] }
-
框架
A
只能以用户guest
运行任务,不能以其他用户运行,代码如下所示:{ "run_tasks": [ { "principals": { "values": [ "A" ] }, "users": { "values": ["guest"] } }, { "principals": { "values": [ "A" ] }, "users": { "type": "NONE" } } ] } The framework A can register with X and Y roles, as follows: { "register_frameworks": [ { "principals": { "values": ["A"] }, "roles": { "values": ["X", "Y"] } } ] } Only the framework A and no one else can register with X role, as follows: { "register_frameworks": [ { "principals": { "values": ["A"] }, "roles": { "values": ["X"] } }, { "principals": { "type": "NONE" }, "roles": { "values": ["X"] } } ] }
-
框架
A
只能注册X
角色,不能注册其他角色。此外,其他框架也不能注册任何角色,具体代码如下:{ "permissive" : false, "register_frameworks": [ { "principals": { "values": ["A"] }, "roles": { "values": ["X"] } } ] } Only the P principal can shut down any frameworks through the /teardown HTTP endpoint. Take a look at the following code: { "permissive" : false, "shutdown_frameworks":[ { "principals": { "values": ["P"] }, "framework_principals":{"type":"ANY"} } ] }
Mesos API
Mesos 提供了一个 API,允许开发者构建自定义框架,可以在底层分布式基础设施上运行。有关 Mesos API 的详细解释,请参阅 第一章中的 API 详情 部分。利用该 API 开发定制框架的详细步骤将在后续章节中通过示例进行描述。
Mesos 还实现了 演员式消息传递编程模型,以实现不同 Mesos 组件之间的非阻塞通信,并利用协议缓冲区实现这一目标。
此外,最近还推出了新的调度器 HTTP API,接下来将简要讨论。
调度器 HTTP API
自 Mesos 版本 0.24.0 起,引入了对新的 HTTP API(目前仅支持调度器)的实验性支持。Mesos 主节点托管 api/v1/scheduler
端点,调度器通过该端点与主节点进行通信。
请求调用
主节点当前接受以下请求调用:
订阅
调度器通过HTTP POST
请求发送一个Subscribe
消息,包含相关的框架信息,例如名称等,以便与主节点进行通信。响应包括订阅确认和框架 ID 等详细信息,后续所有的通信都会使用该 ID。
SUBSCRIBE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"type" : "SUBSCRIBE",
"subscribe" : {
"framework_info" : {
"user" : "U",
"name" : "N"
},
"force" : true
}
}
SUBSCRIBE
响应(JSON)如下:
HTTP/1.1 200 OK
TEARDOWN
调度器在希望关闭自己时发送此请求。接收到此请求后,Mesos 将终止所有执行器,终止所有正在运行的任务。然后,Mesos 会移除框架并结束主节点与调度器之间的所有通信。
TEARDOWN
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "TEARDOWN",
}
TEARDOWN
响应如下:
HTTP/1.1 202 Accepted
ACCEPT
当调度器想要接受 Mesos 主节点提供的资源时,它会发送此请求。调度器打算执行的操作作为参数包含在请求中(例如,启动任务、创建卷和保留资源)。
ACCEPT
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_Value>"},
"type" : "ACCEPT",
"accept" : {
"offer_ids" : [
{"value" : "<some_Value>"},
{"value" : "<some_value>"}
],
"operations" : [ {"type" : "LAUNCH", "launch" : {...}} ],
"filters" : {...}
}
}
ACCEPT
响应如下:
HTTP/1.1 202 Accepted
DECLINE
当调度器想要拒绝 Mesos 主节点提供的资源时,它会发送此请求。
DECLINE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_Value>"},
"type" : "DECLINE",
"decline" : {
"offer_ids" : [
{"value" : "<some_value>"},
{"value" : "<some_Value>"}
],
"filters" : {...}
}
}
DECLINE
响应如下:
HTTP/1.1 202 Accepted
REVIVE
如果之前的接受或拒绝请求设置了过滤器,则调度器可以发送 revive 调用来移除它们。
REVIVE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "REVIVE",
}
REVIVE
响应如下:
HTTP/1.1 202 Accepted
KILL
如果需要终止特定任务,则调度器可以发送 KILL
请求。如果存在自定义执行器,则请求将传递给该执行器,由其处理。如果主节点未知道需要终止的任务,它将生成 Task_Lost
消息。
KILL
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "KILL",
"kill" : {
"task_id" : {"value" : "<some_Value>"},
"agent_id" : {"value" : "<some_value>"}
}
}
KILL
响应如下:
HTTP/1.1 202 Accepted
SHUTDOWN
当调度器希望结束特定自定义执行器时,会发送此新引入的调用。
SHUTDOWN
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "SHUTDOWN",
"shutdown" : {
"executor_id": {"value" : "<some_value>"},
"agent_id": {"value" : "<some_value>"}
}
}
SHUTDOWN
响应如下:
HTTP/1.1 202 Accepted
ACKNOWLEDGE
当需要确认状态更新时,调度器发送此请求。
ACKNOWLEDGE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "ACKNOWLEDGE",
"acknowledge" : {
"agent_id" : {"value" : "<some_Value>"},
"task_id" : {"value" : "<some_value>"},
"uuid" : "<some_value>"
}
}
ACKNOWLEDGE
响应如下:
HTTP/1.1 202 Accepted
RECONCILE
当需要查询状态非终结任务时,调度器发送此请求。对于列表中的每个任务,主节点会返回一个更新事件。
RECONCILE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "RECONCILE",
"reconcile" : {
"tasks" : [
{ "task_id" : { "<some_value>" },
"agent_id" : { "<some_value>" }
}
]
}
}
RECONCILE
响应如下:
HTTP/1.1 202 Accepted
MESSAGE
如果需要将任意二进制数据发送到执行器,调度器使用此请求。
MESSAGE
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "MESSAGE",
"message" : {
"agent_id" : {"value" : "<some_value>"},
"executor_id" : {"value" : "<some_value>"},
"data" : "<some_Value>"
}
}
MESSAGE
响应如下:
HTTP/1.1 202 Accepted
REQUEST
这是调度器向主节点请求资源时使用的。
REQUEST
请求(JSON)如下:
POST /api/v1/scheduler HTTP/1.1
{
"framework_id" : {"value" : "<some_value>"},
"type" : "REQUEST",
"requests" : [
{
"agent_id" : {"value" : "<some_value>"},
"resources" : {}
},
]
}
REQUEST
响应如下:
HTTP/1.1 202 Accepted
响应事件
主节点当前作为响应发送以下事件。
SUBSCRIBED
当调度器发出 Subscribe
请求时,主节点发送的第一个事件是 Subscribed
事件。格式如下所示:
SUBSCRIBED
事件(JSON)如下:
<event-length>
{
"type" : "SUBSCRIBED",
"subscribed" : {
"framework_id" : {"value":"<some_value>"},
"heartbeat_interval_seconds" : 10
}
}
OFFERS
当主节点能够向框架提供一组新资源时,它会发送此事件。每个 offer 对应于一个从属节点上的资源组。假设这些资源已经分配,直到调度器向主节点发出 Accept
或 Decline
调用。如果丢失从属节点或超时,offer 会被撤销。
OFFERS
事件(JSON)如下:
<event-length>
{
"type" : "OFFERS",
"offers" : [
{
"offer_id":{"value": "<some_value>"},
"framework_id":{"value": "<some_Value>"},
"agent_id":{"value": "<some_value>"},
"hostname":"agent.host",
"resources":[...],
"attributes":[...],
"executor_ids":[]
}
]
}
RESCIND
当一个 offer 的有效期到期(例如,当 offer 中提到的从属节点丢失时),主节点会发送此事件撤销该 offer。如果调度器在未来做出任何调用,这些 offer 会被视为无效。
RESCIND
事件(JSON)如下:
<event-length>
{
"type":"RESCIND",
"rescind":{
"offer_id":{"value":"<some_value>"}
}
}
更新
如果执行器创建了有关正在运行任务的状态更新,主节点会发送此事件。例如,如果生成了Task_Finished
更新,主节点可以释放分配给任务的资源以供其他地方使用。
UPDATE
事件(JSON)如下:
<event-length>
{
"type" : "UPDATE",
"update" : {
"status" : {
"task_id" : { "value" : "<some_value>"},
"state" : "TASK_FINISHED",
"source" : "SOURCE_EXECUTOR",
"uuid" : "<some_value>",
"bytes" : "<some_Value"
}
}
}
信息
Mesos 主节点通过传递 Message
事件,将执行器生成的消息转发给调度器,不进行解释或交付保证。如果消息传递因任何原因失败,执行器需要重新发送请求。
MESSAGE
事件(JSON)如下:
<event-length>
{
"type":"MESSAGE",
"message":{
"agent_id":{"value":"<some_value>"},
"executor_id":{"value":"<some_value>"},
"data":"<some_value>"
}
}
失败
主节点在从属节点移除或执行器终止时发送此事件。
FAILURE
事件(JSON)如下:
<event-length>
{
"type":"FAILURE",
"failure":{
"agent_id":{"value":"<some_Value>"},
"executor_id":{"value":"<some_Value>"},
"status": 1
}
}
错误
主节点在发生错误时发送此事件(例如,某个角色的框架未经授权订阅请求的资源)。推荐做法是框架在收到此事件后应中止并重新订阅。
ERROR
事件(JSON)如下:
<event-length>
{
"type":"ERROR",
"message":"Framework is not authorized"
}
心跳
主节点在固定时间间隔内发送此事件,以通知调度器已建立的订阅连接处于活动状态。这有助于确保活跃连接不会因数据传输不足而被终止。
HEARTBEAT
事件(JSON)如下:
<event-length>
{
"type":"HEARTBEAT",
}
有关调度器 HTTP API 的更多信息,请访问 mesos.apache.org/documentation/latest/scheduler-http-api/
。
在 Mesos 上构建自定义框架
正如我们所知,Mesos 框架是运行在 Mesos 上的应用程序。在本模块中,我们将了解如何创建我们自己的 Mesos 框架。为了简化起见,我们将创建一个简单的 Java 应用程序来计算 pi 的值。一个 Mesos 框架由以下三个组件组成:
-
驱动程序:这是将任务提交到框架的代码部分
-
执行器:这是在 Mesos 从属节点上启动的用于运行框架任务的代码部分
-
调度器:这是与主节点注册、向其请求资源并在执行器上运行任务的代码部分
现在,让我们看看如何在接下来的章节中开发这些组件以构建自定义的 Mesos 框架。
驱动程序实现
驱动程序程序是创建 执行器信息 的程序。执行器信息由 executorID
(一个字符串值)和通过 Linux /bin/sh-c
命令执行的命令组成。这可以在我们的 Java 代码中实现,如下所示:
Protos.ExecutorInfopiExecutorInfo =
Protos.ExecutorInfo.newBuilder()
.setExecutorId(Protos.ExecutorID.newBuilder()
.setValue("CalculatePi"))
.setCommand(piCommandInfo)
.setName("PiExecutor")
.setSource("java")
.build();
在这里,我们使用了 Google 协议缓冲区与 Mesos 通信,并使用构建者模式构造执行器信息。
现在我们已经准备好了执行器信息,接下来是框架信息。框架信息包括任务应该以哪个 Unix 用户身份启动(如果我们留空该字段,可以使用默认设置)以及故障恢复时间,这使得主节点在调度器失败时会等待一段时间,然后再移除框架。这两个属性通过 .setUser
和 .setFailOverTimeout
调用设置,代码如下:
Protos.FrameworkInfo.BuilderframeworkBuilder = Protos.FrameworkInfo.newBuilder()
.setFailoverTimeout(120000)
.setUser("")
.setName("PiFramework");
现在,我们可以初始化 Scheduler
,并将提交给执行器的任务数量传递给它,以便运行我们的程序。以下是实现的代码:
Scheduler scheduler = new PiScheduler(piExecutorInfo, 1);
PiScheduler
被实例化时只有一个任务,因为我们只需要一个任务来计算它。
MesosSchedulerDriver
用于在 Mesos 上启动和终止任务。它还通过 start
、stop
和 等待任务完成
调用来管理调度器的生命周期。以下是实现的代码:
MesosSchedulerDriverschedulerDriver =
new MesosSchedulerDriver(scheduler,frameworkBuilder.build(), MESOS-MASTER);
int status = schedulerDriver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1;
schedulerDriver.stop();
System.exit(status);
现在我们已经讨论了驱动程序的所有部分,让我们将它们整合在一起,看看实际的代码是如何写的。如下所示:
/* Create the PiDriver class with the following contents: */
import com.google.protobuf.ByteString;
import org.apache.log4j.Logger;
import org.apache.mesos.MesosSchedulerDriver;
import org.apache.mesos.Protos;
import org.apache.mesos.Scheduler;
public class PiDriver {
private final static Logger LOGGER = Logger.getLogger(PiDriver.class);
public static void main(String[] args) {
String path = System.getProperty("user.dir") + "/target/scala-2.10/mesos-pi-assembly-1.0.jar";
/* Defining the executor */
Protos.CommandInfo.URIuriProtos.CommandInfo.URI.newBuilder().setValue(path).setExtract(false).build();
String commandPi = "java -cpmesos-pi-assembly-1.0.jarPiExecutor";
Protos.CommandInfopiCommandInfo = Protos.CommandInfo.newBuilder().setValue(commandPi).addUris(uri).build();
/* Setting the executor information */
Protos.ExecutorInfopiExecutorInfo = Protos.ExecutorInfo.newBuilder().setExecutorId(Protos.ExecutorID.newBuilder().setValue("CalculatePi")).setCommand(piCommandInfo).setName("PiExecutor").setSource("java").build();
/* Defining framework & specifying related information*/
Protos.FrameworkInfo.BuilderframeworkBuilder = Protos.FrameworkInfo.newBuilder().setFailoverTimeout(120000).setUser("").setName("PiFramework").setPrincipal("test-framework-java");
/* Enabling checkpointing */
if (System.getenv("MESOS_CHECKPOINT") != null) {
System.out.println("Enabling checkpoint for the framework");
frameworkBuilder.setCheckpoint(true);
}
/* Initializing the scheduler */
Scheduler scheduler = new PiScheduler(piExecutorInfo, 1);
/* Defining the scheduler driver */
MesosSchedulerDriverschedulerDriver = new MesosSchedulerDriver(scheduler, frameworkBuilder.build(), args[0]);;
int status = schedulerDriver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1;
schedulerDriver.stop();
System.exit(status);
}
}
执行器实现
PiExecutor
是我们的执行器组件。我们需要实现 Executor 接口并重写一些方法。在这里,我们将重点讨论核心方法,即 launchTask()
调用——在我们的例子中,这是实际计算 pi
值的调用。我们还需要在这个方法中设置任务的一些属性,具体如下:
@Override
public void launchTask(final ExecutorDriverexecutorDriver, final Protos.TaskInfotaskInfo)
{
/* Set the task status as running for the task ID with a builder pattern. */
Protos.TaskStatustaskStatus = Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId())
.setState(Protos.TaskState.TASK_RUNNING).build();
/* Send the status update to the framework scheduler retrying as necessary until an acknowledgement has been receivedor the executor is terminated, in which case,a TASK_LOST status update will be sent.+*/
executorDriver.sendStatusUpdate(taskStatus);
/* Actual Pi computation */
try {
message = computePi().getBytes();
}
catch (IOException e) {
LOGGER.error("Error computing Pi :" + e.getMessage());
}
/* Return the value of Pi to framework */
executorDriver.sendFrameworkMessage(message);
/* Mark state of task as finished and send status update to framework scheduler. */
taskStatus = Protos.TaskStatus.newBuilder().setTaskId(taskInfo.getTaskId())
.setState(Protos.TaskState.TASK_FINISHED)
.build();
executorDriver.sendStatusUpdate(taskStatus);
}
现在,我们可以通过实现 Executor
类并重写默认方法来创建一个 PiExecutor
类。完整代码如下:
/* Create the PiExecutor class with the following contents: */
import org.apache.log4j.Logger;
import org.apache.mesos.Executor;
import org.apache.mesos.ExecutorDriver;
import org.apache.mesos.MesosExecutorDriver;
import org.apache.mesos.Protos;
import java.io.IOException;
public class PiExecutor implements Executor {
private final static Logger LOGGER = Logger.getLogger(PiExecutor.class);
/**
*Invoked once the executor driver has been able to
*successfully connect with Mesos.
*In particular, a scheduler can pass some
*data to it's executors through the
*{@linkorg.apache.mesos.Protos.ExecutorInfo#getData()}field.
*@param driver - The executor driver that was registered and connectedto the Mesos cluster.
*@paramexecutorInfo - Describes information about the registered executor.
*@paramframeworkInfo - Describes the framework that was registered.
*@paramslaveInfo - Describes the slave that will be used to launchthe tasks for this executor.
*For more details, seeorg.apache.mesos.ExecutorDriver and org.apache.mesos.MesosSchedulerDriver
*/
@Override
public void registered(ExecutorDriver driver, Protos.ExecutorInfoexecutorInfo,Protos.FrameworkInfoframeworkInfo, Protos.SlaveInfoslaveInfo) {
LOGGER.info("Registered PinUserBoardExecutor on " + slaveInfo.getHostname());
}
/*
* Invoked when executor re-registers with a restarted slave.
* @param driver - The executor driver that was re-registered with Mesos master.
* @paramslaveInfo - Describes the slave that will be used to launch the tasks for this executor.
* For more details, see org.apache.mesos.ExecutorDriver
*/
@Override
public void reregistered(ExecutorDriver driver, Protos.SlaveInfoslaveInfo) {
}
/*
* Invoked when executor becomes 'disconnected' from slave.
* (e.g. when the slave is being restarted due to an upgrade)
* @param driver - The executor driver that was disconnected.
*/
@Override
public void disconnected(ExecutorDriver driver) {
}
/*
* Invoked when a task has been launched on this executor
* (initiated via
* {@linkorg.apache.mesos.SchedulerDriver#launchTasks}.
* Note that this task can be realized with a
* thread, a process, or some simple computation,
* however, no other callbacks will be invoked on this executor
* until this callback has returned.
*
* @param driver - The executor driver that launched the task.
* @param task - Describes the task that was launched.
* For more details, see
* org.apache.mesos.ExecutorDriver and
* org.apache.mesos.Protos.TaskInfo
*/
@Override
public void launchTask(ExecutorDriver driver, Protos.TaskInfo task) {
LOGGER.info("Launching task in PinUserBoardExecutor..");
Protos.TaskStatustaskStatus = Protos.TaskStatus.newBuilder().setTaskId(task.getTaskId()).setState(Protos.TaskState.TASK_RUNNING).build();
driver.sendStatusUpdate(taskStatus);
String url = task.getData().toStringUtf8();
byte[] message = new byte[0];
try {
message = computePi().getBytes();
}
catch (IOException e) {
LOGGER.error("Error computing Pi :" + e.getMessage());
}
LOGGER.info("Sending framework message and marking task finished."+ getClass().getName());
driver.sendFrameworkMessage(message);
taskStatus = Protos.TaskStatus.newBuilder().setTaskId(task.getTaskId()).setState(Protos.TaskState.TASK_FINISHED).build();
driver.sendStatusUpdate(taskStatus);
}
/* Code to compute Pi */
private String computePi() throws IOException {
double pi = 0;
double y = 1;
intlps = 90000000*2;
intcnt = 0;
for(int x=1; x <lps; x+=2) {
pi = pi + (y/x);
y = -y;
cnt++;
}
return "Value of PI=" + 4*pi + " after " + cnt;
/* PI=3.141592642478473 after 90000000 */
}
/*
* Invoked when a task running within this executor
* has been killed
* (via {@link org.apache.mesos.SchedulerDriver#killTask}).
* Note that no status update will be sent
* on behalf of the executor, the executor is responsible for
* creating new TaskStatus (i.e., with TASK_KILLED) &invoking
* {@link org.apache.mesos.ExecutorDriver#sendStatusUpdate}.
* @param driver - The executor driver that owned the task that was killed.
@param task - The ID of the task that was killed.
@For more details, see org.apache.mesos.ExecutorDriver andorg.apache.mesos.Protos.TaskID
*/
@Override
public void killTask(ExecutorDriver driver, Protos.TaskIDtaskId) {
}
/*
@Invoked when a framework message has arrived
*for this executor.
*These messages are best effort;
*do not expect a framework message to be
*retransmitted in any reliable fashion.
*@param driver - The executor driver that received the message.
@param data - The message payload.
* For more details, see
* org.apache.mesos.ExecutorDriver
*/
@Override
public void frameworkMessage(ExecutorDriver driver, byte[] data) {
}
/*
* Invoked when the executor should terminate
* all of it's currently running tasks.
* Note that after Mesos has determined that
* an executor has terminated any tasks that
* the executor did not send terminal status updates
* for (e.g. TASK_KILLED, TASK_FINISHED, TASK_FAILED and so on)
* a TASK_LOST status update will be created.
* @param driver - The executor driver that should terminate
* For more details, see org.apache.mesos.ExecutorDriver
*/
@Override
public void shutdown(ExecutorDriver driver) {
}
/*
* Invoked when a fatal error has occurred with
* the executor and/or executor driver.
* The driver will be aborted BEFORE invoking this callback
* @param driver - The executor driver that was aborted due to this error
* @param message - The error message.
* For more details, see org.apache.mesos.ExecutorDriver
*/
@Override
public void error(ExecutorDriver driver, String message) {
}
/* The main method in which we initiates and calls the run() method in MesosExecutorDriver */
public static void main(String[] args) {
MesosExecutorDrivermesosExecutorDriver = new MesosExecutorDriver(new PiExecutor());System.exit(mesosExecutorDriver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1);
}
}
调度器实现
接下来,我们将讨论调度器实现,它负责框架的调度。我们将创建 PiScheduler
,它实际上实现了 Mesos 的 Scheduler 接口。我们将专注于重写 resourceOffer()
、statusUpdate()
和 frameworkMessage()
方法,这些方法构成了 PiScheduler
的核心部分。
完整的代码如下:
import org.apache.log4j.Logger;
import org.apache.mesos.Protos;
import org.apache.mesos.Scheduler;
import org.apache.mesos.SchedulerDriver;
import java.util.ArrayList;
import java.util.List;
public class PiScheduler implements Scheduler {
private final static Logger LOGGER = Logger.getLogger(PiScheduler.class);
private final Protos.ExecutorInfopiExecutor;
private final inttotalTasks;
private intlaunchedTasks = 0;
private intfinishedTasks = 0;
/**ThePiSchedulertakes information from the PiExecutor and
* the total number of tasks through its constructor
* which is implemented below
*/
public PiScheduler(Protos.ExecutorInfo _piExecutor, int _totalTasks) {
this.totalTasks = _totalTasks;
this.piExecutor = _piExecutor;
}
@Override public void registered(SchedulerDriverschedulerDriver, Protos.FrameworkIDframeworkID, Protos.MasterInfomasterInfo) {
LOGGER.info("Registered! ID = " + frameworkID.getValue());
}
@Override public void reregistered(SchedulerDriverschedulerDriver, Protos.MasterInfomasterInfo) {
}
/**The resourceoffer() method is invoked when resources
* have been offered to this framework.
* We will set the number of CPUs, and amount of memory that
* our framework requires through the call described below
*/
@Override public void resourceOffers(SchedulerDriverschedulerDriver, List<Protos.Offer> list) {
/* The amount of CPU cores and Memory required for our tasks is specified */
double CPUS_PER_TASK = 1;
double MEM_PER_TASK = 128;
for (Protos.Offer offer : list) {
List<Protos.TaskInfo>taskInfoList = new ArrayList<Protos.TaskInfo>();
double offerCpus = 0;
double offerMem = 0;
for (Protos.Resource resource : offer.getResourcesList()) {
if (resource.getName().equals("cpus")) {
offerCpus += resource.getScalar().getValue();
} else if (resource.getName().equals("mem")) {
offerMem += resource.getScalar().getValue();
}
}
LOGGER.info("Received Offer : " + offer.getId().getValue() + "with cpus = " + offerCpus + " and mem ="+ offerMem);
double remainingCpus = offerCpus;
double remainingMem = offerMem;
if (launchedTasks<totalTasks&&remainingCpus>= CPUS_PER_TASK&&remainingMem>= MEM_PER_TASK) {
Protos.TaskIDtaskID = Protos.TaskID.newBuilder().setValue(Integer.toString(launchedTasks++)).build();
LOGGER.info("Launching task :" + taskID.getValue() + " using the offer : " + offer.getId().getValue());
/* PiExecutor is created as a task and is launched*/
Protos.TaskInfopiTaskInfo = Protos.TaskInfo.newBuilder()
.setName("task " + taskID.getValue()).setTaskId(taskID).setSlaveId(offer.getSlaveId()).addResources(Protos.Resource.newBuilder().setName("cpus")
.setType(Protos.Value.Type.SCALAR).setScalar(Protos.Value.Scalar.newBuilder().setValue(CPUS_PER_TASK))).addResources(Protos.Resource.newBuilder().setName("mem").setType(Protos.Value.Type.SCALAR).setScalar(Protos.Value.Scalar.newBuilder().setValue(MEM_PER_TASK))).setExecutor(Protos.ExecutorInfo.newBuilder(piExecutor)).build();
taskID = Protos.TaskID.newBuilder().setValue(Integer.toString(launchedTasks++)).build();
LOGGER.info("Launching task :" + taskID.getValue() + " using the offer : " + offer.getId().getValue());
taskInfoList.add(piTaskInfo);
}
schedulerDriver.launchTasks(offer.getId(), taskInfoList);
}
}
/** Invoked when an offer is no longer valid
* (e.g., the slave was lost or another framework used
* resources in the offer).
* If for whatever reason an offer is never rescinded
* (e.g., dropped message, failing over framework and so on),
* a framework that attempts to launch tasks
* using an invalid offer will receive TASK_LOST
* status update for those tasks */
*/
@Override public void offerRescinded(SchedulerDriverschedulerDriver, Protos.OfferIDofferID) {
}
/**The statusUpdate() call is invoked when
* the status of a task has changed
* (e.g., a slave is lost which means the task is lost,
* a task finishes and executor sends a status update)
*/
@Override public void statusUpdate(SchedulerDriverschedulerDriver, Protos.TaskStatustaskStatus) {
LOGGER.info("Status update : Task ID "+ taskStatus.getTaskId()
.getValue() + "in state : "+ taskStatus.getState().getValueDescriptor().getName());
if (taskStatus.getState() == Protos.TaskState.TASK_FINISHED) {
finishedTasks++;
LOGGER.info("Finished tasks : " + finishedTasks);
/* We can stop the scheduler once the tasks are completed */
if (finishedTasks == totalTasks) {
schedulerDriver.stop();
}
}
if (taskStatus.getState() == Protos.TaskState.TASK_FAILED|| taskStatus.getState() == Protos.TaskState.TASK_KILLED|| taskStatus.getState() == Protos.TaskState.TASK_LOST) {
LOGGER.error("Aborting because the task "+ taskStatus.getTaskId().getValue() + " is in unexpected state : "+ taskStatus.getState().getValueDescriptor().getName() + "with reason : "+ taskStatus.getReason().getValueDescriptor().getName()+ " from source : "+ taskStatus.getSource().getValueDescriptor().getName() + " with message : "+ taskStatus.getMessage());
schedulerDriver.abort();
}
}
/**The frameworkMessage() is invoked when an executor
* sends a message. In our case we will be sending
* the value of Pi from our executor as the message.
*/
@Override public void frameworkMessage(SchedulerDriverschedulerDriver, Protos.ExecutorIDexecutorID,Protos.SlaveIDslaveID, byte[] bytes) {
String data = new String(bytes);
System.out.println(data);
LOGGER.info("Output :\n=========\n " + data);
}
/* Invoked when scheduler becomes disconnected from master
* (e.g. master fails and another is taking over)
*/
@Override public void disconnected(SchedulerDriverschedulerDriver) {
}
/* Invoked when a slave has been determined unreachable
* (e.g., machine failure, network partition).
* Most frameworks will need to reschedule
* any tasks launched on this slave on a new slave.
*/
@Override public void slaveLost
(SchedulerDriverschedulerDriver, Protos.SlaveIDslaveID) {
}
/* Invoked when an executor has exited/terminated.
* Note that any tasks running will have
* TASK_LOST status updates automatically generated.
*/
@Override public void executorLost(SchedulerDriverschedulerDriver, Protos.ExecutorIDexecutorID,Protos.SlaveIDslaveID, inti) {
}
/* Invoked when there is an unrecoverable error
* in the scheduler or driver.
* The driver will be aborted before invoking this callback.
*/
@Override public void error(SchedulerDriverschedulerDriver, String s) {
LOGGER.error("Error : " + s);
}
}
运行框架
我们需要将 JAR 文件与所有依赖项一起打包,以便运行框架。为此,我们可以创建一个 build.sbt
文件,并将 Mesos 和 log4j 作为依赖项。这将类似于以下内容:
import AssemblyKeys._
assemblySettings
name := "mesos-pi"
version := "1.0"
scalaVersion := "2.10.4"
libraryDependencies += "org.apache.mesos" % "mesos" % "0.26.0"
libraryDependencies += "log4j" % "log4j" % "1.2.17"
现在我们可以使用以下 sbt
命令创建一个 Uber JAR 文件:
$ sbt assembly
然后,我们将使用以下命令运行程序:
$ sbt'run 127.0.0.1:5050'
Multiple main classes detected, select one to run:
[1] PiDriver
[2] PiExecutor
Enter number: 1
请注意,我们在 PiDriver
程序中将 Mesos 主节点的 URI 作为参数指定。
一旦我们运行程序,您将在终端中看到以下输出:
Value of PI=3.141592642478473 after 90000000
我们还可以看到框架已经在 Mesos 主节点的 UI 中注册,Mesos 主节点运行在 5050
端口,截图如下:
如果我们点击框架 ID,仍然可以在相同的 UI 中看到计算 Pi 值的单个任务已经完成:
总结
阅读完本章后,你应该对 Mesos 中的框架安全功能有一个基本了解,比如授权、认证和访问控制。同时,你也应该能够使用 Mesos API 构建你自己的自定义应用程序/框架。此外,本章还介绍了实验性调度器 HTTP API。
在下一章,我们将探讨一些有趣的项目,如 Myriad(在 Mesos 上的 YARN)和 Kubernetes 在 Mesos 上的部署。我们还将讨论如何使用 Docker 部署容器化应用,以及如何在 Mesos 上使用 Jenkins 设置持续集成流程。
第七章:Mesos 容器化器
本章简要介绍了容器的概念,并稍微谈了一下Docker,可能是当今最流行的容器技术。它还提供了 Mesos 中不同容器化器选项的详细概述,并介绍了其他一些主题,如 Mesos 管理的容器的网络配置和获取器缓存。最后,提供了一个在 Mesos 中部署容器化应用的示例,帮助更好地理解。以下是本章将涵盖的主题:
-
容器
-
Docker
-
Mesos 容器化器
-
Mesos 容器化器
-
Docker 容器化器
-
容器化器组合
-
-
Mesos 管理的容器网络
-
Mesos 镜像提供
-
获取器缓存
-
使用 Docker 和 Mesos 部署容器化应用
容器
Linux 容器(在本章其余部分简单称为容器)允许应用程序在分配的资源份额内,在一个隔离的、独立的环境中运行。由于所有容器共享宿主机的操作系统(OS),并且不需要加载操作系统,因此它们可以在几秒钟内创建。
基于操作系统级虚拟化的容器技术已经存在十多年。操作系统级虚拟化是一种方法,它允许操作系统内核创建多个用户命名空间实例(也称为容器),而不是仅创建一个。
我们可以将容器看作是封装的、可单独部署的组件,这些组件作为隔离的实例在同一个内核上运行。容器相较于传统技术,如裸金属服务器(具有操作系统的服务器)或虚拟化环境(如Microsoft Hyper-V)有很大的优势。从开发者的角度来看,我们只需将应用程序及其依赖项打包到一个容器中,并将其部署到任何支持容器的环境中。通过这样做,我们还使得我们的应用程序容易更新、升级,甚至可以轻松地从一个环境迁移到另一个环境,例如从桌面上的开发环境迁移到云中的测试环境。
两种流行的容器模型是:
-
基于虚拟机监控程序:基于虚拟机监控程序的模型包括冗余的操作系统内核和其他库,这使得该设置效率低下,如下图所示(来源:
aucouranton.com/2014/06/13/linux-containers-parallels-lxc-openvz-docker-and-more/
): -
基于容器:基于容器的方法涉及封装的、可单独部署的组件,这些组件作为隔离的实例在同一个内核上运行,如下图所示(来源:
aucouranton.com/2014/06/13/linux-containers-parallels-lxc-openvz-docker-and-more/
):
为什么选择容器?
以下是使用容器的一些重要好处:
-
以应用程序为中心的管理
-
部署和构建的分离
-
简化应用程序部署过程
-
通过持续集成和部署支持敏捷开发
-
允许将大型单体应用程序拆分为可管理的微服务
-
基础设施环境一致性
-
高可移植性
-
资源隔离
-
资源利用率
Docker
Docker 是一个开源平台,自动化了任何应用程序部署为容器的过程,这些容器是便携的、轻量级的、自给自足的,并且可以在几乎任何地方运行。Docker 主要基于 LXC 或 Linux 容器,开发人员和系统管理员在处理分布式应用程序时使用它。它不仅仅是一个底层技术,而是充当一个综合的抽象层,使开发人员能够将应用程序及其依赖项进行打包或容器化,并在任何基础设施上运行。简单来说,Docker 容器就像货运集装箱,提供了一种标准且可靠的方式来运输任何应用程序。
Docker 为开发和 IT 运维团队提供了急需的敏捷性和控制能力,使其能够“构建、分发和运行任何应用程序,随时随地”。
-
构建:Docker 赋予您从微服务创建应用程序的灵活性,并且不必担心生产和开发环境之间可能存在的不一致性。此外,使用任何特定平台或语言都不受限制。
-
分发:Docker 允许您设计包含应用程序开发、测试和分发的整个周期,并通过一致的用户界面简化其管理。
-
运行:Docker 使您能够选择多种平台,以安全、可靠的方式部署可扩展的服务。
然而,Docker 的主要优势在于它提供了一个“应用优先”的架构,这也允许开发人员和 IT 运维/系统管理员专注于他们的核心职能。
-
开发人员:他们只关注容器内部的内容,如代码、数据、应用程序、库和包管理器。
-
IT 运维:他们需要关注容器外部的内容,即日志、监控、远程访问和网络配置。
Docker 本质上带来的好处可以有效地总结为以下几点:-
-
敏捷性:使用 Docker,开发人员可以自由定义环境。应用程序的创建和部署可以更快、更轻松地完成,IT 运维能够灵活地快速响应变化。
-
控制:确保从基础设施到应用程序的代码的所有权仅属于开发人员。IT 运维负责管理方面,确保操作环境的标准化、安全性和可扩展性。
-
可移植性:让你可以选择与单个开发人员或团队一起工作,使用私有基础设施或公共云提供商,同时避免复杂性。
为了更好地理解该平台,让我们来看一下与 Docker 解决方案相关的一些核心术语。
-
镜像:Docker 容器在静态时的基础。当通过 Docker run 命令实例化时,它们会变成一个容器。
-
Dockerfile:其主要功能是告知镜像构建器镜像应该是什么样子的。
-
Docker Engine:可以安装在物理、虚拟或云主机上。它提供了多个核心功能,包括拉取 Docker 镜像、创建和运行新容器、扩展现有容器,或停止和移除容器。
-
容器:这些是从 Docker 镜像构建的。它们的轻量特性归因于镜像层的共享和联合文件系统的使用。
-
注册表:这是管理、分发和存储 Docker 镜像的地方。
-
Docker Hub:Docker 提供的托管镜像注册表服务,用于管理镜像。
-
Docker Machine:它自动化了在选定的网络或云环境中容器的配置。可以安装在 Linux、Windows 或 Mac OS X 的机器上。
-
Docker Compose:定义需要多个容器的应用程序。
-
Docker 工具箱:将所有 Docker 工具(包括 Engine、Machine、Compose 和 Kitematic)提供给 Mac OS X 或 Windows 系统。
-
Docker 受信任注册表 (DTR):提供一个私有的、专用的镜像注册表。
-
Docker Swarm:用于托管集群和调度容器。
-
Kitematic:基本上是 Docker 的桌面图形界面(GUI)。
下图展示了一个基本的 Docker 系统。
如上图所示,Docker 容器封装了一个软件及其运行所需的所有内容,如代码、运行时、系统库/工具。容器提供的这种封装和隔离性确保了容器在任何运行环境下都能以相同的方式运行。
下图展示了虚拟机和 Docker 容器的对比(来源:www.jayway.com/2015/03/21/a-not-very-short-introduction-to-docker/
):
我们刚才看到的图示展示了虚拟机与 Docker 容器在架构上的差异。Docker 容器仅包含应用程序及其相关依赖,因此享有类似虚拟机的资源隔离和分配的好处,但效率更高且更具可移植性。
Docker 的可插拔架构通过多个开放 API(如 Swarm、Compose、Hub 等)的存在得到了进一步的利用,这些 API 支持创建一个无缝的应用交付生态系统。正是这种结合,使得 Docker 容器能够使分布式应用程序具有组合性、可移植性、动态性,且非常适合敏捷团队。
Docker 高级架构如后续图片所示(来源:www.docker.com
):
当我们考虑一些企业的使用案例时,这些优势变得愈发明显,在这些案例中,组织利用 Docker 平台解决了一些技术或业务挑战:
-
持续集成:通过与 GitHub 和 Jenkins 等工具的集成,Docker 使开发者能够更快地开发和测试应用,并在他们选择的环境中进行。整个过程被完美地简化,开发者可以在 GitHub 中提交代码,进行测试,并自动触发 Jenkins 构建。当镜像构建完成时,可以轻松地将其添加到 Docker 仓库。这一功能不仅节省了构建和设置的时间,还允许开发者在同时处理其他项目时,自动运行测试。再加上处理不同类型环境时不会出现不一致问题,使用 Docker 进行持续集成工作时,效率得到了显著提高。
-
持续交付:除了语言无关性外,Docker 在任何环境中都能工作,有效消除了因环境不一致而产生的问题。Docker 保证将代码以快速、易于消费并支持迅速解决问题的方式,持续交付给质量保证团队和客户。结合 Docker 仓库,以及与其他生产平台如 Docker Universal Control Plane 和 Tutum 的集成,使得代码能够分发给团队,团队可以在上线前在预发布环境中完全测试代码。这帮助企业减少了构建和交付应用所需的时间和费用。
-
DevOps:随着组织越来越多地采用 DevOps 哲学,Docker 提供了帮助开发和运维团队改进应用开发流程的关键工具。这些工具通过将开发和运维团队的工作及关注点进行分隔,打破了传统的团队间壁垒。
-
大数据:如今,企业正在转向大数据系统/技术,旨在对他们收集的数据进行深入和全面的分析。然而,数据源多种多样(如图像、视频、元数据等)。这时,利用一种可以统一在分析平台之间传输数据的系统,就显得尤为重要。采用 Docker 平台提供了显著的竞争优势,因为它促进了应用程序在不同环境之间的自由流动,从而确保了数据分析的无缝进行。
-
基础设施优化:与需要客户端操作系统或虚拟机管理程序的虚拟机不同,Docker 容器仅包含构建、分发和运行应用程序所需的内容。再加上根据需求启动或停止容器的能力,Docker 对企业来说是一个极具价值的附加工具。
Docker 进一步提供了云端可移植性,解放了 IT 运维团队,使其不再受限于特定的环境工具。实际上,Docker 减少了企业的存储和基础设施成本,并提高了效率。
容器化工具
如前所述,容器用于以下目的:
-
隔离任务与其他任务
-
确保任务在有限或受限资源的环境中运行
-
程序化控制任务的单个资源
-
通过打包镜像在不同环境中运行应用程序
-
将应用程序拆分为更小、更易管理的微服务
任务可以通过容器化工具在容器中运行。Mesos 支持流行的容器技术,如 Docker,同时也有自己的容器技术。最近,还增加了对结合不同容器技术并使其协同工作的支持(例如,Mesos 和 Docker)。
动机
集群管理器的关键要求之一是确保为特定框架分配的资源不会影响其他框架的任何正在运行的作业。因此,Mesos 的关键特性之一是提供在从属节点上实施隔离机制以分隔不同任务的能力。Mesos 利用容器进行资源隔离,并且具有可插拔的架构。Mesos 从属节点使用容器化工具 API 提供一个隔离的环境来运行框架的执行器及其相应任务。容器化工具 API 的目标是支持广泛的实现,这意味着可以开发自定义容器化工具和隔离器。当从属进程启动时,可以指定用于启动容器的容器化工具以及用于强制资源约束的隔离器集合。
容器化工具类型
在 Mesos 版本 0.27 中可用的容器化工具选项有:
-
组合
-
Docker
-
Mesos(默认)
用户可以通过代理标志--containerizers
指定容器化工具类型。
例如:
--containerizers=mesos
容器化工具创建
容器化工具由从属节点基于标志配置(使用代理标志--containerizers
)创建。在列出多个容器化工具(例如,Mesos 和 Docker)时,使用组合容器化工具来创建容器化工具,通过--containerizers
标志来指定。
当在TaskInfo
中未特别提到执行器时,Mesos 代理将使用任务默认执行器。
Mesos 中的容器化过程如下图所示(来源:《Apache Mesos Essentials》:www.packtpub.com/big-data-and-business-intelligence/apache-mesos-essentials
):
Mesos 容器化工具
这是 Mesos 提供的默认容器化工具类型。在此类型中,任务可以通过 Mesos 提供的多个可插拔的隔离器来运行。可以通过配置代理标志来启用此类型:
--containerizers=mesos
此类型通常在以下情况下使用:
-
用户需要通过 Mesos 控制任务环境,而不依赖其他容器解决方案。
-
需要精细化操作系统控制。
-
需要为任务添加自定义资源隔离。
-
用户需要控制某些资源参数(例如,磁盘使用限制),而这些参数在其他容器解决方案中不可见。
任何未指定ContainerInfo::DockerInfo
的任务将由 Mesos 容器化工具处理。
启动过程
容器启动过程包括以下步骤:
-
每个隔离器上的调用准备工作都已完成。
-
启动器负责分叉/销毁容器,使用它来分叉执行器。分叉出的“子进程”在隔离步骤完成之前无法执行。
-
执行器通过调用隔离器进行隔离。隔离器负责为容器创建一个环境,在该环境中,CPU、网络、存储和内存等资源可以与其他容器隔离。
-
执行器被获取并通知分叉的子进程执行。首先运行隔离器准备命令,然后进行执行。
Mesos 容器化工具状态
不同的 Mesos 容器化工具状态如下:
-
PREPARING
-
ISOLATING
-
FETCHING
-
RUNNING
-
DESTROYING
内部实现
通过利用 Linux 的命名空间和控制组(cgroups)等功能,Mesos 容器化工具提供资源隔离和轻量级容器化。它提供了选择性启用不同隔离器的能力。此外,虽然仅包括使用报告而不进行实际隔离,但也支持POSIX(可移植操作系统接口)系统。
存在三种隔离器选项,具体描述如下:
共享文件系统
可以通过在 Linux 主机上使用共享文件系统隔离器来启用对每个容器的共享文件系统视图的修改。
可以通过–default_container_info agent
标志或框架中的ContainerInfo
来指定修改。
用于映射共享文件系统部分(例如主机路径与容器视图中的共享文件系统,即容器路径) 的卷,可以通过 ContainerInfo
指定为只读或读写模式。主机的路径可以是绝对路径或相对路径。如果是绝对路径,还会导致文件系统子树在容器路径下对每个容器可访问。如果是相对路径,则该路径会被视为相对于执行器工作目录的目录。该目录会被创建,并且共享文件系统中相应现有目录的权限将被复制过来。
使用此隔离器的主要原因是为了选择性地将某些共享文件系统部分设置为对每个容器私有。例如,可以通过设置 host_path="dir_name"
和 container_path="/dir_name"
来设置一个私有目录 /dir_name
。这将在执行器的工作目录中创建一个目录 dir_name
,并将其挂载为容器中的 /dir_name
。容器无法看到主机的 /dir_name
或其他任何容器的 /dir_name
,并且这个操作对容器内运行的进程是透明的。
Pid 名称空间
此隔离器用于将每个容器隔离到不同的 pid 名称空间,这带来以下两个优势:
-
可见性:容器内运行的执行器及其后代进程无法查看或与名称空间外部运行的外部进程进行交互。
-
清理终止:如果 pid 名称空间中的主进程被终止,内核将终止该名称空间内运行的所有其他进程。
Posix 磁盘隔离器
该隔离器可用于 OS X 和 Linux,用于提供基本的磁盘隔离。可以通过此隔离器报告每个沙箱的磁盘使用情况,并可以强制执行任何设置的磁盘配额。
在启动从属节点时,必须将 posix/disk
添加到 --isolation
标志中以启用此隔离器。
默认情况下禁用磁盘配额强制执行。在启动从属节点时,指定 --enforce_container_disk_quota
来启用它。
每个沙箱的磁盘使用情况通过定期运行 du
命令进行报告。资源统计端点(/monitor/statistics.json
)提供了一种检索磁盘使用情况统计信息的方法。
两个 du
命令之间的时间间隔可以通过代理标志 --container_disk_watch_interval
来配置。例如,--container_disk_watch_interval = 1 mins
指定时间间隔为 1 分钟。默认时间间隔为 15 秒。
Docker 容器化器
该容器化类型允许在 Docker 容器内运行任务。可以将 Docker 镜像作为执行器或任务启动。通过配置代理标志启用它
--containerizers=docker
通常在以下情况下使用:
-
需要使用 Docker 包来运行任务。
-
一个 Mesos 从属节点正在 Docker 容器中运行。
设置
为了在从节点上启用 Docker 容器化工具,从节点必须在启动时将 "Docker" 作为其中一个容器化选项。
mesos-slave --containerizers=docker
每个指定了 Docker 容器化的从节点必须安装 Docker 命令行界面客户端(版本 1.0.0 或以上)。
如果从节点启用了 iptables
,确保 iptables
允许来自桥接接口的所有流量,可以通过以下更改来实现:
iptables -A INPUT -s 172.17.0.0/16 -i docker0 -p tcp -j ACCEPT
启动过程
容器启动过程如下:
-
只有当
ContainerInfo::type
设置为DOCKER
时,才会尝试在 Docker 中启动任务 -
镜像首先从指定的仓库中拉取
-
预启动钩子随后被调用
执行器随后以以下两种方式之一启动:
-
在以下情况下,Mesos 代理会在 Docker 容器中运行:
-
如果存在
--docker_mesos_image
标志,表示 Mesos 代理在 Docker 容器中运行 -
--docker_mesos_image
标志的值被视为用于启动 Mesos 代理的 Docker 镜像 -
如果任务使用不同于默认命令执行器的执行器,它将在 Docker 容器中启动
-
如果任务使用
TaskInfo
,则默认的mesos-docker-executor
会在 Docker 容器中启动,通过 Docker 命令行界面执行命令。
-
-
以下情况下,Mesos 代理不会在 Docker 容器中运行:
-
如果任务使用
TaskInfo
,则会创建一个子进程来运行默认的mesos-docker-executor
。该执行器会通过 Docker 命令行界面生成 Shell 以执行 Docker 命令。 -
如果任务使用了自定义执行器,它将在 Docker 容器中启动。
-
Docker 容器化状态
以下是不同的 Docker 容器化状态:
-
获取中
-
拉取中
-
运行中
-
销毁中
组合容器化工具
这种类型允许结合并使不同的容器技术协同工作(例如,Mesos 和 Docker)。可以通过配置 --containerizers
代理标志,并用逗号分隔的容器化工具名称列表来启用。
--containerizers=mesos,docker
列表中指定的第一个容器化工具提供对任务容器配置的支持,用于任务启动。
- 此选项通常在需要测试具有不同资源隔离类型的任务时使用。框架可以利用组合容器化工具,通过 Mesos 容器化工具提供的受控环境来测试任务,同时确保任务与 Docker 容器兼容。
Mesos 管理的容器的网络配置
在 Mesos 中提供与网络相关的支持的主要目标之一是开发一个可插拔的架构,用户可以根据自己的需求实现定制的网络机制。由于不同部署场景(云环境、本地部署、私有云或其他混合模型)对网络的需求不同,创建一个单一的网络机制来满足所有需求并不现实。Mesos 的可插拔架构在解决这一问题时非常有用。
为提供网络支持,从版本 0.25.0 开始,Mesos 组件引入了许多可选扩展。可选结构允许没有网络支持的现有框架在更新的 Mesos 版本上无缝运行。Mesos 支持与其他网络机制的集成,并提供服务发现、每个容器的 IP 以及任务隔离等功能。
网络支持是通过一个 Mesos 模块提供的,这意味着它被抽象化,脱离了 Mesos 的主节点和从节点。所有所需的支持需要从网络模块本身启用。由于 IP 请求是基于尽力而为的原则处理的,因此框架还需要能够处理因未实现网络模块或模块无法分配 IP 而导致的请求被忽略或拒绝的情况。
框架调度器需要通过在TaskInfo
中使用额外的数据结构来选择网络隔离。这一结构确保与旧框架保持向后兼容。
架构
提供了包括不同组件描述的解决方案架构,如下图所示(来源:mesos.apache.org/documentation/latest/networking-for-mesos-managed-containers/
):
关键术语
关于网络的一些关键术语如下:
-
IP 地址管理(IPAM)服务器
-
IP 按需分配
-
IP 在释放后会被回收
-
IP 可以选择性地使用提供的 ID 进行标记
-
-
IPAM 客户端
-
与对应的 IPAM 服务器紧密耦合
-
作为 IPAM 服务器与网络隔离模块之间的桥梁
-
处理与服务器的 IP 相关通信(释放、请求等)
-
-
网络隔离模块(NIM)
-
Mesos 模块用于实现隔离器接口的从节点
-
任务的 IP 需求由查看
TaskInfo
来决定 -
处理与 IPAM 客户端的 IP 相关通信(释放、请求等)
-
通过与外部网络虚拟化器通信,实现网络隔离
-
-
清理模块:
- 处理清理任务(如 IP 释放),当从节点丢失时
过程
-
Mesos 框架为每个启动的容器请求 IP,使用
TaskInfo
消息。 -
Mesos 主节点在处理完这些消息后,将其转发给从节点,用于启动和运行任务。
-
Mesos 从节点通过分析这些消息来了解容器需求,并为其准备不同的隔离器。
-
网络隔离模块分析这些消息,并决定是否启用网络隔离器。
-
网络隔离模块随后会与 IPAM 客户端通信,请求 IP 地址,前提是它决定需要启用网络隔离器,并向从节点提供状态更新。
-
在收到此更新后,容器将在不同的命名空间中由从节点启动。这是由网络隔离模块完成的,网络隔离模块会在接到从节点的指示后通知网络虚拟化器进行容器隔离。
-
IP 信息由网络隔离模块添加到
TaskStatus
中。 -
网络隔离模块随后会将来自
TaskStatus
的 IP 地址提供给主节点的状态端点,同时通过将TaskStatus
转发给框架,让框架了解这些 IP 地址。 -
如果任务丢失,网络隔离模块会通知 IPAM 客户端,并释放 IP 地址。
-
然后清理模块会收到通知,接着回收所有由 IPAM 客户端释放的 IP 地址。
网络隔离模块的示例实现可以在github.com/mesosphere/net-modules
查看。
框架中的每容器 IP 功能
寻求实现每容器 IP 功能的框架,需要在TaskInfo
内传递NetworkInfo
消息。NetworkInfo
消息的详细信息将在以下部分给出。
NetworkInfo 消息
新的NetworkInfo
消息可以按如下方式引入:
message NetworkInfo {
enum Protocol {
IPv4 = 1;
IPv6 = 2;
}
message IPAddress {
optional Protocol protocol = 1;
optional string ip_address = 2;
}
repeated IPAddress ip_addresses = 5;
repeated string groups = 3;
optional Labels labels = 4;
};
当请求来自 IP 地址管理的 IP 地址时,协议字段需要设置为IPv4
或IPv6
。如果网络隔离模块支持,框架可以为每个容器设置静态 IP 地址。可以通过提供有效的 IP 作为ip_address
字符串来完成。这通常在需要重新启动已终止或丢失的任务,并且任务需要在另一个节点上使用相同 IP 地址时才需要。
指定网络要求的示例
需要启用每个容器一个 IP 的框架需要在TaskInfo
中提供NetworkInfo
消息。以下是几个示例:
-
使用默认命令执行器请求一个 IP 地址,但未指定协议版本:
TaskInfo { ... command: ..., container: ContainerInfo { network_infos: [ NetworkInfo { ip_addresses: [ IPAddress { protocol: None; ip_address: None; } ] groups: []; labels: None; } ] } }
-
使用默认命令执行器请求两个 IP 地址,其中一个是 IPv4 协议,另一个是 IPv6 协议,分为两个组:
TaskInfo {
... command: ..., container: ContainerInfo { network_infos: [ NetworkInfo { ip_addresses: [ IPAddress { protocol: IPv4; ip_address: None; }, IPAddress { protocol: IPv6; ip_address: None; } ] groups: ["A", "B"]; labels: None; } ] } }
-
使用默认命令执行器请求两个网络接口,每个接口一个 IP 地址且分别属于不同的网络组:
TaskInfo { ... command: ..., container: ContainerInfo { network_infos: [ NetworkInfo { ip_addresses: [ IPAddress { protocol: None; ip_address: None; } ] groups: ["A"]; labels: None; }, NetworkInfo { ip_addresses: [ IPAddress { protocol: None; ip_address: None; } ] groups: ["B"]; labels: None; }, ] } }
-
使用自定义执行器请求特定 IP 地址:
TaskInfo { ... executor: ExecutorInfo { ..., container: ContainerInfo { network_infos: [ NetworkInfo { ip_addresses: [ IPAddress { protocol: None; ip_address: "xx.xx.x.x"; } ] groups: []; labels: None; } ] } } }
地址发现
正如前面所见,框架可以在启动 Mesos 从节点上的任务时,通过在 TaskInfo
消息中传递 NetworkInfo
消息来请求分配 IP 地址。框架在选择网络隔离后,还需要一个机制来了解最终分配的 IP 地址,以便进行监控和其他通信活动。这可以通过在 TaskStatus
消息中引入一个额外的字段来实现,如下所示:
message ContainerStatus {
repeated NetworkInfo network_infos;
}
message TaskStatus {
...
optional ContainerStatus container;
...
};
实现自定义网络隔离模块
Mesos 提供了一个 Isolator API,允许在其基础上实现自定义的网络隔离模块。实现的模块会以动态共享库的形式在 Mesos 从节点中显现,并与容器启动过程无缝集成。网络隔离器通常与网络虚拟化器和 IP 地址管理(IPAM)客户端进行通信,以满足框架的需求。
网络隔离模块必须实现的三个关键功能如下:
-
Isolator::prepare()
:此函数为模块提供了一个选项,允许它确定是否需要为指定的容器启用网络隔离。Isolator::prepare
函数指示从节点在启用网络隔离时创建一个不同的网络命名空间。此接口还负责为容器创建一个 IP 地址。 -
Isolator::isolate()
:此函数为模块提供了一个机会,在任务容器创建后但执行器启动之前进行容器隔离。它涉及为容器创建一个虚拟以太网适配器并为其分配 IP 地址。它还可以利用外部虚拟化器进行网络配置。 -
Isolator::cleanup()
:此函数在容器终止时被调用。它的主要任务是回收和再利用已释放的 IP 地址,并进行其他必要的清理工作。
监控容器网络统计信息
每个容器网络的统计信息可以通过 Mesos 的 /monitor/statistics.json
从节点端点提供。
报告了以下计数器:
指标 | 描述 | 类型 |
---|---|---|
net_rx_bytes |
接收的字节 | 计数器 |
net_rx_dropped |
丢弃的包(接收) | 计数器 |
net_rx_errors |
报告的错误(接收) | 计数器 |
net_rx_packets |
接收的包 | 计数器 |
net_tx_bytes |
发送的字节 | 计数器 |
net_tx_dropped |
丢弃的包(发送) | 计数器 |
net_tx_errors |
报告的错误(发送) | 计数器 |
net_tx_packets |
发送的包 | 计数器 |
此外,以下计数器和测量值可以报告用于膨胀减少或带宽限制的元素,这些元素在 statistics/net_traffic_control_statistics
键下实现:
指标 | 描述 | 类型 |
---|---|---|
backlog |
排队的字节(仅在膨胀减少接口上) | 测量 |
bytes |
发送的字节 | 计数器 |
drops |
丢弃的包(发送) | 计数器 |
overlimits |
接口超过传输限制的次数 | 计数器 |
packets |
发送的数据包 | 计数器 |
qlen |
排队的数据包 | 测量 |
ratebps |
传输速率(当前始终为 0 字节/秒) | 测量 |
ratepps |
传输速率(当前始终为 0 数据包/秒) | 测量 |
requeues |
失败的数据包 | 计数器 |
示例统计
以下是获得的统计数据示例:
$ curl -s http://localhost:5051/monitor/statistics | python2.6 -mjson.tool
[
{
"executor_id": "job.<job_id>",
"executor_name": "Command Executor (Task: job.<job_id>) (Command: sh -c 'iperf ....')",
"framework_id": "<some_id>",
"source": "job.<job_id>",
"statistics": {
"cpus_limit": <some_value>,
"cpus_nr_periods": <some_value>,
"cpus_nr_throttled": <some_value>,
"cpus_system_time_secs": <some_value>,
"cpus_throttled_time_secs": <some_value>,
"cpus_user_time_secs": <some_value>,
"mem_anon_bytes": <some_value>,
"mem_cache_bytes": <some_value>,
"mem_critical_pressure_counter": <some_value>,
"mem_file_bytes": <some_value>,
"mem_limit_bytes": <some_value>,
"mem_low_pressure_counter": <some_value>,
"mem_mapped_file_bytes": <some_value>,
"mem_medium_pressure_counter": <some_value>,
"mem_rss_bytes": <some_value>,
"mem_total_bytes": <some_value>,
"net_rx_bytes": <some_value>,
"net_rx_dropped": <some_value>,
"net_rx_errors": <some_value>,
"net_rx_packets": <some_value>,
"net_traffic_control_statistics": [
{
"backlog": <some_value>,
"bytes": <some_value>,
"drops": <some_value>,
"id": "bw_limit",
"overlimits": <some_value>,
"packets": <some_value>,
"qlen": <some_value>,
"ratebps": 0,
"ratepps": 0,
"requeues": 0
},
{
"backlog": <some_value>,
"bytes": <some_value>,
"drops": <some_value>,
"id": "bloat_reduction",
"overlimits": <some_value>,
"packets": <some_value>,
"qlen": <some_value>,
"ratebps": 0,
"ratepps": 0,
"requeues": 0
}
],
"net_tx_bytes": <some_value>,
"net_tx_dropped": <some_value>,
"net_tx_errors": <some_value>,
"net_tx_packets": <some_value>,
"perf": {
"duration": <some_value>,
"timestamp": <some_value>,
},
"timestamp": <some_value>,
}
}
]
Mesos 图像供应器
Image 是根文件系统更改的有序集合,以及用于容器运行时的相应执行参数。
大多数容器规范,如 Docker、App Container(AppC)或 Open Container Project(OCP),在很大程度上将图像格式的实现和规范与其他容器组件(如资源隔离和任务执行)结合在一起。Mesos Image Provisioner 旨在通过支持来自多种图像格式的容器文件系统供应,增强 Mesos 容器化器的功能,同时还通过与其他组件(如隔离器)结合,提供资源隔离等特性。
使用 Mesos 容器化器创建的 Mesos 容器提供了一个已经用 Docker 或 AppC 图像配置好的根文件系统,该文件系统是通过 Mesos 图像供应器进行供应的。
容器文件系统图像通过一个新的消息Image
来描述,代码如下所示:
message Image {
enum Type {
DOCKER = 1;
APPC = 2;
// More Image types.
}
message Docker {
// Docker configurations
}
message Appc {
// Appc configurations.
}
required Type type = 1;
// Only one of the following image messages should be set to match
// the type.
optional Docker docker = xx;
optional Appc appc = yy;
}
此消息包含了图像规范类型以及相应的类型配置。该消息可以通过'Volume'或ContainerInfo
传递。当在ContainerInfo
中配置图像时,会为任务提供一个根文件系统,而在'Volume'中指定'Image'消息的情况下,卷与图像文件系统一起挂载。
容器镜像的请求被转发给相应的供应商,以供应各层。层通常是文件系统更改集,实际上就是添加、修改或删除文件的归档。供应器进一步要求配置好的设置根据这些层供应一个根文件系统。
设置和配置选项
要在从节点上启用 Mesos 容器化功能,必须使用 mesos containerizer
(默认选项)启动,如本章前文所示。该从节点还需要在 Linux 系统上运行并具有 root 权限。
接下来,必须通过 --isolation
标志将文件系统/Linux 设置为隔离器选项。可以通过将图像供应商作为逗号分隔的列表传递到代理标志 --image_providers
中来配置图像供应商,并且可以通过 --image_provisioner_backend
标志指定支持的后端。如果需要,也可以为每个图像供应商提供额外的配置。
以下是一个示例:
mesos-slave --containerizers=mesos --image_providers=appc,docker --image_provisioner_backend=copy --isolation=filesystem/linux
欲了解更多信息,请访问 mesos.readthedocs.io/en/latest/mesos-provisioner/
。
Mesos 获取器
Mesos 获取器是一种在准备任务执行时可以将资源下载到 task sandbox
目录的方式。请求执行任务的框架将一组 CommandInfo::URI
值作为 TaskInfo
消息的一部分发送,这些值作为 Mesos 获取器的输入。
Mesos 获取器原生支持 FTP 和 HTTP 协议,还能够从文件系统中复制文件。它还支持所有 Hadoop 客户端协议,如Amazon 简单存储服务(S3)、Hadoop 分布式文件系统(HDFS)等。
每个请求的统一资源标识符(URI)默认会直接下载到 sandbox
目录。对相同 URI 的多个请求将导致该资源反复被下载。下载的 URI 也可以选择性地缓存到指定目录中,以供重用。
机制
该机制包括以下内容:
-
每个从属节点包含一个内部获取器实例,所有容器化类型都会利用该实例。获取器进程包含在从属节点程序中,负责对缓存中存在的内容进行账务管理。
-
还有一个外部
mesos-fetcher
程序,它会在需要从指定 URI 下载资源到缓存或目录时由步骤 1 中提到的内部程序调用。当缓存中的资源需要移动到sandbox
目录时,也会调用该程序。除了删除和查询文件大小外,所有的磁盘和网络操作都由它执行。它更像是一个简单的辅助程序,而所有的智能都位于内部程序中。
除了缓存文件外,整个系统不会维护任何持久状态。这有助于管理同时获取和缓存的复杂性,并且通常简化了整体架构。
下图描述了内部获取器进程与外部 mesos-fetcher
程序之间的交互。后续的图表将更详细地讲解内部工作流程。来源:mesos.apache.org/documentation/latest/fetcher-cache-internals/
。
缓存条目
获取器进程可以提供关于缓存 URI 的详细信息,除了提供有关缓存文件位置、处理阶段等信息外。
HashMap 被用来存储之前提到的所有信息。每个 Cache::Entry
对象包含一个缓存文件的详细信息。这些对象可以通过多个回调引用,以对应于多个获取操作,并存储在 HashMap 中。
每个缓存条目在整个文件存储期间都与一个磁盘缓存文件一一对应,这个过程包括文件创建之前和之后的时间段。该条目包含与该阶段相关的所有状态信息以及相应的 URI 抓取结果。
如果一个缓存条目正在被引用,它不能通过任何试图腾出空间以便为新下载的缓存文件腾出位置的抓取操作被删除。
下一个图示展示了缓存条目的各种状态。它说明了在一次抓取运行中的事件序列。图中的颜色代表以下含义:
-
蓝色:没有缓存存在
-
绿色:此运行未引用的缓存条目
-
橙色:缓存条目已引用
只要有任何事件引用了某个缓存条目,该条目就不应被删除。这是通过一种叫做引用计数的机制来强制执行的。每当一个缓存条目被抓取操作使用时,字段“引用计数”中的值会增加,而在运行结束时,无论该运行是否成功,该值都会减少。值的增加发生在以下情况:
-
创建新缓存条目
-
等待下载现有条目的文件
-
抓取与条目对应的缓存文件
每次值的增加都会在列表中标注。导致增量的活动结束时,该值会减少。
给出的图示展示了缓存条目可以处于的不同状态。
URI 流程图
抓取进程决定处理每个正在抓取的 URI 的控制逻辑。以下是说明此流程图的图示:
缓存驱逐
假设有两个资源,A 和 B,它们分别被抓取并缓存到两个沙盒中,如下图所示。在此过程中,每个资源都会创建一个缓存文件,并将资源与相应的缓存文件进行映射。来源:mesos.apache.org/documentation/latest/fetcher-cache-internals/
。
现在假设有一个第三个资源 C,它被下载并缓存到一个新的沙盒中。假设没有足够的空间容纳所有三个条目,那么就需要删除或驱逐一个现有的缓存文件及其相应条目,以便为新的缓存条目腾出空间。假设资源 A 在 B 之前被抓取,那么缓存驱逐过程会移除 A,因为它是最早的条目。移除步骤如下:
-
A 的缓存条目从条目表中被移除。
-
C 被抓取,这会创建一个新的缓存文件,并生成与之关联的缓存条目。
如果第一次请求的资源 A 被再次获取,那么 B 将会被逐出,因为它是最旧的条目;当然,前提是没有足够的空间来容纳三个缓存文件。
使用 Docker 和 Mesos 部署容器化应用
本节简要概述了如何在 Mesos 上使用 Marathon 部署一个 Docker 容器化的 Node.js 应用。你需要在机器上预先安装 Docker 和 fig。接下来,让我们按照以下步骤进行部署:
-
由于我们部署的是一个简单的 Node.js 应用,我们可以从创建一个简单的
App.js
文件开始,打印出Hello World
,这是一个简单的 hello world Node.js 程序。var http = require('http'); // Configure our HTTP server to respond with Hello World to all requests. var server = http.createServer(function (request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.end("Hello World "); }); // Listen on port 8000, IP defaults to "0.0.0.0" server.listen(8000); // Put a friendly message on the terminal console.log("Server running at http://127.0.0.1:8000/");
-
接下来,我们创建
package.json
文件,内容如下:{ "name": "hello-world", "description": "hello world", "version": "0.0.1", "private": true, "dependencies": { "express": "3.x" }, "scripts": {"start": "node app.js"} }
-
下一步是将我们的应用容器化。为此,我们可以创建一个包含以下内容的
Dockerfile
文件:FROM google/nodejs WORKDIR /app ADD package.json /app/ RUN npm install ADD . /app EXPOSE 8000 CMD [] ENTRYPOINT ["/nodejs/bin/npm", "start"]
-
现在,我们可以使用以下命令构建容器:
$ docker build -t my_nodejs_image . $ docker run -p 8000:8000 my_nodejs_image
我们可以通过打开浏览器中的 localhost:8000
来确保一切正常运行,这将显示我们的 Hello World 文本。
-
下一步是构建我们的
fig.yml
文件。在这里,我们将使用 fig 作为编排引擎,通过单个命令部署所有核心服务。fig.yml
文件的内容如下:# Zookeeper: -p 2181:2181 -p 2888:2888 -p 3888:3888 zookeeper: image: jplock/zookeeper:3.4.5 ports: - "2181" master: image: redjack/mesos-master:0.21.0 hostname: master links: - zookeeper:zookeeper environment: - MESOS_ZK=zk://zookeeper:2181/mesos - MESOS_QUORUM=1 - MESOS_WORK_DIR=/var/lib/mesos - MESOS_LOG_DIR=/var/log ports: - "5050:5050" marathon: #image: garland/mesosphere-docker-marathon image: mesosphere/marathon links: - zookeeper:zookeeper ports: - "8080:8080" # Adding the params via command command: --master zk://zookeeper:2181/mesos --zk zk://zookeeper:2181/marathon slave: image: redjack/mesos-slave:0.21.0 links: - zookeeper:zookeeper - master:master environment: - MESOS_MASTER=zk://zookeeper:2181/mesos - MESOS_EXECUTOR_REGISTRATION_TIMEOUT=5mins - MESOS_CONTAINERIZERS=docker,mesos - MESOS_ISOLATOR=cgroups/cpu,cgroups/mem - MESOS_LOG_DIR=/var/log volumes: - /var/run/docker.sock:/run/docker.sock - /usr/bin/docker:/usr/bin/docker - /sys:/sys:ro - mesosslace-stuff:/var/log expose: - "5051" registry: image: registry environment: - STORAGE_PATH=/registry volumes: - registry-stuff:/registry ports: - "5000:5000"
-
在前面的配置中,我们已经配置了 Docker 注册表运行在
5000
端口,同时还配置了一个 Mesos 从节点、Mesos 主节点、Marathon 和 ZooKeeper 用于内部通信。 -
现在,我们可以使用以下命令启动 Docker 注册表服务:
$ fig up
该命令将启动前述配置文件中列出的所有服务,如 Mesos 主节点、Mesos 从节点、Marathon、ZooKeeper 和 Docker 注册表。
-
现在,我们可以构建 Docker 镜像,并使用以下命令将其推送到注册表:
# Build an image $ docker build -t localhost:5000/containersol/nodejs_app # Push it to the registry $ docker push localhost:5000/containersol/nodejs_app
-
现在我们已经准备好集群,可以通过 Marathon 部署我们的 Node.js 应用。为此,我们需要创建一个应用配置文件(
app_marathon.json
),内容如下:{ "id": "app", "container": { "docker": { "image": "localhost:5000/containersol/nodejs_app:latest", "network": "BRIDGE", "portMappings": [ {"containerPort": 8000, "servicePort": 8000} ] } }, "cpus": 0.2, "mem": 512.0, "instances": 1 }
-
然后,我们使用以下命令将其部署到 Marathon 上:
$ curl -X POST -H "Content-Type: application/json" http://localhost:8080/v2/apps -d@app_marathon.json
-
现在,我们可以打开运行在
8080
端口的 Marathon Web UI,并查看我们的 Node.js 应用是否正常运行:
我们可以通过将浏览器指向 localhost:31000
端口来查看我们的 Node.js hello world 应用:
(参考:container-solutions.com/continuous-delivery-with-docker-on-mesos-in-less-than-a-minute/
)
container-solutions.com/continuous-delivery-with-docker-on-mesos-in-less-than-a-minute-part-2/
)
总结
本章节涵盖了与 Mesos 中容器化相关的几个重要主题。资源隔离是 Mesos 最受推崇的功能之一,本章节中介绍的主题希望能帮助您理解这一特性。
在接下来的章节中,我们将看到一些当前由 Mesos 支持的重要大数据框架,如 Hadoop、Spark 和 Storm,并了解如何在 Mesos 上进行设置和配置。
第八章:Mesos 大数据框架
本章是关于如何在 Mesos 上部署重要的大数据处理框架(如 Hadoop、Spark、Storm 和 Samza)的指南。
Hadoop on Mesos
本节将介绍 Hadoop,解释如何在 Mesos 上设置 Hadoop 栈,并讨论设置栈时常遇到的问题。
Hadoop 简介
Hadoop 是由 Mike Cafarella 和 Doug Cutting 于 2006 年开发的,用于管理 Nutch 项目的分发。该项目的名称来源于 Doug 儿子玩具大象的名字。
以下模块构成了 Apache Hadoop 框架:
-
Hadoop Common:包含其他模块所需的公共库和工具。
-
Hadoop 分布式文件系统(HDFS):这是一个分布式、可扩展的文件系统,能够在普通硬件上存储 PB 级的数据。
-
Hadoop YARN:这是一个资源管理器,用于管理集群资源(类似于 Mesos)。
-
Hadoop MapReduce:这是一种大规模并行数据处理的模型。
MapReduce
MapReduce 是一种处理模型,可以在分布式的普通硬件基础设施上并行处理大量数据,可靠且具有容错能力。
MapReduce 这个词是 Map 和 Reduce 任务的组合,这里描述了它们:
-
Map 任务:在此步骤中,对输入数据集的所有元素执行操作,根据需要进行转换(例如应用过滤条件)。
-
Reduce 任务:下一步使用 map 任务生成的输出作为输入,并对其应用聚合操作,生成最终输出(例如,对所有值求和)。
任务的调度、执行和监控由框架可靠地处理,应用程序员无需担心这些问题。
Hadoop 分布式文件系统
基于Google 文件系统(GFS),Hadoop 分布式文件系统(HDFS)提供了一个可扩展、分布式的文件系统,能够以可靠、容错的方式存储大量数据。
HDFS 基于主/从架构,主节点由单一的NameNode组成,负责处理文件系统的元数据,且一个或多个从节点负责存储数据,并被称为DataNode。
HDFS 中的每个文件都被分成多个块,每个块存储在 DataNode 中。NameNode 负责维护关于每个块在哪个 DataNode 中的信息。像读写操作这样的任务由 DataNode 处理,同时也负责块管理任务,如根据 NameNode 的指令进行创建、删除和复制。
交互通过一个 shell 进行,在 shell 中可以使用一组命令与文件系统进行通信。
在 Mesos 上设置 Hadoop
本节将解释如何在 Mesos 上设置 Hadoop。现有的 Hadoop 分发包也可以在 Mesos 上进行设置。要在 Mesos 上运行 Hadoop,我们的 Hadoop 分发包必须包含Hadoop-Mesos-0.1.0.jar
(本书撰写时的版本)。对于任何使用 protobuf 版本高于 2.5.0 的 Hadoop 分发包,这都是必需的。我们还需要设置一些配置属性,以完成设置,具体如下所述。请注意,在本章节撰写时,YARN 和 MRv2 尚不支持。
请按照这里提到的步骤操作:
-
打开集群中的终端,并运行以下命令以在 Mesos 上设置 Hadoop:
# Install snappy-java package if not alr eady installed. $ sudo apt-get install libsnappy-dev # Clone the repository $ git clone https://github.com/Mesos/Hadoop $ cd Hadoop # Build the Hadoop-Mesos-0.1.0.jar $ mvn package
-
执行前述命令后,将会创建
target/Hadoop-Mesos-0.1.0.jar
文件。需要注意的一点是,如果您使用的是较旧版本的 Mesos,并且需要根据该版本构建 jar,那么您需要编辑
pom.xml
文件,选择适当的版本。我们可以在pom.xml
文件中更改以下版本:<!-- runtime deps versions --> <commons-logging.version>1.1.3</commons-logging.version> <commons-httpclient.version>3.1</commons-httpclient.version> <Hadoop-client.version>2.5.0-mr1-cdh5.2.0</Hadoop-client.version> <Mesos.version>0.23.1</Mesos.version> <protobuf.version>2.5.0</protobuf.version> <metrics.version>3.1.0</metrics.version> <snappy-java.version>1.0.5</snappy-java.version>
-
现在,我们可以下载 Hadoop 分发包。如您所见,我们已使用
hadoop-2.5.0-mr1-cdh5.2.0
版本编译了Hadoop-Mesos jar
。可以通过以下命令下载:$ wget http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.5.0-cdh5.2.0.tar.gz # Extract the contents $ tar zxf hadoop-2.5.0-cdh5.2.0.tar.gz
-
现在,我们需要将
Hadoop-Mesos-0.1.0.jar
复制到 Hadoop 的share/Hadoop/common/lib
目录中。具体操作如下:$ cp hadoop-Mesos-0.1.0.jar hadoop-2.5.0-cdh5.2.0/share/hadoop/common/lib/
-
我们现在需要更新 CHD5 分发包的符号链接,以指向正确的版本(因为它包含 MRv1 和 MRv2(YARN)),可以使用以下命令:
$ cd hadoop-2.5.0-cdh5.2.0 $ mv bin bin-mapreduce2 $ mv examples examples-mapreduce2 $ ln -s bin-mapreduce1 bin $ ln -s examples-mapreduce1 examples $ pushd etc $ mv hadoop hadoop-mapreduce2 $ ln -s hadoop-mapreduce1 Hadoop $ popd $ pushd share/hadoop $ rm mapreduce $ ln -s mapreduce1 mapreduce $ popd
-
所有配置已准备好。我们可以将 Hadoop 分发包归档并上传到现有的 Hadoop 分布式文件系统(HDFS)中,这样 Mesos 就可以访问它。请查看以下命令:
$ tar czf hadoop-2.5.0-cdh5.2.0.tar.gz hadoop-2.5.0-cdh5.2.0 $ hadoop fs -put hadoop-2.5.0-cdh5.2.0.tar.gz /hadoop-2.5.0-cdh5.2.0.tar.gz
-
完成后,我们可以通过编辑
mapred-site.xml
文件来配置JobTracker
,以便在 Mesos 上启动每个TaskTracker
节点,如下所示:<property> <name>mapred.job.tracker</name> <value>localhost:9001</value> </property> <property> <name>mapred.jobtracker.taskScheduler</name> <value>org.apache.Hadoop.mapred.MesosScheduler</value> </property> <property> <name>mapred.Mesos.taskScheduler</name> <value>org.apache.Hadoop.mapred.JobQueueTaskScheduler</value> </property> <property> <name>mapred.Mesos.master</name> <value>localhost:5050</value> </property> <property> <name>mapred.Mesos.executor.uri</name> <value>hdfs://localhost:9000/Hadoop-2.5.0-cdh5.2.0.tar.gz</value> </property>
-
mapred-site.xml
文件中的一些属性是 Mesos 特有的,例如mapred.Mesos.master
或mapred.Mesos.executor.uri
。 -
我们现在可以通过以下命令启动
JobTracker
服务,并包括 Mesos 原生库:$ MESOS_NATIVE_LIBRARY=/path/to/libMesos.so Hadoop jobtracker
高级配置指南
有关 Mesos 配置设置的更多详细信息,请参见 github.com/mesos/hadoop/blob/master/configuration.md
。
常见问题及解决方案
在 Mesos 上设置 Hadoop 时最常遇到的两个问题是:
-
无法在环境中设置 Mesos 库
-
构建失败
这两个问题的解决方案在这里进行了描述:
-
环境中缺少 Mesos 库: 如果我们忘记在环境中设置 Mesos 库,会在以下网址遇到异常堆栈:
github.com/mesos/hadoop/issues/25
。这可以通过设置以下环境变量来解决:
$ export MESOS_NATIVE_LIBRARY=/usr/local/lib/libMesos.so $ export MESOS_NATIVE_JAVA_LIBRARY=/usr/local/lib/libMesos.so
-
Maven 构建失败: 在某些情况下,我们可能无法构建包,因为构建失败。一个构建失败的示例可以在这里找到:
github.com/mesos/hadoop/issues/64
。可以通过从环境中删除较旧的 Maven 依赖项并重新构建来避免这种情况。
这是一个示例:
$ mv ~/.m2 ~/.mv_back $ mvn package
Mesos 上的 Spark
Apache Spark 是一个强大的开源处理引擎,围绕速度、易用性和复杂分析构建。它目前是增长最快的大数据技术之一,并被多家领先公司在生产中使用。
有趣的是,Apache Spark 最初是在 2009 年由 UC Berkeley 的 AmpLab 作为一个研究项目启动的,目的是证明一个利用内存资源的分布式处理框架可以在 Apache Mesos 上运行。它于 2010 年开源,2013 年进入 Apache 孵化器,并于 2014 年成为 Apache 顶级项目。在其短暂的存在期间,Apache Spark 成功吸引了开发者社区的关注,并且正在慢慢进入商业决策者的词汇中。此外,它目前已在超过 5000 个组织中投入生产,这充分说明了它的多功能性和实用性。
为什么选择 Spark
在早期的分布式并行计算框架(如 Map Reduce)中,每个计算步骤都必须从磁盘中读取并写入。例如,考虑一个标准的词频计数示例,计算一组文本文件中每个单词出现的次数。这里的第一步是一个映射任务,它从磁盘读取文本文件(如果需要的话,将其分成更小的块),从中取出一行,将其拆分成单独的单词,然后输出一个键值对 <<word>
,1>
(注意,一个中间合并步骤可以在每个映射器内存中将每个单词的出现次数合并,从而提高效率)。在整个集群中会启动多个映射器,来并行高效地处理来自每个文本文件的所有行。所有这些映射任务的最终输出会写入磁盘。在下一步中,减少任务需要将相同的单词收集到一台机器中,以便将它们全部加起来并生成最终计数。为此,有一个洗牌操作,它读取从映射步骤生成的中间输出,并确保一个单词的所有输出都发送到一个且仅一个 reducer。经过减少步骤后,最终输出会被收集并写入磁盘。
对于涉及多次重复前述步骤的迭代工作负载,这将导致大量的磁盘 I/O。映射器将读取第一组输入,并将中间输出写入磁盘。然后,减速器将从磁盘读取中间输出,并将它们的输出写入磁盘,然后由第 2 阶段的映射器读取,并以此类推。磁盘读取速度非常慢,对于长时间的迭代计算,它们通常会成为瓶颈,而不是 CPU。这是 Spark 旨在解决的基本问题之一。许多迭代或延迟敏感的应用程序(例如交互式查询或流处理)由于基本设计约束,并未得到批处理框架(如 Map Reduce)充分解决。Spark 通过提出一种新颖的架构来解决这个问题。
为了最小化磁盘 I/O,Spark 的创建者决定将内存视为一个潜在的替代方案。趋势表明,随着每年的过去,内存成本呈指数级下降。经济实惠的内存意味着可以将更多内存打包到商品服务器中,而不会增加成本。为了有效处理新兴的商业应用类别,例如迭代机器学习、交互式数据挖掘或流处理,内存为基础的框架还必须开发出对容错和分区控制常见问题的优雅解决方案。例如,容错(或更精确地说是高可用性)可以通过在不同的机器上复制数据或者在数据库中定期更新状态来实现。然而,这是一种非常耗时的方法,利用了大量的网络带宽,并且会导致作业执行速度大大降低,这正是该框架首先要解决的问题。分区控制和能够尽可能地保持任务所需的所有数据靠近它非常重要,因为没有这一点,就无法实现高执行速度。
为了解决所有这些问题,开发了一种新的更高级别的抽象称为弹性分布式数据集(RDDs)。这些新的数据结构允许程序员将它们显式地缓存到内存中,将它们放置在所需的分区以获得最佳性能,并且可以根据其构建蓝图重新构建它们,以防数据丢失。程序员只需编写一个驱动程序,封装其应用程序的逻辑工作流,并在集群中并行执行各种组成操作。
Spark 提供的两个主要抽象是:
-
弹性分布式数据集(或 RDDs),如前所述。
-
需要在这些数据集上应用处理操作(map、filter、join、for each、collect 等),以生成所需的输出。
RDD 还允许将相同的操作/功能并行地应用于多个数据点。通过记录构建数据集的过程(谱系),它可以有效地在发生故障时,通过利用这个存储的蓝图重新构建数据集,甚至在分区级别进行恢复。
Spark 已被证明在磁盘上的速度比 MapReduce 快 10 倍,在内存中则快 100 倍,适用于某些类型的应用程序。它还大大减少了编写逻辑工作流的复杂性,即使是复杂的应用程序程序,现在也不再需要超过几百行代码,而不再是之前的 1,000 行或 10,000 行。
Hadoop 和 Spark 中的逻辑回归
Spark 在迭代式机器学习、流处理和交互式查询等应用场景中特别有用,它通过增强的处理速度和编程简便性,助力驱动越来越多的业务或组织价值。来源:spark.apache.org
。
Spark 的通用性不仅使其非常适合前面提到的用例,而且也适用于传统的批处理应用程序。Spark 的多功能性还体现在它提供了丰富且表达力强的 API,支持 Python、Java、Scala 和 SQL 等多种语言,并且拥有其他内置库。它还具有很高的互操作性,能够与所有标准的数据存储工具兼容,如 HDFS 和 Cassandra。
Spark 生态系统
Spark 生态系统包括多个互补的组件,这些组件旨在无缝协作。Spark 的多功能和通用结构使得可以在其基础上构建针对特定工作负载的专用库,如Spark SQL用于通过 SQL 接口查询结构化数据,MLib用于机器学习,Spark Streaming用于处理动态数据流,以及GraphX用于图计算。支撑这些组件的是 Spark 核心引擎,它定义了 Spark 的基本结构,包括其核心抽象——弹性分布式数据集(或 RDD)。
Spark 高度互操作性设计原则以及多个紧密耦合的组件共享一个共同核心的方式具有多种优势。生态系统中的所有高级库都可以直接利用对基础框架所做的任何功能添加或改进。拥有总拥有成本的降低,因为只需要设置和维护一个软件栈,而不是多个独立的系统。它还作为一个统一的数据分析栈,适用于多种使用场景,减少了整体的学习曲线、部署、测试和运行周期。涉及流数据处理、应用机器学习算法和通过 SQL 接口查询最终输出的应用程序,可以通过使用 Spark 的所有不同库轻松构建。此外,Spark 提供的加速性能和较低的基础设施成本也解锁了新的使用场景,从实时处理流数据到开发涉及复杂机器学习算法的应用程序。来源:spark.apache.org
。
Spark 生态系统的各个组件在以下章节中进行了描述。
Spark 核心
Spark Core 是基础组件,包含了通用执行引擎,并涵盖了 Spark 的核心功能。它包括内存管理、故障恢复、与不同存储系统的连接或互操作性、作业调度以及丰富、表达力强的 API(包括定义 RDD 的 API)来构建应用程序工作流等功能。
Spark SQL
Spark SQL 是使分析师可以使用 SQL 分析和查询结构化数据集的组件。它支持多种数据格式,包括 CSV、Parquet、JSON 和 AVRO。如前所述,Spark 的集成设计还允许数据工程师将复杂的分析与 Spark SQL 查询交织在一起,从而通过公共 RDD 抽象将一个组件的输出传递给统一应用程序中的其他组件。
该领域的另一个重要发展是引入了 DataFrame API(灵感来自 R 和 Python 的 DataFrame),该 API 于 Spark 1.3 中引入。DataFrame 是优化的、表格型的、按列组织的分布式数据集,允许大量熟悉此概念的分析师和数据科学家能够在 Spark 中利用它们。它们可以从许多来源生成,如结构化文件、Hive 表或其他 RDD,并且可以使用提供的领域特定语言(DSL)进行操作。
Spark Streaming
Spark Streaming 是一个组件,允许实时处理流数据或移动中的数据,如机器日志、服务器日志、社交媒体信息流等。它处理实时数据流的模型是核心 API 处理批量数据的逻辑扩展。它以小批量模式处理数据;也就是说,它收集一个时间窗口内的数据,在此小批量上应用处理逻辑,然后收集下一个小批量,依此类推。这使得将为批处理工作流编写的代码重用于流数据场景变得非常容易。
MLlib
Spark 配备了一个可扩展的机器学习库,包含丰富的分布式算法,适用于多种使用场景,如分类、聚类、协同过滤、分解和回归。它还包括一些更深层次的原语,如梯度下降优化。
GraphX
GraphX 组件对于处理图形(例如社交网络图)非常有用,通过一组丰富的操作符,如 mapVertices 和 subgraph,以及用于图形分析(PageRank)、社区检测(三角形计数)和结构预测(吉布斯采样)的标准算法。Spark API 被扩展(类似于 Spark SQL 和 Spark Streaming)来开发有向图,其中可以为每个边和顶点分配自定义属性。
Spark 的可扩展性也促进了大量第三方包、连接器和其他库的发展,进一步增强了其实用性。其中一些更受欢迎的包括 SparkR、由 Sigmoid 开发的用于不同云基础设施提供商(如 GCE)的启动脚本、用于 Redshift 和 Elasticsearch 的连接器,以及作业执行服务器。
在 Mesos 上设置 Spark
本节详细解释了如何在 Mesos 上运行 Spark。与 Hadoop 配置类似,我们需要将 Spark 二进制包上传到 Mesos 可访问的位置,并配置 Spark 驱动程序程序以连接到 Mesos。
另一种选择是在所有 Mesos 从属节点上安装 Spark,并设置 Spark.Mesos.executor.home
指向此位置。
以下步骤可以用来将 Spark 二进制包上传到一个 Mesos 可访问的位置。
每当 Mesos 在 Mesos 从属节点上首次运行任务时,从属节点必须拥有一个 Spark 二进制包才能运行 Spark Mesos 执行器后端(该后端随 Spark 一起提供)。Mesos 可以访问的位置可以是 HDFS、HTTP、S3 等。我们可以通过访问以下网站从官方网站下载最新版本的 Spark 二进制包:
spark.apache.org/downloads.html
。
例如,在写这本书时,Spark 的最新版本是 1.6.0,我们可以使用以下命令将其下载并上传到 HDFS:
$ wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.0-bin-hadoop2.6.tgz
$ hadoop fs -put spark-1.6.0-bin-hadoop2.6.tgz /
在驱动程序中,我们现在可以将 master URL 设置为 Mesos master URL,对于单 master Mesos 集群,它将是 Mesos://master-host:5050
的形式。对于多 master Mesos 集群,它可能类似于 Mesos://zk://host1:2181,host2:2181
。
向 Mesos 提交 Spark 作业有两种模式,下面的章节将解释这两种模式。
在客户端模式下提交作业
Spark Mesos 框架直接在客户端机器上启动,并且在客户端模式下等待驱动程序的输出。我们需要在 Spark-env.sh
文件中设置一些与 Mesos 交互的 Mesos 特定配置,这些配置列在这里:
export MESOS_NATIVE_JAVA_LIBRARY=<path to libMesos.so>
export SPARK_EXECUTOR_URI=<URL of Spark-1.6.0.tar.gz uploaded above>
现在,在集群上启动 Spark 应用时,创建 SparkContext
时将 Mesos://
URL 作为 master。下面是一个示例:
val conf = new SparkConf().setMaster("Mesos://HOST:5050").setAppName("My app").set("Spark.executor.uri", "<path to Spark-1.6.0.tar.gz uploaded above>")
val sc = new SparkContext(conf)
在集群模式下提交作业
在集群模式下,驱动程序在集群中启动,客户端可以在 Mesos Web UI 上查看驱动程序的结果。我们需要启动 MesosClusterDispatcher
才能使用集群模式。启动 MesosClusterDispatcher
的脚本位于 sbin/start-Mesos-dispatcher.sh
,该脚本需要传入 Mesos master URL。然后,我们可以通过指定 MesosClusterDispatcher
的 URL(例如 Mesos://dispatcher:7077
)将作业提交到 Mesos 集群。驱动程序状态将在 Spark 集群 Web UI 上显示。
这是一个示例:
./bin/Spark-submit \
--class org.apache.Spark.examples.SparkPi \
--master Mesos://207.184.161.138:7077 \
--deploy-mode cluster
--supervise
--executor-memory 20G \
--total-executor-cores 100 \
http://path/to/examples.jar \
1000
请注意,传递给 Spark-submit 的 jars 或 Python 文件应该是 Mesos slave 可访问的 URI,因为 Spark 驱动程序不会自动上传本地的 jars。
高级配置指南
Spark 当前支持两种在 Mesos 上运行的模式:粗粒度 模式 和 细粒度 模式:
-
粗粒度 模式:粗粒度模式是默认模式,它会在每个 Mesos 机器上启动一个长时间运行的 Spark 任务,并在其中动态调度自己的子任务。这个模式通常用于需要较低启动开销的场景,但它的代价是需要在应用程序整个持续时间内保留 Mesos 资源。我们可以通过在
SparkConf
中设置Spark.cores.max
属性来控制 Spark 在粗粒度模式下获取的最大资源数量。默认情况下,它会获取集群中所有可用的资源。 -
细粒度 模式:在细粒度模式下,每个 Spark 任务都作为一个独立的 Mesos 任务运行。这可以更精细地共享集群资源,在工作负载增加或减少时,每个应用可以获得更多或更少的机器。缺点是每个任务启动时会有额外的开销。此模式不适用于低延迟要求的场景,如交互式查询或 Web 请求服务。要运行细粒度模式,我们可以通过设置以下属性来关闭粗粒度模式:
conf.set("Spark.Mesos.coarse", "false")
Spark 配置属性
Mesos 特定的 Spark 配置属性可以在 spark.apache.org/docs/latest/running-on-mesos.html#configuration
中找到。
Storm on Mesos
Storm 是一个实时的 分布式数据处理系统,用于处理高速流入的数据。它可以每秒处理数百万条记录,并特别适用于对毫秒级延迟有严格要求的应用(例如,安全威胁检测、欺诈检测、运营监控等)。
Storm 架构
一个典型的 Storm 集群包含三种类型的节点:
-
Nimbus 或主节点:负责提交和分发计算执行,此外还处理启动从节点、监控执行等任务
-
ZooKeeper 节点:负责协调集群
-
Supervisor 节点:负责根据 Nimbus 节点发送的指令启动和停止从节点!Storm 架构
Storm 中使用的一些重要术语包括:
-
元组:这是一个有序的元素列表
-
流:这是一个元组的序列
-
Spouts:这些是计算中的流源(例如,Twitter API)
-
Bolts:这些是处理输入流并产生输出流的组件
-
拓扑:这些是以 spouts 和 bolts 网络形式表示的整体计算,如下所示:
设置 Storm on Mesos
本节解释了如何将 Storm 与 Mesos 集群资源管理器集成。让我们按照这里提到的步骤操作:
-
我们可以先通过执行以下命令来探索 Storm on Mesos 仓库:
$ git clone https://github.com/Mesos/Storm $ cd Storm
-
现在,我们可以编辑
conf/Storm.yaml
中列出的配置文件:# Please change these for your cluster to reflect your cluster settings # ----------------------------------------------------------- Mesos.master.url: "zk://localhost:2181/Mesos" Storm.zookeeper.servers: - "localhost" # ----------------------------------------------------------- # Worker resources topology.Mesos.worker.cpu: 1.0 # Worker heap with 25% overhead topology.Mesos.worker.mem.mb: 512 worker.childopts: "-Xmx384m" # Supervisor resources topology.Mesos.executor.cpu: 0.1 topology.Mesos.executor.mem.mb: 500 # Supervisor memory, with 20% overhead supervisor.childopts: "-Xmx400m" # The default behavior is to launch the 'logviewer' unless 'autostart' is false. If you enable the logviewer, you'll need to add memory overhead to the executor for the logviewer. logviewer.port: 8000 logviewer.childopts: "-Xmx128m" logviewer.cleanup.age.mins: 10080 logviewer.appender.name: "A1" supervisor.autostart.logviewer: true # Use the public Mesosphere Storm build. Please note that it won't work with other distributions. You may want to make this empty if you use `Mesos.container.docker.image` instead. # Mesos.executor.uri: "file:///usr/local/Storm/Storm-Mesos-0.9.6.tgz" # Alternatively, use a Docker image instead of URI. If an image is specified, Docker will be used instead of Mesos containers. Mesos.container.docker.image: "Mesosphere/Storm" # Use Netty to avoid ZMQ dependencies Storm.messaging.transport: "backtype.Storm.messaging.netty.Context" Storm.local.dir: "Storm-local" # role must be one of the Mesos-master's roles defined in the --roles flag Mesos.framework.role: "*" Mesos.framework.checkpoint: true Mesos.framework.name: "Storm" # For setting up the necessary Mesos authentication see Mesos authentication page and set the Mesos-master flags --credentials, --authenticate, --acls, and --roles. Mesos.framework.principal: "Storm" # The "secret" phrase cannot be followed by a NL Mesos.framework.secret.file: "Storm-local/secret" #Mesos.allowed.hosts: - host1 #Mesos.disallowed.hosts: - host1
-
根据我们集群的配置编辑属性,并执行以下命令以启动 nimbus:
$ bin/Storm-Mesos nimbus
-
我们需要在与 nimbus 相同的机器上启动 UI,使用以下命令:
$ bin/Storm ui
拓扑以与常规 Storm 集群相同的方式提交给 Storm/Mesos 集群。Storm/Mesos 提供了拓扑之间的资源隔离,因此,您不必担心拓扑之间的相互干扰。
运行示例拓扑
一旦 nimbus 启动,我们可以使用以下命令启动其中一个 Storm-starter 拓扑:
$ ./bin/Storm jar -c nimbus.host=10.0.0.1 -c nimbus.thrift.port=32001 examples/Storm-starter/Storm-starter-topologies-0.9.6.jar Storm.starter.WordCountTopology word-count
在这里,我们将 nimbus 主机和 thrift 端口指定为参数。
一个高级配置指南
如果我们想将发布版本构建为不同版本的 Mesos 或 Storm,我们可以使用 build-release.sh
脚本,通过执行以下命令下载适当的版本:
STORM_RELEASE=x.x.x MESOS_RELEASE=y.y.y bin/build-release.sh
这里 x.x.x
和 y.y.y
是我们将要构建的 Storm 和 Mesos 的适当版本。此命令将构建一个 Mesos 执行程序包。
bin/build-release.sh
脚本接收以下子命令:
子命令 | 用法 |
---|---|
main |
用于使用 Mesos 调度器构建 Storm 包。此命令的输出可以用作 mesos.executor.uri 的包。 |
clean |
尝试清理在构建时创建的工作文件和目录。 |
downloadStormRelease |
这是一个实用程序,用于下载目标 Storm 版本的发行 tar 包。设置 MIRROR 环境变量来配置下载镜像。 |
mvnPackage |
运行构建 Storm Mesos 框架所需的 Maven 目标。 |
prePackage |
准备工作目录,以便能够打包 Storm Mesos 框架,是一个可选参数,指定要打包的 Storm 发行 tar 包。 |
package |
打包 Storm Mesos 框架。 |
dockerImage |
从当前代码构建一个 Docker 镜像。 |
help |
输出 build-release.sh 脚本的使用信息。 |
通过 Marathon 部署 Storm
我们可以通过设置 MESOS_MASTER_ZK
环境变量,将其指向集群的 ZooKeeper 节点,轻松地在 Mesos 上运行 Storm 并通过 Marathon 启动。该代码库还包含一个脚本,bin/run-with-marathon.sh
,它设置了所需的参数并启动了 UI 和 Nimbus。由于 Storm 会将有状态数据写入磁盘,我们需要确保设置了 storm.local.dir config
。我们可以通过提交以下 JSON 数据从 Marathon 中运行:
{
"id": "storm-nimbus",
"cmd": "./bin/run-with-marathon.sh",
"cpus": 1.0,
"mem": 1024,
"ports": [0, 1],
"instances": 1,
"container": {
"type": "DOCKER",
"docker": {
"image": "mesosphere/storm",
"network": "HOST",
"forcePullImage":true
}
},
"healthChecks": [
{
"protocol": "HTTP",
"portIndex": 0,
"path": "/",
"gracePeriodSeconds": 120,
"intervalSeconds": 20,
"maxConsecutiveFailures": 3
}
]
}
我们可以将前述 JSON 代码保存为 storm-mesos.json
并发送 curl
请求到 Marathon API 端点,以使用以下命令进行部署:
$ curl -X POST -H "Content-Type: application/json" -d storm-mesos.json http://marathon-machine:8080/v2/apps
在 Mesos 上运行 Samza
Samza 是一个开源的分布式流处理框架,最初由 LinkedIn 开发。它具有以下特点:
-
一个简单的 API
-
状态管理
-
容错
-
持久性
-
可扩展性
-
可插拔性
-
处理器隔离
Samza 的重要概念
Samza 中的一些概念将在以下章节中描述。
流
Samza 处理数据流——例如,网站点击流、服务器日志或任何其他事件数据。消息可以添加到数据流中并从中读取。多个框架可以访问同一数据流,并根据消息中存在的键对数据进行分区。
任务
Samza 任务是计算逻辑,它从输入流中读取数据,对数据进行一些转换,并将结果消息输出到多个输出流。
分区
每个流被拆分为一个或多个分区。每个分区是一个有序的消息序列。
任务
一个任务被细分为多个子任务,以实现计算的并行性。每个任务从任务的每个输入流的单个分区中读取数据。
数据流图
可以组合多个任务来开发数据流图,其中节点是数据流,边是任务。
在 Mesos 上设置 Samza
本主题介绍了如何在 Mesos 集群上运行 Samza 作业。为了简化,我们将把 Samza 作业打包成 tarball。Samza 也支持将其打包为 Docker 镜像。
在编写本书时,Samza 在 Mesos 上还处于早期阶段,且据我们所知,尚未在生产环境中进行过测试。让我们按照此处提到的步骤在 Mesos 上设置 Samza:
-
我们首先需要在环境中部署
samza-mesos
jar 文件。为此,我们可以克隆仓库并使用以下命令构建:$ git clone https://github.com/Banno/samza-mesos $ cd samza-mesos $ mvn clean install
-
一旦完成这些设置,我们可以开始将 Maven 依赖项导入到我们的项目中,方法如下:
<dependency> <groupId>eu.inn</groupId> <artifactId>samza-mesos</artifactId> <version>0.1.0-SNAPSHOT</version> </dependency>
通过 Marathon 部署 Samza
Samza 作业可以通过 Marathon 部署。每个 Samza 作业都是一个 Mesos 框架,它为每个 Samza 容器创建一个 Mesos 任务。正如这里所描述的那样,通过 Marathon 在 Mesos 上部署 Samza 作业更加简便。
Samza 作业通常以 tarball 形式部署,其中应包含以下顶级目录:
-
Bin
:此目录包含标准的 Samza 分布式 Shell 脚本 -
Config
:此目录应包含你的作业.properties
文件 -
Lib
:此目录包含所有的.jar
文件
现在,让我们来看一下如何提交 Samza 作业到 Marathon 以便在 Mesos 上部署。相应的 JSON 请求将如下所示:
{
"id": "samza-jobs.my-job", /Job ID/
"uris": [
"hdfs://master-machine/my-job.tgz" /Job Resource/
],
"cmd": "bin/run-job.sh --config-path=file://$PWD/config/my-job.properties --config=job.factory.class=eu.inn.samza.mesos.MesosJobFactory --config=mesos.master.connect=zk://zookeeper-machine:2181/mesos --config=mesos.package.path=hdfs://master-machine/my-job.tgz --config=mesos.executor.count=1", /Job Properties/
"cpus": 0.1,
"mem": 64, /Resources/
"instances": 1,
"env": {
"JAVA_HEAP_OPTS": "-Xms64M -Xmx64M"
}
}
请注意,mesos.package.path
是指向 Samza tarball 的参数,它存储在 HDFS 中。
我们可以将前面的 JSON 记录保存为名为 samza-job.json
的文件,并使用以下 curl
命令将其提交到 Marathon:
$ curl -X POST -H "Content-Type: application/json" -d samza-job.json http://marathon-machine:8080/v2/apps
高级配置指南
支持的配置属性列出在 github.com/Banno/samza-mesos
。
概述
本章向读者介绍了一些重要的大数据处理框架,并涵盖了如何在分布式基础设施上使用 Mesos 设置、配置和管理这些框架等主题。
在下一章中,我们将讨论一些目前 Mesos 支持的重要大数据存储框架(无论是处于测试版还是生产就绪状态),例如 Cassandra、Elasticsearch 和 Kafka,并了解如何在 Mesos 上进行设置和配置。
第九章:Mesos 大数据框架 2
本章将介绍如何在 Mesos 上部署重要的大数据存储框架,如 Cassandra、Elasticsearch-Logstash-Kibana(ELK)堆栈和 Kafka。
Cassandra 在 Mesos 上的使用
本节将介绍 Cassandra,并解释如何在 Mesos 上部署 Cassandra,同时讨论在设置过程中常遇到的问题。
Cassandra 介绍
Cassandra 是一个开源、可扩展的 NoSQL 数据库,完全分布式且没有单点故障,对于大多数标准使用案例具有高性能。它既支持横向扩展也支持纵向扩展。横向扩展 或 扩展解决方案 涉及通过添加更多的普通硬件节点来扩展现有集群,而 纵向扩展 或 扩展升级解决方案 则意味着通过为节点添加更多的 CPU 和内存资源来使用专用硬件。
Cassandra 是由 Facebook 工程师开发的,旨在解决收件箱搜索的使用案例,灵感来自 Google Bigtable(它为 Cassandra 的存储模型提供了基础)以及 Amazon DynamoDB(它为 Cassandra 的分布式模型提供了基础)。Cassandra 于 2008 年开源,并在 2010 年初成为 Apache 顶级项目。它提供了一种名为 Cassandra 查询语言 或 CQL 的查询语言,语法类似 SQL,用于与数据库进行通信。
Cassandra 提供了多种功能,例如:
-
高性能
-
持续的正常运行时间(无单点故障)
-
易用性
-
数据在数据中心之间的复制和分发
Cassandra 的架构采用优雅且简洁的 环形设计,没有任何主节点,而不是使用传统的主从或分片设计。这使得它能够提供前述所有特性和优势。
Cassandra 环形设计图如下所示(来源:www.planetcassandra.org):
许多公司在生产环境中使用 Cassandra,包括 Apple、Instagram、eBay、Spotify、Comcast 和 Netflix 等公司。
Cassandra 最适用于以下场景:
-
无单点故障
-
实时写入
-
灵活性
-
横向扩展
-
可靠性
-
在 NoSQL 环境中拥有明确定义的表结构
以下是一些常见的使用场景:
-
存储、管理以及对由消息应用程序生成的数据进行分析(Instagram 和 Comcast 等公司都使用 Cassandra 来处理这类数据)
-
存储用于检测欺诈活动的数据模式
-
存储用户选择和策划的项目(购物车、播放列表等)
-
推荐和个性化
性能基准
以下由独立数据库公司进行的性能基准测试显示,对于混合的操作和分析工作负载,Cassandra 明显优于其他开源 NoSQL 技术(来源:www.datastax.com):
在 Mesos 上设置 Cassandra
本节介绍了在 Mesos 上部署 Cassandra 的过程。推荐的 Cassandra 在 Mesos 上的部署方式是通过 Marathon。在编写本书时,Cassandra 在 Mesos 上的配置仍处于实验阶段,此处描述的配置可能在未来的版本中发生变化。
Mesosphere 团队已经将所需的 JAR 文件和 Cassandra 执行器打包在一个 tar 包中,可以通过以下 JSON 代码直接提交给 Mesos 并通过 Marathon 运行:
{
"healthChecks": [
{
"timeoutSeconds": 5,
"protocol": "HTTP",
"portIndex": 0,
"path": "/health/cluster",
"maxConsecutiveFailures": 0,
"intervalSeconds": 30,
"gracePeriodSeconds": 120
},
{
"timeoutSeconds": 5,
"protocol": "HTTP",
"portIndex": 0,
"path": "/health/process",
"maxConsecutiveFailures": 3,
"intervalSeconds": 30,
"gracePeriodSeconds": 120
}
],
"id": "/cassandra/dev-test",
"instances": 1,
"cpus": 0.5,
"mem": 512,
"ports": [0],
"uris": [
"https://downloads.mesosphere.io/cassandra-mesos/artifacts/0.2.1-SNAPSHOT-608-master-d1c2cf30c8/cassandra-mesos-0.2.1-SNAPSHOT-608-master-d1c2cf30c8.tar.gz",
"https://downloads.mesosphere.io/java/jre-7u76-linux-x64.tar.gz"
],
"env": {
"CASSANDRA_ZK_TIMEOUT_MS": "10000",
"CASSANDRA_HEALTH_CHECK_INTERVAL_SECONDS": "60",
"MESOS_ZK": "zk://localhost:2181/mesos",
"JAVA_OPTS": "-Xms256m -Xmx256m",
"CASSANDRA_CLUSTER_NAME": "dev-test",
"CASSANDRA_ZK": "zk://localhost:2181/cassandra-mesos",
"CASSANDRA_NODE_COUNT": "3",
"CASSANDRA_RESOURCE_CPU_CORES": "2.0",
"CASSANDRA_RESOURCE_MEM_MB": "2048",
"CASSANDRA_RESOURCE_DISK_MB": "2048"
},
"cmd": "$(pwd)/jre*/bin/java $JAVA_OPTS -classpath cassandra-mesos-framework.jar io.mesosphere.mesos.frameworks.cassandra.framework.Main"
}
编辑 JSON 代码,指向MESOS_ZK
及任何其他需要更改的参数,将此 JSON 代码保存为cassandra-mesos.json
,然后通过以下命令将其提交给 Marathon:
$ curl -X POST -H "Content-Type: application/json" -d cassandra-mesos.json http://marathon-machine:8080/v2/apps
一旦提交,框架将自我引导。我们还需要扩展每个 Mesos 节点管理的端口范围,以包括标准的 Cassandra 端口。我们可以在启动过程中将端口范围作为资源传递。
这是一个示例:
--resources='ports:[31000-32000,7000-7001,7199-7199,9042-9042,9160-9160]'
Mesos 上的 Cassandra 提供了一个 REST 端点,用于调整设置。我们可以通过默认端口18080
访问此端点(除非已更改)。
高级配置指南
如前所述,Mesos 上的 Cassandra 通过环境变量接受运行时配置。我们可以使用以下环境变量来引导框架的配置。在初始运行后,配置会从 ZooKeeper 中存储的框架状态中读取:
# name of the cassandra cluster, this will be part of the framework name in Mesos
CASSANDRA_CLUSTER_NAME=dev-cluster
# Mesos ZooKeeper URL to locate leading master
MESOS_ZK=zk://localhost:2181/mesos
# ZooKeeper URL to be used to store framework state
CASSANDRA_ZK=zk://localhost:2181/cassandra-mesos
# The number of nodes in the cluster (default 3)
CASSANDRA_NODE_COUNT=3
# The number of seed nodes in the cluster (default 2)
# set this to 1, if you only want to spawn one node
CASSANDRA_SEED_COUNT=2
# The number of CPU Cores for each Cassandra Node (default 2.0)
CASSANDRA_RESOURCE_CPU_CORES=2.0
# The number of Megabytes of RAM for each Cassandra Node (default 2048)
CASSANDRA_RESOURCE_MEM_MB=2048
# The number of Megabytes of Disk for each Cassandra Node (default 2048)
CASSANDRA_RESOURCE_DISK_MB=2048
# The number of seconds between each health check of the Cassandra node (default 60)
CASSANDRA_HEALTH_CHECK_INTERVAL_SECONDS=60
# The default bootstrap grace time - the minimum interval between two node starts
# You may set this to a lower value in pure local development environments.
CASSANDRA_BOOTSTRAP_GRACE_TIME_SECONDS=120
# The number of seconds that should be used as the mesos framework timeout (default 604800 seconds / 7 days)
CASSANDRA_FAILOVER_TIMEOUT_SECONDS=604800
# The mesos role to used to reserve resources (default *). If this is set, the framework accepts offers that have resources for that role or the default role *
CASSANDRA_FRAMEWORK_MESOS_ROLE=*
# A pre-defined data directory specifying where Cassandra should write its data.
# Ensure that this directory can be created by the user the framework is running as (default. [mesos sandbox]).
# NOTE:
# This field is slated to be removed and the framework will be able to allocate the data volume itself.
CASSANDRA_DATA_DIRECTORY=.
这里有一些参考资料:
Mesos 上的 Elasticsearch-Logstash-Kibana(ELK)栈
本节将介绍Elasticsearch-Logstash-Kibana(ELK)栈,并解释如何在 Mesos 上设置它,同时讨论在设置过程中常见的问题。
Elasticsearch、Logstash 和 Kibana 简介
ELK 栈,Elasticsearch、Logstash和Kibana的组合,是一个端到端的日志分析解决方案。Elasticsearch 提供搜索功能,Logstash 是日志管理软件,而 Kibana 作为可视化层。该栈由名为Elastic的公司提供商业支持。
Elasticsearch
Elasticsearch 是一个基于 Lucene 的开源分布式搜索引擎,旨在实现高可扩展性和快速的搜索查询响应时间。它通过提供一个强大的 REST API 来简化 Lucene 的使用,Lucene 是一个高性能的搜索引擎库。以下是 Elasticsearch 中的一些重要概念:
-
文档:这是存储在索引中的一个 JSON 对象
-
索引:这是一个文档集合
-
类型:这是一个表示文档类别的索引逻辑分区
-
字段:这是文档中的一个键值对
-
映射:用于将每个字段与其数据类型进行映射
-
Shard:这是存储索引数据的物理位置(数据存储在一个主分片上,并复制到一组副本分片上)
Logstash
这是一个收集和处理由各种系统生成的日志事件的工具。它包括一组丰富的输入和输出连接器,用于获取日志并使其可供分析。一些重要功能包括:
-
将日志转换为通用格式以便于使用的能力
-
处理多种日志格式的能力,包括自定义格式
-
丰富的输入和输出连接器集
Kibana
这是一个基于 Elasticsearch 的数据可视化工具,具有多种图表和仪表板功能。它依赖于存储在 Elasticsearch 索引中的数据,完全使用 HTML 和 JavaScript 开发。其一些最重要的功能包括:
-
用于仪表板构建的图形用户界面
-
丰富的图表集(地图、饼图、直方图等)
-
将图表嵌入用户应用程序的能力
ELK 栈数据管道
查看以下图示(来源:Learning ELK Stack,Packt 出版社):
在标准 ELK 栈管道中,各种应用程序服务器的日志通过 Logstash 传输到中央索引模块。该索引模块随后将输出传输到 Elasticsearch 集群,在那里可以直接查询或通过 Kibana 在仪表板中进行可视化。
在 Mesos 上设置 Elasticsearch-Logstash-Kibana
本节解释如何在 Mesos 上设置 Elasticsearch、Logstash 和 Kibana。我们将首先介绍如何在 Mesos 上设置 Elasticsearch,然后是 Logstash 和 Kibana。
Mesos 上的 Elasticsearch
我们将使用 Marathon 来部署 Elasticsearch,这可以通过两种方式完成:通过 Docker 镜像(强烈推荐),以及通过elasticsearch-mesos jar
。这两种方式将在以下部分进行解释。
我们可以使用以下 Marathon 文件将 Elasticsearch 部署到 Mesos 上。它使用 Docker 镜像:
{
"id": "elasticsearch-mesos-scheduler",
"container": {
"docker": {
"image": "mesos/elasticsearch-scheduler",
"network": "HOST"
}
},
"args": ["--zookeeperMesosUrl", "zk://zookeeper-node:2181/mesos"],
"cpus": 0.2,
"mem": 512.0,
"env": {
"JAVA_OPTS": "-Xms128m -Xmx256m"
},
"instances": 1
}
确保将zookeeper-node
更改为集群中 ZooKeeper 节点的地址。我们可以将其保存到elasticsearch.json
文件,并通过以下命令在 Marathon 上部署:
$ curl -k -XPOST -d @elasticsearch.json -H "Content-Type: application/json" http://marathon-machine:8080/v2/apps
如前所述,我们也可以使用 JAR 文件通过以下 Marathon 文件将 Elasticsearch 部署到 Mesos 上:
{
"id": "elasticsearch",
"cpus": 0.2,
"mem": 512,
"instances": 1,
"cmd": "java -jar scheduler-0.7.0.jar --frameworkUseDocker false --zookeeperMesosUrl zk://10.0.0.254:2181 --frameworkName elasticsearch --elasticsearchClusterName mesos-elasticsearch --elasticsearchCpu 1 --elasticsearchRam 1024 --elasticsearchDisk 1024 --elasticsearchNodes 3 --elasticsearchSettingsLocation /home/ubuntu/elasticsearch.yml",
"uris": ["https://github.com/mesos/elasticsearch/releases/download/0.7.0/scheduler-0.7.0.jar"],
"env": {
"JAVA_OPTS": "-Xms256m -Xmx512m"
},
"ports": [31100],
"requirePorts": true,
"healthChecks": [
{
"gracePeriodSeconds": 120,
"intervalSeconds": 10,
"maxConsecutiveFailures": 6,
"path": "/",
"portIndex": 0,
"protocol": "HTTP",
"timeoutSeconds": 5
}
]
}
在这两种情况下,都需要JAVA_OPTS
环境变量,如果没有设置,可能会导致 Java 堆内存空间的问题。我们可以将其保存为elasticsearch.json
文件,并通过以下命令提交给 Marathon:
$ curl -k -XPOST -d @elasticsearch.json -H "Content-Type: application/json" http://MARATHON_IP_ADDRESS:8080/v2/apps
Docker 镜像和 JAR 文件都需要以下命令行参数,类似于--zookeeperMesosUrl
参数:
--dataDir
The host data directory used by Docker volumes in the executors. [DOCKER MODE ONLY]
Default: /var/lib/mesos/slave/elasticsearch
--elasticsearchClusterName
Name of the Elasticsearch cluster
Default: mesos-ha
--elasticsearchCpu
The amount of CPU resource to allocate to the Elasticsearch instance.
Default: 1.0
--elasticsearchDisk
The amount of Disk resource to allocate to the Elasticsearch instance
(MB).
Default: 1024.0
--elasticsearchExecutorCpu
The amount of CPU resource to allocate to the Elasticsearch executor.
Default: 0.1
--elasticsearchExecutorRam
The amount of ram resource to allocate to the Elasticsearch executor
(MB).
Default: 32.0
--elasticsearchNodes
Number of Elasticsearch instances.
Default: 3
--elasticsearchPorts
User specified Elasticsearch HTTP and transport ports. [NOT RECOMMENDED]
Default: <empty string>
--elasticsearchRam
The amount of ram resource to allocate to the Elasticsearch instance
(MB).
Default: 256.0
--elasticsearchSettingsLocation
Path or URL to Elasticsearch yml settings file. [In docker mode file must be in /tmp/config] E.g. '/tmp/config/elasticsearch.yml' or 'https://gist.githubusercontent.com/mmaloney/5e1da5daa58b70a3a671/raw/elasticsearch.yml'
Default: <empty string>
--executorForcePullImage
Option to force pull the executor image. [DOCKER MODE ONLY]
Default: false
--executorImage
The docker executor image to use. E.g. 'elasticsearch:latest' [DOCKER
MODE ONLY]
Default: elasticsearch:latest
--executorName
The name given to the executor task.
Default: elasticsearch-executor
--frameworkFailoverTimeout
The time before Mesos kills a scheduler and tasks if it has not recovered
(ms).
Default: 2592000.0
--frameworkName
The name given to the framework.
Default: elasticsearch
--frameworkPrincipal
The principal to use when registering the framework (username).
Default: <empty string>
--frameworkRole
Used to group frameworks for allocation decisions, depending on the
allocation policy being used.
Default: *
--frameworkSecretPath
The path to the file which contains the secret for the principal
(password). Password in file must not have a newline.
Default: <empty string>
--frameworkUseDocker
The framework will use docker if true, or jar files if false. If false, the user must ensure that the scheduler jar is available to all slaves.
Default: true
--javaHome
When starting in jar mode, if java is not on the path, you can specify
the path here. [JAR MODE ONLY]
Default: <empty string>
--useIpAddress
If true, the framework will resolve the local ip address. If false, it
uses the hostname.
Default: false
--webUiPort
TCP port for web ui interface.
Default: 31100
--zookeeperMesosTimeout
The timeout for connecting to zookeeper for Mesos (ms).
Default: 20000
* --zookeeperMesosUrl
Zookeeper urls for Mesos in the format zk://IP:PORT,IP:PORT,...)
Default: zk://mesos.master:2181
Mesos 上的 Logstash
本节介绍如何在 Mesos 上运行 Logstash。Logstash 部署到集群后,任何在 Mesos 上运行的程序都可以记录事件,事件随后通过 Logstash 被传递并发送到中央日志位置。
我们可以将 Logstash 作为 Marathon 应用程序运行,并通过以下 Marathon 文件在 Mesos 上部署:
{
"id": "/logstash",
"cpus": 1,
"mem": 1024.0,
"instances": 1,
"container": {
"type": "DOCKER",
"docker": {
"image": "mesos/logstash-scheduler:0.0.6",
"network": "HOST"
}
},
"env": {
"ZK_URL": "zk://123.0.0.12:5181/logstash",
"ZK_TIMEOUT": "20000",
"FRAMEWORK_NAME": "logstash",
"FAILOVER_TIMEOUT": "60",
"MESOS_ROLE": "logstash",
"MESOS_USER": "root",
"LOGSTASH_HEAP_SIZE": "64",
"LOGSTASH_ELASTICSEARCH_URL": "http://elasticsearch.service.consul:1234",
"EXECUTOR_CPUS": "0.5",
"EXECUTOR_HEAP_SIZE": "128",
"ENABLE_FAILOVER": "false",
"ENABLE_COLLECTD": "true",
"ENABLE_SYSLOG": "true",
"ENABLE_FILE": "true",
"ENABLE_DOCKER": "true",
"EXECUTOR_FILE_PATH": "/var/log/*,/home/jhf/example.log"
}
}
在这里,我们使用了用于部署的 Docker 镜像,其配置可以根据您的集群规格进行更改。将前面的文件保存为 logstash.json
并使用以下命令提交给 Marathon:
$ curl -k -XPOST -d @logstash.json -H "Content-Type: application/json" http://MARATHON_IP_ADDRESS:8080/v2/apps
Mesos 上的 Logstash 配置
Logstash 和 Elasticsearch 经 Mesos 版本 0.25.0 及更高版本测试。我们需要将 Logstash 添加到每个 Mesos 主节点的角色列表中。这可以通过以下命令完成:
$ sudo echo logstash > /etc/mesos-master/roles
如果 Logstash 的目的是监控 syslog
(一种消息日志标准),则需要在集群中每个 Mesos 节点的资源列表中添加 TCP 和 UDP 端口 514
。可以通过在 /etc/mesos-slave/resources
文件中添加以下条目来实现:
ports(logstash):[514-514]
要监控 collectd
,我们需要通过在 /etc/mesos-slave/resources
文件中添加以下行来将 TCP 和 UDP 端口 25826
添加到 Logstash 角色的资源中:
ports(logstash):[25826-25826]
Kibana 在 Mesos 上
如果我们在 Mesos 上运行 Kibana,那么每个 Kibana 实例将作为 Docker 镜像在 Mesos 集群中运行。对于每个 Elasticsearch 实例,可以部署一个或多个 Kibana 实例来为用户提供服务。
我们可以从以下代码库克隆 Mesos 项目上的 Kibana:
$ git clone https://github.com/mesos/kibana
使用以下命令构建项目:
$ cd kibana
$ gradlew jar
这将生成 Kibana JAR 文件(kibana.jar
)。
一旦 kibana.jar
文件生成,我们可以使用以下命令进行部署:
$ java -jar /path/to/kibana.jar -zk zk://zookeeper:2181/mesos -v 4.3.1 -es http://es-host:9200
在这里,-zk
代表 ZooKeeper URI,-es
指向我们在前一节中部署的 Elasticsearch 端点。请根据需要设置它们。
kibana.jar
文件还支持以下命令行选项:
短关键词 | 关键词 | 定义 |
---|---|---|
-zk |
-zookeeper |
这是 Mesos ZooKeeper 的 URL(必需) |
-di |
-dockerimage |
这是要使用的 Docker 镜像名称(默认值为 kibana ) |
-v |
-version |
这是要使用的 Kibana Docker 镜像的版本(默认值为 latest ) |
-mem |
-requiredMem |
这是分配给单个 Kibana 实例的内存量(单位:MB,默认值为 128 ) |
-cpu |
-requiredCpu |
这是分配给单个 Kibana 实例的 CPU 数量(默认值为 0.1 ) |
-disk |
-requiredDisk |
这是分配给单个 Kibana 实例的磁盘空间量(单位:MB,默认值为 25 ) |
-es |
-elasticsearch |
这些是启动时用于启动 Kibana 的 Elasticsearch URL |
这里有一些参考资料:
Kafka on Mesos
本节将介绍 Kafka,并解释如何在 Mesos 上进行设置,同时讨论在设置过程中常见的问题。
Kafka 简介
Kafka 是一个分布式发布-订阅消息系统,旨在提供速度、可扩展性、可靠性和耐久性。Kafka 中使用的一些关键术语如下所示:
-
主题:这些是 Kafka 维护消息流的类别。
-
生产者:这些是上游进程,它们将消息发送到特定的 Kafka 主题。
-
消费者:这些是下游进程,它们监听主题中传入的消息,并根据要求处理它们。
-
代理:Kafka 集群中的每个节点称为代理。
请查看以下 Kafka 的高层次示意图(来源:kafka.apache.org/documentation.html#introduction
):
Kafka 集群为每个主题维护一个分区日志,类似于以下内容(来源:kafka.apache.org/documentation.html#intro_topics
):
Kafka 的使用案例
这里描述了一些 Kafka 的重要用途:
-
网站活动跟踪:网站活动事件,例如页面浏览和用户搜索,可以由 Web 应用程序发送到 Kafka 主题。下游处理系统可以订阅这些主题并消费消息,用于批量分析、监控、实时仪表盘等用例。
-
日志聚合:Kafka 作为传统日志聚合系统的替代方案。可以从各种服务收集物理日志文件并推送到不同的 Kafka 主题,在那里不同的消费者可以读取和处理它们。Kafka 抽象了文件细节,从而实现了更快的处理并支持多种数据源。
-
流处理:框架,例如 Spark Streaming,可以从 Kafka 主题中消费数据,按照要求处理,然后将处理后的输出发布到另一个 Kafka 主题,在那里该输出可以被其他应用程序消费。
Kafka 设置
在 Mesos 上安装 Kafka 之前,确保机器上已安装以下应用程序:
-
Java 版本 7 或更高版本(
openjdk.java.net/install/
) -
Gradle(
gradle.org/installation
)
我们可以从以下仓库克隆并构建 Kafka on Mesos 项目:
$ git clone https://github.com/mesos/kafka
$ cd kafka
$ ./gradlew jar
我们还需要 Kafka 执行器,可以通过以下命令下载:
$ wget https://archive.apache.org/dist/kafka/0.8.2.2/kafka_2.10-0.8.2.2.tgz
我们还需要设置以下环境变量,以指向libmesos.so
文件:
$ export MESOS_NATIVE_JAVA_LIBRARY=/usr/local/lib/libmesos.so
一旦这些设置好,我们可以使用kafka-mesos.sh
脚本在 Mesos 上启动并配置 Kafka。在此之前,我们需要创建kafka-mesos.properties
文件,其内容如下:
storage=file:kafka-mesos.json
master=zk://master:2181/mesos
zk=master:2181
api=http://master:7000
如果我们不希望每次都将参数传递给调度器,可以使用该文件来配置调度器(kafka-mesos.sh
)。调度器支持以下命令行参数:
选项 | 描述 |
---|---|
--api |
这是 API URL,例如http://master:7000 。 |
--bind-address |
这是调度器绑定地址(例如 master,0.0.0.0 ,192.168.50.* ,以及if:eth1 )。默认值是all 。 |
--debug <Boolean> |
这是调试模式。默认值是false 。 |
--framework-name |
这是框架名称。默认值是kafka 。 |
--framework-role |
这是框架角色。默认值是* 。 |
--framework-timeout |
这是框架超时时间(30s、1m 或 1h)。默认值是30d 。 |
--jre |
这是 JRE 压缩文件(jre-7-openjdk.zip )。默认值是none 。 |
--log |
这是要使用的日志文件。默认值是stdout 。 |
--master |
这些是主连接设置。一些示例如下:- master:5050 - master:5050,master2:5050 - zk://master:2181/mesos - zk://username:password@master:2181 - zk://master:2181,master2:2181/mesos |
--principal |
这是用于注册框架的主体(用户名)。默认值是none 。 |
--secret |
这是用于注册框架的密码(密码)。默认值是none 。 |
--storage |
这是集群状态的存储位置。一些示例如下:- file:kafka-mesos.json - zk:/kafka-mesos 默认值是file:kafka-mesos.json 。 |
--user |
这是运行任务的 Mesos 用户。默认值是none 。 |
--zk |
这是 Kafka 的zookeeper.connect 。一些示例如下:- master:2181 - master:2181,master2:2181 |
现在,我们可以使用调度器通过以下命令运行 Kafka 调度器:
#Start the kafka scheduler
$ ./kafka-mesos.sh scheduler
接下来,我们需要做的事情是以默认设置启动一个 Kafka 代理。这可以通过以下命令完成:
$ ./kafka-mesos.sh broker add 0
broker added:
id: 0
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m
此时,我们的 Kafka 集群将有一个代理尚未启动。我们可以通过以下命令验证这一点:
$ ./kafka-mesos.sh broker list
broker:
id: 0
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m
我们现在可以使用以下命令启动这个代理:
$ ./kafka-mesos.sh broker start 0
broker started:
id: 0
active: true
state: running
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m, hostname:slave0
task:
id: broker-0-d2d94520-2f3e-4779-b276-771b4843043c
running: true
endpoint: 192.168.25.62:31000
attributes: rack=r1
如果显示上面的输出,则我们的代理已经准备好生成和消费消息。我们现在可以使用kafkacat
来测试这个设置。
kafkacat
可以通过以下命令安装到系统中:
$ sudo apt-get install kafkacat
$ echo "test" |kafkacat -P -b "192.168.25.62:31000" -t testTopic -p 0
既然我们已经将测试推送到代理上,我们可以通过以下命令将其读取回来:
$ kafkacat -C -b "192.168.25.62:31000" -t testTopic -p 0 -e
test
现在,让我们来看一下如何一次性向集群添加更多代理。运行以下命令:
$ ./kafka-mesos.sh broker add 0..2 --heap 1024 --mem 2048
前面的命令将会向集群添加三个kafka
代理,输出如下:
brokers added:
id: 0
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m
id: 1
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m
id: 2
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m
我们可以通过以下命令一次性启动所有三个代理:
$ ./kafka-mesos.sh broker start 0..2
brokers started:
id: 0
active: true
state: running
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m, hostname:slave0
task:
id: broker-0-d2d94520-2f3e-4779-b276-771b4843043c
running: true
endpoint: 192.168.25.62:31000
attributes: rack=r1
id: 1
active: true
state: running
id: 2
active: true
state: running
如果我们需要更改 Kafka 日志存储数据的位置,我们需要首先停止特定代理,然后使用以下命令更新位置:
$ ./kafka-mesos.sh broker stop 0
broker stopped:
id: 0
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m, hostname:slave0, expires:2015-07-10 15:51:43+03
$ ./kafka-mesos.sh broker update 0 --options log.dirs=/mnt/kafka/broker0
broker updated:
id: 0
active: false
state: stopped
resources: cpus:1.00, mem:2048, heap:1024, port:auto
options: log.dirs=/mnt/kafka/broker0
failover: delay:1m, max-delay:10m
stickiness: period:10m, hostname:slave0, expires:2015-07-10 15:51:43+03
完成后,我们可以使用以下命令重新启动代理:
$ ./kafka-mesos.sh broker start 0
broker started:
id: 0
active: true
state: running
resources: cpus:1.00, mem:2048, heap:1024, port:auto
failover: delay:1m, max-delay:10m
stickiness: period:10m, hostname:slave0
task:
id: broker-0-d2d94520-2f3e-4779-b276-771b4843043c
running: true
endpoint: 192.168.25.62:31000
attributes: rack=r1
Kafka 日志管理
我们可以通过以下命令获取集群中任何代理的日志的最后 100 行(stdout
- 默认或stderr
):
$ ./kafka-mesos.sh broker log 0
如果我们需要从stderr
文件读取,则可以使用以下命令:
$ ./kafka-mesos.sh broker log 0 --name stderr
我们可以通过将文件名传递给--name
选项来读取kafka-*/log/
目录中的任何文件。例如,如果我们需要读取server.log
,可以使用以下命令:
$ ./kafka-mesos.sh broker log 0 --name server.log
此外,如果我们需要从日志中读取更多行数,可以使用--lines
选项,方法如下:
$ ./kafka-mesos.sh broker log 0 --name server.log --lines 200
高级配置指南
以下是在集群中添加代理时可用的配置选项:
$ ./kafka-mesos.sh help broker add
Add broker
Usage: broker add <broker-expr> [options]
Option Description
--bind-address broker bind address (broker0, 192.168.50.*, if:eth1). Default - auto
--constraints constraints (hostname=like:master,rack=like:1.*). See below.
--cpus <Double> cpu amount (0.5, 1, 2)
--failover-delay failover delay (10s, 5m, 3h)
--failover-max-delay max failover delay. See failoverDelay.
--failover-max-tries max failover tries. Default - none
--heap <Long> heap amount in Mb
--jvm-options jvm options string (-Xms128m -XX:PermSize=48m)
--log4j-options log4j options or file. Examples
log4j.logger.kafka=DEBUG\, kafkaAppender
file:log4j.properties
--mem <Long> mem amount in Mb
--options options or file. Examples:
log.dirs=/tmp/kafka/$id,num.io.threads=16
file:server.properties
--port port or range (31092, 31090..31100). Default - auto
--stickiness-period stickiness period to preserve same node for broker (5m, 10m, 1h)
--volume pre-reserved persistent volume id
Generic Options
Option Description
------ -----------
--api Api url. Example: http://master:7000broker-expr examples:
0 - broker 0
0,1 - brokers 0,1
0..2 - brokers 0,1,2
0,1..2 - brokers 0,1,2
* - any broker
attribute filtering:
*[rack=r1] - any broker having rack=r1
*[hostname=slave*] - any broker on host with name starting with 'slave'
0..4[rack=r1,dc=dc1] - any broker having rack=r1 and dc=dc1
constraint examples:
like:master - value equals 'master'
unlike:master - value not equals 'master'
like:slave.* - value starts with 'slave'
unique - all values are unique
cluster - all values are the same
cluster:master - value equals 'master'
groupBy - all values are the same
groupBy:3 - all values are within 3 different groups
现在我们来看一下启动代理时可用的选项:
$ ./kafka-mesos.sh help broker start
Start broker
Usage: broker start <broker-expr> [options]
Option Description
------ -----------
--timeout timeout (30s, 1m, 1h). 0s - no timeout
Generic Options
Option Description
------ -----------
--api Api url. Example: http://master:7000
broker - expr examples:
0 - broker 0
0,1 - brokers 0,1
0..2 - brokers 0,1,2
0,1..2 - brokers 0,1,2
* - any broker
attribute filtering:
*[rack=r1] - any broker having rack=r1
*[hostname=slave*] - any broker on host with name starting with 'slave'
0..4[rack=r1,dc=dc1] - any broker having rack=r1 and dc=dc1
以下是在集群中更新代理时可用的配置选项:
$ ./kafka-mesos.sh help broker update
Update broker
Usage: broker update <broker-expr> [options]
Option Description
------ -----------
--bind-address broker bind address (broker0, 192.168.50.*, if:eth1). Default - auto
--constraints constraints (hostname=like:master,rack=like:1.*). See below.
--cpus <Double> cpu amount (0.5, 1, 2)
--failover-delay failover delay (10s, 5m, 3h)
--failover-max-delay max failover delay. See failoverDelay.
--failover-max-tries max failover tries. Default - none
--heap <Long> heap amount in Mb
--jvm-options jvm options string (-Xms128m -XX:PermSize=48m)
--log4j-options log4j options or file. Examples:
log4j.logger.kafka=DEBUG\, kafkaAppender
file:log4j.properties
--mem <Long> mem amount in Mb
--options options or file. Examples:
log.dirs=/tmp/kafka/$id,num.io.threads=16
file:server.properties
--port port or range (31092, 31090..31100). Default - auto
--stickiness-period stickiness period to preserve same node for broker (5m, 10m, 1h)
--volume pre-reserved persistent volume id
Generic Options
Option Description
------ -----------
--api Api url. Example: http://master:7000
broker-expr examples:
0 - broker 0
0,1 - brokers 0,1
0..2 - brokers 0,1,2
0,1..2 - brokers 0,1,2
* - any broker
attribute filtering:
*[rack=r1] - any broker having rack=r1
*[hostname=slave*] - any broker on host with name starting with 'slave'
0..4[rack=r1,dc=dc1] - any broker having rack=r1 and dc=dc1
constraint examples:
like:master - value equals 'master'
unlike:master - value not equals 'master'
like:slave.* - value starts with 'slave'
unique - all values are unique
cluster - all values are the same
cluster:master - value equals 'master'
groupBy - all values are the same
groupBy:3 - all values are within 3 different groups
Note: use "" arg to unset an option
以下是在集群中停止代理时可用的配置选项:
$ ./kafka-mesos.sh help broker stop
Stop broker
Usage: broker stop <broker-expr> [options]
Option Description
------ -----------
--force forcibly stop
--timeout timeout (30s, 1m, 1h). 0s - no timeout
Generic Options
Option Description
------ -----------
--api Api url. Example: http://master:7000
broker-expr examples:
0 - broker 0
0,1 - brokers 0,1
0..2 - brokers 0,1,2
0,1..2 - brokers 0,1,2
* - any broker
attribute filtering:
*[rack=r1] - any broker having rack=r1
*[hostname=slave*] - any broker on host with name starting with 'slave'
0..4[rack=r1,dc=dc1] - any broker having rack=r1 and dc=dc1
以下是在集群中向代理添加主题时可用的配置选项:
$ ./kafka-mesos.sh help topic add
Add topic
Usage: topic add <topic-expr> [options]
Option Description
------ -----------
--broker <broker-expr>. Default - *. See below.
--options topic options. Example: flush.ms=60000,retention.ms=6000000
--partitions <Integer> partitions count. Default - 1
--replicas <Integer> replicas count. Default - 1
topic-expr examples:
t0 - topic t0
t0,t1 - topics t0, t1
* - any topic
t* - topics starting with 't'
broker-expr examples:
0 - broker 0
0,1 - brokers 0,1
0..2 - brokers 0,1,2
0,1..2 - brokers 0,1,2
* - any broker
参考资料:github.com/mesos/kafka
。
总结
本章介绍了如 Cassandra、ELK 堆栈和 Kafka 等一些重要的大数据存储框架,并涵盖了如何在分布式基础设施上使用 Mesos 进行这些框架的设置、配置和管理等主题。
我希望本书已经为您提供了管理当今现代数据中心需求复杂性的所有资源。通过遵循使用您选择的 DevOps 工具部署 Mesos 集群的详细分步指南,您现在应该能够顺利处理组织的系统管理需求。