你好,欢迎来到第 07 讲。上一讲我介绍了云架构高可用原理,这一讲我将给你详细介绍云架构主要系统里,如何通过主备切换实现故障转移和恢复。
回想一下,当系统出现故障时,你的第一反应是什么?是不是第一时间就去排查故障原因?
但问题是,排查故障原因可能花很长时间。要知道,在一个复杂的软件系统中,有很多基础设施和子系统,它们都有可能出现故障。等你找到原因再解决问题,可能系统的 SLA 就从 99.99% 降到 99.9% 了。
你可能要问了:为什么在排查故障原因的时候 SLA 会下降?
还记得可用性指标计算公式吗?SLA = MTBF/(MTBF+MTTR) ,在一定时间内,也就是总时间 MTBF + MTTR 不变,当故障时间 MTTR 延长,相应地,可用时间 MTBF 就会缩短,SLA 也就降低了。
所以,如果不能及时排查故障原因,也是会影响系统可用性的。
那么,有没有更快的方法保证系统可用性不变呢?有!那就是故障转移。具体来说就是:当发现故障节点的时候,不是尝试修复它,而是立即把它隔离,同时将流量转移到正常节点上。这样通过故障转移,不仅减少了 MTTR 提升了 SLA,还为修复故障节点赢得了足够的时间。
那么,这个故障转移是怎么做到的呢?这就需要主备切换了。
所谓主备切换,按主从关系划分的话,有狭义和广义之分。
狭义上的主备切换,有明确的主从角色划分,主节点承担了主要的集群管理工作,当主节点故障,从节点变成主节点,接管主节点的工作。这种主备切换比较适合有状态的系统,也就是有数据存储功能的系统,比如存储系统等。
广义上的主备切换,它没有明确的主从角色划分,任何一个节点发生故障,它的工作就会分配到其他任何一个正常节点上,不需要指定主节点。这种主备切换比较适合无状态的系统,也就是没有数据存储功能的系统,比如 http 服务。
主备切换的目的主要是为了始终保障系统中有可用节点,一旦发现某个节点出现故障,它就会自动将故障节点的流量快速转移到其他可用节点上,待故障节点恢复正常后,又自动将其加回到系统中。
那主备切换的大致过程是怎样的呢?
主备切换在转移故障的时候,主要分三步:
第一步故障自动侦测(Auto-detect),采用健康检查、心跳等技术手段自动侦测故障节点;
第二步自动转移(FailOver),当侦测到故障节点后,采用摘除流量、脱离集群等方式隔离故障节点,将流量转移到正常节点;
第三步自动恢复(FailBack),当故障节点恢复正常后,自动将其加入集群中,确保集群资源与故障前一致。
从这三个步骤的描述当中,你会发现,整个过程都是自动化进行的。所以,主备切换的优点是非常快,耗时通常是几十毫秒到几秒不等。假如某个系统要求 SLA 为 99.999%,意味着该系统一天内的不可用时间为 864ms,如果主备切换在 500ms 完成,则一天内最多允许出现一次故障。
前面我为你介绍了主备切换的机制,那么,在云架构各种系统中,主备切换是如何做的呢?
我们先来看一个云架构拓扑图。
云架构拓扑图
在云架构拓扑图中,总体可以分为三大类节点:网络节点、计算节点、存储节点。
其中,存储节点属于存储系统,主要用于将数据存储到磁盘,在云架构中属于最底层,是整个云架构的基石。
计算节点主要是云主机,用于运行业务系统,它的数据由存储系统负责存储。
网络节点包括:DNS、CDN、路由器、防火墙、交换机、SLB。它们主要用于将外网流量接入到内网的可用区内,最终转发到各个计算节点。
从这个云架构拓扑图的组成部分我们就知道,存储系统、业务系统、SLB 和 DNS 非常重要,所以,接下来我就为你介绍下,在这几个系统中,主备切换是如何实现的。
我们来看下案例一。
某公司采购了一套 NAS 存储系统,该系统支持主备切换。某一天,存储系统中的某个主节点因磁盘问题而出现故障。在主备切换的机制下,系统检测到了故障,瞬间将主节点从集群中隔离,并将备节点提升为主节点,整个过程上游系统毫无感知,确保了 SLA。
在案例一中,存储系统是有状态的,因为它负责将数据存储到磁盘,确保数据的完整性和一致性。当节点出现故障的时候,它需要立即检测出来并执行主备切换。对于存储系统来说,主备切换依赖节点间的心跳机制来自动侦测节点状态。像 NAS 存储、MySQL、Redis、 etcd 等都是这么做的。
通常,NAS 存储系统中会用到一种叫 DRBD (Distributed Replicated Block Device,分布式复制块设备) 的技术。该技术能将存储节点的磁盘作为块设备,通过互相映射的方式实现互相备份。如下图所示:
当然,DRBD 技术只是实现了各个节点间的磁盘互备,各个节点的心跳和主备切换是通过Heartbeat 来实现的。
Heartbeat 是什么呢?Heartbeat 是 Linux-HA 工程的一个组成部分,用于实现高可用集群。简单来说,Heartbeat 部署在集群内节点上,用于给节点提供心跳机制,各节点通过侦测其他节点心跳来判断其他节点是否有故障。如果侦测到故障节点,则由正常节点接管故障节点的流量,然后继续侦测故障节点的心跳。
另外,Heartbeat 还能将主备节点的网卡绑定到一个共同的 VIP (Virtual IP,虚拟 IP) ,以便将主备节点作为一个整体对外提供服务。
除了 Heartbeat 外,使用 Keepalived 也能为集群节点提供心跳和主备切换功能。与 Heartbeat 用于存储系统的主备切换不同, Keepalived 常用于负载均衡器的主备切换。
说完存储系统的主备切换,接下来我们聊聊存储系统上游的业务系统,它是如何做主备切换的?
我们来看下案例二:
某电商平台商品中心服务使用了微服务框架。当服务正常时,微服务框架定期上报心跳到服务注册中心。
某天,其中某个节点出现故障,微服务框架停止了上报心跳,服务注册中心检测到了该节点异常,并将其从注册中心移除。商品中心的调用方通过微服务框架和服务注册中心感知到了该节点的异常,后续新的请求都没有转发到该节点,确保了请求正常返回。
在案例二中,商品中心是无状态的业务服务。其实,为了方便水平扩展,大多数业务系统都是无状态的。对于无状态的系统来说,可以通过外部系统或者调用方探测节点存活,然后简单粗暴地从节点列表中移除故障节点,并选择正常节点重试请求。
比如,利用 Nginx、微服务框架等组件的侦测能力,就可以识别故障节点,并将其摘除,然后选择正常的节点重试出错的请求。
以 Nginx 为例,为了给后端服务做健康检查,我们可以在 Nginx 配置文件中添加如下配置:
upstream test_web {
server 192.168.1.21:80;
server 192.168.1.22:80;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
}
- 1
- 2
- 3
- 4
- 5
在这些配置中,server 指令用于配置后端节点,check 指令用于配置节点的健康检查。在 check 指令后面有几个健康检查相关的参数:interval、rise、fall、timeout、type。
它们表示什么意思呢?
interval:用于配置健康检查的间隔,如 3000 毫秒;
rise:用于配置连续几次请求成功后表示该节点恢复了,如 2 次;
fall:用于配置连续几次请求失败后表示该节点出故障了,如 5 次;
timeout:用于配置健康检查请求的超时时间,超时后表示请求失败,如 1000 毫秒;
type:用于配置请求协议的类型,如 HTTP 请求。
前面我们了解了业务系统和存储的主备切换原理,那么最上游的 SLB 和 DNS 是如何做主备切换的呢?
我们来看下案例三:
某电商平台初期用户量较少,系统部署在单一可用区内,只使用了一个 SLB 对外提供服务。有一天,运营人员配置了一场促销活动,但是系统所在的可用区网络故障,导致整个活动受影响无法正常进行,错失了活动的最佳时机。
后来,研发人员和运营人员对系统架构进行了调整,将系统部署在两个可用区,每个可用区有两个 SLB,由 DDNS 负责 SLB 的负载均衡。在后续促销活动中,虽然也发生了类似的故障,但由于有多个 SLB,以及用 DDNS 为 SLB 做域名解析负载均衡和健康检查,DDNS 自动将流量调度到了正常的可用区,保障了促销活动的顺利进行。
在案例三中,SLB 负责可用区内业务节点的健康检查和负载均衡,确保流量始终转发到正常的节点上。DDNS 负责多个 SLB 的健康检查和负载均衡,确保域名解析始终解析到正常的 SLB 的 IP 上。
实际上,DDNS 可能并不一定完全满足需求。DNS 中 IP 更新是有时间限制的,通常默认是 5 分钟。也就是说,假如 SLB 故障,IP 列表更新了,由于本地 DNS 有缓存,可能会导致 5 分钟内请求都失败。而且用户的本地 DNS 可能会被恶意劫持,导致客户端请求失败。
针对这种情况,有的公司会选择实现 HTTPDNS 接口给客户端下发 IP 列表。
通常,一个 HTTPDNS 返回的结构是这样子的:
{
"www.mi.com": {
"ips": [
"103.104.168.101",
"116.211.122.1"
],
"ttl": 57
}
}
客户端拿到域名与 IP 列表的对应关系后,缓存在本地,然后随机选择一个 IP 建立连接。如果连接失败则从剩下的 IP 中再随机取 IP 来建立连接,直到连接成功或者达到最大重试次数。假如发现该数据过期了,则客户端会重新发起 HTTPDNS 请求获取最新的 IP 列表。
以上三个案例属于云架构中不同层级的故障转移问题。存储系统属于整个架构中的底层,对故障转移要求最高,所以它采用了狭义上的主备切换,有明确的主备关系。而业务系统和 SLB 通常是无状态的,更倾向于依靠上游组件来做故障转移,以便提供更好的水平扩展能力。
这一讲,我们介绍了故障转移和恢复的作用,并通过主备切换的三个案例,介绍了云架构中每一层都是如何把故障转移和恢复的。
总的来说,对于无状态的系统,不需要系统本身处理故障转移,更倾向于采用系统外的组件,比如 DDNS、Nginx 来做流量调度即可。对于有状态的系统,对故障转移的要求非常严格,需要集群内部节点利用心跳机制自动侦测故障,并自动执行主备切换和故障恢复。
接下来,你也可以思考一下:如果要设计一套支持 Web 访问的网络存储系统,需要从哪些方面做故障转移呢?
这一讲就介绍到这里,下一讲,我将给你介绍“如何通过熔断和限流解决流量过载问题”,到时候见。
1)写入时,保存多副本,这里涉及到写入成功的策略问题,异步写副本,还是至少等待一个副本写入成功后才算成功2)引入协调器,读取请求由协调器统一分发,感知到某个副本所在节点不可工作后,将这个节点从副本可用节点列表中删除
“如果要设计一套支持 Web 访问的网络存储系统” 同样需要考虑 存储系统、业务系统和网络节点的主备切换吧
是的。这个需求里存储系统是最核心的,要满足数据一致性和可用性