去哪儿网利用Mesos和Docker构建dev—beta环境 | 数盟社区
大家好,我是去哪儿网的张宁,之前是做配置管理,2015年6月份转到去哪网做运维开发,现在主要从事的工作是基于Mesos和Docker快速构建dev和beta环境,基础架构是Mesos Marathon。
最初我们是想做一个内部的PaaS平台,但是因为底层的架构也刚开始尝试Docker,所以说基层架构现在不是特别完善。包括我们内部在开发的一些系统,现在基本完成的是发布这个环节,在这里主要跟大家分享一下发布这一块,以及在使用Docker和Marathon过程中遇到的一些问题。
本次演讲主要分五部分:

背景
首先是业务线的一些期望,因为我们之前有一个发布系统,最早的时候是单模块发布,但是我们业务上线的时候基本上需要多模块一起上线才能组成一个完整的服务,所以大家在发布的时候就会比较头疼,需要手工把所有的项目给发布上线。尤其是dev和开发环境,大家在准备起来的时候经常会遇到很多问题,环境比较乱,所以开发和QA在部署环境时是比较头疼的,他们希望可以一键部署整套环境,包括MySQL、Redis、ZooKeeper、Memcached这些基础模块,同时也希望能够快速部署环境,可以得到快速的反馈。
其次就是环境隔离,使用传统部署方式时,大家因为部署环境特别麻烦,就经常使用公用环境,导致环境里面经常会有一些脏数据,所以他们希望每个人能有自己的环境,不受其他人的影响。
最后一点就是学习和维护成本尽量要低,希望可以让新来的同学点一个按纽就可以发布,也不用过多地去了解里面的一些概念。总结下来要解决他们的各种环境问题。
我们为什么选择Docker Mesos架构?

从我们要做这个发布系统的初衷来说:
轻量级、快;
Mesos Marathon架构符合现在的功能需求。
其实最早我们也调研过Kubernetes,但是因为在我们的使用场景中都是单容器,不需要两个容器里边的应用共享网络或者资源的情况,用不到Kubernetes中pod的一些优势,所以我们就直接用了Mesos Marathon,全都是单独的Docker容器。另外一个就是因为我们更多的是多项目之间有关联的这种情况,其实像Swarm的Compose文件可能支持编排,但是这种编排也只是顺序启动,中间没有什么校验的环节。还有像Kubernetes其实也可以做这种编排,但是也是缺少这种更具个性化校验的功能,所以我们在系统之间依赖这一层做的东西全都是发布系统里面需做的一些check。现在公司的日志系统是基于Mesos和Marathon的,已经在上线使用,如果我们用这个架构的话就可以借鉴之前的一些经验,也可以避免一些之前遇到的坑。
如何落地?

虽然上面我们选型基本定了,但是在落地的时候其实还是有一些细节是需要考虑的。
第一是是否每次都要build镜像。每一个使用Docker做发布系统的团队都会考虑的这个问题,我们是使用固定的镜像,每次是把编译的代码上传到一个共享存储,然后是使用公共的镜像启动之后直接去拉取代码,没有每次去build镜像,因为在我们的dev和beta环境,这两个环境可能大家的代码变化频率是很大的,并没有达到Docker最原始的初衷,就是一个Container搬到哪都能用。但是如果我们要给它打成一个镜像的话,其实镜像里面代码是经常变的,这个Container并不是dev环境用完,beta就能用,所以我们就想了一个更灵活的方式,就是每次编译完代码上传到一个对象存储,然后在Docker容器启动后去拉代码,这样任何时候启动都可以拿到最新的一个版本,任何版本都可以通过相应的参数来指定。
第二是是否使用Jenkins-Mesos。其实Mesos有基于Jenkins原生的CI解决方案,我们这边也没有用,我也建议大家如果硬件能满足条件的情况下还是用固定的机器会比较好。我们的编译就统一使用的是SSD硬盘然后加大内存,这样可以保证编译速度。如果要是在Mesos Marathon的集群里面去完成这件事的,不可能把整个集群都做成SSD硬盘的这种硬件设施。另外一方面就是我们这边项目比较多,如果有一个 Maven的local repo,就会加快编译速度,如果是每次在生成的新容器里边编译可能也会慢。
第三是如何加快dev和beta环境构建。我们目前做的是所有模块是并行编译,然后分批部署,没有做并行部署的原因是因为我们有一些依赖,所以我们会在开始部署之前根据依赖关系计算一个部署顺序,然后再去分批部署。现在大概是40多个模块的一个环境,可以在20分钟以内完成全部编译和部署。
第四是使用方式如何能够平滑过渡。我们现在做的这个发布系统是基于我们公司最早的一套发布系统做的,之前的架构包括分支策略都已经确定了,我觉得之前的方案也是很好的,我们基本上是延续之前的那个发布系统来做的,只是再把Deploy里边的很多操作给转移到Docker里面去做,但是编译阶段是延续之前的,所以我们保留了之前发布系统里面的一些必要参数,然后简化了一些部署阶段的参数,这样用户学习和接受起来比较容易。
第五是如何建立容器发布的流程标准。每个公司开发的流程是不一样的,运维体系也是不一样的,所以说这个只是根据我们现在的一个运维体系以及开发的流程重新做了一个系统,下面会有具体的介绍。
第六是运维能否支撑。因为我们现在日志平台已经在线上使用了,所以说我们就直接借鉴了他们的一些经验,目前看来基于这个架构来做还是比较顺畅的。
第七是资源利用率是否真的提高了。这个也是大家比较关心的,我们公司原来是OpenStack上面有KVM,一般大家会申请4G内存的机器,可以部署三个应用。然后我这边做 Docker 的话,大概是一个Docker里面一个应用,默认是1G内存,所以说有可能少部分的主项目它占用的内存比较多,大概需要2G或者3G,但是80%的项目都1G就够用了,我觉得大概资源利用率至少可以提高20%左右。我现在还没有做资源的压缩,现在系统还在试用阶段,等系统稳定以后,还要把现在有富裕的一些再进行压缩一下,资源利用率还会再细化。
技术架构

下面讲一下系统架构,上图是我们这个平台的主要架构,底层资源层是Mesos Marathon,上层封了一个OpenResty,主要是做一个泛域名,方便启在Marathon上面的所有应用可以通过一个统一的入口进行测试。因为可能大家用Marathon的情况大多数都是用host的模式,host的模式会随机分配主机和端口号,如果每次发布端口号都会变的话,用户在测试的时候是很难记住它的名字的,所以我们就封装了一个泛域名,只要这一套环境名不变、应用名不变,它访问的域名就是固定的。上一层是我们自己开发的一个用户入口,就是现在最基本的应用树,基于应用树可以录入基础的应用信息(后面都会有具体讲解),可以在应用树上面去做资源的管理,申请资源、环境管理。我们其实发布还是用的Jenkins,上面开发了一个插件,环境管理是直接调用的Jenkins的接口去进行config文件的同步,触发构建这样的操作。然后是数据库管理,因为发布的时候大家也需要数据库支持,我们现在使用的是一个比较粗暴的方式,是用一个标准库是定期去同步线上的表结构,但是这个操作现在大部分还是手动的,希望以后做成自动的(数据库管理系统来做这件事,下面也会有具体的讲解)。通过发布可以调用Marathon的接口,然后创建Docker容器,有各种类型的一些服务,有Web的Container这种,然后上层是接的我们公司现有的一些监控平台,日志平台还有可以直接访问容器里面的客户端,我们内部叫“八爪鱼”,它有网页版,也有客户端版,其实这个主要就是封装的一个Docker的EXEC,然后可以直接登陆到里面去。

上图对系统架构图的一个细化,这个是底层资源层,像Mesos、Marathon有一些定时任务是用Chronos,上面应用层比如说业务有部署的Canal、MySQL以及Solr,然后基础服务比如说有Confd日志、Heka、cAdvisor做Docker自己本身的监控,Prism是我们自己内部的一个日志平台,Watcher是做业务监控。

QAECI这个架构主要是发布系统的那一个环节,是从Git或者SVN上下载代码,然后在编译机上进行统一的编译,编译完的以后上传到Swift上,一个共享存储,然后就调用Marathon的接口去创建Docker容器,启动Docker的时候去拉取代码,然后做一些配制替换,包括某些项目一些特殊的操作,可以通过service脚本去执行,这样就完成了部署。基础的操作做完了以后会启动服务,因为我们这边大部分都是Web服务,所以我们会有一个check url的工作去来检查这个服务是正常启动的。我刚才说像Compose还有YAML文件的编排没有办法直接来做,就是因为我们为了保证服务是正常的,需要做check url,虽然Marathon有health check的功能,但是我们并没有直接用,是因为想给用户保留一个现场。如果health check没有通过,Marathon会一直去重启,这样的话用户没有办法去发现它的环境到底出了什么问题,就是说我们是通过自己的脚本去做这个检查。

QAECI系统里面的一些关键点,简单跟大家介绍一下。
第一个是使用app_code作为应用的唯一标识。因为我们之前的发布系统是每新建一个job就要把一个应用所有的参数全部填写一遍,这样就需要分不同的环境需要建多个job。有一些基础信息需要变化的时候,每一个job都需要去修改,而且在系统之间做关联的时候也没有一个唯一的标识服务,比如说日志系统,然后还有应用中心那边,或者监控,可能大家都没有一个通信的ID,所以我们就统一了一个标识服务,只要它有一个唯一ID,在任何系统之间都是打通的,就是它的信息是可以带到任何系统里面去。
第二个是基础信息统一记录。基础信息不需要在每一次发布的时候再去维护,而是在一个地方统一去维护,然后发布的job上基本只需要勾选上自己要发的模块,以及要发的分支就可以简单的去进行发布了,没有太多额外的参数、维护成本特别低。
第三个是配制文件模板化。这个也是比较重要的一点,之前在建多个环境的时候,Maven的Procfile文件都需要有多套,就是因为有一些环境的信息比如说连接其他环境的一些接口的地址如MySQL的地址,这些全都是写成固定的,每一套环境里面都是不一样,我新建一套环境就要新加一套配制文件,但是现在为了可以简单的部署一套环境,我们把这个配制文件模板化了,这种环境相关的情况,只要按照我们提供的常量去改,然后我们在起环境的时候就可以自动进行替换,这样就可以保证每个人有自己的环境,而不需要再去维护那么多配套文件。
第四个是计算部署顺序。这个也是我们业务相关的,就是部署经常会有那种Web服务有依赖,所以我们要在发布的时候计算这个部署顺序,然后以保证它起来的环境是可用的。
第五个是泛域名。我们用OpenResty做的一个域名服务,这个是可以方便大家很容易的测试自己的服务的一个工具。
功能展示

上图就是我们在记录系统基础信息的一个截图,信息相对多一些。图中有一些Java的选项要填,然后可以看到其实它最基本的就只有scm_root,就是一个版本控制的地址,编译的结果、编译类型还有check url,下面比较重要的是一个部署依赖,这个就是会计算它部署顺序的,基本只需要这些信息。

这个是一个发布job的展示,我们做了自己的插件——Qunar Build。大家可以看到右边上面是一些基础信息,这个决定它的环境是什么,是dev、beta还是线上,我这里面没有展示,其实它勾选每一个,就是每一个有check box的都是一个app_code,然后它具体的信息是通过刚才这个平台的接口去获取的,所以说它下面每一个app_code都不需要再填自己的基础信息,只要填自己本次要部署的分支名称就可以了。

上图就是我们做的环境管理,这个环境管理其实调用的是Jenkins的一些接口,这边展示的信息会比那边更直观一些,可以看到它每一个app_code后面都只有自己的tag name,就是相当于只有它的分支,其他的比如说build_type是自己这定义的,它就会覆盖上面的,如果这边没有定义的话,就都只用上面公共的。
存在的问题及未来计划
下面说一下我们在使用过程中目前系统还存在的问题以及未来的计划,我们现在在用Marathon的时候,刚才有说现在主要用host模式,host模式它很灵活的一点就是,比较简单,而且都不需要我们管理,IP地址直接用的是宿主机的,但是存在的一个问题就是一旦有容器挂掉,或者说有宿主机有问题,还会在飘的时候再随机启动,这样如果通过固定IP或者端口号来通信的一些服务,有可能会有问题,找不到之前的服务,因为它里面可能记录的是之前的那个接点,所以我们现在也在尝试给 Mesos 分配固定 IP 这样一些网络解决方案,我们现在尝试的是Calico。

上图是一个网上截图,我就主要说一下我们这边的使用方式。发布系统根据app_code等信息自动生成一个主机名,到DNSDB里边bind一个IP地址,Marathon需要改成bridge模式,通过Marathon的配置文件把主机名和IP地址赋给新建的容器。但是我们并没有大规模使用这个的原因是,dev和beta环境可以这样用,因为它基本只有一个instance,每一个Marathon上面的APP只有一个instance,但是如果是真正的线上环境的话,肯定需要多个instance,在多实例的情况下,Marathon没有办法确定如何指定每一个instance具体的IP地址。当然Calico可以auto的方式去分,但是采用auto的方式,我们现在遇到一些问题,也可能是设置问题,就是它会把管理IP也分给容器,这样的话就有可能会导致整个网络有问题。
另外如果通过auto这种方式分给Docker容器的话,这个信息是不能返回给Marathon或者Mesos接口的,所以我们是没有办法再次拿到这个信息。因为我们拿到这个信息是要给下面的一些依赖以及服务用的,我们现在还没有做所有基于集群的宿主的一个Agent,所以我们拿不到这个信息,现在也没有真正在线上用。我们现在用Calico,只有给Nginx的这种容器使用Calico,这个就是方便大家可以本地配host去测试,可以不用每次总是改IP地址。

下一个问题就是集群里面的宿主问题,应用自动飘可能会启动失败,它如果有强依赖的话,一旦飘有可能找不到之前已经记录的端口。如果暂时没有办法通过Calico去解决的话,后面会在Entrypoint 里边去做检查,也会用上Marathon自己的一个Dependency功能,它其实自己有Dependency功能,但是这个Dependency功能默认是基于它的health check,如果加了health check之后启动成功了,然后才会起后面依赖的应用,但是如果在我们没有加health check的情况下,它只是判断上一个应用起来了,然后下一个应用才会去启动。这样在启动的时候,因为没有去检查上一个应用是不是真的在check url时能返回200,有可能启动失败,所以以后我们可能会自己做更多的一些检查。下面就是DBCI,这个也是我们之后要做的一个系统,DBA现在有一个系统是可以发布SQL语句上线,然后我们以后要跟DBA的系统做一个打通,那边脚本有上线的话,我们这边会自动同步到标准库以及基于这个标准库创建的各个Docker容器里面。
关于数据库使用标准库这块也有一个经验,之前我们用标准库是把标准库里面的数据给Dump出来,然后在Docker容器里面import,这样的话,如果一个标准库特别大的话像我们有一个标准库能达到四五十个库,占用的时间就会特别长,大概17分钟左右。后来我们做了一个改进,就是标准库的data目录也是打包直接上传到对象存储上面,MySQL的Docker容器会从它对应的标准库的地址,从Swift 上面去拉相应的tar包,然后解压,这样大概能缩短至1分半左右,所以说速度有很大的提升。其实关于数据库这块,我觉得从根本上解决问题的办法是数据库版本控制,但是因为我们现在业务线特别多,推起来也比较困难,所以我们现在是采用这种解决方案。

我们后面要接进来的是日志,还有ELK查询,然后还有监控和报警,监控是cAdvisor,主要是做Docker自身的监控,我们公司有做了一些二次开发。watcher是我们公司基于一个开源的监控系统做了一个二次开发,这个主要是做业务监控。

上图是Kabana上查看日志的一个简介,它可以定制一些面板,有各种搜索。

上图是我们监控的一些展示,有一些业务的流量,还可以定制各种监控指标。

这个是我们未来要做的,考虑应用自身的扩容,就是动态Slave 。针对我们底层的集群平台也会做一些动态的扩容,因为现在业务在慢慢往Docker上迁的时候,OpenStack的虚拟机会有一些空闲,可能以后会动态的去检查请OpenStack的虚拟机那边的空闲情况来做一些平衡(如果要是申请真正的实体机来作为我们的Docker集群的话,这个会要走一个采购流程)。
一些经验总结

第一点是Docker Devicemapper限制要调大。这个也是我之前遇到的一个问题,默认Docker Devicemapper只有100G的大小,如果像256G内存的一个机器上大概能启200个容器,虽然我们是把日志还有数据比较大的都已经给挂载到宿主机上,但是启一个容器还是要占一些空间的,所以说100G很容易就吃满,然后会导致整个Docker就没有办法启其他的进程,所以建议大家一开始在启Docker Devicemapper的时候把限制调大。
第二点是高可用。基于Marathon和Mesos本身就有高可用的架构,大家可以直接使用。
第三点是Marathon相关的:
一个是低版本的Marathon没有做压缩。Znode中存储的数据最多只有500G,而且它自己有一个group的概念,虽然我这边启动全都是直接用的APP,并没有用它的group,但是它会自动去创建group,而且在删应用的时候它不会去删除它的group,当group达到一定的数量的时候,它就没有办法再去调接口来创建应用。如果大家刚开始使用的时候,可以直接用高版本的,高版本里面大小可以到1兆,且默认是开启压缩的,只要大家不去改它那个静默压缩,默认是已经开启压缩的。我们这边也担心Marathon 自己的调度以及并发的问题,所以我们并没有在所有的环境的使用同一个Marathon,现在是beta、dev 已经线上分开为三个Marathon,这样在用的时候,在Marathon上看信息的时候也会更容易一些,而且也可以减轻Marathon的压力。
另一个是app_id中不能用重叠字段。app_id里面如果有类似** /abc/** 后面再有 /abc/,就不能创建容器,也无法创建应用,后来我向官方提了一个issue,官方反馈说大概要在15版本才去解决这个问题,然后我们现在已经升级到最新的13版本,Mesos用的是26版本。关于Mesos的使用,就是在master和slave要尽量保持版本的一致性,因为当时在升级集群的时候,最开始把Mesos升级到26版本,但是所有的slave也都升了,就只有用Calico的没有升,因为当时查了一下,Calico还不支持Mesos 26版本,所以说Calico机器说还是24版本。因为24版本和26版本在本地存储的路径上会有一些差别,所以说在Mesos上面看24版本的slave上的Sandbox是看不了的,但是我之后查了一下现在已经支持26版本了,所以下周准备再升级一下。
最后一点是新节点预热。也就是为了不影响发布效率,新加进集群的机器,最好把环境部署会用到的镜像拉取一遍。
注:转载文章均来自于公开网络,仅供学习使用,不会用于任何商业用途,如果侵犯到原作者的权益,请您与我们联系删除或者授权事宜,联系邮箱:contact@dataunion.org。转载数盟网站文章请注明原文章作者,否则产生的任何版权纠纷与数盟无关。
期待你一针见血的评论,Come on!

不用想啦,马上 "登录"  发表自已的想法.