扩容、监控和容灾
一、系统容量设计
设计目标:
1)不改代码的前提下,架构支持20w台服务器的监控;
2)不改代码的前提下,架构支持4w个视图的监控;
3)不改代码的前提下,每台服务器最多支持上报1w个属性;
4)不改代码的前提下,每个视图最多支持绑定1w个属性;
系统当前容量:
1)目前接入监控的服务器数达4w多台,平均每台服务器上报属性数200多个,保存2天的分钟粒度数据。前面计算过,共占用内存约96G,预留一定的buffer,按照150G内存计算,目前主备各使用3台B6服务器来存储内存数据。
2)目前计入监控的视图数达4k多个,平均每个视图绑定的属性数按照100个评估,保存2天的分钟粒度数据。前面计算过,需要占用内存约4.8G,目前主备各使用1台B5来存储这些内存数据。
3)每天的单机文件大小约为48G左右。90天内的文件存放到一台TS3的历史机上,共需要4.5T左右的空间;
4)每天的视图文件大小约为1.4G左右。90天的文件数据,共需要126G左右的硬盘空间。
5)单机对应的两块索引共占用内存16.15G(id->attr:6.4G,attr->id:9.75G);
6)视图对应的两块索引共占用内存将近1G。视图和单机索引共同占用内存小于18G,考虑到buffer,采用1台B6的机器存储;
系统未来容量:
1)服务器接入数达到10w台,平均每台服务器上报属性数200多个,保存2天的分钟粒度数据。共需要内存240G,在预留buffer的情况下可以使用5台B6服务器就能够支撑住;(需要从3台B6扩为5台B6)
2)视图数达到4w个,平均每个视图绑定100个属性,保存2天的分钟数据。共需要内存48G,采用1台B6来存储就能够支撑住;(需要从1台B5扩为1台B6)
3)每天的单机文件大小约为120G左右。保存40天的历史数据,共需要占用磁盘空间5T;(不需扩容)
4)每天的视图文件大小约为14G左右。保存40天的历史数据,共需要占用磁盘空间560G;(不需扩容,1台TS3)
5)单机对应的两块索引占用空间约36G。(id->attr:16G,attr->id:20G)
6)视图对应的两块索引占用空间约10G。视图和单机索引共占用内存大约46G,采用1台B6的服务器足够应对;(不需扩容,1台B6)
规模继续扩大的瓶颈分析:
1)视图内存数据采用单机存储,如果视图个数超过5w个,那么占用内存就超过60G,当一台B6无法支撑的时候,可以有两种方案:
a)不修改架构。采用M1的机器存储视图数据,其内存为120G,可以支持大约10w个视图;
b)修改架构。仿照单机存储模块,改为分布式存储;
2)索引模块采用单机存储,如果单机数达到20w,内存占用量就会超过60G,此时也需要考虑使用M1机型或者改为分布式架构;
二、扩容
架构方面,按照前面的容量评估(接入服务器达到10w台,视图数达到4w个),系统中的除了单机内存数据存储模块被设计为多台分布式存储以外,其他模块均采用单服务器即可支撑。下面主要讨论一下单机内存数据存储模块扩容的方案。
单机是按照一致性hash的算法将server_id作为key进行打散存储到后端不同的处理机上的,其中一致性hash(虚拟节点)的原理这里就不再详述,可以在km或google上找到很多相关资料。这种算法的好处在于,扩容之后的效果是:它会均匀的把每台处理机上的一部分数据迁移到新增加的处理机上,剩下的数据存储的位置保持不变。
按照一致性hash扩容之后的数据分布特性,serverid在扩容前后只有两种情况:
1)server_id1在扩容前后对应的proc没有发生变化。这种情况扩容的时候不需要特殊对待。
2)server_id2在扩容前后对应的proc发生变化。例如,扩容前server_id2存储在proc1上,扩容后存储到proc2上。
下面给出扩容的步骤:
1)将待扩容的proc设置为“expanding”的状态;
2)判断server_id在扩容前后是否需要迁移到不同的proc中;
3)由proxy负责双写扩容前后的两个proc(如果两个不同的话),此时数据还是从扩容前的那个proc上读;
4)待扩容的proc中数据已经完整(2天时间)之后,将其状态修改为“working”,此时数据就由新的proc提供;
5)扩容前的proc会由算法自动淘汰掉无效节点(2天时间),从而实现内存清理;
解释几点:
1)扩容步骤中没有任何迁移数据的操作。这样做的目的是为了简单,因为一旦涉及到数据迁移的话,那么随之而来,就需要判断数据一致性。迁移数据步骤也比较繁琐,常规做法是先把数据某个时刻的切片复制过去,然后再将复制过程中缺少的数据,采用流水日志的方式进行恢复,过程相当复杂。
2)扩容开始到结束至少需要2天时间,完全扩容完毕需要4天时间。因为按照设计内存中存放2天的数据,所以前2天是为了保证新扩容机器上数据的完整性,之后扩容的机器就可以转为可用状态;后2天主要是为了释放内存,这个阶段可以自动完成,不需要特别关注。
三、监控
此问题有点类似“鸡生蛋,蛋生鸡”的问题,监控系统本身就是提供监控服务的,那么其自身的监控要怎么做呢?是否可以自己对自己进行监控呢?
估计凭直觉就能回答上来,自己监控自己肯定不可靠。那有哪些手段可以保证系统提供的服务是可靠的呢?主要依靠下面3种手段:
1)系统自身可以监控正常运行时的状态和并提供预警。例如共享内存使用率、接口调用量、错误量等一些指标。但是如果系统出现严重问题,导致服务不可用的时候,那就需要依赖其他两种手段了。
2独立部署一套最小化的集群,作为主监控集群的监控者。
3)极端异常情况直接走消息(短信/微信)发送通道。例如当告警的检查和发送模块出现故障,此时已经丧失了告警知会的能力,因此在告警模块的机器上部署一些脚本定时巡检,一旦发现问题,直接通过消息通道知会到系统负责人。
四、容灾
作为业务系统的眼睛,monitor系统自身的容灾一定要做好。在讨论容灾之前,我们先介绍一下CAP理论(参考文章: http://km.oa.com/group/608/articles/show/149131 ),其中C代表数据一致性,A代表可用性,P代表分区容错性(可分布性),CAP理论告诉我们一个分布式系统不可能同时满足这三个特性。那就要求我们需要做一些取舍,其中P代表的可分布特性一般是防止数据丢失而必须满足的,那么我们需要一致性和可用性方面进行权衡。对于监控系统而言,它对数据一致性的要求比线上业务要低,但是可用性如果较差,例如想查看监控曲线无法展示出来,或者该发出来的告警没有发出,那么后果就会比较严重。所以针对monitor系统,我们倾向于把可用性(容灾)做的好一些,而数据一致性稍微弱一些。
容灾的主要思路是去除系统中的单点,去中心化,每个节点都做到至少有主备同时提供服务,并提供跨IDC容灾的能力。
\1) 对于逻辑层的容灾,我们这里采用热备/热切换的方式(即平时主备同时提供服务)。之所以没有采用冷备/冷切换是因为冷切换的备机平时不提供服务,所以其可信度较低;
\2) 对于数据层的容灾,由于我们对数据一致性的要求不算太高,所以这里采用不分主备(完全分布式)的方式来实现快速主备切换。所谓不分主备是指,同时双写两份数据到不同服务器,两者的数据认为是基本一致的。如果某个服务器写失败或者网络出现问题,可以手动禁用掉这些机器。另外在数据一致性方面,我们提供异步核对机制,当检查出主备服务器的数据差别过大,就会通知系统负责人进行人工干预。这里还需要提到另外一种思路,就是单路写一个主节点,由主节点同步到各个备节点。这种思路的数据一致性较好,但是方案相对复杂一些:首先要提供切片同步和增量同步的能力;其次要准确判断不可写的状态,实现自动写切换;写切换完成之后主节点就改变了,等主节点恢复之后,还需要将主节点再快速恢复。可以看出,整个过程较前一种方式会复杂很多,对于我们这种一致性要求不高的情况,简单处理即可。
\3) 各个模块和各个接口尽可能支持可重入和符合幂等操作的设计原则。索引存储模块中,将一个写请求重放多次的效果基本不变,所以可以近似认为是符合幂等性的,这种特性可以很容易的实现多路数据由同一个进程接收的时候,能够保证数据正确且实现多路数据容灾。
\4) 为了提供异地容灾能力,一共搭建了3套系统,主系统搭建在深圳,其余两套分别放在上海和天津(冷备)。最常用的是深圳这套系统,其每个节点都有至少主备两台服务器,主服务器集群放在深圳电信福永,备服务器集群放在深圳联通同乐,所以深圳这套系统自身也是具备跨IDC的容灾能力的。最后,各套系统都提供外网访问紧急通道,一旦天津或者上海内网故障成为孤岛,那么极端情况下可以通过外网访问当地的监控系统,从而准确了解当地服务器的监控信息。