一台计算机在数据中心肯定不够:
垂直扩展和水平扩展的选择问题如何保持高可用性(HighAvailability)一致性问题(Consistency)
从硬件升级到水平扩展
现在在云服务商购买服务器的成本和方便程度都已经很高。不过因只是个业余时间的小项目,一开始这台服务器的配置也不会太高。我以我现在公司所用的GoogleCloud为例。最低的配置差不多是1个CPU核心、3.75G内存以及一块10G的SSD系统盘。这样一台服务器每个月的价格差不多是28美元。
很幸运,你的网站很受大家欢迎,访问量上来了。这台单核心的服务器性能有点不够用。需要升级你的服务器。
于是,面临如下选择:
升级现在这台服务器的硬件,变成2个CPU核心、7.5G内存,垂直扩展(ScaleUp)再租用一台和之前一样的服务器。就有了2台1个CPU核心、3.75G内存的服务器,水平扩展(ScaleOut)
成本上看起来没有什么差异。2核心、7.5G内存的服务器,成本是56.61美元,而2台1核心、3.75G内存的服务器价格,成本是57美元,这之间的价格差异不到1%。
但随着流量不断增长。到最后,只会变成一个选择:既垂直扩展又水平扩展,并最终依靠水平扩展,来支撑Google、Facebook、阿里、腾讯这样体量的互联网服务。
垂直扩展通常无需改造程序,即无研发成本。那为何最终还要水平扩展?无法不停垂直扩展。若访问量逐渐增大,一台96核心的服务器也支撑不了,就没法再垂直扩展。这就不得不采用水平扩展。96个CPU也就是30~50台日常使用的开发机的计算性能。而我们今天在互联网上遇到的问题,是每天数亿的访问量,靠30~50台个人电脑的计算能力想要支撑这样的计算需求,可谓天方夜谭。
然而,一旦开始采用水平扩展,就面临软件层面改造:要进行分布式计算。需引入负载均衡(LoadBalancer)组件分配流量。需拆分应用服务器、数据库服务器进行垂直功能的切分。也需要不同的应用之间通过MQ进行异步任务的执行。
这些软件层改造都是在做分布式计算的一个核心工作:通过消息传递(MessagePassing)而非共享内存(ShadMemory)的方式,让多台计算机协作。
而因最终必然要水平扩展,需在系统设计早期就基于消息传递而非共享内存设计系统。即使这些消息只是在同一台服务器传递。
不少增长迅猛的公司,早期没准备好通过水平扩展支撑访问量,一味通过提升硬件配置ScaleUp,最终影响了公司的存亡。最典型的MySpace。
高可用性和单点故障
尽管1个CPU核心服务器支撑不了访问量,垂直扩展是最简单办法。不过若是我,第一次扩展我会选择水平扩展:自然“强迫”从开发角度,尽早让系统能够支持水平扩展,避免流量快速增长时,垂直扩展解决方案跟不上。不过,其实还有一个更重要的理由,那就是系统的可用性问题。
上面的1核变2核的垂直扩展的方式,扩展完之后,我们还是只有1台服务器。如果这台服务器出现了一点硬件故障,比如,CPU坏了,那我们的整个系统就坏了,就不可用了。
如采用水平扩展,即便有一台服务器CPU坏,还有另外一台仍能提供服务。负载均衡能通过健康检测(HealthCheck)发现坏掉的服务器无响应,自动把所有流量切换到第2台服务器,这叫故障转移(Failover),系统仍可用。
系统的可用性(Avaiability)
系统可正常服务的时间占比。无论因软硬件故障,还是要对系统进行停机升级,都会损失系统可用性。可用性通常是用一个百分比的数字来表示,比如99.99%。我们说,系统每个月的可用性要保障在99.99%,就是意味着一个月里,你的服务宕机的时间不能超过4.32分钟。
有些系统可用性的损失在计划内,如停机升级,计划内停机时间(ScheduledDowntime)。有些在计划外的,如一台服务器的硬盘忽然坏了,即计划外停机时间(UnscheduledDowntime)。
系统不可能做到%可用,特别是计划外停机时间。从简单硬件损坏,到机房停电、光缆被挖断,乃至于各种自然灾害,如地震、洪水、海啸,都有可能使系统不可用。我们要做的是尽可能低成本提高系统可用性。
硬件服务器的可用性
如有个三台服务器组成的小系统,一台部署Nginx作为负载均衡和反向代理,一台跑PHP-FPM作为Web应用服务器,一台作为MySQL数据库服务器。每台服务器的可用性都是99.99%。那么我们整个系统的可用性是多少呢?99.99%×99.99%×99.99%=99.97%。在这个系统当中,这个数字看起来似乎没有那么大区别。从损失了0.01%的可用性,变成了损失0.03%的可用性,不可用的时间变成原来3倍。如有0台服务器,那么整个的可用性,就会变成99.99%^0=90.5%。也就是说,我们的服务一年里有超过一个月是不可用的。这可怎么办呀?
要解决单点故障,第一点要移除单点,最典型的场景,让两台服务器提供相同功能,然后通过负载均衡把流量分发到两台不同服务器。即使一台服务器挂了,还有一台服务器可以正常提供服务。
单点故障其实在数据中心里无处不在。现在的云上两台虚拟机。如这两台虚拟机托管在同一台物理机,那这台物理机本身又成为个单点。就需要把这两台虚拟机分到两台物理机。
还不够。如这两台物理机在同一机架(Rack),那机架上的交换机(Switch)就成一个单点。即使放到不同机架,还有可能出现整个数据中心遭遇意外故障的情况。
部署在Azu上的服务所在的数据中心,因为散热问题触发了整个数据中心所有服务器被关闭的问题。面对这种情况,就需异地多活。所以,在现代的云服务,你在买服务器的时候可以选择服务器的aa(地区)和zone(区域),而要不要把服务器放在不同的地区或者区域里,也是避免单点故障的一个重要因素。
只是能去除单点,其实可用性问题还没解决。用负载均衡把流量均匀地分发到2台服务器上,当一台应用服务器挂掉的时候,我们的确还有一台提供服务。但负载均衡会把一半的流量发到已经挂掉的服务器上,只算一半可用。
想让整个服务完全可用,就需有套故障转移(Failover)机制。想要进行故障转移,就首先要能发现故障。
以PHP-FPM的Web应用为例,负载均衡通常会定时去请求一个Web应用提供的健康检测(HealthCheck)的地址。这个时间间隔可能是5秒钟,如果连续2~3次发现健康检测失败,负载均衡就会自动将这台服务器的流量切换到其他服务器上。于是,我们就自动地产生了一次故障转移。故障转移的自动化在大型系统里是很重要的,因为服务器越多,出现故障基本就是个必然发生的事情。而自动化的故障转移既能够减少运维的人手需求,也能够缩短从故障发现到问题解决的时间周期,提高可用性。
在Web应用上设置了一个Heartbeat接口,每20s检查一次,出现问题的时候可以进行故障转移切换。
通过水平扩展相同功能的服务器来去掉单点故障且通过健康检查机制触发自动故障转移,可用性会变成多少?其实只要有任何一台服务器能够正常运转,就能正常提供服务。可用性就是:
%-(%-99.99%)×(%-99.99%)=99.%
不能提供服务的时间就减少到了原来的万分之一。实际情况可用性没法做到这么理想。光从硬件角度,从服务器到交换机,从网线连接到机房电力,从机房的整体散热到外部的光纤线路等等,出现问题地方太多。这也是为什么要从整个系统层面设计系统的高可用性。
不能提供服务的时间就减少到了原来的万分之一。实际情况可用性没法做到这么理想。光从硬件角度,从服务器到交换机,从网线连接到机房电力,从机房的整体散热到外部的光纤线路等等,出现问题地方太多。这也是为什么要从整个系统层面设计系统的高可用性。