专栏名称: 分布式实验室
最专业的Docker文章,最权威的Docker新闻。关注容器生态圈的发展。
目录
相关文章推荐
51好读  ›  专栏  ›  分布式实验室

一个可供中小型团队参考的的容器化案例

分布式实验室  · 公众号  · 后端  · 2017-03-17 07:47

正文

由于之前推送文章图片有点小问题,今天重新推送一次。

GrowingIO是基于用户行为的新一代数据分析产品,提供全球领先的数据采集和分析技术。企业无需在网站或APP中埋点,即可获取并分析全面、用数据驱动用户和营收的增长。为了应对高速变化的业务增长,我们在系统设计之初就采用了微服务的架构,但是随着时间的推移,微服务的缺点也渐渐体现:

  1. 系统架构过于复杂,随着时间推移运维成本会逐渐增加。

  2. 传统的运维方式可复制性不高,环境迁移和横向扩展带来的成本过高。

  3. 资源利用率低,大量的专有机器。

这些问题都会导致研发效率下降。为了解决这些问题,经过了半年的探索,团队使用容器相关技术来运行微服务的应用,取得了良好的效果。在继续分享前,我先简单介绍一下我们的技术背景,我们是一家大数据创业公司,整个技术组织架构分为三层,分别是前端,服务端,数据端。前端使用react框架,服务端和数据端统一采用scala技术栈,服务端采取微服务的架构。

迈出第一步

容器化的第一步当然是把应用打包成一个镜像,我们尝试了两种方案。第一种是比较常见的做法,把编译环境放到容器里,比如下面这个镜像的Dockerfile。这个方案的好处是能做到百分之百复现一个镜像,因为没有任何外部依赖。坏处也很明显,对于依赖非常庞大的语言,比如Java和Scala,这种方案的编译速度非常慢,尤其是Scala本身的编译速度并不快,这种方案对我们来说太重了。

第二种方案也是我们现在采用的方案是在宿主机打包,镜像只提供基础的运行环境。这种做法的优点是速度和原生编译一样,缺点是对宿主机的环境会有一定的要求。因为我们的团队规模不大,所以固定配置几台打包的机器完全是可行的,相反如果开发人员因为要把应用放到容器里运行,就得牺牲大量的时间在编译上,肯定是无法让人接受的。


管理配置

应用容器化后碰到的第一个问题肯定是配置管理问题。总结一下我们尝试过的两种方案:

  1. 使用专门的配置管理中心,比如etcd,Consul。应用需要编写配置管理逻辑,需要额外开发工作。应用和环境多了以后使用界面来回切换编辑非常笨拙。好处是能够做到实时配置的更改。

  2. 使用环境变量,大部分语言和工具都会对环境变量提供支持,比如Typesafe的config和Openresty的conf,就算不支持也可以在应用启动的时候通过entrypoint脚本对配置文件进行渲染替换。启动后无法再更新配置,配置更改后需要重新启动应用。好处是工作量比较轻,结合具体的编排工具可以很方便的管理不同环境的配置。

最后我们结合了两种方案的优点,Consul管理需要热更新的配置项,大部分固定的配置通过环境变量注入。


选择一个编排工具

刚开始测试容器的时候,大部分情况都是开发人员自己编写一些脚本来控制容器的分发,创建和销毁。没过多久就发现亟需一个统一的编排工具来规范操作,减少自己造轮子的情况。我们把目光放在了业内最流行的三款编排工具上,他们分别是Docker官方支持的Swarm,源自Google的Kubernetes、Mesos资源调度框架Marathon。

选工具是非常主观的事情,不管一个开源项目本身宣传的理念有多好,最终都要实际上手一遍才会知道和自身的实际情况是否匹配。这里我们先抓取了这三个项目在GitHub上的一些信息,结合自身的实践来做一个以我们团队视角的主观评价。

Swarm :架构最简单,部署最快,关注度随着Docker的官方支持上升较快。缺点是Docker团队刚投入研发没多久,没有大规模经过生产考验。同时最新的Swarm和Docker绑定在了一起,升级是个问题。

Kubernetes :过去的一年里编排工具最闪耀的明星肯定是Kubernetes,Kubernetes受到的关注度和他的竞争对手完全不在一个量级。Kubernetes的理念非常适合微服务的架构,Pod,Service,Cluster IP,ReplicationController。然而我们在实际使用后发现复杂度过高,组件过于分散,调试起来非常麻烦。去年Kubernetes推出了kubeadm,很大程度的降低了部署的复杂度。但易用度上和Swarm以及Marathon都还是有差距。让我们放弃Kubernetes的另一个原因是Kubernetes对大数据的支持力度不够,从长远来看我们需要做大数据应用的容器化。虽然最近听到社区有Kubernetes-Mesos这种企图将两者的长处合并的项目,不过现在还完全不成气候。

Marathon :与单台主机的调度问题相比,跨集群之间的调度会更加复杂。单台主机的调度重点关注少数CPU上如何运行尽可能多线程和进程的问题,保证单个进程不会运行太长时间并且确保进程能命中资源。分布式背景下的调度问题要复杂的多,因为主机之间的网络交互会有延迟,和Kubernetes不同,Mesos是以资源为基础进行调度,关注点在资源分配上。Mesos本身只维护集群资源,然后基于优势资源公平算法决定把资源分配给哪个服务框架。所以Mesos不关心资源分配的细节,细节都交给二次调度框架。Marathon就是一个构建在Mesos之上的二次资源调度系统,Marathon的架构非常简单,是典型的master-slave架构,本身依赖ZooKeeper实现HA。

Marathon的开发时间是最早的,在2013年就开始研发。同时也是最早到达生产可用的,使用Marathon的大公司最著名就是推特了,稳定性方面有保证。另一个很重要的原因就是之前提到的,Mesos和大数据应用,比如Spark集成非常友好,Mesosphere最近推出基于Marathon的DCOS就是抓住了这个卖点,将业务系统和大数据系统跑在同一个集群上。这对成本压缩非常有好处,因为这两类系统的资源利用时间正好错峰,业务系统一般在白天达到请求的峰值,而大数据系统则在夜晚才会开始计算当天的数据。同时Marathon本身是Scala编写的,UI是React编写的,前面也介绍过我们团队的技术背景了,和我们的技术栈非常吻合,有利于二次开发。

Marathon特性介绍

在Marathon上部署一个应用非常简单,你可以选择在界面上编辑,也可以使用它提供的REST接口。实际测试下来ui还是有些不稳定的bug,虽然在UI上操作很直观但在生产环境肯定不能使用有bug的UI。所以我们还是选择使用脚本来控制应用的更新,使用JSON文件存储应用的定义。

这里有个坑提一下,新手在部署的时候可能会经常碰到应用的状态显示waitting。这是因为Mesos无法响应Marathon的资源offer,这个时候要检查资源配置,比如机器的CPU是不是不够用,另一个常见的原因是Mesos把端口当成一种资源,如果Mesos Slave节点在启动的时候没有申明自己的端口范围,很可能应用指定的端口不在默认端口范围里。所以需要在启动的时候指定端口资源范围 --resources='ports(*):[1-32000]'

约束功能

为了防止应用在你的资源池里随意漂移,Marathon提供了约束功能。Hostname是一种常见的约束,你可以指定应用跑在一台特定IP的机器上。这点在团队正在向容器化过渡的期间非常有用,我们只是把原来部署在机器上的应用跑在容器里,其他的条件还是和原来一致。

另一种约束是基于lable的,你可以在Mesos Slave启动的时候指定这台Slave的lable集合。这样我们就可以在调度的时候指定特定的lable。如图所示,指定这个应用跑在机架编号1-3的机器上。

弹性伸缩

弹性伸缩是根据业务需求和策略,自动调整其计算资源,达到优化资源组合的能力。在业务量上升时增加计算能力,当业务量下降时减小计算能力,以此保障业务系统的稳定性和高可用性,同时节约计算资源成本。

弹性伸缩又分为水平伸缩和垂直伸缩,水平伸缩指的是增加更多的机器,增强应用的计算能力来支撑增加的访问量。垂直伸缩指的是增强单机的配置,比如升级CUP核数,或者增加内存,扩展性有限。

无状态的应用,比如一般的Web应用,把数据存储在数据库和缓存中间件里,在容器化后不依赖具体的机器,这种应用非常适合水平扩展。编排工具最重要的特性也正是提供这种水平扩展的能力,配合前面介绍的约束功能,只需更改实例个数的数值就可以达到水平伸缩的目的。

服务发现

服务发现通常有两种做法,第一种是基于DNS的实现。第二种是在服务前面挂一个Proxy。对应的Marathon分布提供了Mesos-DNS和Marathon-LB两个工具。Mesos-DNS提供服务名和IP端口号查询服务,没有负载均衡功能。Marathon-LB监听Marathon的事件总线,提供基于端口号的TCP负载均衡。

因为DNS需要自己实现负载均衡功能,所以我们选择了LB的方案。lb本身是一个HAProxy,通过监听Marathon的事件总线,LB会动态更新配置文件以达到服务发现的目的。

上面提到lb是基于端口的负载均衡,也就是说LB本身是通过端口来区分不同的服务的。打开LB的9090端口,可以看到LB的负载均衡信息,包括对外的服务端口和对应的后台服务的实际IP和端口号。

LB通过Marathon获取服务的具体IP和端口,也就是说我们要保证Marathon能正确获取服务的端口,在网桥模式下我们会指定Container port和host port,但是在host网络模式下没有映射信息,需要我们显示的声明服务需要的端口号,即定义port同时标注我们需要这个端口资源。

健康检查

应用的健康检查是保证服务稳定非常重要的一个功能,通常是编写脚本定期的去轮询健康检查的接口,如果发现接口返回异常则采取相应的措施,比如重启。Marathon也提供类似的健康检查功能,功能非常强大。支持多种协议,包括http/https/tcp,也可以自己编写shell命令进行检查。支持设置应用的启动时间,在这段时间内会忽略健康检查。支持超时设置,检查的时间间隔设置,最大失败次数设置。







请到「今天看啥」查看全文