bz container 2

讲到我们资源划分和容器编排,真的是一肚子心酸,为什么呢,因为一路都是坑。

在分享这段血泪史之前,让我们回归问题的本质,我们为什么要用docker,为什么要用容器编排和调度方案?

很明显:

docker的加入:

快速实现测试/生产环境的搭建,缩短浪费在部署和维护上的时间以及人员配置。

容器编排调度方案:

解决docker在集群上的碎片化问题,规范服务的发布流程,更好的利用资源,减少运维和管理成本。

总结就是:让员工少操心,多干事,让公司更省钱。

所以为了实现这个目的,挑选最合适的方案成为主要问题。

为此我们进行了大量的调研和测试,基本上将大多数开源免费(这个很重要,哈哈)的框架都测试了一遍。

当然,测试用的环境很简陋,所以结果仅供参考。

另外下文会有一些比较强烈的主观因素,不喜勿喷。

前方高能预警,请非战斗人员迅速离开。

1. tutum

tutum自从被docker收购,也变的越来越牛x起来。

我们刚开始用tutum,主要是因为它安装方便,图形界面美观。而且其本满足我们的需求。

但随着时间推移,还是发现有很多问题的。

首先是墙,大概从去年下半年开始经常会被墙,一方面每次进入管理界面的时候很不方便,另一方面,我们很是担心tutum-agent和它老东家的链接会不会也被墙掉,所以在用过一段时间之后就放弃了。

其次是tutum-agent自身嵌入了docker,此docker要和tutum的版本统一。升级很麻烦。

最后是卸载,卸载tutum服务简直反人类,docker stop, docker rm -f 都没用 ,不服的可以装个试试。

现在国内有很多云做得类似tutum ,都是主机上跑agent,但是实际体验比tutum好得多,咱这个做的比老外强。

2. swarm + docker-machine + consul + docker-compose

由于tutum的居多问题,我们开始尝试其他方案。最先接触的docker swarm体系,swarm可以说是docker 官方推荐的cluster方案了。

swarm体系主要包括:

— docker-machine

平台管理工具,可以非常方便的在云服务商申请主机加入当前集群,国内很多厂商已经支持了。

— consul

一个分布式的服务发现和注册系统 ,用来注册集群节点的信息。类似zookeeper、etcd。

— docker-compose

可以看成docker run的替代 ,非常适合部署多个容器相互依赖的服务。

来个例子

容器会把 db redis 的ip写入 wordpress 所在容器的hosts表,这样wordpress数据库地址和redis地址直接写 db:3306和redis:6379就好了。

docker swarm 优点非常明显。首先,docker machine 可以非常方便的从云服务商那里购买主机,一键扩充集群规模,非常适合做弹性扩容。consul安装简单,性能出色,不仅仅能承载swarm,也可以承载公司其他应用。整套方案非常轻量,基本能满足我们的需求。但是很遗憾,最后并没有上生产。为什么呢?

swarm只适合小公司做简单应用和大公司做复杂应用。

为什么这么说,对于小公司而言,轻量的swarm部署简单,维护成本低廉,提供的api基本上能满足公司的日常运用,服务的发布,扩容,管理都能很好的完成。但是它实在太过于轻量,相应的生态并不成熟,没有配套的ui,没有rolling update,没有监控,等等,只适合做生态的一个module,要想基于它做一套完善的体系,成本实在太高。对于大公司就不一样了,轻量的swarm可以配套各种组件,比如dns,监控,调度,可以非常方便的嵌入,当然前提是大公司,有人,有钱。

所以对我们暴漫这样的小公司,没钱还喜欢玩高大上的东西^_^,所以我们果断否了这个方案。

最近docker Datacenter发布了,swarm作为基本组件嵌入进去,有兴趣的可以去玩下。

3. mesos + marathon +haproxy+haroxy-bridge

我们为什么引入mesos,这个也是考虑很久的决定。

首先是现有资源使用不均匀,虽然说暴漫的机器是按需开的,但是由于平台性质(传媒)和用户群体,流量非常不稳定,高峰期主要集中在寒暑假。平常流量相对较少,资源相对冗余,所以我们想把资源更好的安排和利用。

其次考虑到现有的数据分析组件的扩展性,目前spark集群是standalone,可以非常方便的将他们迁移到mesos上去,hadoop生态圈其他组件加入也是如此。

mesos比较完美的将集群资源整合到一起,构成一个巨大的资源池,让我们能够对现有资源池做一个评估,如果资源不够时候申请主机加入资源池,冗余太多也可以从资源池减去。如果这个过程是自动化的,那就是弹性扩容了。非常遗憾目前我们还没有做到,估计下一阶段会重点做这个模块。另外mesos生态非常健全,基于它的资源调度社区制作了各种framework,比如容器编排的marathon、k8s,事物调度chornos等等,合理的编排这些framework,可以让应用部署的更好,更合理、更容易维护。

盗一张图和一段话来表示mesos对分布式集群做细粒度资源分配

“左边有三个集群,每个集群3台服务器,分别装3种分布式计算平台,比如上面装3台Hadoop,中间3台是Spark,下面3台是Storm,3个不同的框架分别进行管理。右边是Mesos集群统一管理9台服务器,所有来自Spark、Hadoop或Storm的任务都在9台服务器上混合运行。Mesos首先提高了资源冗余率。粗粒资源管理肯定带来一定的浪费,细粒的资源提高资源管理能力。”

Mesos资源调度与管理的深入分享和交流

既然选择mesos作为资源调度,那么要搭配怎样的组件才能实现更好的容器编排调度能力呢?这里我们最先测试使用了社区的marathon

marathon具体有哪些特性大伙自行看文档,这里列一下最吸引我们的地方:

— UI

为什么放在第一点,因为这是个看脸的时代

总体来说marathon的ui界面美观,功能完善,不想看图的直接略过

界面比较精简,监控服务的运行状态。

发布界面比较完善的配置参数

同时 可以对服务做停止、重启、暂停、扩容等多种操作。

当容器的部署,重启,扩容 出现问题时,可以直接stop/rollback

— rolling update

有人说容器时代对ops来说是一个解放,但是我并不这么认为。为什么呢,因为运维对线上负责 ,我们允许新技术的存在,但是不允许风险超出我们的底线,容器时代的服务升级意味着容器的stop和start,关闭-启动的方式决定着他不可能做到热部署。于是我们只能尽量把服务升级时候的服务震荡做到最低。灰度发布是解决此类问题的好方法。

docker 容器的更新方式决定着它不能做真正意义上的热部署。因为服务的升级意味着容器的stop和start,即使这个时间间隙很短,但总是有一段时间服务处于不可用状态。这里借助灰度发布来尽量将损失做到最低。

marathon想要设置rolling update,需要在服务发布文件上加入如下配置

"upgradeStrategy": {
"minimumHealthCapacity": 0.5,
"maximumOverCapacity": 0.5
}

官方给出的解释如下:

During an upgrade all instances of an application get replaced by a new version. The upgradeStrategy controls how Marathon stops old versions and launches new versions. It consists of two values:

  • minimumHealthCapacity (Optional. Default: 1.0) — a number between 0and 1 that is multiplied with the instance count. This is the minimum number of healthy nodes that do not sacrifice overall application purpose. Marathon will make sure, during the upgrade process, that at any point of time this number of healthy instances are up.
  • maximumOverCapacity (Optional. Default: 1.0) — a number between 0 and 1 which is multiplied with the instance count. This is the maximum number of additional instances launched at any point of time during the upgrade process.

翻译一下,update策略就是用新的实例替换旧的实例。其中minimumHealthCapacity设置集群中最小健康实例数量,比如设为0.5,保证升级的时候有一半的集群是正常工作的,maximumOverCapacity设置最大超出容器。比如设置0.1,容器在升级的时候会多开0.1倍的新容器,加快更新速度。

比如:

"upgradeStrategy": {
"minimumHealthCapacity": 0.5,
"maximumOverCapacity": 0
}

升级开始时docker会先关闭容器,存活容器数量下降到50%的时候,停止关闭容器,开始启动新容器加入集群。每启动一台,就关闭一台,使可用容器数量始终>50%。

当然个人推荐使用

"upgradeStrategy": {
"minimumHealthCapacity": 1,
"maximumOverCapacity": 0
}

这样可以使容器数量始终保持100%,且更新时只会超出1台容器的资源,

红线之上是用marathon scalling了20个容器,红线之下是rolling update过程,先启动一个新的,关闭一个旧的,平滑过渡。

与rolling update对应的是rollback过程,容器更新的时候难免出现各种问题,如果出现错误可通过api或者web界面rollback,非常好用。

另外 有个小经验,如果发现服务状态是waiting,多半由于以下原因造成

- 集群资源不够用

- 网络出错

- 服务deploy的Json写错了

其中新手Json出错的可能性最大,marathon这点没有反馈确实不太好。

— 健康状态检测

"healthChecks": [
{
"command": null,
"gracePeriodSeconds": 3,
"intervalSeconds": 10,
"maxConsecutiveFailures": 3,
"path": "/",
"portIndex": 0,
"protocol": "HTTP",
"timeoutSeconds": 5
}
],

容器存活不代表服务存活。健康检测是一个非常有用的功能,通过心跳检测服务的运行状态,

rollback的加入使得如果升级发布的时候出现问题可以快速会滚。

当然 显然还是很有问题的 官方推荐的loadbalance策略是结合bridege 但是事实上效果并不好,首先haproxy自身的性能就比nginx等稍差 链接 对比

再者 官方推荐的loadblace实际上上是借助haproxy去同步haproxyconfig的列表 。但是心跳是设置的60s 这就意味着 每隔一分钟才去同步一次。

如果rolling update再这一分钟之内完成,将造成集群port更新但是没有haproxy没有更新的问题 这就意味着大量的访问将failed。

所以目前marathon非常适合单容器实例的应用,而且对可用性要求不是特别高的项目去运行

所以说如果说保持服务升级的时候高可用性,然用户无感知,还是要从以下两个方面入手

1. 容器更新的时候 负载均衡器要尽可能的最快速度去获取更新变换后的心的prot

2. 对容器来说 docker stop过程是给容器发了两个信号,一个是signal一个是kill 要在接受signal的以后这段时间尽量将请求完成

简单点说

在容器更新之前,,就要把他从proxy列表中去掉,不再接受新请求,把遗留的请求处理完毕,而新启动的容器需要先启动,初始化完成之后再加入proxy列表

在目前的解决方案里面 并没有成熟的可以做到上边的方案,我们只能尽量将损失缩到最小

4. mesos +marathon + docker registrator + consul + nginx-upsync

回顾bridge的缺点 确实他的同步时间太长 所以我们尝试将同步时间缩短,

docker registration基于docker event编写

key value service 链接

Show your support

Clapping shows how much you appreciated husheng’s story.