高可用的目标

  • 提升平均无故障间隔(MTTF)
  • 缩短平均修复时间(MTTR)

定义上, 高可用其实分为以下几种.

  1. 冗余
    1. 以及可以基于这个剩下的节点提供除冗余外的完整的服务.
  2. 降级
    1. 关闭某些功能的情况下继续提供服务.
  3. 熔断
    1. 熔断更多指检测到连续故障, 让后续的也先拦截掉, 避免无法提供服务但依旧冲击服务压力. 检测到恢复之后再自愈那种. ceph-osd假设重构降级了, 这个情况下业务io继续打下来, 这个时候应该是阻塞, 而并没有通过熔断中断io. 但是如果是业务io, 发现io异常了, 倒是上层可以自行熔断. 只是这种其实更要求下层的可靠性.
    2. 不然上游服务器应该会拥塞住. 假设上游服务器还承担其他功能, 此时就会被僵死.
    3. 扛不住这点, 根本上应该通过限流控制, 当组件存在异常后, 此时带来的处理能力不足, 就需要熔断来尽量避免堆积. 熔断后短时间无法恢复则应该考虑整体降级功能了.
  4. 限流
    1. 针对自身能提供的能力通过限流拦住.

高并发整体可用性:一文详解降级、限流和熔断_Hollis的技术博客_51CTO博客

面试官:说说降级、熔断、限流 - 掘金

服务降级与服务熔断区别 - 知乎

完整高可用方案的分类

完整的高可用方案目前理随着当前发展主要分为以下几类.

  • 冷备
    • 手动恢复
  • 双机热备
    • active/standy
    • 即应用级灾备
  • 同城双活
  • 异地双活
  • 异地多活

本文不讨论完整的灾备/多活方案, 主要只涉及本地的高可用, 即双机热备那种, 应用级灾备.

应用级高可用方案细分

客户端

客户端直接填写多个服务端ip, 故障后自行切换. 比如ceph的客户端节点访问mon, 主要依赖客户端节点的配置中mon的地址. 根据mon地址去连接. 当一个mon故障时, 会根据其他mon地址进行连接.

服务之间

指的就是基于选举自身实现高可用了. 比如ceph-mon之间选举leader,

中间件

如早期不支持集群的redis上层增加的Codis等等

或者就是存储端上层, 增加keepalived/nginx等进行处理, 比如云计算当节点存在异常时, 会疏散切换到另一个节点, 比如keepalive提供的vip切换节点的能力.

或是提供dns层级的

脑裂问题

脑裂问题按目前理解, 算是高可用问题中存在风险较大的一个了

常见解决方案[^HA高可用集群中"脑裂"问题解决 - 运维总结 - 散尽浮华 - 博客园]

  1. 添加冗余的心跳线路
    1. 如网口绑定, 避免单根线故障, 引起节点直接异常.
      1. 做的更极端一些, 两根线连不同的交换机? 避免交换机的单点故障? 但一般小规模集群不需要. 大规模集群有节点/机柜层级的冗余了.
  2. 设置第三方仲裁机制
    1. 显式的仲裁, 即明确的第三方节点参数选举/判定等
    2. 借助环境中节点以外的资源进行仲裁
      1. 如主备的keepalived配置中增加周期ping网关的check操作, ping不通则代表本节点有问题, 需要自行停止服务
      2. 如双控设备, 借助同时连接的硬盘, 在硬盘上设置标记, 实现磁盘锁.
    3. 存在奇数节点的选举算法
  3. fense(屏蔽)机制 --硬件设备
    1. 利用ipmi的fence设备, 将故障节点踢出集群? 为啥不是局部组件踢出?
    2. RHCS的Fence设备分为内部和外部两种Fence。内部fence有IBMRSAII卡,HP的ILO卡,以及IPMI设备等;外部FENCE设备有UPS,SANswitch ,Networkswitch等。
    3. 对于小规模集群有必要吗? 如果是多个组件, 比如内网异常可能只影响到某些用内网的组件呀. 为啥要把整个节点的服务都踢出呢? 大规模集群的话合理, 但是小规模呢?
      1. 屏蔽可以定义为一种使 HA 群集具有已知状态的方法。
      2. 根据上面这句话来理解, 至少能够做到对节点的处理能力状态进行一定程序的定义.
        1. 资源级别和节点级别的屏蔽
      3. 在 Pacemaker 群集中,节点级别屏蔽的实施方式是 STONITH(关闭其他节点)
    4. 屏障和 STONITH | 管理指南 | SUSE Linux Enterprise High Availability Extension 15 SP1

[^HA高可用集群中"脑裂"问题解决 - 运维总结 - 散尽浮华 - 博客园](https://www.cnblogs.com/kevingrace/p/7205846.html)

场景

目前主要是以下场景

2节点场景下, 需要提供数据存储/计算资源/等的高可用能力(有些情况还是有这种需求) 3节点以上场景, 则基本都是正常的疏散逻辑能够处理的.

这里我们讨论的, 目前主要是存储的冗余能力.

回到关心的存储的冗余的场景里, 指的是2个节点下, 其中一个节点彻底离线, 或者是出现了2个节点之间的网络中断, 而期望对外的业务服务并不中断的场景.

P.S. 其实严格意义上来说, 存储冗余之外,上层业务也需要做上面的另外三种方案. 只是如虚拟机等方案比如存储外再通过本地存储备份拉起可用虚拟机做降级等, 可能在一般的冗余要求下 投入成本过高.

以ceph 2节点举例.

以具体的存储比如ceph举例, ceph如果是基于rbd/cephfs/rgw协议直接对接使用时, 是需要先基于rados协议与mon节点建立连接, 获取到当前最新的osdmap后, 根据osdmap计算出当前请求的目标osd, 然后客户端与osd直接建立连接的.

当2节点场景下, 按官方默认推荐方案仅实施1个mon/mgr时, 当这个mon所在节点离线的情况下, 就会存在无法查询到最新的osdmap, 也就无法与osd建立连接的情况.

发散一下思路

1. 是否有可能让ceph在无法与mon通信的情况下, 依旧支持根据现有的osdmap去建立通信的方案?

可能有. 但是去中心化, 参考区块链的定义, 其实也意味着数据要么在任意协议中都有保存, 要么本地曾经有过, 保存下来了.

那可能的实现思路就是基于本地历史的osdmap去计算目标源. 然后连上osd之后, osd再承接一部分osdmap分发的能力, 转派真实的osd地址. 这样技术上有一定的实现难度, 但看起来似乎也不是不行.

2. 是否可以在2节点情况下, 让存活足够数量的mon

比如2节点运行3个mon?

这里的方案, 其实就首先每个节点运行一个节点服务, 另外1个节点会运行一个额外的服务. 当出现节点异常时, 通过感知到对端离线, 本地去启动那个额外的服务, 确保本节点数量是满足要求的.

这里引出一个需求点: 这个额外的服务的数据库哪里来? 无论是ceph-mon还是zookeeper其实都具有一个特点, 就是数据强一致性, 即数据库是完全可重用的. 即当判断时, 临时复制出一个也不是不可以. 或是通过一个共享存储空间.

P.S. 这里其实共享存储空间是否可能并不是决定性的? 因为本身节点之间通过强一致算法去写数据, 这里数据库之间不一致的检查是为了处理跨节点因网络或各式因素引起的强一致性不可靠. 而对于单节点拉起2个程序, 这点上, 起到的冗余效果可能只是进程级别的? 基本可能没有太大作用? 只是为了实现2节点下高可用才进行的.

按理想的场景 确实对端彻底离线了, 那切换确实问题不大. 但是这里的核心问题点, 当出现网络隔离或脑裂时, 如果两端真的同时拉起新服务, 同时起集群. 那就会导致数据不一致了.

换言之, 这个方案也还是需要一个第三方在网络隔离时, 能够识别出哪个节点才是真实可服务的.

比如就拉起2个mon?

通过设置mon之间的权重优先级,是否有可能?

其实是有可能的, 关键问题其实是在于脑裂, 网络隔离时, 两边都无法感知到对端时, 如何不两边同时拉起 或停止服务. 即需要有一个第三方仲裁者来完成这件事.

思路

如果除了ceph和keepalived的ip之外, 这两个节点还使用了如keepalived这类vip层级. 即2节点其实是共用一个vip的. 这时vip的共用引入了一个第三方, 网关(三层交换机/路由器).

此时利用路由器网关的能力, 通过vip ping一下网关, 确保一个子网里, 只有一个vip存活.

这种思路, 其实就是依赖了ip冲突的检测能力, 从而完成了一个仲裁.

TODO: 这里存在一个疑问项, 网关按RFC协议如何响应呢?

双控服务器 则是特例

如果是双控服务器, 则双控服务器接入了同一套expander, 可以从expander层面作为一个第三方, 物理磁盘是同一套, 不同控制器可以在磁盘上存储标记位, 检测磁盘锁. 这样确保一个控制器离线的情况下, 另一个节点能够从磁盘锁上判断对端是否存活, 是否在本节点拉起额外的服务.

现实化场景

依旧如果是普通节点的情况下, 划分管理网络,前端网络, 后端网络, 2节点场景

业务程序分为运行在本节点上, 用户基于BS/CS使用本节点上的业务, 和外部节点通过前端网络通信 两种

脑裂为主要场景, 所以这里主要讨论网络异常引起的那种. 不讨论节点正常离线那种.

业务运行在本节点上(如计算/存储一体, 此时存储的前后端网络其实都是节点的后端网络)

前端网络断开

此时计算需要进行冗余处理. 只需要计算资源能够确认哪个节点是继续提供服务节点即可. 即通过上面的高可用常见解决方案来完成

此时存储无异常.

后端网络断开

一台设备内网线全异常了.

这个情况下, 这俩与外网依旧能通, 但是这俩节点之间的后端网络不通了.

此时计算无异常. 此时, 因为前端网络依旧是通的, 所以vip本身还是可以保障的, 对外的vip一般应该是不变的. 即用户访问计算资源, 还是可以正常连接上的.

此时存储需要冗余处理. 但是ceph-mon的程序无法工作, 导致计算资源无法再操作存储了.

如果需要解决, 即需要存储在这个情况下不脑裂继续提供服务. 其实是可以通过上面说的拉起3个/以及后端网络也存在vip来处理. 因为当断一个外部网络, 存储自身通过网关检测到哪个节点的后端网络先能使用这个ip, 则由该节点拉起足够数量的mon继续提供服务.

全断开

此时计算/存储均出问题.

此时计算, 可能会出现两端都拉起了计算程序. 因为彻底无第三方, 则可能计算节点自身同时启动.

存储如果也做了上面的拉起足够数量的mon继续提供服务, 则也会出现脑裂问题. 两端同时启动, 同时对本机上的计算资源提供服务.

此时如果业务会有周期操作, 如快照等. 则会出现节点内产生的数据不一致的写操作.

业务是外部节点 通过存储的前端网络通信

前端网络断开

业务请求无法访问某一台了, 存储如果没做上面拉起足够数量mon的方案, 则在mon节点离线时无法响应.

如果做了, 则正常冗余切换到另一节点继续提供服务即可.

后端网络断开

此时即存储内网, 会正常通过心跳降级掉无法连上的节点的osd, 继续提供服务.

全断开

如果采用了拉起足够数量mon方案, 此时两端会同时尝试提供降级服务.

但是由于业务节点不在存储端, 倒是不会产生数据不一致问题.

总结

根据上面可以看到, 在有些场景下, 提高了可用性, 会引起数据不一致风险. C和A在此时互相冲突.

这里就不再是技术问题了, 而是主要看市场如何了. 是支持高可用带来的利润足以抵消几起数据不一致风险, 还是为了数据一致性, 牺牲部分场景的高可用支持能力了.

Reference

  1. 超大规模数据库集群保稳系列之一:高可用系统 - 美团技术团队