本文作者: 林轩、白慕、潇谦
前言
在基础设施方面,今年双11最大的变化是支撑双11的所有交易核心应用都跑在了Docker容器中。几十万Docker容器撑起了双11交易17.5万笔每秒的下单峰值。众所周知Docker技术这几年大热,但如果期望阿里这么大体量的应用全部使用Docker,这可不是一朝一夕就能完成的事情。阿里的应用数量庞大,种类众多,光兼容性的验证没个1、2年的时间没人敢把核心应用放上去。因此虽然Docker能给研发和运维带来的好处,作为技术人员大家都心领神会,但是想直接去使用,那面对Docker浪潮却只能是坐观弄潮者,徒有羡鱼情。
1 T4和Docker的融合
所幸的是,这个状况在去年7月份有了改变,这事要从阿里内部的容器技术产品T4说起。T4是阿里在2011年的时候基于Linux Container(LXC)开发的容器技术基础设施。从12年到15年3年时间里用的应用越来越多,实际上已经覆盖了电商领域大部分App。相比Docker的模式和理念,T4其实更适合阿里内部的运维现状。T4是从阿里内部的资源管理和日常运维中土生土长出来的产品,在诞生的第一天就针对内部基础设施、运维工具甚至是运维习惯做了很多特别的设计。T4在LXC容器的基础上,对容器资源和各种统计的可见性做了很多卓有成效的隔离,使得在容器内部看到的资源就是分配给这个容器的资源,内部看到的负载就是这个容器的负载等等。同时在LXC之外还做了容器的磁盘空间配额限制和隔离,容器内看到的那块磁盘大小就是创建容器时分配给他的磁盘配额大小。这样在CPU、网络、内存、磁盘等资源使用和统计监控上做到了容器内和物理机上基本没有区别。原来跑在物理机,或者KVM、Xen中的应用,能够平滑无感知的迁移到T4中。当年从Xen、Kvm迁移到T4的过程中,很多应用的开发者和Owner确实不知道什么时候完成迁移的,可能在某次常规的应用发布中,后台工具系统已经自动做完迁移了。甚至直到去年在和一个应用研发的沟通中,他坚信自己的应用是跑在KVM中的,我们到后台查了一下,其实已经在T4上了。
我们在去年5月份接手了T4的整个维护工作,发现T4的功能和Docker非常相似,但是T4和Docker相比有一块很大的短板就是没有镜像机制,再加上T4这种对人工运维的友好性,在长期使用中就出现了一个问题,就是应用的容器里面遗留了越来越多的不一致,对运维的标准化造成了阻碍。比如有相当一部分应用,在迁移到另外一台物理机上后就没法运行了。因为从第一次上线到存活期间做的各种环境变更、软件升级等,很难严格的记录到发布系统里。另外一个发现是T4的技术栈和Docker非常相似,都是基于linux内核的cgroup、namespace、chroot、bridge等机制运作起来的。T4使用了LXC,Docker的执行引擎也支持LXC,但神奇的是T4却能够在阿里内部老版本的OS和内核上跑起来。因此直觉告诉我将T4和Docker结合起来应该是可行的。于是从去年6月份开始对这个方向做了探索,终于找到一个恰当的模式,对Docker和T4都做了一些修改整合后,将两者融合为了一个产品,相当于既让T4具备了Docker的镜像能力,又让Docker具备了T4对内部运维体系的友好性,并且能够运行在内部早期的AliOS5u和2.6内核上。这个产品在内部称为AliDocker,在去年8月份推出了第一个雏形版本。另外这个版本还解决了Docker当时很严重的一个问题,就是Daemon退出其上所有的容器都会退出,这一点在真正生产环境大规模部署时是无法接受的。Docker官方直到1.10版本才开始部分解决这个问题。我们当时的Docker版本从1.5一直跟进到后来大规模部署的1.9版本,通过Docker的Daemon管控进程和容器的解耦,Daemon重启后对之前运行容器的自动识别和重新接管,解决了这个问题。这样Docker在阿里内部大规模应用就有了可能。
从这个版本发布到能够替换T4大规模部署还走了很长的路,首先T4和Docker毕竟是两个不同的产品,除了大家耳熟能详的容器机制之外,其实还有非常多的细节和特性。为了让应用在迁移中无感知,AliDocker对原先T4容器的细节功能做了全面的兼容,同时对上层的运维系统做了大量改造,使其支持Docker场景下的发布和运维模式,从Docker镜像构建到分发启停、扩容迁移都做了完备的工具和流程支持。其次在T4到AliDocker切换的过程中,我们做了2者混跑的支持,也就是说同一台物理机上可以同时跑原来的T4容器和新的AliDocker容器,互不干扰并且能统一运维。因为众多应用的实例是交错部署在众多物理机上的,同一个物理机上往往有十几个不同应用的实例混跑。这种兼容机制就保证了不同应用可以按各自的节奏逐步完成Docker化,而不需要在某个时间和空间做一刀切,避免了大规模升级Docker的过程中不必要的应用腾挪和迁移。然后在我们将AliDocker和T4功能完全对齐,从实例级别到应用级别做了足够的灰度后,推送了一个开关,使得从那一刻开始创建新T4实例时会自动创建为AliDocker实例,从而完成了增量实例的切换。对于存量的T4实例,我们选择了一个完整的深圳交易单元,分批次做了批量切换,在切换期间如果发生大的问题,可以把深圳单元的流量全部切换到上海。这一保障要得益于阿里的异地多活灾备架构,对于这类底层基础设施的升级能够提供理想的兜底方案,使我们敢于放开手脚去做,而又能有效的控制风险。所幸在整个升级洗牌的过程中,没有动用到这个大杀器的功能,虽然出了一些小问题但都能及时修复,影响不大,也让我们的系统更加健壮,让各个部门的人对交易核心流量切换到AliDocker这件事情更有信心。
2 核心应用镜像化
但是仅仅将运行容器从T4切换到Docker其实对我们带来的改变并不大。Docker真正的核心价值在于镜像机制,以及镜像机制带来的研发与运维模式的变革。应用镜像化大致来说有2种方式,一种是比较保守的方式,镜像中只包含基础环境,容器起来后,再登录到容器中部署应用包。这种方式和原先的T4类似,镜像化上不够彻底。为了彻底根治环境不一致的沉疴,从机制上杜绝非标准变更,让每个环境改变都沉淀下来,我们采取了另一种更激进的方式:镜像中除了包含基础环境外,还包含应用程序。应用新版本发布时,直接销毁原有的容器,用新版本的镜像启动新的容器提供服务。这样任何在上一个容器中做的小动作都会随着下一次发布全部清洗掉,如果想要保留下来,就必须固化到应用的Dockerfile中。一个应用镜像就代表了应用的所有依赖环境和当前版本。任何时间任何地点将应用最新镜像拉起,都能得到和线上其他实例一致的服务和行为。我们在推广AliDocker的过程中,一直和所有的应用方强调这个理念,也获得了大家的认同。
于是在今年5月底,我们成立了专门的项目组,快速推进这个事情,目标是把双11流量覆盖的核心应用全部升级为镜像化模式的Docker应用。由于我们做到了运行态与T4保持一致,改造过程比较顺利,但实际上线中也遇到了许多问题,其中最大的问题就是发布与扩容速度。在镜像化之前,应用新版本发布时只需要分发应用包本身,而应用包一般只有几百M的大小;切换到镜像化模式之后,由于镜像包含了一个完整OS的lib库以及应用依赖的rpm包,往往有几个G的大小。应用一下感觉发布过程变得非常慢了,有时慢到难以忍受。同时这个变化对分发网络和Docker镜像仓库也造成了非常大的压力。在大规模发布或扩容时,很容易把仓库的下载源打挂,发布失败率居高不下,严重影响了整个发布速度和体验。为了解决这个问题,我们成立了紧急攻坚小组,集中精力对发布扩容链条的各个环节做了大力优化。
首先,在存储上,AliDocker镜像仓库的存储直接使用了阿里云的分布式文件存储产品OSS。阿里内部的线下开发测试环境和线上正式生产环境在网络上是隔离的,而OSS产品在服务端做了线上和线下的打通。AliDocker除了用OSS解决了镜像存储的高可用问题以外,也借助OSS的这个特性实现了线下push镜像线上pull镜像的功能。
其次,在分发架构上,由于阿里在全球各地都有机房,大规模扩容时除了异地机房延迟增加之外,还有可能把长传带宽打满,所以我们在每个地区搭建了镜像mirror和超级缓存中心。在节点上下载镜像的单个层时采用了类似BT(BitTorrent)的P2P链式分发技术,P2P分发中超级缓存中心作为默认回源节点。这样镜像仓库mirror加链式分发组成的多级细粒度分布式分发网络大大加快了分发速度,彻底解决了服务端网络和存储的压力。
最后,在发布流程上,我们针对内部发布系统做了多项优化。比如采取了预热模式,线下构建好镜像后就直接异步分发到线上各地域的mirror中,分批发布中第一批机器在执行时,第二批机器就提前去pull镜像。另外把之前固定的分批发布策略改成了智能滚动分批。每个批次的机器发布执行过程中,往往总有几台长尾机器比较慢,如果等这些最慢的机器都执行完才去执行下一批,在实例很多的时候会拖慢整个发布的过程。改成智能滚动分批后,在第一批发布完成应用owner确认没有问题之后,后面批次的发布不会等待长尾实例结束就会直接执行下一批,这样大大提高了并行度,减少了发布自动执行过程中不必要的等待时间。
在所有这些优化之后,实例最多(近8千个实例),发布最慢的那个应用,整体发布时间缩短到了原来的1/5,并且发布成功率也大大提高,因分发问题带来的稳定性也随之消失。
3对Swarm的定制和优化
今年双11的另外一个变化是,大部分的流量都跑在了云上;交易主链路的核心应用在云上和云下都有实例。在AliDocker出现之前,云上和非云是两套完全不同的虚拟化机制,云上是ECS中直接跑应用,云下是物理机的T4容器中跑应用,应用下层的虚拟化机制不同也造成云上和云下只能采用两套不同的运维发布系统,让整个运维体系比较痛苦和臃肿。AliDocker出现后,将这2者统一了起来,云下在物理机上跑AliDocker容器,云上在ECS上跑AliDocker容器,应用及上层运维系统看到的都是AliDocker容器。统一的机制带来更简单的实现。我们在这个时间点引入了Swarm来做Docker容器的管控。Swarm协议基本兼容daemon协议,只在很小的几个点做了改动,可以将一个物理机集群当做一台宿主机来操作,为集群背景下的测试和运维带来了非常大的便捷。我们对Swarm做了一些改造,使Swarm支持批量创建容器,功能类似后来Docker1.12版本的swarm模式引进的replica概念。因为我们绝大多数应用最小化的部署实体都是对等部署的,多个容器中跑的是完全相同的镜像和配置,因此只是将Swarm中一次创建容器的请求处理过程,并行在多个物理机上执行就达到了目的。至于这多个物理机如何选出来,我们现有的资源管理平台有一套自己的调度系统,因此我们修改了Swarm的调度部分直接对接到了我们自己的调度系统上,同时对接了我们内部的集中式ip资源管理系统。每个容器的ip独立于宿主机的ip,不需要端口映射,就可以直接发布到服务注册中心,供其他应用直接调用。
这个定制化的Swarm产品在内部称为为AliSwarm,除了调度之外的基础功能都与官方Swarm相同。但是随着AliSwarm接入的节点越来越多,官方Swarm的实现部分暴露出了诸多性能问题。比如Swarm内部存在系统线程会随连接数增长的问题,当node达到一定量时大批量增删node很容易造成Swarm实例直接crash;在node节点数变多的情况下,总会有那么一部分node节点不那么健康,在这种网络频繁时断时续的极端情况下,Swarm的内部处理存在连接泄露问题,会使系统资源消耗越来越大;每次大规模节点上下线时node列表会发生频繁变化刷新,内部识别哪些是新增node哪些是删除node时做的diff操作会大量消耗cpu;另外对单个容器的查询需要遍历全集群的容器信息,当节点规模在1W以上时,这种遍历也会消耗大量cpu,等等;AliSwarm在逐渐接入整个电商规模节点的过程中逐步发现这些性能问题,并一一做了解决。官方Swarm单实例能够平稳运行的最大集群规模,按之前官方公布的数字是1000台宿主机。AliSwarm在双11之前实际线上运行的单实例集群规模已经在3万以上,并且32核机器cpu的load在5以下,在双11建站期间能够支持并发3千个以上的实例同时扩容。
节点规模变大之后,每次swarm自身发布初始化过程会持续几分钟,系统可用性降低。同时我们有部分业务需要独占的隔离资源池,如果为每个资源池都搭建一套Swarm维护成本比较高。因此我们开发了SwarmProxy系统,做了基于TLS证书管理多个集群的功能。同时每个集群运行2个对等的实例,一主一备,同一时间,只有主实例提供服务。主实例发生故障时可以自动切换到热备实例。这样AliSwarm自身版本升级时,就不需要等待新实例初始化完成才能提供服务,而是先让旧实例继续提供服务,新实例启动初始化完成后替换原来的热备实例,成为新的热备,然后再做一次主备切换完成升级。这个主备切换过程会根据版本号大小和新版本初始化完成情况自动执行,毫秒级完成。
4阿里中间件(Aliware)Docker化
除了交易应用外,今年我们还推进了阿里中间件(Aliware)的Docker化,中间件相对于应用来说主要区别是有状态、性能敏感、资源需求差异大、有个性化的运维标准等。中间件的状态经过分析主要分为三类:数据状态、配置状态、运行时状态。针对数据状态,我们通过挂载Volume的方式,让所有需要持久化的数据,全部直接落到宿主机的指定目录中,以保证容器升级或变更后,数据能够不丢失;针对配置状态,我们采用了镜像和配置分离的方式,容器启动时,会根据启动参数或环境变量去配置中心动态获取运行时配置,通过这种方式可以实现在任何环境下均使用相同的镜像,以减少镜像构建和传输的成本;针对运行时状态,我们制定了中间件镜像标准和运行时管控标准,对容器进行操作的时候容器管控平台会按照既定标准和流程进行,以保证整个过程平滑和能够被容器内应用感知并进行相关前置操作。
阿里中间件(Aliware)作为集团内的P0级基础服务,对性能的要求近乎苛刻,我们在Docker化之初就定下目标,性能损失需要控制在3~5%之内。影响中间件性能的因素主要有网络、CPU、内存、磁盘、内核、依赖包等,所以在实施过程中,我们对每一款中间件均进行了详细的性能测试,测试场景涵盖物理机+Docker、物理机+VM+Docker、物理机+T4,经过测试、内核、网络、容器、中间件等多个团队的通力合作,最终所有中间件Docker化后的性能均达到预期,并沉淀出了Docker化场景下针对不同性能影响因素的调优方案和策略。
阿里中间件(Aliware)对资源的诉求也比较多样,比如消息中间件对网络、磁盘需求较大;缓存中间件对内存需求较大;数据中间件强依赖CPU性能等,针对这种情况我们在资源调度上会均衡各产品需求,让有不同资源诉求的中间件共享宿主机资源,以保证宿主机的资源使用率保持在合理的范围,既节约了成本,又提升了中间件的稳定性和性能表现。在双11大促备战期间,我们还对Docker化的中间件进行了资源的精细化调整,测试并评估了超线程(HT)带来的影响,并对强依赖CPU的中间件进行了调核,以规避资源争抢风险,进一步提高了中间件的稳定性,此外,我们还具备Docker化中间件资源动态调整的能力,比如双11大促前,我们可以动态为数据中间件增加CPU配额、为消息中间件增加磁盘配额。
在阿里集团内部,中间件(Aliware)每一款产品都有自己的个性化运维需求和流程,在这样的背景下,我们以AliDocker为推手,完成了中间件统一运维标准和体系的建立,并产出了中间件Caas平台和场景化运维平台,中间件运维效率、资源管理效率均得到了极大提升,双11前我们实现了所有中间件的Docker化改造,并承担了线上20%-100%的流量,按计划到财年底我们将实现中间件的100%Docker化。
在今年刚刚过去的双11中,从AliDocker引擎到上层运维部署体系都经历了一次大考,最终交易全链路所有核心应用全部在AliDocker容器中圆满完成了1207亿成交额的答卷,也证明了AliDocker技术体系在电商交易级别的大规模应用中能够担负重任!