编者按:
《微博混合云架构》专栏是InfoQ向新浪微博技术团队的系列约稿,本专栏包含8篇内容,详细阐述以DCP设计理念为指导思想的混合云架构实践。本文是该系列的第五篇,主要介绍容器编排的设计与实现。
《微博混合云架构》专栏主要包括以下8篇内容:
最近由于个人原因,与InfoQ约稿的专栏《微博混合云架构》很久没更新了,在此深表歉意。今天的主题内容主要写容器编排设计与实践。在开篇之前,我觉得有必要回顾一下前面内容。目前已经发表了4篇。
再看本篇前,可以回去看看InfoQ官网上这些篇的内容,有助于更好的理解接下来的内容。
编排是Orchestration在国内的常见译法,刚开始接触这个词时,我也很迷惑,为什么这么翻译,等理解了才明白一二,它其实是一种“设计”方法,在很多领域可见,在Container技术大火后,才真正进入我的视线。举个不恰当的例子:编舞师要想创作一个好的舞蹈作品,必须精心的编排,这包括灵感及创意、挑选演员,人数安排,舞美设计,人物动作表情,排练及突发情况下的应对等等,是一个复杂的过程。
对标到Container的场景,我们知道原生的Docker,其实是个单机版的,所以Docker官方才会出了一系列的工具,比如最早的时候单机版的编排工具Fig(团队被收购),远程下的编排工具Swarm,甚至发布了Docker Datacenter,其中的UCP就嵌入了Swarm,以实现对Docker的环境管理与编排。
因此,对于容器编排,可以从以下两个方面理解:
容器编排是跨主机去管理多个集群Container的一种行为,随着Docker的发展,生态圈越来越完善,比较常见的Kubernetes,Mesos + Mathon,甚至DCOS等都属于此类范畴。
容量编排是为了将资源利用率最大化,同时均衡系统因容错需要不断变化的需求。可以看下图
我们可以看到,早期的应用场景,经典的三层架构中,不管物理机或者VM,一般一个机器一般只跑一个服务,而在Container时代,不同的服务可能跑在同一个机器节点上,这种服务的组合是不固定,动态的调配,对工程师是透明的。这种动态的调整能力可以称得上是容器的编排。
从这个图中我们可以抽象出,容器编排需要解决哪些方面
服务定义:一般以IP + port唯一标识一个服务,这里端口的分配与管理会成为重点。
资源管理:一个服务要运行,需要相应的资源,一般是CPU/MEM/DISK/NET等,怎么获取到这些资源,并合理分配是资源管理需要关注的部分,一般采用池化处理(资源池)。这也是编排的核心,Mesos在这块做的比较好,通用且稳定。
容器调度:有了资源,怎么合理的选择资源节点,采取什么策略,怎么做容错处理等等,这些是容器调度需要关注的。
服务检测:服务在起来后,要对外提供,需要验证其正确性,这里一般会做端口及业务检测
服务发现:验证通过的服务,如果真正对外提供服务,需要引入流量,也就是自动挂到LB上,这也是一种WEB类的服务发现。
以上5种我觉得是容器编排里最核心的内容,实际上,生产环境的编排单元远远比这里多很多。
注: 这里插播一个,我经常看到很多同学把容器编排和容器调度等同,其实吧,无所谓对错了,这可能主要的原因是大家的理解不一样,表达的方式也就有差异,不用纠结,只要把你的业务问题很好解决了,就是棒棒哒。
随着行业的发展,我们看到互联网技术领域的一些趋势:
在这样的趋势下,互联网公司多多少少都会根据自己的业务场景做出一些改变,比如升级应用架构,业务上云等等,这其实对运维来说,复杂性在过渡阶段是增加的。就拿我们的业务来说,建设私有云的同时,也推业务上云,我们的基础架构很难用标准化的方式去满足需求,这也推动我们去重新建设运维平台,也就有了混合云系统DCP的诞生。我们在设计之初的时候,遇到了一个很明显的问题,我们很难用单一的IaaS,PaaS或CaaS去定义我们的场景,于是我们选择了混合云的模式,系统采用分层设计的方法去适应业务场景。
在这个过程中,我对编排的理解又有了一些变化。我觉得不同的层是存在不同的编排方法的。简单来说,IaaS的编排核心重点在计算资源,PaaS的编排核心在于应用,CaaS的编排核心在于容器即服务。而对于我们的混合云DCP,我把他分为两大方面:
对于资源的编排,混合云模式下,专线的落地是最重要的,决定着整个设计及业务上云的规模,如下图,我们与阿里云之间就通过VPC技术拉通了常备的专线,打通内网。
在分层设计之上,我们在工具选型上就明朗多了,主要选用了自研的Dispatch和Swarm。
微博目前开发的DCP,主要包含3层,资源管理层,容器调度层及业务编排层。底层的资源管理分为两个部分:内网主机和阿里云ECS,我们的目标是对上层业务提供统一、不可变的基础环境;中间的容器调度层,主要是根据资源去调度容器,并启动容器,提供各种原子型的操作,比如基于任务模式的容器起、停、删、重启、服务注册、服务反注册、服务检测等等;最上层的业务编排层,则是基于调度层的API,去串起整个业务的常见操作,比如服务部署、扩容、缩容、容器迁移等等。根据上面对于编排的理解,我觉得把资源层算作资源编排,而调度层与业务编排层,统一称服务编排。我们看下图:
这里需要说明的是,分层设计的指导思想的核心:
细化出来,DCP的功能模块是很多的,可见下图:
其中,资源管理层的模块主要是调度运算资源。目前设计了共享池、Buffer资源池,配额管理,及多租户管理机制,借此实现弹性调度资源,这也是DCP的核心之一,可以看做是一个定制的IaaS吧。调度层后面会细说,基于任务机制的容器调度。
这里重点说下业务编排层:最上层的是对业务场景的抽象,根据目前的梳理,微博的业务,前后端各种语言都有,加上大数据体系的服务,在这种情况下,我们很难抽象出用一种非常通用的模式,直接支持各种语言的服务,这也受限于不同服务的Docker化程序。于是我们做了如下设计:
服务设计:我们设计了集群,服务池,容器节点这样的三级维度去描述服务
业务级隔离:产品线对应集群,集群与集群之间在业务层上是产品线之间的隔离,各家自己的服务互不影响。
开放业务编排层:微博后端几乎是Java类的服务,前端是PHP类,同时大数据体系又有离线计算和实时计算的,还有一些小语言的,每一类的服务,我们做成是通用的模式,比如Java类的就独立设计其编排模式,并且把这个设计开放到公司的范畴,别的部门也可以来一起参与设计并实现。在开放的过程中,我们的原则:底层的资源,调度,服务服务一定是全公司内统一的。
从这些架构设计角度,可以看出,我们的DCP的核心设计理念包括4个:弹性,自动化,业务定制及all in one。相对XaaS类的服务,我们更注重业务需求的落地,解决实际问题。
ps:综合来看,我觉得有一个很重要的点,虽然Docker很火,但好多公司都很难将自己的业务Docker化的比较彻底,这其实也在影响着你的架构设计。比如与物理机还有一些依赖的东西,比如程序部署的逻辑关系,比如日志及围绕日志的周边工具,但是业界的一些开源编排方案,schedule/scale/rolling update等这些操作,其实对业务的标准化要求很高,包括试用的一些云服务,要想把业务以最小成本的迁移部署,有一定难度的,所以有些公司内搞Docker,定制化的需要还是很多的。所以如果你想大规模用,其实可以考虑牺牲一些通用性的原则的,我觉得微服务看来是一个解决方案,由于历史原因,我们很早就是服务化了的,要改造成微服务化,成本蛮高,因此从14年业务上云之后,虽然相对来说,走在了前面,但是一直还在路上摸索着。
这一节,我们再来看看DCP对服务的设计,我们的核心需求是弹性,设计了集群,服务池,节点这三个维度,而弹性是建立在服务池这个维度的,以IP + port唯一标示一个服务。如下图:
这里有个两个易混淆的概念,众筹池是所有集群的冗余设备;Buffer池是集群内的冗余设备。这么设计的主要考虑是,某个业务在集群内的服务之间,是很容易自己去弹性调整的,集群内容量不足了,则会去私有云内调度,私有云内也没有的,会去公有云调度。保证服务可用性,同时为了保证每个集群(产品线)不乱用,给集群设置了配额的管理。从业务运维的角度可以看到如下的流程:
除此之外,值得一提的就是DCP的用户视角,我们设计了三类角色:
有了服务设计以及原子型的调度API,去实现业务编排就简单很多了,大体可以分为2个部分:首先,根据某类业务场景,设计较为通用的Docker化方案;其次是服务部署及弹性扩缩容。
在概述篇中,我们已经讲到,服务的弹性扩缩容其实是一些很重的操作,在DCP中,做了3个流程的设计,来串起这些复杂的操作,这3个流程是:资源申请、设备初始化及服务上线。弹性扩容任务所需要的设备来源是内部的集群以及外部的阿里云。而申请到足够设备后,也必须对设备进行初始化、部署环境及配置管理。最后一步则是将服务上线,开始执行Container调度以及弹性扩容的任务。
第一步,则是资源申请,这在服务的设计已经详细说了,私有云的设备来源,主要来自离线集群、低负载集群以及错峰时间空出的多余设备。具体来说:离线集群并不是时时刻刻都在执行运算,即使稍微减少计算资源,也不会对业务产生重大影响;而工作负载较低的集群,如果有多余的资源也可以进行调度。此外,微博有上百个业务线,每个业务线峰值出现的时间点都不一样,而在此种状况下,各业务也可以互相支援,共享多余的计算资源。私有云的设备不足,会去公有云上自动创建,整个操作流是自动化的。
第二步,则是设备初始化,我们已开发了工具系统:可以达到操作系统升级自动化、系统操作API化,而服务器所依赖的基础环境、需要的软体等环境配置等需要标准化。如下图
私有云内通过配置管理工具Puppet,将需要的配置写成模块,并使用RPM机制打包,进行安装。而公有云则是通过Ansible来做此类工作。这块业界中的一些做法也可以免去初始化的流程。例如导入为Container而生的容器操作系统,像CoreOS、RancherOS或Red Hat Atomic。不过,虽然这样可以,但是此种做法的可维护度高,但是缺点在于迁移设备的成本较高。
当然这个操作也是耗时操作,主要的时间取决于申请资源的时间,实际初始化也就只需要1分钟而已。
(点击放大图像)
第三步,则是最为复杂的弹性扩容操作,前一线的运维人员,只要在系统页面上输入服务池名称、服务类型、Container类型、需要Container数量以及镜像地址等参数,即可在5分钟内完成扩容,扩容完成后,系统也会产出完整的报告,列出扩容程序中,执行的步骤,以及每件任务的成功、失败状况。我们先来看下扩容流程:
我们再来看看耗时分析:可以看到大部分的弹性扩容任务都是分钟级的,总体上,基本能完成10分钟扩容上千Container的能力。
(点击放大图像)
由上面可以看到,这些每一个流程能够很好的串起来,以及容错机制,这背后是有一套任务引擎在工作的,这个就是任务系统。其主要的思路是改造原有自研的jpool系统为任务引擎,来串起这些操作。
详细信息,可以看我在Qcon 2015上有个Jpool的分享,这个就不细说了。
再次回到概述篇,我们建立这一套系统,核心要实现的是服务具有弹性,能根据容量情况进行自动的弹性伸缩,以此来解决明显的早晚高峰及热点事件的峰值问题。截止到上面讲的,我们的系统已经完备了,但是操作的触发是需要人的参与,而且也会有几个操作步骤,人本身是懒惰的且是易犯错的,且还会存在人力成本,这还没达到我们的目标,离无人值守还差一段距离,于是,我们又开发了两个工具:
经过分析,我们的调度框架需要有以下几个特性
支持各种调度策略,初期可考虑实现基于时间的调度方式,类似crontab触发机制,Chronos、 Quartz提供可借鉴的任务调度实现机制。
支持主备模式,如Mesos和Swarm多提供了多主的实现,其中Swarm采用consul、etcd、 zk等协调服务,各master间通过竞争一个分布式锁的方式来获取leader权限,获得leader权限的进程对外提供服务,未获得leader权限的进程处在等待状态,可参考Swarm的多主实现方式。
提供Http API运维配置,Consul提供了非常优秀的RestFul协议和UI,可参考。
支持服务的依赖,A服务依赖B,B在自动弹性扩容时,需扩容好服务B。
所有参数,可配置化。
最后形成的架构如下,并用Go做了实现:
可以看到,在更高层面的调度框架具有以下特点:
这个调度框架组件目前已经很稳定,每天都会定时的在晚高峰进行自动弹性扩缩容。有了它,我们私有云的核心服务的冗余,从之前的冗余150%减少冗余120%,这样就可以下线调一大批机器,以节省成本,真正实现基于公有云的弹性。
除此之外,基于这个框架API,做了个可配置的简单页面,定义服务以来及弹性扩容参数,非常的方便,见下图:
(点击放大图像)
ps:此任务非上面任务系统中的任务。
基于容器的业务编排在整个混合云的设计当中,是非常核心的内容,为新浪微博元旦,春晚及日常晚高峰的弹性扩容提供了强有力的支持。我们当前还在不断的完善系统的建设,同时,也正在纳入更多的业务场景,还是那句话,基于Docker的云应用,我们一直在路上。希望我们的方案对大家有一定的参考意义。
感谢魏星对本文的策划和审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们。