1

这是一篇“温和有趣”的技术文章,如果你初识Docker,对微服务充满兴趣,不妨一读。或许你的第一次微服务体验,就从本文开始……

在本文中,Mesos、Zookeeper、Marathon、Bamboo + HaProxy、Logstash、MesosDns、ElasticSearch和Kibana + Nginx等纷纷亮相,并配有详细的代码说明。本文旨在从最初的安装和环境基础建立开始,一步步指引你搭建自己的集群,实现你的目标架构,并在其上运行分布式服务。

小数友情提示:本文篇幅很长,干货多多,值得收藏。

当开发者开始构建自己的第一款微服务应用程序时,大家通常不会过多考虑编排之类的问题。这时我们掌握的有两到四台服务器,而Ansible脚本已经能够解决大部分问题。不过一旦大家的应用程序规模更大,或者各位决定使用一套环境承载多个不同项目,那么必然需要更多服务器……还有一款用于管理各服务器之上运行的服务。

不过刚刚我们已经提到了Ansible,难道它还不足以解决问题?这个嘛……答案是否定的。Ansible只能解决一项难题——部署。利用它,大家仍然需要搞定其它多种与微服务相关的问题:我们必须记得每台服务器中还有多少剩余资源、手动管理各清单文件以匹配服务器容量、监控应用程序是否正常运行、当节点出现故障时进行服务回弹以及控制端口号冲突等等。如果大家拥有四台服务器与十项服务,那么这些问题就变得比较明显了。

而以此为基础,我们就需要求助于Mesos了。Mesos是什么?这是一款集群管理器,其能够帮助大家在分布式环境之下运行应用程序。Mesos的关键优势包括:

  • 资源管理与使用效率;

  • 应用程序生命周期控制;

  • Docker容器支持能力。

最后一点也使得Mesos成为我们最为完美的解决方案选项。

安装

由于找到七台无需使用的服务器对于任何企业都是一项难题,因此我们在这里使用Vagrant(这是一款管理虚拟化流程的完美工具)在一台本地设备(惠普Z230,i7-4770 3.40 GHz,16 GB内存)上构建自己的集群。另外,我们也提到了Ansible是一种便捷的部署方式,因此我们完全可以将整个安装流程拆分成多个Ansible角色,从而封装其内部所需要使用的标准Linux命令。

环境

首先,我们需要建立Mesos集群基础——这是一系列虚拟机系统,供我们在以下步骤中使用。这项工作利用Vagrant能够轻松完成。
以下为来自Vargantfile的部分代码:

图片描述

如大家所见,我们可以使用一点Ruby代码执行以下步骤:

  • 基于CentOS 7.1镜像构建虚拟机;

  • 设置各虚拟机的资源上限(CPU与内存);

  • 将其与一套网络相结合;

*利用每台主机上的一组预定义变量启动一套Ansible剧本(ansible/master.yml)。

简单来讲,Vagrant文件负责描述如何构建7套虚拟机系统:3套主虚拟机、3套从虚拟机与1套日志存储(从技术层面讲,这部分也应该进行分布处理,不过主机设备的资源较为有限)。大家需要做的是利用下面这条简单命令将其投入运行:

`vagrant up`
初始集群状态

图片描述

前三步都是由Vagrant实现的,也不属于我们今天的讨论重点。接下来让我们正式进入主题,通过Ansible设置集群。

集群

正如大家在Vagrantfile当中所见,这里有3套主剧本及其各自角色:

主角色- 主机包含:
Mesos主实例;
Zookeeper实例;
Marathon实例;
Bamboo + HaProxy实例;

节点- 主机包含:
Mesos从实例;
Docker服务;
Logstash实例;
MesosDns实例;

日志- 主机包含:
ElasticSearch实例;
Kibana + Nginx实例。

接下来,我们将分别考虑各个角色,但这里需要首先强调一点:每个角色都取决于“OS”角色。该角色与本地网络及YUM repo的DNS配置设置相关。严格地讲,我们应当将这种关联性从剧本层级中剥离出来,但为了简单起见,这里我们先让其保持原状。

Zookeeper

Zookeeper属于我们系统中的一大重要组成部分。它将帮助我们构建集群并允许来自Mesos生态系统中的其它应用程序(例如Bambbo、MesosDns)与之进行通信。

Zookeeper的安装过程非常简单,而且不需要什么技巧——从RPM软件包中获取一套repo即可。我发现很多朋友倾向于利用现有源代码构建应用,但在这里我们将使用基于供应商的Mesosphere软件包。

大家需要的就是安装该软件包,设置节点ID,更新配置并启动该服务。

下面来看Zookeeper安装任务中的代码片段:

图片描述
在这里,我们需要回顾一下前面提到过的Vagrantfile——大家还记得下面这部分内容吗?

图片描述

Vagrant将全部必要的变量传递至Ansible,因此我们可以轻松在角色中对其加以利用。

正如Zookeeper的配置一样,我们只需要对其中的主机IP进行配置:

图片描述

以下为我们集群最终状态下的简单Zookeeper UI(简称ZK UI)屏幕:

图片描述

该步骤后的集群状态

图片描述

Mesos

正如之前所提到,MesoSphere的工作人员非常贴心地把Mesos软件包加以整合,因此整个安装流程也将变得更加轻松。

其中惟一需要注意的是,Mesos实际上由两部分构成:

主节点(负责全部管理逻辑);

从节点(负责运行应用并收集与主机资源相关的信息);

换句话来说,我们需要为主节点与从节点实现不同的安装逻辑。幸运的是,Ansible能够非常轻松地完成这项任务。

以下为Mesos安装任务的代码片段:

图片描述

面向这二者,我们需要安装“mesos”软件包(请记住,相关repo由“OS”角色提供)与Zookeeper URL。

下一站是进行设定。在这里,我们只提供诸如主节点quorum size与从节点docker支持等强制性设定。如果大家希望了解更多与特定配置相关的内容,不妨阅读Mesos官方说明文档。

由于MesoSphere软件包同时提供主与从服务,因此我们需要根据当前角色(主/从)禁用其中的冗余部分。

为实现这一目标,大家应当使用Ansible的“conditional”机制。如果大家曾经认真阅读过“master”剧本,就会发现我们已经传递了一条特殊的mesos_type变量:

图片描述

在Mesos安装完成后,大家可以审视其Web UI并尝试点击其中的按钮:http://192.168.99.11:5050
需要注意的是,如果当前主机并非Mesos Master的集群主节点,那么UI会将大家重新定向至主节点主机。另外,由于我们在虚拟机当中使用了DNS快捷方式(例如“master1”),因此大家也应当在自己的主机设备上使用同样的快捷方式。
好了,就是这样——我们的集群已经构建完成了。

图片描述

图片描述
在痛饮庆功酒之前,我们还需要回顾其中一些有趣的细节。

首先,大家需要记住——每个任务都拥有自己的背景信息,我们将其称为“sandbox”。大家可以将其打开并分析全部输出结果(可参阅Marathon部分的截屏内容)。需要注意的是,Docker容器必须首先进行pull——因此,如果大家没有分配足够的时间用于容器启动,那么任务可能无法在UI中w/o任何消息(大家仍然能够在相关节点的/var/logs/messages中看到消息内容):

图片描述

要对其进行修复,需要如以上片段所示配置其中的

图片描述

另外,也不要忘记设置运行状态检查的宽限时长(Java应用往往会长时间处于活动状态)。否则大家的应用很可能被关闭,并在其重新启动前进行回弹(运行状态检查设定问题稍后我们再继续讨论):

图片描述

此步骤后的集群状态:

图片描述

Docker

由于我们需要在自己的从主机上运行Docker容器,因此在这些主机上安装Docker本体就成了必要任务。幸运的是,其安装过程非常简单:

下面来看Docker安装任务中的代码片段:

图片描述

该步骤后的集群状态:

图片描述

Marathon

尽管我们的Mesos集群已经上线并开始运行,但我们仍然无法在其上运行自己的Docker容器。严格地讲,这时我们能够在Mesos上直接运行的只有一套Mesos框架。当然,我们还拥有另一个更符合需求的选项——Marathon。

从技术角度来看,Marathon只是一款简单的Java软件包,且能够通过jar命令进行启动。但好在我们还拥有一套RPM软件包,因此我们不需要担心其“后台化”、配置与控制等问题。此外,由于我们的软件包由MesoSphere负责提供,因此其使用的是同样的配置文件(Zookeeper URL),所以我们不需要对其另行设置。
Marathon安装任务中的代码片段:

图片描述

Marathon还拥有一套Web UI,大家可以通过URL: http://master1:8080
进行访问。

图片描述
下面让咱们找点乐子,部署一项简单的REST服务(该服务及部署设定稍后另行讨论):

图片描述

图片描述

现在我们已经可以监控其状态以及分配端口:

图片描述
因此,我们可以对其进行调用并检查其是否正常工作(当然,结果一切正常)。

图片描述
我们甚至可以对服务进行规模扩展——如果有必要的话(目前规模扩展还没有任何意义):

图片描述
另外,通过“sandbox”分析其日志(通过Mesos UI):

图片描述

该步骤后的集群状态:

图片描述

Bamboo与HaProxy

在以上示例当中,我们只部署了一个服务实例。不过如果我们希望在其中使用大量实例与负载均衡机制,又该如何处理?这个嘛,作为答案的一部分,我们将采用HaProxy。这确实是一款非常出色的负载均衡器。不过如何对其进行配置?很简单,交给Bamboo项目处理即可——它能够与Zookeeper相对接,读取Mesos状态并生成HaProxy配置(利用每款Mesos应用的用户定义角色)。

其安装过程本来非常简单,不过遗憾的是目前还没有任何集成有Bamboo软件包的公共RPM repo。大家可以对其进行手动构建,并通过本地文件实现安装,但整个过程其实有点复杂。

以下为Bamboo安装任务中的代码片段:

图片描述

在Bamboo安装完成之后,大家可以通过其Web UI进行设置: http://master1:8000

图片描述
另外也可以通过HaProxy访问我们的服务: http://master1/services/fibonacci/1

请注意,我们在Bamboo安装中使用了单独的Ansible剧本(master_bamboo.yml)。之所以这样处理,是因为我们需要借此保证其RPM软件包在于剧本内运行之前被上传至虚拟主机当中。

由于Vagrant会在虚拟机初始化过程中自动执行Ansible配置任务,因此惟一的解决办法就是将Bamboo相关内容提取至单独的剧本当中,并执行以下算法:

利用vagrant up启动虚拟机;

将RPM文件通过SCP上传至虚拟机当中;

对Vagrantfile中的ansible.playbook进行变更;

运行vagrant provision master1命令。

如大家所见,Bamboo是整套生态系统中最为杂乱的部分。所以我们不妨了解其替代方案——例如Marathon负载均衡器。

该步骤后的集群状态:

图片描述

MesosDns

我们一直没有谈到一个重要的问题——如果我们的服务需要彼此进行通信,该如何加以实现?我们是否能够立足于单一Mesos集群内部实现服务发现?是的,答案是肯定的!相关方案也非常明确——MesosDns。这里我们的解决思路非常明确——读取Mesos集群状态并通过DNS(A与SRV记录)及HTTP API进行发布。后一点非常重要,因为这将帮助我们轻松构建客户端负载均衡w/o【1,2】。

整个安装过程稍有些麻烦,不过没什么特别之处需要注意。

以下为MesosDns安装任务中的代码片段:

图片描述
Config文件中也没有什么值得一提的内容:

图片描述

大家可以通过以下SRV记录请求检查已安装的实例:

http://172.17.42.1:8123/v1/services/_fibonacci-service._tcp.marathon.mesos.

图片描述

此步骤完成后的集群状态:

图片描述

日志记录

欢呼!我们距离成功已经很近了!必须承认,如果大家已经耐着性子读到这里,那么这个议题肯定非常有趣。

值得庆幸的是,这部分并没有太多内容可谈。日志记录就是日志记录。我们只需要将Logstash安装在全部Mesos从节点当中即可……

以下为LogStash安装任务中的代码片段:

图片描述
另外对其config文件进行配置,确保将数据发布至日志节点当中:

图片描述
与此同时,我们还需要在这些节点上安装ElasticSearch与Kibana。
ELK安装任务中的代码片段:

图片描述

这里使用Docker的惟一理解就是其更为简便。当然,我们也不需要也不应该将日志数据保存在容器当中。

在安装完成之后,大家就可以通过Kibana的Web界面分析ES当中的日志了:http://log1:5601

目标架构

下面就是我们的目标架构,或者说我们的最终构建成果。看起来不错,对吧?
图十八
在我们的脚本当中,惟一的单点故障来源就是HaProxy/Bamboo。不过大家可以将二者部署至全部主节点当中并面向用户使用基于DNS的轮循机制,从而轻松解决这个问题。

分布式服务

到这里我们已经拥有了自己的集群。现在,是时候考虑我们计划运行于其上的分布式服务了(运行简单应用太过无聊,这里不再赘述)。

我已经开发出了一套基于REST服务的小巧SpringBoot,其能够计算斐波那契序列中的第N个数字。这项服务的核心功能在于,其可以调用自身其它实例以计算该序列中的前一个值。

图片描述
我知道,这种实现方式效率极低而且很容易导致死锁(大家不妨想想这是为什么),但我在这里主要是借此对跨服务通信进行说明。
该服务利用MesosDns HTTP API进行服务发现:

图片描述
另外还有一套客户端负载均衡机制(我们当然需要尽可能减少故障点数量,对吧?):

图片描述

部署

在我们开始部署工作之前,首先需要为自己的应用程序构建一套Docker镜像。在这里我就不赘述整个流程了,感兴趣的朋友可以查看Gradle配置与Docker说明文件了解相关内容。

完成之后,我们将该镜像发布至一套Docker注册表当中(也可以通过Gradle实现),这样Marathon就能够通过从节点上的Docker实例对其进行下载了。大家可以在这里找到我创建的示例: https://hub.docker.com/r/krestjaninoff/fibonacci-service/

最后也是最重要的部分,Marathon。如之前所提到,我们可以利用Marathon UI实现部署任务。不过这并不是最具“技巧性”的办法。Marathon也拥有自己的REST API,我们可以通过简单的“curl”客户端对其加以使用:

以下为SpringBoot mesos installation manifest中的代码片段:

图片描述
让我来简单介绍一下该配置文件中的内容(各部分逐一说明):

应用程序 id (在Bamboo配置中使用);

资源限制(如果该集群不具备必要容量,应用程序将不会启动——这一点对于基于虚拟机的集群尤为重要)与实例数量;

容器设置:

forcePullImage是在正确时间点对容器进行更新的惟一方式;

SpringBoot允许我们通过各环境变量进行config文件覆盖,因此这是一种良好的容器操作方式;

由于$HOST变量负责提供DNS名称(这一点甚至在Marathon官方说明文件中亦没有体现),因此从Docker容器内获取主机本地IP的惟一方式就是默认Docker网络接口172.17.42.1;

backoff因数/设定避免集群运行“异常”应用,即无法正常启动并将因此造成反复尝试的应用(具体方式为延后每一次启示尝试);

运行状态检查允许Marathon了解应用实例处于正常运行抑或是必须执行回弹;

upgradeStrategy帮助大家在无需接入能力的前提下实现应用更新(首先延后新版本,而后停止当前版本)。

最后要提到的是Bamboo,其同样可以通过REST API进行配置。这项任务非常简单:

图片描述
到这里,我们就已经拥有了“deploy”角色……
以下为Deployment安装任务中的代码片段:

图片描述
大家可以通过运行“deploy”剧本的方式进行调用:

图片描述

结果

到这里全部结束!

调用该服务 curl http://master1:5000/service/fibonacci/15 (或者直接调用curl http://node2:31135/15);

检查结果(正确值为610);

检查日志内容(http://log1:5601)

最后一点非常有趣。大家可以看到哪台主机被调用或者响应耗费了多长时间:

小数译后记

好了,今天的文章到这里就结束了。没错,其篇幅远超一般的“Hello World”指南——不过平心而论,内容还是非常有趣的,对吧?

英文原文链接:http://trustmeiamadeveloper.com/2015/12/17/mesos-as-a-docker-containers-farm/


优云数智
1.3k 声望267 粉丝

优云数智(上海优铭云计算有限公司)是一家专注于提供企业级私有云产品与解决方案的云计算厂商,提供PAAS+IAAS的一站式解决方案,团队核心成员来自Google、华为、Mirantis、盛大云等一流云计算公司,公司总部位于...