Elasticsearch 之所以如此流行,其中一个原因是它可以从只有几个节点的小集群扩展到拥有数百个节点的大集群。它的核心是集群协调子系统。Elasticsearch 7 提供了一个新的集群协调子系统,与早期版本相比,它提供了很多优点。本文将介绍 Elasticsearch 7 对这个子系统的改进,描述了如何使用新的子系统、新的变更对 Elasticsearch 6 升级有哪些影响,以及这些改进如何防止用户无意中将数据置于危险之中,最后将介绍新子系统的工作原理。
什么是集群协调?
Elasticsearch 集群可以执行需要多个节点协同工作的任务。例如,搜索必须被路由到所有正确的分片,以确保搜索结果是准确的。在索引或删除某些文档时,必须更新每个副本。每个客户端请求都必须从接收它的节点转发到能够处理它的节点。每个节点都了解集群的概况,这样它们就可以执行搜索、索引和其他协调活动。这个概况就是指集群状态。集群状态决定了索引的映射和设置、分配给每个节点的分片以及同步的分片副本。在集群中保持这些信息的一致性是非常重要的。最近推出的很多特性(包括基于序号的复制和跨集群复制)之所以能够正确工作,主要是因为它们依赖了集群状态的一致性。
协调子系统会选择一个特定的节点作为集群的主节点,这个选定的主节点需要确保集群中的所有节点都能够收到有关集群状态的更新。这比看起来的要困难,因为像 Elasticsearch 这样的分布式系统必须为各种奇怪的情况做好准备。节点有时候运行缓慢,有时候垃圾回收会导致停顿,或者突然断电。网络会出现分区、包丢失、高延迟,或者乱序的消息。有时候一次性会发生多个这样的问题,而且可能间歇性地出现。不管怎样,集群协调子系统必须能够确保每个节点都具有集群状态的一致视图。
关键是 Elasticsearch 必须具备针对单个节点故障的弹性。它使用了节点仲裁机制,只有在多数节点接受集群状态更新之后,集群状态的更新才被认为是成功的,从而实现了这种弹性。仲裁是集群中符合主节点条件的节点的一个子集。仲裁的优势在于,一些节点可能会失败,但不会影响集群的可用性。在选择仲裁节点时要十分小心,这样集群就不会选举出两个独立的主节点,避免这两个主节点做出不一致的决策,最终导致数据丢失。
通常,我们建议集群可以有三个符合主节点条件的节点,如果其中一个节点失败,其他两个节点仍然可以形成仲裁。如果一个集群少于三个符合主节点条件的节点,那它就不能安全地容忍其中任何一个节点发生故障。相反,如果一个集群有超过三个符合主节点条件的节点,那么选举和集群状态更新可能需要更长的时间。
进化还是革命?
Elasticsearch 6.x 及之前的版本使用了一个叫作 Zen Discovery 的集群协调子系统。这个子系统经过多年的发展,成功地为大大小小的集群提供支持。然而,我们想做出一些改进,这需要对它的工作方式作出一些根本性的修改。
Zen Discovery 允许用户通过设置 discovery.zen.minimum_master_nodes 来决定多少个符合主节点条件的节点可以形成仲裁。在每个节点上一定要正确地配置这个参数,如果集群进行动态扩展,也需要正确地更新它,这一点非常重要。系统不可能检测到用户是否错误配置了这个参数,而且在实践当中,在添加或删除节点之后很容易忘记调整这个参数。Zen Discovery 试图通过在每次主节点选举过程中等待几秒来防止出现这种错误配置。这意味着,如果所选的主节点失败,在选择替代节点之前,集群至少在几秒钟内是不可用的。如果集群无法选举出一个主节点,有时候很难知道是为什么。
我们重新设计并重建了 Elasticsearch 7.0 的集群协调子系统:
移除 minimum_master_nodes 参数,让 Elasticsearch 自己选择可以形成仲裁的节点。
典型的主节点选举现在只需要很短的时间就可以完成。
集群的伸缩变得更安全、更容易,并且可能造成丢失数据的系统配置选项更少了。
节点更清楚地记录它们的状态,有助于诊断为什么它们不能加入集群或为什么无法选举出主节点。
随着节点的添加或删除,Elasticsearch 会自动更新集群的选举配置,以保持最佳的容错级别。选举配置是一组符合主节点条件的节点,在做出决策时,它们具有投票权。通常,选举配置包含集群中所有符合主节点条件的节点。仲裁就是指选举配置中的多数节点:所有集群状态更新都需要选举配置中超过半数节点同意。由于选举配置和仲裁由系统负责管理,所以可以避免任何可能导致数据丢失的错误配置。
从 Elasticsearch 7.0 开始,如果一个节点无法发现主节点,而且也无法赢得选举,Elasticsearch 将会定期记录警告消息,消息中将提供描述其当前状态的详细信息,帮助诊断一些常见的问题。
此外,Zen Discovery 有一个非常罕见的故障模式,在 Elasticsearch 弹性状态页上是这么描述的:“重复的网络分区可能导致集群状态更新丢失”。不过这个问题将不会再发生,已经被标记为已解析。
如何使用?
如果你完全使用默认配置启动新安装的 Elasticsearch 节点,它们会自动查找在同一主机上运行的其他节点,并在几秒钟内形成集群。如果在同一台主机上启动更多节点,那么默认情况下它们也会发现并加入这个集群。所以,在 Elasticsearch 7.0 中,启用多节点开发集群与早期版本一样容易。
这种全自动集群形成机制在单台主机上可以很好地运作,但在生产环境或其他分布式环境中还不够健壮。现在还存在一些风险:节点可能无法及时发现彼此,可能会形成两个或多个独立的集群。从 7.0 开始,如果你想要启动一个全新的集群,并且集群在多台主机上都有节点,那么你必须指定该集群在第一次选举中应该使用的一组符合主节点条件的节点作为选举配置。这就是所谓的集群引导,只在首次形成集群时才需要。已经加入集群的节点将选举配置存储在它们的数据文件夹中,并在重新启动后重用这些配置,而正在加入现有集群的新节点可以从集群的当前主节点接收这些信息。
你可以通过为 cluster.initial_master_nodes 参数设置一系列符合主节点条件的节点的主机名或 IP 地址来引导启动集群。你可以在命令行或 elasticsearch.yml 中提供这些信息。你还需要配置发现子系统,这样节点就知道如何找到彼此。
如果未设置 initial_master_nodes,那么在启动新节点时会尝试发现已有的集群。如果节点找不到可以加入的集群,将定期记录警告消息:
在向集群添加新的符合主节点条件的节点时不再需要任何特殊的仪式,只需配置新节点,让它们可以发现已有集群,并启动它们。当有新节点加入时,集群将会自动地调整选举配置。只要你没有同时停止一半或更多符合主节点条件的节点,即使移除节点也是安全的。如果你需要停止一半或更多符合主节点条件的节点,或者你有更复杂的伸缩和编排需求,可以使用 API 直接调整选举配置。
如何升级?
你可以通过滚动升级或完全重新启动集群将 Elasticsearch 集群从版本 6 升级到版本 7。我们建议进行滚动升级,因为这样可以保持集群的可用性。在滚动升级到版本 7 之前,必须将版本 6 升级到版本 6.7。你也可以通过完全重新启动从 6.x 版本升级到版本 7,但这需要关闭整个集群,然后重新启动。
在执行滚动升级时,集群会根据节点数量和 minimum_master_nodes 设置自动进行引导。这意味着在开始升级之前,需要确保设置是正确的。这里不需要设置 initial_master_nodes,因为在执行滚动升级时,集群引导会自动发生。在主节点选举过程中,版本 7 的主节点更愿意为版本 6.7 的节点投票,因此,在升级期间,版本 6.7 的节点将更有成为会成为主节点,直到所有符合主节点条件的节点升级完毕。
如果你选择了另一种升级方式,则必须按照上面描述的方式引导升级后的集群:在启动新升级的集群之前,必须先将 initial_master_nodes 设置为符合主节点条件的节点的主机名或 IP 地址。
在版本 6 和更早的版本中,还有一些其他以 discovery.zen.*开头的选项,允许你配置 Zen Discovery 的行为。其中一些设置不再有效,已被删除。其他的已经改名。如果一个参数已经被改名,那么它的旧名称在版本 7 中就被弃用,你需要调整配置来使用新名称。
新的集群协调子系统包括一个新的故障检测机制。这意味着 discovery.zen.fd.* 开头的 Zen Discovery 错误检测设置不再有效。大多数用户应该在版本 7 或更高版本中使用默认的故障检测配置,如果需要进行任何更改,可以使用cluster.fault_detection.
安全第一
7.0 之前的 Elasticsearch 有时候允许你执行一系列步骤,这些步骤有可能会让集群的状态出现不一致。而 7.0 及更高版本中,Elasticsearch 会让你知道自己可能正在做一些不安全的事情,并且会向你发起确认,然后再决定要不要继续。
例如,如果有一半或更多符合主节点的节点永久丢失,Elasticsearch 7.0 集群将无法自动恢复。集群中通常有三个符合主节点条件的节点,让 Elasticsearch 可以在不停机的情况下容忍其中一个节点丢失。如果有两个节点永久丢失,那么剩下的节点就无法继续。
7.0 之前的 Elasticsearch 会悄悄地允许集群从这种情况中恢复。用户可以通过启动新的、空的、符合主节点条件的节点来替换丢失节点,从而使集群重新上线。从丢失一半或更多符合主节点条件的节点中进行自动恢复是不安全的,因为剩余的节点中没有一个肯定具有最新集群状态的副本。这可能会导致数据丢失。例如,一个分片副本可能已经从同步集中删除。如果其他节点都不知道这一点,那么这个旧分片副本可能被分配给一个主节点。最危险的部分是,用户完全没有意识到这一系列步骤已经将他们的集群置于危险之中。用户可能需要几周或几个月的时间才能发现其中的不一致。
在 7.0 及更高的版本中,这种不安全的活动受到了更多的限制。集群宁愿保持不可用状态,也不愿承担这种风险。在没有备份的情况下,如果绝对必要,仍然可以执行这种不安全的操作。它会采取一些额外的步骤,确认你已经意识到这些风险的存在,并避免意外地进行不安全的操作。
如果丢失了一半或更多符合主节点条件的节点,首先要做的就是将这些节点恢复到联机状态。如果节点的数据目录仍然完整,那么最好的方法是使用这些数据目录启动新节点。如果可以这么做,那么就可以使用最新的集群状态形成新集群。
接下来要做的是从最近的快照中恢复集群。这将使集群进入一种已知的状态,但会丢失自捕获快照以来写入的数据。然后,你可以再次索引丢失的数据,因为你知道丢失的时间段。因为快照是增量的,所以你可以非常频繁地执行这些操作。可以每 30 分钟获取一次快照,将丢失的数据量控制在一定范围内。
如果这两种恢复操作都不可行,那么最后的方法就是使用不安全的 elasticsearch-node 恢复工具。这是一个命令行工具,系统管理员可以用它来执行不安全的操作,比如从少数节点中选择一个过时的主节点。
工作原理
如果你熟悉分布式系统理论,可能会将集群协调看成是一个可以使用分布式共识来解决的问题。Elasticsearch 的开发早期,分布式共识并没有得到广泛的应用,但是近年来,这种技术的发展已经取得了显著的进步。
Zen Discovery 采用了很多分布式共识算法的想法,但并没有严格遵循这个理论所规定的模型。它也提供了非常保守的超时机制,导致它有时后在发生故障后恢复得非常慢。7.0 中引入的新的集群协调子系统使我们有机会更紧密地遵循这个理论模型。
分布式协调是一个难以解决的问题。我们严重依赖形式化的方法来预先验证我们的设计,自动化工具在正确性和安全性方面为我们提供了强有力的保证。你可以在公共 Elasticsearch 形式化模型库中找到 Elasticsearch 新集群协调算法的形式化规范。这个算法的核心安全模块简单明了,形式化模型与 Elasticsearch 存储库代码之间存在直接的一一对应关系。
如果你熟悉 Paxos、Raft、Zab 和 Viewstamped Replication(VR)等分布式共识算法,那么应该也熟悉核心安全模块。它模拟了一个可重写寄存器,并使用了主节点的概念,这个概念与 Paxos 的投票、Raft 的条款和 VR 的视图类似。核心安全模块及其形式化模型还包括集群引导、跨节点重启持久性和动态重配置。所有这些特性对于确保系统能够在所有情况下正常运行来说是至关重要的。
我们构建了一个活动层,用来确保无论集群发生什么故障,一旦网络恢复并且有足够的节点在线,就会选举出一个主节点,并且这个主节点能够发布集群状态更新。这个活动层使用了很多最先进的技术来避免大部分常见问题。选举调度程序是自适应的,可以根据网络条件改变自己的行为,以避免出现过多的竞争选举。Raft 风格的预选环节会在选举开始前压制无法获胜的选举,避免流氓节点的干扰。延迟检测可以防止节点在远远落后于主节点时破坏集群。主动双向故障检测可以确保集群中的节点始终能够相互通信。大多数集群状态更新都以较小的 diff 进行高效的发布,从而避免了在节点之间复制整个集群的状态。被优雅终止的首领节点将明确地让位给继任者,通过避免进行全面选举可以减少故障转移期间的停机时间。我们开发了测试基础设施来有效地模拟可能持续几秒、几分钟或几小时的非正常中断影响,从而验证一旦中断得到解决集群总是能够快速恢复。
为什么不选择 Raft?
我们经常被问到的一个问题是,为什么我们不简单地“插入”一个标准的分布式公司算法,比如 Raft。很多著名的算法都提供了不同的权衡。我们仔细评估并从所有能够找到的材料中汲取灵感。我们早期的概念验证使用了一个更接近 Raft 的协议。我们从这次经验中了解到,将其与 Elasticsearch 完全集成需要做出根本上的变更。很多标准算法还规定了一些对于 Elasticsearch 来说不是最优的设计决策。例如:
1 标准算法通常是围绕操作日志,而 Elasticsearch 的集群协调直接基于集群状态本身,因此可以做出一些优化,例如批处理(将相关操作合并到单个广播中),这比基于操作的优化更加简单。
2 标准算法通常具有相当有限的集群伸缩能力,需要一系列步骤来完成很多维护任务,而 Elasticsearch 的集群协调可以在单个步骤中执行任意的重新配置。
3 标准算法通常高度关注安全性,对如何保证活动性的细节保持开放,并且没有描述如果发现节点不健康时集群应该作何反应。Elasticsearch 的健康检查机制非常复杂,已经经过多年的改进,对我们来说,保持其现有行为很重要。事实上,实现安全性要比保证其活动性所花费的精力要少得多。大多数实现工作都集中在系统的活动性上。
4 项目的目标之一是支持从运行 Zen Discovery 的版本 6.7 集群滚动升级到运行新协调子系统的版本 7 集群,并且时零停机时间。将标准算法调整为允许进行这种滚动升级的算法似乎都不可行。
开发一个完整的、工业强度的分布式共识算法需要大量的投入,并且需要超越学术文献所描述的范围。在实践中不可避免地需要进行定制,但是协调协议很复杂,任何定制都有引入错误的风险。从工程的角度来看,将这些定制视为开发新协议可能更有意义。
总结
Elasticsearch 7.0 提供了一个更快、更安全、更易于使用的新集群协调子系统。它支持从 6.7 进行零停机滚动升级,并为弹性数据复制提供了基础。要试用新的群集协调子系统,请下载最新的7.0 beta版本。
英文原文:https://www.elastic.co/cn/blog/a-new-era-for-cluster-coordination-in-elasticsearch
评论 1 条评论