编者按:《微博混合云架构》专栏是InfoQ向新浪微博技术团队的系列约稿,本专栏包含8篇内容,详细阐述以DCP设计理念为指导思想的混合云架构实践。本文是该系列的第二篇,主要讲解了在新浪微博业务背景下DCP的不可变基础设施服务。
《微博混合云架构》专栏主要包括以下8篇内容:
不可变,顾名思义就是一旦创建,便不再修改。这听起来和我们常见的诉求——灵活的部署环境,似乎背道而驰。对于DCP中的基础设施而言,这并不矛盾,因为创建后就没有必要去修改了。整个基础环境作为一种版本化管理的资源,和代码类似。运行时如果有一定要变更的部分,那么直接更新源码,重新创建资源即可。老版的环境依然存在,可以在需要时用于回滚。
为了做到“不可变”,有两个必要条件:
第一点很容易理解,如果重建创建的过程太慢,比如几个小时甚至数天,那将无法满足线上业务的需求,反而极大提高了运维成本;第二点在容器化技术出现之前,是相当难做到的。业务应用大量依赖本地环境的配置、文件以及缓存等,耦合过于严重导致不同的上层业务需要不同的运行环境,迫使我们对基础设施进行复杂的异构。利用Docker技术,可以将业务应用运行时的绝大部分依赖都打包到容器中。这样一来,需要维护的基础环境数量大大减少,便于统一管理,也能够支持更多的业务方来接入。
本文将主要介绍新浪微博混合云架构上的不可变基础设施服务。
正如概述中所说,私有云的设备申请和环境部署流程过于繁杂,如下图所示:
整个周期可能需要几周到数月之久,一旦有设备损坏,又需要进入另一个同样冗长的报修流程中。而短时间补充计算资源的方式只有从非核心业务中下线服务器挪过来顶上,费时也费力。但是私有云的一些优势是毋庸置疑的:安全性高、可控度高,相比以虚拟机为基础的公有云性能和稳定性更好。
基础设施方面,传统私有云面临这样几个困难:
在向混合云架构转变的过程中,我们主要处理的也就是这些问题。
微博混合云的资源分为两个部分:内网主机和阿里云ECS,我们的目标是对上层业务提供统一、不可变的基础环境。但是如果彻底不可变,反而带来了不小的麻烦。还有人提出拒绝SSH,目的也是如此。试想仅仅需要临时改变一个系统参数,就必须完整构建一次环境,并且重新部署,这是令人难以接受的。
因此我们决定将不可变的范围缩小,那些耗时相对较长,修改频率又很低的模块纳入不可变范围,这些模块的变更需要重新制作整个环境;而另一些模块放到实时的配置管理系统中,以便在需要的时候进行更新。这样既能保证构建速度,又不带来过高的变更成本的方案,同时解决上面的问题。
不同业务对环境的不同需求是客观存在的,理论上也不可能完全抹平差异,因此我们的服务必须支持各类用户定制自己的环境,自下而上包括三部分:主机环境、配置管理脚本和基础容器。
最下层是我们认为不可变的部分,而中间的配置是可能变更的模块,最上面是一些标准的容器,包括面向业务的JAVA和PHP,也包括面向运维的OPS。这样明确各层的职责,便于分别进行管理。
一致性包括两个方面:
docker.sock
挂载进去。这样就可以在容器内部操作Docker Daemon,而其他修改也仅仅只在容器内部,不会污染主机环境。公有云的使用场景原则上不会发生环境漂移的现象,毕竟每次新建的资源使用时间不会超过12小时,同时使用上述运维镜像,更加没有必要操作宿主机。 yum update
或者yum install docker
类似的命令。因此我们使用了内部搭建的软件仓库,可自己对软件的更新频率和范围进行控制,同时也要求脚本中所有安装软件的命令带上版本号,如yum install docker-1.6.2
。基础设施即服务,服务即代码,自然可以进行版本化管理。
可以看到图中以CentOS 7发行版加基础配置为起点,类似Git中分支的概念,衍生出两个不同的基础环境A和B,而从A又产生了新的分支C。每种环境都有自己的版本,在必要的时候可以回滚。需要注意的一点是,各环境的第一个版本在制作时我们需要进行更全面的测试,因为它是后面环境变更的基础,万一出问题,并没有更老的版本可供回滚。
去年微博刚启动混合云项目时,我们首先尝试的是Docker Machine——一个官方出品的Docker环境构建工具。当时这个工具才刚推出不久,还不能直接创建阿里云ECS,也无法对CentOS进行构建,于是我们自行开发了对这两者的支持。它是一个命令行工具,原生支持创建Swarm集群,使用起来构建环境只需要简单几步:
docker-machine create
对于个人使用者而言,Docker Machine非常方便,但是对于混合云项目的需求,有几点它都难以满足:
--insecure-registry
。 其实在初步尝试过程中,前4点都通过修改源码解决了,但那就没办法再合入官方的代码,不是长久之计。因此放弃了Docker Machine,使用了自己设计的另一套方案。
前面提到,我们将所有的模块分为不可变和可变两个部分,其实就分别对应了这个方案中的虚拟机镜像和配置管理。利用阿里云ECS提供的功能,把耗时长、变更很少的模块直接打到虚机镜像中,而可能变化的模块使用Ansible进行管理,能够随时执行。两部分共同构成了一套版本化的环境。如果变更的部分需要被长期使用,通过Ansible即时下发后也能够同步到镜像,保证下次构建环境时直接生效。整体设计如下图所示:
我们在阿里云API的基础上,提供了一个快捷制作镜像的服务,它使用了Ansible,用户只需要编写yml描述文件,即可生成相应的ECS镜像。类似于Dockerfile,环境描述文件也支持FROM
语法,对应的值就是上层镜像的名字和版本号,再加上INCLUDE
和ROLE
,分别定义需要执行的playbook和role,例如:
FROM weibo_plat_init:1.0
INCLUDE user.yml
INCLUDE config.yml
ROLE hongbao
在使用ECS镜像的过程中,有两点也是需要注意的:
/etc/fstab
中自动挂载的配置,否则该镜像将由于找不到第二块硬盘的分区而无法启动。另外我们选用Ansible作为配置管理工具,主要因为它架构简单、依赖少,只要有SSH便可以工作;学习曲线平滑,上手比较容易;模块也足够丰富,能够满足需求。它被广泛诟病的是性能问题,我们对此进行了一些优化:
最后,从阿里云ECS启动完成到初始化结束的用时被缩短到1分钟之内。
准备工作完成之后,就是环境发布了。我们对于公有云的使用是用于弹性扩容,所以发布也主要是针对阿里云ECS。每次发布可以选择一个VM镜像和一套Ansible脚本进行构建。整体流程为:
上述第三步的命令的批量发布使用了Ansible Scheduler来调度。但由于Ansible本身不提供HTTP接口,因此我们基于它的Python API进行了开发。
Scheduler本身是无状态的,可任意水平扩容,主要负责分发初始化命令,可控制并发度、步长等参数。每个ECS镜像都对应Ansible的一个role,在一台主机上执行的role我们称为Task,其中又包含很多Action。在每个Action开始和结束时,都会触发我们自定义的回调模块,异步将消息写入队列,最后存储到数据库中。DCP的界面上即可显示所有主机环境初始化的情况,包括执行了哪些模块、耗时和成功与否。
除了线上应用,DCP中这整套基础设施构建的系统,也在为其他公共组件提供服务:
FROM weibo_plat_init:1.0 INCLUDE user.yml INCLUDE config.yml ROLE hongbao
在大规模扩容中,这些组件也是需要支持弹性伸缩的。以DNS为例,为避免解析请求跨机房,我们在阿里云上常规部署了两台DNS服务。同时由于所有扩容的ECS都是新建的,不存在DNS缓存,业务首次启动会有大量域名解析的请求,也需要保证DNS服务的高可用。这里我们使用了阿里云的内网SLB服务,作为VIP对DNS做负载均衡。两个SLB分别指向所有的DNS服务器,设置UDP转发,并且增加健康检查,便于摘除不可用节点。
由于构建了统一的环境,整套基础监控也是标准化的,包括CPU、内存、硬盘、网络、Docker进程等等。其中对于专线带宽的监控是相当重要的一个部分,跨云的专线没法和自有机房的线路相比,不论是稳定性和带宽使用,都需要实时监控。
首先我们根据网络资源的分配在阿里云建立VPC时选择一个独立的网段,如10.85.0.0/16,然后每台ECS创建后都会安装专线带宽监控的软件,只要过滤掉所有在上述网段内部传输的流量,就是穿越专线的流量,同时也定时监测自有机房到阿里云的网络延迟。最后将监控数据推送到中心存储节点,通过grafana进行展示:
专线监控精确到单机,因此也可以自行设置聚合逻辑,从而统计各业务使用的总体带宽。
基础设施的快速构建为新浪微博混合云的弹性扩容提供了强有力的支持,不可变在很多时候是简单而高效的,但不是绝对的。实际应用中,我们也需要将基础环境中长期稳定和可能变更的模块分别处理,从而在构建速度和变更成本之间取得平衡。