今天的话题主要是弹性伸缩,扩缩容。当微信的业务量越来越大的时候,我们更关心的是效率。前期可能两三个月成交量翻倍。我们如何确保我们的运营效率能够跟上?后期主要关注的是成本问题。我们的增长在2014年之后有所放缓,因此主要关注点将放在成本上。如上图所示,我主要从四个方面进行分享:操作规范。云端管理。容量管理。自动调度。OperationSpecifications配置文件规格首先来看配置文件规格。我们在前期花费了很多精力。可能整个系统设计比较复杂。一开始会有一些配置管理工程师专门处理。后面我们会把这部分做的更规范。配置文件规范分为以下几项:目录结构标准定义服务部署到线上时的目录结构。您应该首先定义这些目录结构标准。跨服务同配置项管理为什么要提这个?在这个服务中可能有一些配置项是你需要的,在另一个服务中你也需要这些配置项。如果改了这个配置项,是不是要重新发送服务A和服务B?我们有一套机制。每台机器和每个目录都会有一个全局共享的配置项。我们这里会通过一些自动化的灰度方式来控制发布。这一块是单独拿出来的。同一个服务中不同实例的差异化配置项管理。不知道大家在操作过程中会不会遇到类似的问题。即使你用的是Docker,你的镜像管理也很好。当您部署一个实例时,您的业务可能需要对实例1做一些调整,当然您也可以使用脚本来管理。但我们认为这是一个比较复杂的状态,我们会统一为它提取所有这些差异,并尽量让它的配置文件的MD5在所有环境下保持一致。开发/测试/现网差异化配置项管理,前期也会有这个问题。我们一般不关心开发环境。测试环境也由运维管理。当然,现网是运维管理的。基本上,测试和现网唯一的区别就是路由,我们保证其他配置项完全一致。有了这样的需求,大概的效果就是不管你用什么方式扩容,我们直接复制一个镜像或者直接复制一个包,就没有后续的脚本去改了。对于同一个服务下同一个版本的多个实例,配置文件的MD5在所有环境下都是严格一致的。名称服务规范名称服务比较重要,分为三层:接入层、类LVS实现。逻辑层,像etcd一样实现。存储层、路由配置自动化。服务伸缩是一个运维项目,独立于研发变更和发布。接入层和逻辑层是类似的实现。我们内部根据自己的业务特点做了一些业务开发。存储层和QQ有点区别。QQ似乎在逻辑层和存储层都使用同一个服务名。我们还没有在存储层配置这个。我们现在的存储层和逻辑层是比较分离的。在数据方面,我们几乎可以认为是运维可以配置的一种方式,但并不是完全可配置的。使用那些辅助脚本来配置。服务伸缩是一个运维工程。我们不想在扩展服务时考虑其他因素,并独立于研发发布更改。我们有一个特点,研发是运维提供的变更系统。他基本不需要对它做任何操作,整个链条就打通了。数据存储规范数据存储规范如下:接入层,无数据。逻辑层,有短周期缓存,有静态数据,禁止动态数据登陆。存储层,具有长周期缓存,基于paxos协议的数据落地。接入层和逻辑层的业务伸缩不需要考虑数据迁移和缓存命中率。在数据存储方面,单页阅读的话,会觉得这个场景更受欢迎。比如接入层肯定不会承载任何数据。希望逻辑层不带数据,我们也严格要求不带数据。经常会有这样的场景,逻辑层需要自动伸缩。运行一段时间后,启动了一些逻辑,上面保存了一些数据。在下面缩小时,它会被欺骗。会有这个问题,所以我们会建立这个规范。逻辑层不承载数据,但是会有静态数据,包括用户发送的消息,用户发送的朋友圈,显然应该放在存储层。接入层不承载数据。事实上,我们在历史上曾经携带过数据。每次过年抢红包,大家都在摇。数量非常可怕。摇红包的重点是我们设计的每次摇红包的请求都是真正打到我们的接入层的,不会有客户端摇五次才上来一个请求。我们接入层的性能保证我们可以承受这个量,但是后面的逻辑层肯定支撑不住这个量,几千万每秒。这个时候我们会做一些很特殊的逻辑,直接在接入层承载红包数据。当然,这是一个比较特殊的场景。我们只是在过年的时候没有执行这个规范。SummaryofOperationalSpecificationsSummaryofOperationalSpecifications:Stage目标服务可运维实施措施改变系统拦截全网扫描不规范服务这里有几点,我们的目标很简单就是服务可以运维和维护,这意味着你在做扩容和缩容时不需要手动操作。如果您进行手动操作,则无法进行操作和维护。为了使服务可运维,我们在变更系统中做了一些拦截。如果下一次变更可能不符合我们的运营规范,或者之前有不符合我们运营规范的地方,我们会屏蔽并要求变更来实现我们的运营。尺寸规格。云管理接下来说一下云。云大家都用的比较多,这几年也很流行。可能大家用Docker比较多。微信这里不使用Docker,后面再说为什么没用。的原因。迁移到云端有两个原因:微服务总数接近5k。同一台物理机上多个服务的资源抢占。先说说为什么要上云。2013年和2014年,我们拥有近5,000个微服务。也是最近几年才知道,这叫微服务。我们之前的实现方式多年来一直被认为是微服务。刚开始讲微服务的时候,记得QQ海量运营的一些课程都会提到。我觉得这个思路和微服务是完全一致的。当我们运维端做一套新的服务发布的时候,基本上不会对研发有什么限制,说这个服务你不要做太多。所以整个系统的数量是相当夸张的。当然会出现它的多个服务必须部署在同一台机器上,因为里面有5000个微服务,必须部署在同一台物理机上。部署多个服务会造成资源抢占。基于这个因素,我们走向云端。哪部分上云,哪部分上云:接入层,独享物理机,容量充足,改动少。逻辑层,混合部署,容量不可控,变更频繁。存储层,专属物理机,容量可控,改动少。哪一个要上云?刚才讲到,因为接入层要处理比较大的体量,所以微信用户比较多。如果它有任何问题,接入层可能会雪崩。所以,我们主要是独占物理机,容量够用,改动少,暂时没必要上云。在逻辑层,前面说了5000多个微服务,所以比较乱,变化多,容量不可控,所以这一块在云端。在存储层,没有云访问,但是有单独的容量管理和数据迁移。Cgroup-basedcloudificationCgroup-basedcloudification:虚拟模型定制VC11=1cpucores+1GmemoryVC24=2cpucores+4GmemoryPhysicalmachinesharding我们cloud的方式直接就是Cgroup,Cgroup这个名字可能有些人不知道,我们使用内核Cgroup机制。在现网中,定制了类似简单的虚拟模型,比如1cpu+1G内存。我们前期也考虑了一些流量因素,后来取消了。我们的系统架构保证的流量似乎问题不大。以下是虚拟机分片方法的简要列表。有很多方法可以将它们分开。我们的随机分离也带来了另一个问题。系统在运行过程中,会出现一些类似磁盘碎片的东西,就像你的Windows一样。运行久了,会有磁盘碎片。我们也有一些特殊的方法来处理这个问题。Docker未在线启用。不使用Docker的原因有几个:svrkit框架全网100%覆盖,已经标准化。该框架本身在很大程度上依赖于IPC交互。自研非侵入式vsDocker侵入式。Docker非常火,我们在2014、2015年左右推出了少量上线。我们的svrkit框架是微信内部的自研框架,全网100%覆盖。100%是什么意思?根本没有开源代码,包括大家用的最多的Nginx,用于访问。我们也转向了自研,包括后面的存储。前期用的是MySQL,后期换成了我们自己的组件。现在的网络100%是我们自己的框架覆盖的,我们的规范化和标准化做的比较好。可以说,Docker解决的一些问题在这里并不是问题:我们对Docker的需求不是很强。我们在框架本身中使用了很多IPC交互,这些东西在Docker中有严格的规定。如果我们使用Docker,它可能会破坏Docker中的各种机制。如果是这样的话,你还不如不用Docker。我们对Docker接入方式的实现还是有顾虑的。一两年前有人和我讨论过,当Docker主进程启动时,服务可能会因为需要更新Docker本身而重启。最近好像解决了这个问题。在早期,我们认为这是一个严重的问题。我不想说Docker本身的变化对在线服务有什么影响。私有云调度系统基于以上几点,我们开发了一套云管理Docker系统。上图是我们的私有云调度系统:基于svrkit框架自主研发。参考borg/yarn/k8s/mesos等主流调度系统的优点。覆盖80%的微服务。基于前面提到的svrkit框架+自研,调度系统当然要有我们自己的调度系统覆盖,目前覆盖了80%的微服务,离100%的覆盖还有点差距。私有云调度系统架构这是我们的架构。熟悉它的人就会知道,它和行业没什么区别。中间有一些虚拟化的坑。也算是和大家用的差别不大,就不细说了。云管理总结云管理总结:阶段目标服务资源隔离服务伸缩和页面操作实现措施服务之间,不要因为一个服务异常导致第二个服务异常。第二个是服务缩放和基于页面的操作。有些服务还是很大的,一个服务可能有几万个单元。这时候如果不想让你上机的话,直接点击页面就可以了。为了达到这个目的,我们会部署系统,把没有上云的都屏蔽掉。他要用老的部署方式上线,他要用老的流程走老的商业模式。相信大家对于以上的操作规范和云管理,或多或少都会有自己的实现方法和结论。后面的容量不知道大家是怎么改的。容量管理如何支持业务发展这是业务量增长的曲线,也是我们容量的曲线。一般来说,你扩容一次后,这里就一直是平衡的。到了某个时候,你发现自己扛不住了,需要扩容。当你扩展的时候,你会一直保持这个状态,这和容量曲线不太吻合。第一点,当你的容量低于现有网络的业务量时,这实际上是容量不足。你能不能很快发现这个问题。第二点是处理效率。当容量不足时,曲线会被标记出来。扩容需要多长时间?如果是几天或几分钟,效率是不同的。我们希望达到一个最优容量,完全匹配业务增长。这条曲线并不难实现。只要扩容足够频繁,就会完全匹配最优容量曲线。如果你发现容量不足是分钟级别的,扩容也是分钟级别的,那你就可以画出这样一组一模一样的曲线。用硬件指标来评估容量,我们会说你怎么知道这个服务的容量不够?一般我们第一反应是看硬件指标,包括CPU占用率、磁盘空间、网卡容量、内存等。这也解决了问题,但并没有解决所有问题。使用CPU来评估容量服务能力=当前峰值/经验CPU上限,这是一种使用CPU来计算服务能力的方法。这是一个简单的例子。比如你现在的CPU峰值是40%,你自己的经验认为这个可以达到CPU的80%,简单减去就是50%的容量。硬件指标可靠吗?这东西靠谱吗?我这里画了两个示意图:有的像左图靠谱的,容量基本上是随着CPU的增长而增长的。有些类似于右边的窄口瓶图片。当CPU上升到一定程度时,容量不会增加。真实案例这是实时网络上流式传输流量的示例。如果人为上传流量,你会发现有的服务怎么玩都在80%;硬件指标的局限性我们认为硬件指标有一些局限性:不同的服务取决于硬件的类型,有的受CPU限制,有的受内存限制。临界点的质量是不可预测的。当CPU接近80%或100%时,我们观察到某些服务的质量不可预测且不稳定。有的服务可以跑到80%,CPU依然可以长时间稳定服务,有的一旦达到80%,可能会有一些请求不会被退回。事实上,有了更多的在线服务,你所做的事情就更难控制了。我们觉得应该做压测,得到更准确的容量模型。压力测试的方法有两种:一种是模拟流量,一种是真实流量环境。压力测试也有两种类型:测试环境和现网环境。一些验证。这种测试是测试团队的职责,我们并没有过多的参与。模拟流量打到现网有点类似于全链路压测。比如淘宝经常在双十一做这个。对于微信,我们只在春节期间这样做。微信过年好像跟淘宝的双十一差不多。这是一个疯狂的状态,所以我们会在春节前用模拟流量来打现网。这不是一个很常见的。压力测量方法。真实流量被发送到测试环境。一般我们要验证一些存储性能的时候会这样做。我们将在线流量绕过测试环境并查看存储性能。可以在测试环境中更好的模拟。out,不会影响现网。真实流量的现网环境,我这里想说的是,我们真的在对现网进行压力测试,用真实流量打现网环境是什么情况。现有网络压力测试现有网络压力测试实施起来非常简单。当你的nameservice比较标准的时候,就是一个统一的流程,也就是压边流程。不断调整其中一项服务的权重并观察其后果。就是这样。现网压测可能导致的异常就是这样的问题。大家可以想一想。当你按服务停止的时候,你怎么能保证不出事呢。我们认为以下三点比较关键:压力测试会不会导致失败,能不能及时发现。压测会不会带来一些低级的问题,其实你的监控是查不出来的。真正的失败之后,如何才能快速恢复。服务自我保护你的服务有自我保护吗?我们引入了快速拒绝的概念。在服务正常的情况下,你每分钟100万的在线请求也可能能正常服务。当上游服务给你500万的时候,你只能处理100万。你能拒绝其他400万个请求吗?这是我们框架的能力。上游服务重试保护什么是上游服务重试保护?例如,当你按下其中一个实例时,你不断增加它的权重,导致它很快拒绝返回并失败。你有没有一种机制去其他实例来完成整个过程?这也是一个关键点,我们在框架中也支持这一点。立体监控监控体系是否足够完善,包括硬件监控,刚才说的快拒的监控,还有前后端的耗时监控,还有刚才说的前一个,都有有没有失败?整条线是我们需要关注的。可以说只有服务的自我保护,上游服务的重试保护,立体的监控,才敢做压力测试。如果不能解决这三点,就无法进行压力测试。秒级监控能否快速发现你的异常?为了解决这个问题,我们将分钟级监控升级为秒级监控,10秒内发现所有异常。整个实现方法大家应该都差不多。每台机器都有一个集合。之前是每分钟报一次队列,然后汇总,再入库。现在我们把这个逻辑改成6秒做一个采集,然后转换数据,再做一个秒级的数据汇总。以上还是分钟级别。volumerate的动态控制还有一个是rate的动态控制,这里比较复杂,不知道这种情况怎么解释。左图是我们之前的压测实现。它从一个点开始,可能会用比较快的方式调整权重,直到调用失败,然后回滚,继续往上压。停不下来,用这种方式来接近你的能力极限。这点大家可以很明显的看出问题。压测的时候,运维其实是在给自己找麻烦。每次压力测试命中,它都会掉下来,它会命中。失效曲线就像狗啃咬一样,总有一条摇晃的曲线。这种压测方式,运维本身就受不了。我们后来调整了它。其实这也和我们服务框架提供的能力有关。我们整个服务框架都会有一个队列进入/退出机制,每个微服务本身都会有这样一个机制。这里队列的积压是一个关键指标。我们会监控入队列的延迟,当它出队列的时候,我们发现一旦有积压,性能就跟不上了。我们通过这个指标来调整压测的速率,大致达到了。结果是右边的图像。前期重量比较快。当观察到队列中有积压和延迟时,它会开始减速,直到到达一个点。可能最后一点是有点压力测试失败。要达到这样的效果,整个过程中可能只有那么几步。秒级获取性能模型,获取压测实际值。现场网络压力测试结果这是我们真正在线压缩的结果。这张图所示的斜率是先上升比较快,后面慢慢上升,然后停止这个测压点。为了做出这张图的效果,我们已经研究了相当长的时间,可能需要一年多的时间才能达到这个状态,基本不会给现网服务带来故障。容量管理小结这件事我们做完了,感觉有几个好处:服务的资源需求可以精确量化,刚才说的上百个微服务是怎么计算的?可以确定服务的最佳模型。你怎么知道你的微服务适合哪种模型?根据我们的压力测试,一些服务会发现某些场景与其他场景不同。我们只需要实现自动压测的方法,每个模型都试一下,压一下就知道最优的模型是什么了。业务增长自动调度和自动扩容解决业务增长自动扩容,因为你的业务量在增加,你的用户请求,包括各种请求量,根据指标在上升。你知道业务量是怎么增长的,你知道微服务的增长曲线,你知道之前压测每个实例的性能,你就能准确知道我现在的容量比在哪条线上。我们一般让它运行在50%和60%的范围内,66%是灾难恢复的储备。我们先部署三个IDC。如果要挂两个,另一个不会有任何异常。,可能是这两个极限。我们会留出一些时间给我们买机器,一些服务的流量会控制在50%。异常情况自动扩展还有一些异常情况:业务量突然增加,这是因为有些产品在没有通知我们的情况下发起了活动,我们会用一些粗暴的方法来解决,包括CPU,我们会设置一个Point,当你的CPU超过这个点的时候,我们会自动启动扩容。程序的性能下降了,你的业务没怎么变,但是你的程序变了,其实会导致容量不行。我们此时可以监控这种情况,具体取决于我们压力测试的频率。如果每天都压测,可以画出压测曲线。程序性能退化的理性评估2015年我们几乎有一些每天都在承受压力的服务,我们每天为它们的性能画一条曲线。可能会发现有一些时间点性能下降的比较远。比如这里的例子是他发布了一个大版本,在feature中加入了一些处理逻辑,所以性能下降比较明显。一两个月后,有同事出来修了。我觉得这种性能监控是之前容量管理和智能交互的副产品。我们大量使用这个副产品,所以我们可以准确地知道整个微服务的性能如何变化?绩效管理的闭环能否阻止它在这个新的下降点出现?我们每天都在按它。能不能快点,上线就直接停止?比如开发同学对一台电脑灰度,我们马上在线对灰度后的机器进行压力测试,看其性能如何。如果我们发现它的性能下降,我们将停止它。几种业务形态这是我们目前网络的一些业务形态。上图可能还有更多,晚上9点迎来高峰。至晚上10:00中间的图片一般是工作相关的,比如微信,上班时间用的比较多。底部还有一个。一个典型的例子就是微信活动。微信活动好像是晚上22:00来的。还有一些比较老的业务,比如漂流瓶,很多脑迷都在用,会0点守望。我们想用这些业务曲线做什么?山峰那么高,我们所有的设备都是为了应对最高峰而设计的。能不能对这峰做一些特殊处理,免得给他们带来危险的装备。半夜过后,业务量这么低,设备很浪费。我们希望有一个削峰填谷的功能。原理还是很简单的,就是把你的一些服务的峰值和其他服务的峰值错开。有的服务高峰过去了,它的资源就让出来了,谁用都无所谓了。在线业务调峰在线业务调峰:定时服务,在高峰期后释放资源。错峰服务,获取资源。离线计算填谷离线计算填谷:离线任务运行期01:00~08:00无限任务08:00~20:00任务入队控制离线任务cpu.shares+memory的资源使用.limit_in_bytes+blkiooffline任务离线计算的优先级最低,凌晨的时候真的很空闲。这个时候更适合离线计算来调度。在微信群之前,线下计算的东西比较少,主要是语音输入等人工智能的东西。最近可能会有一些新的功能,大家可以看看搜索一下。他们大量使用离线计算调度。在资源占用上,这些地方的配置基本都被Cgroup控制的比较严格。然后调整离线任务的优先级,用离线任务来填补我们低峰的低谷。自动调度总结这是我们在自动调度领域实现的一个目标:全面控制所有在线服务,充分利用资源。离线任务不需要单独申请计算资源,直接使用我们在线的部分CPU内存,单独存储即可。微信基础平台运维负责人吴磊,2010年加入QQ邮箱运维团队,从第一版开始支持了从0到数亿同时在线的野蛮增长微信。目前负责消息、朋友圈等基础服务的运维工作,专注于自动化运维系统的建设。
