精华内容
下载资源
问答
  • GitOps | 一种云原生的持续交付模型

    千次阅读 2018-08-28 10:19:07
    GitOps是一种实现持续交付的模型,它的核心思想是将应用系统的声明性基础架构和应用程序存放在的Git的版本控制库中.Choerodon猪齿鱼在构建持续交付流水线时参考了GitOps,并进行了实践,俗话说“兵马未动,理论先行...

    在此之前您可能听说过“GitOps”,但并不知道它到底是什么,除了GitOps,您可能还听说过的DevOps,或者AIOps,GOP的等,是的,现在是“行动”盛行的时代。

    GitOps是一种实现持续交付的模型,它的核心思想是将应用系统的声明性基础架构和应用程序存放在的Git的版本控制库中.Choerodon猪齿鱼在构建持续交付流水线时参考了GitOps,并进行了实践,俗话说“兵马未动,理论先行”,在本文中,将重点阐述GitOps工作流程的原理和模式,以及将它们应用在生产和大规模运行Kubernetes中的一些实践经验。在下一篇文章中,将介绍Choerodon猪齿鱼是如何实践和落地GitOps,从而构建了一个可重复且可靠的交付过程

    GitOps,90%的最佳实践,10%有意思的新东西需要我们去构建。 - “GitOps - 拉动请求的操作”

    这篇文章是根据Weave Cloud的几篇关于GitOps的文章翻译整理而来:

    • GitOps

    • GitOps:Pull Request操作

    • GitOps管道 - 第2部分

    • GitOps - 第3部分:可观察性

    • GitOps - 第4部分:应用交付合规性和安全CICD

       

    主要内容

    • 什么是GitOps?

      • GitOps的主要优点

    • GitOps的应用场景 - 适合云原生的持续交付

    • GitOps的基本原则

    • 最佳实践

    • 拉式流水线--Pull Request操作

      • GitOps工作流

      • 可视化

      • 应用交付的合规性和安全的CI / CD

    • GitOps带来的价值

    什么是GitOps?

    GitOps是一种持续交付的方式。它的核心思想是将应用系统的声明性基础架构和应用程序存放在Git的版本库中。

    将Git作为交付流水线的核心,每个开发人员都可以提交拉取请求(Pull Request)并使用Gi​​t来加速和简化Kubernetes的应用程序部署和运维任务。通过使用像Git这样的简单熟悉工具,开发人员可以更高效地将注意力集中在创建新功能而不是运维相关任务上(例如,应用系统安装,配置,迁移等)。

    GitOps:在声明性基础架构之上的版本化CI / CD。停止脚本并开始发货。https://t.co/SgUlHgNrnY - Kelsey Hightower(@kelseyhightower)2018年1月17日

    GitOps的主要优点

    通过GitOps,当使用Git的提交基础架构代码更改时,自动化的交付流水线会将这些更改应用到应用程序的实际基础架构上但是GitOps的想法远不止于此 - 它还会使用工具将整个应用程序的实际生产状态与基础架构源代码进行比较,然后它会告诉集群哪些基础架构源代码与实际环境不匹配。

    通过应用GitOps最佳实践,应用系统的基础架构和应用程序代码都有“真实来源。” - 其实是将基础架构和应用程序代码都存放在gitlab,或者github上等版本控制系统上这使开发团队可以提高开发和部署速度并提高应用系统可靠性。

    将GitOps理论方法应用在持续交付流水线上,有诸多优势和特点:

    • 安全的云原生CI / CD管道模型

    • 更快的平均部署时间和平均恢复时间

    • 稳定且可重现的回滚(例如,根据Git恢复/回滚/前叉)

    • 与监控和可视化工具相结合,对已经部署的应用进行全方位的监控

    GitOps应用场景 - 满足云原生环境下的持续交付

    作为CI / CD流水线的方案,GitOps被描述为软件开发过程的“圣杯”。由于没有单一工具可以完成流水线中所需的所有工作,因此可以自由地为流水线的不同部分选择最佳工具。可以从开源生态系统中选择一组工具,也可以从封闭源中选择一组工具,或者根据使用情况,甚至可以将它们组合在一起,其实,创建流水线最困难的部分是将所有部件粘合在一起。

    不管如何选择构造自己的交付流水线,将基于GIT中(或者其他版本控制工具)的GitOps最佳实践应用在交付流水线中都是一个不二选择,这将使构建持续交付流水线,以及后续的推广变得更加容易,这不仅从技术角度而且从文化角度来看都是如此。

    当然,GitOps也不是万能的,它也有相应的应用场景。

    不可变基础设施

    应用都需要运行在多台机器上,它们被组织成不同的环境,例如开发环境,测试环境和生产环境等等。需要将相同的应用部署到不同的机器上。通常需要系统管理员确保所有的机器都处于相同的状态。接着所有的修改,补丁,升级需要在所有的机器中进行。随着时间的推移,很难再确保所有的机器处于相同的状态,同时越来越容易出错。这就是传统的可变架构中经常出现的问题。这时我们有了不可变架构,它将整个机器环境打包成一个单一的不可变单元,而不是传统方式仅仅打包应用。这个单元包含了之前所说的整个环境栈和应用所有的修改,补丁和升级,这就解决了前面的问题。 - 摘自InfoQ的“关于不可变架构以及为什么需要不可变架构”作者百占辉

    “不可变基础设施”这一概念不是刚刚冒出来的,它也不是必须需要容器技术。然而,通过容器,它变得更易于理解,更加实用,并引起了业内广泛注意。“不可变基础设施”让我们以全新的方式理解和面对应用系统,尤其是使以微服务为代表的分布式系统在部署,运营等方面变得不那么复杂,而有很好的可控性。

    那么,如何比较方便地在实际的生产过程中应用“不可变基础设施”,这给业界也提出了另外一个问题.GitOps是在具体Kubernetes的应用实践中出现的,GitOps需要依托于“不可变基础”架构”才能发挥其作用。在一定程度上说,‘不可变基础架构’为GitOps的出现创造了必要的条件,反过来GitOps应用Kubernetes的容器编排能力,能够迅速的使用镜像搭建出应用系统所需的组件。

    声明性容器编排

    Kubermetes作为一个云原生的工具,可以把它的“声明性”看作是“代码”,意味着声明配置由一组事实而不是一组指令组成,例如,“有十个Redis的服务器”,而不是“启动十个Redis的服务器,告诉我它是否有效”。

    借助Kubermetes的声明性特点,应用系统的整个配置文件集可以在Git的库中进行版本控制。通过使用Git的库,应用程序更容易部署到Kubernetes中,以及进行版本回滚。更重要的是,当灾难发生时,群集的基础架构可以从Git的库中可靠且快速地恢复。

    Kubernetes等云原生工具的声明性体现在可以对实例,容器,网络,存储,CPU等配置通过一组代码方便的表达出来,Kubernetes等云原生工具可以利用这些配置代码运行出来一套基于容器的应用系统,例如YMAL,
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
     name: nginx-deployment
    spec:
     replicas: 1
     template:
       metadata:
         labels:
           app: nginx
       spec:
         containers:
         - name: nginx
      image: registry.choerodon.com.cn/operation-choerodon-dev/nginx-demo:1.13.5-alpine
           ports:
           - containerPort: 80

     

    GitOps充分利用了不可变基础设施和声明性容器编排,通过GitOps可以轻松地管理多个部署。为了最大限度地降低部署后的变更风险,无论是有意还是偶然的“配置偏差”,GitOps构建了一个可重复且可靠的部署过程,在整个应用系统宕机或者损坏情况下,为快速且完全恢复提供了所需条件。

    GitOps的基本原则

    以下是几条在云原生环境中,GitOps的原则:

    ● 任何能够被描述的内容都必须存储在的Git库中

    通过使用GIT中作为存储声明性基础架构和应用程序代码的存储仓库,可以方便地监控集群,以及检查比较实际环境的状态与代码库上的状态是否一致。所以,我们的目标是描述系统相关的所有内容:策略,代码,配置,甚至监控事件和版本控制等,并且将这些内容全部存储在版本库中,在通过版本库中的内容构建系统的基础架构或者应用程序的时候,如果没有成功,则可以迅速的回滚,并且重新来过。

    ● 不应直接使用Kubectl

    作为一般规则,不提倡在命令行中直接使用kubectl命令操作执行部署基础架构或应用程序到集群中。还有一些开发者使用CI工具驱动应用程序的部署,但如果这样做,可能会给生产环境带来潜在不可预测的风险。

    ● 调用Kubernetes的API的接口或者控制器应该遵循运营商模式

    调用Kubernetes的API的接口或者控制器应该遵循操作员模式(什么是操作员模式?),集群的状态和Git库中的配置文件等要保持一致,并且查看分析它们之间的状态差异。

    最佳实践

    以Git的作为事实的唯一真实来源

    Git是每个开发人员工具包的一部分。学习起来感觉自然而且不那么令人生畏,而且工具本身也非常简单。通过使用Git作为应用系统的事实来源,几乎可以操作所有东西。例如,版本控制,历史记录,评审和回滚都是通过GIT中进行的,而无需使用像kubectl这样的工具。

    所以,Git是GitOps形成的最基础的内容,就像第一条原则“任何能够被描述的内容都必须存储在Git库中”描述的那样:通过使用Git作为存储声明性基础架构和应用程序代码的存储仓库,可以方便地监控集群,以及检查比较实际环境的状态与代码库上的状态是否一致所以我们的目标是描述系统相关的所有内容:策略,代码,配置,甚至监控事件和版本控制等,并且将这些内容全部存储在版本库中,在通过版本库中的内容构建系统的基础架构或者应用程序的时候,如果没有成功,则可以迅速的回滚,并且重新来过。

    拉式流水线--Pull Request操作

    ▌推送流水线

    目前大多数CI / CD工具都使用基于推送的模型。基于推送的流水线意味着代码从CI系统开始,通过一系列构建测试等最终生成镜像,最后手动使用“kubectl”将任何更改推送到Kubernetes集群。

    很多开发人员不愿意在CI中启动CD部署流程,或者使用命令行工具操作启动CD部署流程的原因可能是这样做会将集群的用户和密码等公布出去。虽然可以有措施保护CI / CD脚本和命令行,但是这些操作毕竟还是在集群外部非可信区工作的。所以,类似做法是不可取的,会给系统安全带来潜在的风险。

    具有集群外读/写(R / W)权限的典型推送流水线:

    

    • CI运行测试,输出传递到容器映像存储库。

    • CD系统自动部署容器(或根据请求,即手动)。

    ▌拉式流水线

    在GitOps中,镜像被拉出并且凭证保留在集群中:

    

    GIT中库是拉式流水线模式的核心,它存储应用程序和配置文件集开发人员将更新的代码推送到GIT中代码库。CI工具获取更改并最终构建多克镜像.GitOps检测到有镜像,从存储库中提取新镜像,然后在Git的配置仓库中更新其YAML。然后,GitOps会检测到群集已过期,并从配置库中提取已更改的清单,并将新镜像部署到群集。

    GitOps的流水线

    在上节中介绍了GitOps采用拉式模式构建交付流水线,本节将详细地介绍在构建GitOps流水时需要注意哪些事情,有哪些最佳实践。

    ▌GitOps流水线

    

    这是一个新图,显示部署上游的所有内容都围绕的Git库工作的。在“拉式流水线”中讲过,开发人员将更新的代码推送到Git的代码库,CI工具获取更改并最终构建多克镜像。GitOps的配置更新检测到有镜像,从存储库中提取新镜像,然后在Git的配置仓库中更新其YAML。然后,GitOps的部署操作会检测到群集已过期,并从配置库中提取已更改的清单,并将新镜像部署到群集。

    使用群集内部的部署运营商,群集凭据不会在生产环境之外公开。一旦将部署运营商安装到集群与Git仓库建立连接,线上环境中的任何更改都将通过具有完全回滚的Git pull请求以及Git的提供的方便审计日志完成。

    ▌自动混帐→集群同步 

    由于没有单一工具可以完成流水线中所需的所有工作,可以从开源生态系统中选择一组工具,也可以从封闭源中选择一组工具,或者根据使用情况,甚至可以将它们组合在一起,其实,创建流水线最困难的部分是将所有部件粘合在一起。要实现GitOps,必须要开发出新的组件,用于粘合这些工具,实现拉式交付流水线。部署和发布自动化是应用落实GitOps,并使交付流水线工作的基础.GitOps不仅要保证,当开发人员通过GIT中更新配置文件集的时候,GitOps流水线要自动根据最新的配置文件状态更新线上环境,而且GitOps还要能够实时比对GIT中库中配置文件集最新的状态与线上环境最新的状态保持一致。

    

    在上节中提到了两个名词:Config Update  和  Deploy Operator,根据GitOps的实践,配置更新和部署操作员是需要进行设计开发的,它们是实现GitOps流水线必须的关键组件.GitOps赋予了它们神奇的魔法,它们既是自动化容器升级和发布到线上环境的工具,可能也要负责服务,部署,网络策略甚至路由规则等任务。因此,配置更新  和  部署操作员是映射代码,服务和运行集群之间所有关系的“粘合剂”。

    当然,您可以根据具体的设计,赋予各种其他的功能,但是自动同步是一定需要的,确保如果对存储库进行任何更改,这些更改将自动部署到线上环境中。

    仅部署容器配置状语从句:

    GitOps建议不直接将应用程序部署到线上环境中,而是将应用程序和相关配置打包成镜像,并存储到镜像库中,最后,通过镜像的方式生成容器,并部署到线上环境中。

    容器为什么如此重要?在GitOps模型中,我们使用不可变基础架构模式。一旦代码在的Git中提交,GitOps就不希望任何其他内容发生变化,这样可以最大限度地降低系统潜在不确定性,不一致性风险。例如,需要将相同的应用部署到不同的机器上。通常需要系统管理员确保所有的机器都处于相同的状态。接着所有的修改,补丁,升级需要在所有的机器中进行。随着时间的推移,很难再确保所有的机器处于相同的状态,同时越来越容易出错。然而,容器是比较完美地解决了这个问题,当然,使用虚拟机是可以的,显然使用容器更加方便。

    GitOps的可观察性

    “可观察性就像生产中的驱动测试一样。如果你不知道如何确定它是否正常工作,请勿接受拉请求。@ mipsytipsy” - Adriano Bastos

    在GitOps中,使用的Git库来存储应用系统的配置文集和应用程序,它确保开发人员将所有对于应用系统的配置和程序的新增,修改等都通过的Git库进行版本控制,使混帐成为配置和程序的唯一真实来源。而GitOps的可观察性则是确保线上环境的真实状态与Git的库中的保持一致性。本章节将给大家介绍GitOps的可观察性。

    ▌可观察性是另一个真理来源 

    在GitOps中,我们使用Git的作为系统所需状态的真实来源。例如,如果应用系统宕机,GitOps可以回滚到之前正确状态。而可观察性是系统实际运行状态的真实来源,系统开发人员或者运维人员可以监控系统的状态。这是一张显示流程的图片。

    ▌通过观察需寻找问题的答案

    如果大家使用Kubernetes作为云原生环境和容器编排工具,相信大家会有这样的感触,虽然Kubernetes是一个非常棒的编排容器平台,但是随之而来的缺乏友好的可视化管理界面给开发人员或者运维人员带来诸多不便例如:

    • 我的部署成功了吗?我的系统现在处于工作的状态,我现在可以回家吗?

    • 我的系统与以前有什么不同?我可以使用Git的或我们的系统历史记录来检查吗?

    • 我的改变是否改善了整体用户体验?(与系统正确性相对)

    • 我在信息中心找不到我的新服务(例如RED指标)

    • 这个故障是否与我上次的服务更新事件有关,还是和其他操作有关系?

    大家可能会想到通过监控服务器的CPU,内存,网络等,以及应用的日志,甚至微服务的调用链等来解决问题。是的,这个没有错,能够得到一些反馈信息,但是使用过类似监控的开发人员或者运维人员也会感觉,这些监控仪表盘给我们大量冗繁的信息,需要认真地甄别,而且有很多信息在这些仪表盘中是获得不到的。这意味着需要创建新的仪表盘,用于监控新的指标和内容。

    ▌GitOps的可观察性

    可观察性可被视为Kubernetes持续交付周期的主要驱动因素之一,因为它描述了在任何给定时间系统的实际运行状态。观察运行系统以便理解和控制它。新功能和修复程序被推送到GIT中并触发部署管道,当准备好发布时,可以实时查看正在运行的集群。此时,开发人员可以根据此反馈返回到管道的开头,或者将映像部署并释放到生产集群。

    在这里GitOps引入一个新的工具:比较速度,用来监控对比系统状态即:

    • 验证当前线上系统的状态是否和Git的库中描述的状态一致,例如,我上一次发布是否符合期望?

    • 提醒开发人员不一致状态,以及相应的明细信息。

    在文章前面讲过,在Git的库中存储的实际上是“声明性基础设施”,例如Kubernetes的YAML文件,用以构建应用系统所需的各种组件,域名,网络等配置信息.Diffs需要读取的Git库中配置信息,同时,通过API等读取集群的相应信息,并进行比对。

    例如,Kubernetes集群:所需的Kubernetes状态可能是“有4个redis的服务器” .Diffs定期检查群集,并在数量从4变化时发出警报一般而言,比较速度将YAML文件转换为运行状态查询。

    GitOps是面向发布的操作模型,请参见下图。交付速度取决于团队在此周期中绕过各个阶段的速度。

    

    应用交付合规性和安全性

    由于以安全的方式跟踪和记录更改,因此合规性和审计变得微不足道。使用的diff等比较工具还可以将的Git库中定义的集群状态与实际运行的集群进行比较,从而确保更改与实际情况相符。

    在混帐中记录所有的操作日志

    通过上面文章的叙述,开发人员或者运维人员通过Git客作端git commit / git merge的所有操作都会Git库记录下来,审计员可以查看Git,看看谁做了任何更改,何时以及为何以及如何影响正在运行的系统部署。当然,可以根据自身的需求定制不同的交付合规性。相较于直接进入服务器操作或者通过Kubctl操作集群,GIT中记录了每一个操作步骤,这些可以为合规性和审计提供完整的操作日志。

    角色权限状语从句:控制

    几乎所有的GIT中库都提供角色和权限控制,与开发和运维无关的人员没有权限操作GIT中库。而不是直接把服务器或者集群的操作权限散发出去,这样特别容易引起安全泄露。

    GitOps带来的好处

    ▌更加快速地开发 

    借助GitOps的最佳实践,开发人员可以使用熟悉的GIT中工具,便捷地将应用程序和其对应的配置文件集持续部署到Kubernetes等云原生环境,提高业务的敏捷度,快速地相应用户的需求,有助于增加企业市场的竞争力。

    ▌更好地进行运维 

    借助GitOps,可以实现一个完整的端到端的交付流水线。不仅可以实现拉式的持续集成流水线和持续部署流水线,而且系统的运维操作可以通过GIT中来完成。 

    ▌更强大的安全保证 

    几乎所有的GIT中库都提供角色和权限控制,与开发和运维无关的人员没有权限操作GIT中库。而不是直接把服务器或者集群的操作权限散发出去,这样特别容易引起安全泄露。 

    ▌更容易合规的审计 

    由于以安全的方式跟踪和记录更改,因此合规性和审计变得微不足道。使用的diff等比较工具还可以将集群状态的可信定义与实际运行的集群进行比较,从而确保跟踪和可审计的更改与实际情况相符。

    关于猪齿鱼

    Choerodon猪齿鱼是一个开源企业服务平台,是基于Kubernetes的容器编排和管理能力,整合DevOps工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的开源平台,同时提供IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

    大家可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

    欢迎加入Choerodon猪齿鱼社区,共同为企业数字化服务打造一个开放的生态平台。

    展开全文
  • 应用安全测试技术DAST、SAST、IAST对比分析-持续更新 版权来源:安全牛首发文章,本文仅补充完善。 、全球面临软件安全危机 我们即将处于个软件定义一切的时代,这是 “个最好的时代,也是个最坏的时代”...

    应用安全测试技术DAST、SAST、IAST对比分析-持续更新

    版权来源:安全牛首发文章,本文仅补充完善。

    一、全球面临软件安全危机

    我们即将处于一个软件定义一切的时代,这是 “一个最好的时代,也是一个最坏的时代”。

    无论是生活中离不开的通讯、支付、娱乐、餐饮、出行,以及医疗,还是国防领域中的火箭、导弹、卫星等,都离不开软件技术。然而,软件技术在促进社会发展的同时,也可能因为漏洞问题危害人们的个人隐私信息、财产安全甚至生命安全,这类案例不胜枚举。

    2010年,大型社交网站rockyou.com被曝存在SQL注入漏洞,黑客利用此漏洞获取到3200万用户记录(包括E-mail、姓名及明文形式的密码)。

    2015年,英国电话和宽带供应商TalkTalk被一名15岁的黑客利用SQL注入漏洞进行攻击,四百万TalkTalk客户的姓名、地址、出生日期、和信用卡/银行详细信息被黑客窃取。

    2018年,台湾一男子利用花旗银行信用卡业务系统漏洞,刷卡消费达6300余万元(新台币约合人民币1345万元),花旗银行已通过司法途径,向该名客户求偿。

    软件技术的发展与应用伴随着巨大的安全危机,解决软件漏洞问题是软件开发从业者和安全从业者们迫在眉睫的任务。

    二、什么是Web应用安全测试技术?

    为了发现软件的漏洞和缺陷,确保Web应用程序在交付之前和交付之后都是安全的,就需要利用Web应用安全测试技术识别Web应用程序中架构的薄弱点和漏洞,并且必须赶在网络黑客找到和利用它们之前。

    Web应用安全测试技术经过多年的发展,目前业界常用的技术主要分为3大类别。

    DAST:动态应用程序安全测试(Dynamic Application Security Testing)技术在测试或运行阶段分析应用程序的动态运行状态。它模拟黑客行为对应用程序进行动态攻击,分析应用程序的反应,从而确定该Web应用是否易受攻击。

    SAST:静态应用程序安全测试(Static Application Security Testing)技术通常在编码阶段分析应用程序的源代码或二进制文件的语法、结构、过程、接口等来发现程序代码存在的安全漏洞。

    IAST:交互式应用程序安全测试(Interactive Application Security Testing)是2012年Gartner公司提出的一种新的应用程序安全测试方案,通过代理、VPN或者在服务端部署Agent程序,收集、监控Web应用程序运行时函数执行、数据传输,并与扫描器端进行实时交互,高效、准确的识别安全缺陷及漏洞,同时可准确确定漏洞所在的代码文件、行数、函数及参数。IAST相当于是DAST和SAST结合的一种互相关联运行时安全检测技术。

    本文主要分析这3种技术的实现原理、优劣势对比以及应用场景。

    三、DAST

    DAST是一种黑盒测试技术,是目前应用最广泛、使用最简单的一种Web应用安全测试方法,安全工程师常用的工具如AWVS、AppScan等就是基于DAST原理的产品。

    1. 实现原理

    图 1:DAST原理

    1)通过爬虫发现整个 Web 应用结构,爬虫会发现被测Web程序有多少个目录,多少个页面,页面中有哪些参数;

    2)根据爬虫的分析结果,对发现的页面和参数发送修改的 HTTP Request 进行攻击尝试(扫描规则库);

    3)通过对于 Response 的分析验证是否存在安全漏洞。

    2. DAST优劣势分析

    DAST这种测试方法主要测试Web应用程序的功能点,测试人员无需具备编程能力,无需了解应用程序的内部逻辑结构,不区分测试对象的实现语言,采用攻击特征库来做漏洞发现与验证,能发现大部分的高风险问题,因此是业界Web安全测试使用非常普遍的一种安全测试方案。DAST除了可以扫描应用程序本身之外,还可以扫描发现第三方开源组件、第三方框架的漏洞。

    从工作原理也可以分析出,DAST一方面需要爬虫尽可能的把应用程序的结构爬取完整,另一方面需要对被测应用程序发送漏洞攻击包。现在很多的应用程序含有AJAX页面、CSRF Token页面、验证码页面、API孤链、POST表单请求或者是设置了防重放攻击策略,这些页面无法被网络爬虫发现,因此DAST技术无法对这些页面进行安全测试。DAST技术对业务分支覆盖不全,即使爬到一个表单,要提交内容,服务端对内容做判断,是手机号码则进入业务1,不是手机号码进入业务2,爬虫不可能知道这里要填手机号码,所以业务分支1永远不会检测到。

    另外DAST必须发送漏洞攻击包来进行安全测试,这就需要有安全专家不断更新漏洞扫描插件,而且这种测试方式会对业务测试造成一定的影响,安全测试的脏数据会污染业务测试的数据。

    DAST的测试对象为HTTP/HTTPS的Web应用程序,对于IOS/Android上的APP也无能为力。

    DAST发现漏洞后会定位漏洞的URL,无法定位漏洞的具体代码行数和产生漏洞的原因,需要比较长的时间来进行漏洞定位和原因分析,这使得DAST不太适合在DevOps的开发环境中使用。

    图 2:DAST优势与劣势

    四、SAST

    超过50%的安全漏洞是由错误的编码产生的,开发人员一般安全开发意识和安全开发技能不足,更加关注业务功能的实现。想从源头上治理漏洞就需要制定代码检测机制,SAST是一种在开发阶段对源代码进行安全测试发现安全漏洞的测试方案。

    1. 实现原理

    图 3:SAST原理

    1) 首先通过调用语言的编译器或者解释器把前端的语言代码(如JAVA,C/C++源代码)转换成一种中间代码,将其源代码之间的调用关系、执行环境、上下文等分析清楚。

    2)语义分析:分析程序中不安全的函数,方法的使用的安全问题。

    3)数据流分析:跟踪,记录并分析程序中的数据传递过程所产生的安全问题。

    4)控制流分析:分析程序特定时间,状态下执行操作指令的安全问题。

    5)配置分析:分析项目配置文件中的敏感信息和配置缺失的安全问题。

    6)结构分析:分析程序上下文环境,结构中的安全问题。

    7)结合2)-6)的结果,匹配所有规则库中的漏洞特征,一旦发现漏洞就抓取出来。

    8)最后形成包含详细漏洞信息的漏洞检测报告,包括漏洞的具体代码行数以及漏洞修复的建议。

    2.  SAST优劣势分析

    SAST需要从语义上理解程序的代码、依赖关系、配置文件。优势是代码具有高度可视性,能够检测更丰富的问题,包括漏洞及代码规范等问题。测试对象比DAST丰富,除Web应用程序之外还能够检测APP的漏洞,不需要用户界面,可通过IDE插件形式与集成开发环境(如Eclipse、IntelliJ IDEA)结合,实时检测代码漏洞问题,漏洞发现更及时,修复成本更低。

    另一方面SAST不仅需要区分不同的开发语言(PHP、C#、ASP、.NET、Java、Python等),还需要支持使用的Web程序框架,如果SAST工具不支持某个应用程序的开发语言和框架,那么测试时就会遇到障碍。DAST支持测试任何语言和框架开发的HTTP/HTTPS应用程序。

    传统的SAST扫描时间很慢,如果是用SAST去扫描代码仓库,需要数小时甚至数天才能完成,这在日益自动化的持续集成和持续交付(CI/CD)环境中效果不佳。

    还有一点是SAST的误报,业界商业级的SAST工具误报率普遍在30%以上,误报会降低工具的实用性,可能需要花费更多的时间来清除误报而不是修复漏洞。

    SAST只对源代码进行检测,而不会分析整个应用程序,这迫使企业需要购买单独的软件组合分析工具(SCA),即使是SCA也只是识别公开的漏洞;开源、第三方API或框架中的未知漏洞超出了SAST和SCA的范围。

    图 4:SAST优势与劣势

    五、IAST

    IAST交互式应用安全测试技术是最近几年比较火热的应用安全测试新技术,曾被Gartner咨询公司列为网络安全领域的Top 10技术之一。IAST融合了DAST和SAST的优势,漏洞检出率极高、误报率极低,同时可以定位到API接口和代码片段。

    1.  实现原理

    IAST的实现模式较多,常见的有代理模式、VPN、流量镜像、插桩模式,本文介绍最具代表性的2种模式,代理模式和插桩模式。

    代理模式,在PC端浏览器或者移动端APP设置代理,通过代理拿到功能测试的流量,利用功能测试流量模拟多种漏洞检测方式对被测服务器进行安全测试。

    插桩模式,插桩模式是在保证目标程序原有逻辑完整的情况下,在特定的位置插入探针,在应用程序运行时,通过探针获取请求、代码数据流、代码控制流等,基于请求、代码、数据流、控制流综合分析判断漏洞。插桩模式具体实现有2种模式,Active 插桩和Passive 插桩。

    1) 代理模式实现原理

    图 5:代理模式原理

    a. 功能测试人员在浏览器或者APP中设置代理,将IAST设备地址填入;

    b. 功能测试人员开始功能测试,测试流量经过IAST设备,IAST设备将流量复制一份,并且改造成安全测试的流量;

    c. IAST设备利用改造后的流量对被测业务发起安全测试,根据返回的数据包判断漏洞信息。

    插桩需要在服务器中部署Agent,不同的语言不同的容器要不同的Agent,这对有些用户来说是不可接受的。而代理模式不需要服务器中部署Agent,只是测试人员要配置代理,安全测试会产生一定的脏数据,漏洞的详情无法定位到代码片段,适合想用IAST技术又不接受在服务器中部署Agent的用户使用。

    2) Active插桩实现原理

    图 6:Active 插桩原理

    a. 被测试服务器中安装IAST插桩 Agent;

    b. DAST Scanner发起扫描测试;

    c. IAST插桩 Agent追踪被测试应用程序在扫描期间的反应附加测试,覆盖率和上下文,将有关信息发送给Management Server,Management Server展示安全测试结果。

    Active 插桩模式需要在被测试应用程序中部署插桩 Agent,使用时需要外部扫描器去触发这个Agent。一个组件产生恶意攻击流量,另一个组件在被测应用程序中监测应用程序的反应,由此来进行漏洞定位和降低误报。

    Active 插桩模式更像是一种改进版的DAST技术,目前最新的AWVS、AppScan已经采用了Active 插桩模式。AWVS集成了“AcuSensor”模块,通过在源代码中部署传感器来增强定期动态扫描。AcuSensor能够在AWVS扫描期间检查Web应用程序执行时的源代码,在后端抓取应用程序,提供100%爬行覆盖率,查找并测试在黑盒扫描期间未发现的隐藏输入。AppScan则是集成了“Glass Box”服务模块,这使得AppScan支持 Web 2.0、JavaScript 和 AJAX 框架。

    Active 插桩模式解决了传统DAST漏报和无法精确定位漏洞位置的问题,需要先做扫描,扫描触发漏洞需要一定的时间,而且扫描会对业务测试产生影响。在双向HTTPS加密、CSRF Token页面、防攻击重放等场景下Active 插桩模式依然无法进行安全测试。

    1. Passive 插桩实现原理

    图 7:Passive 插桩原理

    a. 被测试服务器中安装插桩 Agent;

    b. 插桩 Agent在应用程序运行时获取请求和代码数据流、代码控制流;

    c. 插桩Agent将获取的信息发送给Management Sever,Management Sever展示安全测试结果。

    Passive 插桩在程序运行时监视应用并分析代码,它不会主动对Web应用程序执行攻击,而是纯粹被动地分析检测代码。这实际上是一个巨大的优势,因为它不会影响同时运行的其他测试活动,并且只需要业务测试(手动或自动)来触发安全测试,有测试流量过来就可以实时的进行漏洞检测。

    插桩模式的关键是Agent,Agent需要根据不同语言进行开发,但是功能基本相同:

    获取请求数据和返回数据;

    代码执行中的参数传递;

    数据库查询(如ODBC);

    目录查询(如LDAP),文件系统权限;

    监听内存中特定的值,识别受污染的输入;

    第三方库的使用;

    对外部应用程序和服务的调用;

    特定代码的执行等。

    2active 和 passive的区别

    3.  IAST优劣势分析

    IAST代理模式的优劣势,在上文已提到,这里不再赘述。

    IAST插桩模式的技术基于请求、代码、数据流、控制流综合分析判断漏洞,漏洞测试准确性高,误报率极低。由于IAST插桩模式可获取更多的应用程序信息,因此发现的安全漏洞既可定位到代码行,还可以得到完整的请求和响应信息,完整的数据流和堆栈信息,便于定位、修复和验证安全漏洞。支持测试AJAX页面、CSRF Token页面、验证码页面、API孤链、POST表单请求等环境。

    IAST插桩模式在完成应用程序功能测试的同时即可以实时完成安全测试,且不会受软件复杂度的影响,适用于各种复杂度的软件产品。不但可以检测应用程序本身的安全弱点,还可以检测应用程序中依赖的第三方软件的版本信息和包含的公开漏洞。整个过程无需安全专家介入,无需额外安全测试时间投入,不会对现有开发流程造成任何影响,符合敏捷开发和DevOps模式下软件产品快速迭代、快速交付的要求。

    IAST插桩模式的核心技术在于探针,探针需要根据不同的语言进行开发,它只能在具有虚拟运行时环境的语言上执行,例如Java,C#,Python和NodeJS。它不支持C,C ++和Golang等语言。其次,由于agent与真实webserver集成,稳定性非常重要,每次更新需要重启webserver,部署成本较大。业务逻辑漏洞也是IAST插桩模式无法解决的问题。

    图 8:IAST插桩模式的优劣势

    六、三种技术的应用场景分析

    上文分析了DAST、SAST、IAST三种技术的具体实现原理和各自的优劣势,技术本身没有优劣之分,不同的技术能够解决不同场景下的问题,需要安全工程师能够因地制宜地选择对应的技术解决对应的问题。

    对比项

    DAST

    SAST

    IAST

    测试对象

    Web应用程序

    Web应用程序

    APP的漏洞

    Web应用程序

    APP的漏洞

    部署成本

    使用成本

    较低, 基本无需人工验证

    高, 人工排除误报

    低, 基本没有误报

    漏洞检出率

    较高

    脏数据

    非常多

    较少

    几乎没有

    研发流程集成

    测试/线上运营阶段

    研发阶段

    测试阶段

    误报率

    极低(几乎为0)

    测试覆盖度

    检查速度

    随测试用例数量稳定增加

    随代码量呈指数增长

    实时检测

    逻辑漏洞检测

    支持部分

    不支持

    支持部分

    影响漏洞检出率因素

    与测试payload覆盖度相关

    企业可优化和扩展

    与检测策略相关

    企业可在定制策略

    与检测策略相关

    企业可定制测量

    第三方组件漏洞检测

    支持

    不支持

    支持

    支持语言

    不区分语言

    区分语言

    区分语言

    支持框架

    不区分框架

    区分框架

    区分框架

    侵入性

    较高,脏数据

    风险程度

    较高,扫挂/脏数据

    漏洞详情

    中,请求

    较高,数据流+代码行数

    高,请求+数据流+代码行数

    CI/CD集成

    不支持

    支持

    支持

    持续安全测试

    不支持

    支持

    支持

    工具集成

    开发环境集成

    构建工具、问题跟踪工具

    构建工具、自动化

    其他

    无法定位漏洞的具体代码行数和产生漏洞的原因

    不支持C,C ++和Golang等语言

    图 9:DAST、SAST、IAST之间的对比

    DAST技术比较适合用于线上运行环境的监控,研发阶段代码检测适合使用SAST技术,QA阶段适合使用IAST技术。

    、三种技术的产品技术分析

    对比项

    DAST

    SAST

    IAST

    商业产品

    AppScan、AWVS、webinpsect

     burpsuite

    FortifyCheckmarx

    默安-雳鉴IAST

    新思Seeker软件

    开源网安SecZone VulHunter

    、墨云VackBot、悬镜灵脉IAST等,国外:Contrast Security等

    开源产品

    Owasp ZAP、Xray

    RaptorRIPS、Seay源代码审计系统、VCG等

    百度RASP等

    部署成本

    使用成本

    较低, 基本无需人工验证

    高, 人工排除误报

    低, 基本没有误报

    漏洞检出率

    较高

    、实际应用总结

    笔者所在的安全厂商已实现上述三大应用安全测试技术的创新性落地。

    软件开发阶段,与程序员对话的源码安全审计主要基于SAST技术打造, SAST工具对用户的困扰主要来自于误报,通过数据流调用分析、变量关联分析、机器学习等多重手段极大地降低了误报率,减少工具对安全测试工作的困扰,改善用户体验,降低工具的使用成本。

    软件测试阶段,基于IAST技术打造,支持代理、VPN、流量信使、流量镜像、爬虫、导入日志、Passive插桩共7种流量收集模式,真正结合了DAST、SAST、IAST三种技术的优势;漏洞检测率极高,包括水平越权、垂直越权等标准IAST技术无法检测的逻辑漏洞,误报率几乎为0;漏洞详情直接定位请求、数据流、代码片段,修复漏洞更容易;采用Passive 插桩技术,无需重放请求,不会形成脏数据,可覆盖加密、防重放、签名等任意场景;近实时检测漏洞,漏洞检测随着应用程序运行实时进行。

    应用上线运营阶段,采用DAST技术打造资产风险监控系统,在大量企业客户中被用来对线上业务环境进行监控。从攻击者视角对企业进行资产探测,全面发现企业的资产暴露面和应用程序的漏洞,保障线上运营环境的安全;并且,部署模式紧跟业务的使用模式,支持在互联网环境、企业IDC、私有云、公有云、混合云等多种场景下部署使用。

    展开全文
  • Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft 将一致性算法...


    摘要

    Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft 将一致性算法分解成了几个关键模块,例如领导人选举、日志复制和安全性。同时它通过实施一个更强的一致性来减少需要考虑的状态的数量。从一个用户研究的结果可以证明,对于学生而言,Raft 算法比 Paxos 算法更加容易学习。Raft 算法还包括一个新的机制来允许集群成员的动态改变,它利用重叠的大多数来保证安全性。

    1 介绍

    一致性算法允许一组机器像一个整体一样工作,即使其中一些机器出现故障也能够继续工作下去。正因为如此,一致性算法在构建可信赖的大规模软件系统中扮演着重要的角色。在过去的 10 年里,Paxos 算法统治着一致性算法这一领域:绝大多数的实现都是基于 Paxos 或者受其影响。同时 Paxos 也成为了教学领域里讲解一致性问题时的示例。

    但是不幸的是,尽管有很多工作都在尝试降低它的复杂性,但是 Paxos 算法依然十分难以理解。并且,Paxos 自身的算法结构需要进行大幅的修改才能够应用到实际的系统中。这些都导致了工业界和学术界都对 Paxos 算法感到十分头疼。

    和 Paxos 算法进行过努力之后,我们开始寻找一种新的一致性算法,可以为构建实际的系统和教学提供更好的基础。我们的做法是不寻常的,我们的首要目标是可理解性:我们是否可以在实际系统中定义一个一致性算法,并且能够比 Paxos 算法以一种更加容易的方式来学习。此外,我们希望该算法方便系统构建者的直觉的发展。不仅一个算法能够工作很重要,而且能够显而易见的知道为什么能工作也很重要。

    Raft 一致性算法就是这些工作的结果。在设计 Raft 算法的时候,我们使用一些特别的技巧来提升它的可理解性,包括算法分解(Raft 主要被分成了领导人选举,日志复制和安全三个模块)和减少状态机的状态(相对于 Paxos,Raft 减少了非确定性和服务器互相处于非一致性的方式)。一份针对两所大学 43 个学生的研究表明 Raft 明显比 Paxos 算法更加容易理解。在这些学生同时学习了这两种算法之后,和 Paxos 比起来,其中 33 个学生能够回答有关于 Raft 的问题。

    Raft 算法在许多方面和现有的一致性算法都很相似(主要是 Oki 和 Liskov 的 Viewstamped Replication),但是它也有一些独特的特性:

    • 强领导者:和其他一致性算法相比,Raft 使用一种更强的领导能力形式。比如,日志条目只从领导者发送给其他的服务器。这种方式简化了对复制日志的管理并且使得 Raft 算法更加易于理解。
    • 领导选举:Raft 算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。在解决冲突的时候会更加简单快捷。
    • 成员关系调整:Raft 使用一种共同一致的方法来处理集群成员变换的问题,在这种方法下,处于调整过程中的两种不同的配置集群中大多数机器会有重叠,这就使得集群在成员变换的时候依然可以继续工作。

    我们相信,Raft 算法不论出于教学目的还是作为实践项目的基础都是要比 Paxos 或者其他一致性算法要优异的。它比其他算法更加简单,更加容易理解;它的算法描述足以实现一个现实的系统;它有好多开源的实现并且在很多公司里使用;它的安全性已经被证明;它的效率和其他算法比起来也不相上下。

    接下来,这篇论文会介绍以下内容:复制状态机问题(第 2 节),讨论 Paxos 的优点和缺点(第 3 节),讨论我们为了可理解性而采取的方法(第 4 节),阐述 Raft 一致性算法(第 5-8 节),评价 Raft 算法(第 9 节),以及一些相关的工作(第 10 节)。

    2 复制状态机

    一致性算法是从复制状态机的背景下提出的(参考英文原文引用37)。在这种方法中,一组服务器上的状态机产生相同状态的副本,并且在一些机器宕掉的情况下也可以继续运行。复制状态机在分布式系统中被用于解决很多容错的问题。例如,大规模的系统中通常都有一个集群领导者,像 GFS、HDFS 和 RAMCloud,典型应用就是一个独立的的复制状态机去管理领导选举和存储配置信息并且在领导人宕机的情况下也要存活下来。比如 Chubby 和 ZooKeeper。

    图 1

    图 1 :复制状态机的结构。一致性算法管理着来自客户端指令的复制日志。状态机从日志中处理相同顺序的相同指令,所以产生的结果也是相同的。

    复制状态机通常都是基于复制日志实现的,如图 1。每一个服务器存储一个包含一系列指令的日志,并且按照日志的顺序进行执行。每一个日志都按照相同的顺序包含相同的指令,所以每一个服务器都执行相同的指令序列。因为每个状态机都是确定的,每一次执行操作都产生相同的状态和同样的序列。

    保证复制日志相同就是一致性算法的工作了。在一台服务器上,一致性模块接收客户端发送来的指令然后增加到自己的日志中去。它和其他服务器上的一致性模块进行通信来保证每一个服务器上的日志最终都以相同的顺序包含相同的请求,尽管有些服务器会宕机。一旦指令被正确的复制,每一个服务器的状态机按照日志顺序处理他们,然后输出结果被返回给客户端。因此,服务器集群看起来形成一个高可靠的状态机。

    实际系统中使用的一致性算法通常含有以下特性:

    • 安全性保证(绝对不会返回一个错误的结果):在非拜占庭错误情况下,包括网络延迟、分区、丢包、冗余和乱序等错误都可以保证正确。
    • 可用性:集群中只要有大多数的机器可运行并且能够相互通信、和客户端通信,就可以保证可用。因此,一个典型的包含 5 个节点的集群可以容忍两个节点的失败。服务器被停止就认为是失败。他们当有稳定的存储的时候可以从状态中恢复回来并重新加入集群。
    • 不依赖时序来保证一致性:物理时钟错误或者极端的消息延迟只有在最坏情况下才会导致可用性问题。
    • 通常情况下,一条指令可以尽可能快的在集群中大多数节点响应一轮远程过程调用时完成。小部分比较慢的节点不会影响系统整体的性能。

    3 Paxos 算法的问题

    在过去的 10 年里,Leslie Lamport 的 Paxos 算法几乎已经成为一致性的代名词:Paxos 是在课程教学中最经常使用的算法,同时也是大多数一致性算法实现的起点。Paxos 首先定义了一个能够达成单一决策一致的协议,比如单条的复制日志项。我们把这一子集叫做单决策 Paxos。然后通过组合多个 Paxos 协议的实例来促进一系列决策的达成。Paxos 保证安全性和活性,同时也支持集群成员关系的变更。Paxos 的正确性已经被证明,在通常情况下也很高效。

    不幸的是,Paxos 有两个明显的缺点。第一个缺点是 Paxos 算法特别的难以理解。完整的解释是出了名的不透明;通过极大的努力之后,也只有少数人成功理解了这个算法。因此,有了几次用更简单的术语来解释 Paxos 的尝试。尽管这些解释都只关注了单决策的子集问题,但依然很具有挑战性。在 2012 年 NSDI 的会议中的一次调查显示,很少有人对 Paxos 算法感到满意,甚至在经验老道的研究者中也是如此。我们自己也尝试去理解 Paxos;我们一直没能理解 Paxos 直到我们读了很多对 Paxos 的简化解释并且设计了我们自己的算法之后,这一过程花了近一年时间。

    我们假设 Paxos 的不透明性来自它选择单决策问题作为它的基础。单决策 Paxos 是晦涩微妙的,它被划分成了两种没有简单直观解释和无法独立理解的情景。因此,这导致了很难建立起直观的感受为什么单决策 Paxos 算法能够工作。构成多决策 Paxos 增加了很多错综复杂的规则。我们相信,在多决策上达成一致性的问题(一份日志而不是单一的日志记录)能够被分解成其他的方式并且更加直接和明显。

    Paxos算法的第二个问题就是它没有提供一个足够好的用来构建一个现实系统的基础。一个原因是还没有一种被广泛认同的多决策问题的算法。Lamport 的描述基本上都是关于单决策 Paxos 的;他简要描述了实施多决策 Paxos 的方法,但是缺乏很多细节。当然也有很多具体化 Paxos 的尝试,但是他们都互相不一样,和 Paxos 的概述也不同。例如 Chubby 这样的系统实现了一个类似于 Paxos 的算法,但是大多数的细节并没有被公开。

    而且,Paxos 算法的结构也不是十分易于构建实践的系统;单决策分解也会产生其他的结果。例如,独立的选择一组日志条目然后合并成一个序列化的日志并没有带来太多的好处,仅仅增加了不少复杂性。围绕着日志来设计一个系统是更加简单高效的;新日志条目以严格限制的顺序增添到日志中去。另一个问题是,Paxos 使用了一种对等的点对点的方式作为它的核心(尽管它最终提议了一种弱领导人的方法来优化性能)。在只有一个决策会被制定的简化世界中是很有意义的,但是很少有现实的系统使用这种方式。如果有一系列的决策需要被制定,首先选择一个领导人,然后让他去协调所有的决议,会更加简单快速。

    因此,实际的系统中很少有和 Paxos 相似的实践。每一种实现都是从 Paxos 开始研究,然后发现很多实现上的难题,再然后开发了一种和 Paxos 明显不一样的结构。这样是非常费时和容易出错的,并且理解 Paxos 的难度使得这个问题更加糟糕。Paxos 算法在理论上被证明是正确可行的,但是现实的系统和 Paxos 差别是如此的大,以至于这些证明没有什么太大的价值。下面来自 Chubby 实现非常典型:

    在Paxos算法描述和实现现实系统中间有着巨大的鸿沟。最终的系统建立在一种没有经过证明的算法之上。

    由于以上问题,我们认为 Paxos 算法既没有提供一个良好的基础给实践的系统,也没有给教学很好的帮助。基于一致性问题在大规模软件系统中的重要性,我们决定看看我们是否可以设计一个拥有更好特性的替代 Paxos 的一致性算法。Raft算法就是这次实验的结果。

    4 为了可理解性的设计

    设计 Raft 算法我们有几个初衷:它必须提供一个完整的实际的系统实现基础,这样才能大大减少开发者的工作;它必须在任何情况下都是安全的并且在大多数的情况下都是可用的;并且它的大部分操作必须是高效的。但是我们最重要也是最大的挑战是可理解性。它必须保证对于普遍的人群都可以十分容易的去理解。另外,它必须能够让人形成直观的认识,这样系统的构建者才能够在现实中进行必然的扩展。

    在设计 Raft 算法的时候,有很多的点需要我们在各种备选方案中进行选择。在这种情况下,我们评估备选方案基于可理解性原则:解释各个备选方案有多大的难度(例如,Raft 的状态空间有多复杂,是否有微妙的暗示)?对于一个读者而言,完全理解这个方案和暗示是否容易?

    我们意识到对这种可理解性分析上具有高度的主观性;尽管如此,我们使用了两种通常适用的技术来解决这个问题。第一个技术就是众所周知的问题分解:只要有可能,我们就将问题分解成几个相对独立的,可被解决的、可解释的和可理解的子问题。例如,Raft 算法被我们分成领导人选举,日志复制,安全性和角色改变几个部分。

    我们使用的第二个方法是通过减少状态的数量来简化需要考虑的状态空间,使得系统更加连贯并且在可能的时候消除不确定性。特别的,所有的日志是不允许有空洞的,并且 Raft 限制了日志之间变成不一致状态的可能。尽管在大多数情况下我们都试图去消除不确定性,但是也有一些情况下不确定性可以提升可理解性。尤其是,随机化方法增加了不确定性,但是他们有利于减少状态空间数量,通过处理所有可能选择时使用相似的方法。我们使用随机化去简化 Raft 中领导人选举算法。

    5 Raft 一致性算法

    Raft 是一种用来管理章节 2 中描述的复制日志的算法。图 2 为了参考之用,总结这个算法的简略版本,图 3 列举了这个算法的一些关键特性。图中的这些元素会在剩下的章节逐一介绍。

    Raft 通过选举一个高贵的领导人,然后给予他全部的管理复制日志的责任来实现一致性。领导人从客户端接收日志条目,把日志条目复制到其他服务器上,并且当保证安全性的时候告诉其他的服务器应用日志条目到他们的状态机中。拥有一个领导人大大简化了对复制日志的管理。例如,领导人可以决定新的日志条目需要放在日志中的什么位置而不需要和其他服务器商议,并且数据都从领导人流向其他服务器。一个领导人可以宕机,可以和其他服务器失去连接,这时一个新的领导人会被选举出来。

    通过领导人的方式,Raft 将一致性问题分解成了三个相对独立的子问题,这些问题会在接下来的子章节中进行讨论:

    • 领导选举:一个新的领导人需要被选举出来,当现存的领导人宕机的时候(章节 5.2)
    • 日志复制:领导人必须从客户端接收日志然后复制到集群中的其他节点,并且强制要求其他节点的日志保持和自己相同。
    • 安全性:在 Raft 中安全性的关键是在图 3 中展示的状态机安全:如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其他服务器节点不能在同一个日志索引位置应用一个不同的指令。章节 5.4 阐述了 Raft 算法是如何保证这个特性的;这个解决方案涉及到一个额外的选举机制(5.2 节)上的限制。

    在展示一致性算法之后,这一章节会讨论可用性的一些问题和计时在系统的作用。

    状态

    状态 所有服务器上持久存在的
    currentTerm 服务器最后一次知道的任期号(初始化为 0,持续递增)
    votedFor 在当前获得选票的候选人的 Id
    log[] 日志条目集;每一个条目包含一个用户状态机执行的指令,和收到时的任期号
    状态 所有服务器上经常变的
    commitIndex 已知的最大的已经被提交的日志条目的索引值
    lastApplied 最后被应用到状态机的日志条目索引值(初始化为 0,持续递增)
    状态 在领导人里经常改变的 (选举后重新初始化)
    nextIndex[] 对于每一个服务器,需要发送给他的下一个日志条目的索引值(初始化为领导人最后索引值加一)
    matchIndex[] 对于每一个服务器,已经复制给他的日志的最高索引值

    附加日志 RPC

    由领导人负责调用来复制日志指令;也会用作heartbeat

    参数 解释
    term 领导人的任期号
    leaderId 领导人的 Id,以便于跟随者重定向请求
    prevLogIndex 新的日志条目紧随之前的索引值
    prevLogTerm prevLogIndex 条目的任期号
    entries[] 准备存储的日志条目(表示心跳时为空;一次性发送多个是为了提高效率)
    leaderCommit 领导人已经提交的日志的索引值
    返回值 解释
    term 当前的任期号,用于领导人去更新自己
    success 跟随者包含了匹配上 prevLogIndex 和 prevLogTerm 的日志时为真

    接收者实现:

    1. 如果 term < currentTerm 就返回 false (5.1 节)
    2. 如果日志在 prevLogIndex 位置处的日志条目的任期号和 prevLogTerm 不匹配,则返回 false (5.3 节)
    3. 如果已经存在的日志条目和新的产生冲突(索引值相同但是任期号不同),删除这一条和之后所有的 (5.3 节)
    4. 附加日志中尚未存在的任何新条目
    5. 如果 leaderCommit > commitIndex,令 commitIndex 等于 leaderCommit 和 新日志条目索引值中较小的一个

    请求投票 RPC

    由候选人负责调用用来征集选票(5.2 节)

    参数 解释
    term 候选人的任期号
    candidateId 请求选票的候选人的 Id
    lastLogIndex 候选人的最后日志条目的索引值
    lastLogTerm 候选人最后日志条目的任期号
    返回值 解释
    term 当前任期号,以便于候选人去更新自己的任期号
    voteGranted 候选人赢得了此张选票时为真

    接收者实现:

    1. 如果term < currentTerm返回 false (5.2 节)
    2. 如果 votedFor 为空或者为 candidateId,并且候选人的日志至少和自己一样新,那么就投票给他(5.2 节,5.4 节)

    所有服务器需遵守的规则

    所有服务器:

    • 如果commitIndex > lastApplied,那么就 lastApplied 加一,并把log[lastApplied]应用到状态机中(5.3 节)
    • 如果接收到的 RPC 请求或响应中,任期号T > currentTerm,那么就令 currentTerm 等于 T,并切换状态为跟随者(5.1 节)

    跟随者(5.2 节):

    • 响应来自候选人和领导者的请求
    • 如果在超过选举超时时间的情况之前都没有收到领导人的心跳,或者是候选人请求投票的,就自己变成候选人

    候选人(5.2 节):

    • 在转变成候选人后就立即开始选举过程
      • 自增当前的任期号(currentTerm)
      • 给自己投票
      • 重置选举超时计时器
      • 发送请求投票的 RPC 给其他所有服务器
    • 如果接收到大多数服务器的选票,那么就变成领导人
    • 如果接收到来自新的领导人的附加日志 RPC,转变成跟随者
    • 如果选举过程超时,再次发起一轮选举

    领导人:

    • 一旦成为领导人:发送空的附加日志 RPC(心跳)给其他所有的服务器;在一定的空余时间之后不停的重复发送,以阻止跟随者超时(5.2 节)
    • 如果接收到来自客户端的请求:附加条目到本地日志中,在条目被应用到状态机后响应客户端(5.3 节)
    • 如果对于一个跟随者,最后日志条目的索引值大于等于 nextIndex,那么:发送从 nextIndex 开始的所有日志条目:
      • 如果成功:更新相应跟随者的 nextIndex 和 matchIndex
      • 如果因为日志不一致而失败,减少 nextIndex 重试
    • 如果存在一个满足N > commitIndex的 N,并且大多数的matchIndex[i] ≥ N成立,并且log[N].term == currentTerm成立,那么令 commitIndex 等于这个 N (5.3 和 5.4 节)

    图 2

    图 2:一个关于 Raft 一致性算法的浓缩总结(不包括成员变换和日志压缩)。

    特性 解释
    选举安全特性 对于一个给定的任期号,最多只会有一个领导人被选举出来(5.2 节)
    领导人只附加原则 领导人绝对不会删除或者覆盖自己的日志,只会增加(5.3 节)
    日志匹配原则 如果两个日志在相同的索引位置的日志条目的任期号相同,那么我们就认为这个日志从头到这个索引位置之间全部完全相同(5.3 节)
    领导人完全特性 如果某个日志条目在某个任期号中已经被提交,那么这个条目必然出现在更大任期号的所有领导人中(5.4 节)
    状态机安全特性 如果一个领导人已经在给定的索引值位置的日志条目应用到状态机中,那么其他任何的服务器在这个索引位置不会提交一个不同的日志(5.4.3 节)

    图 3

    图 3:Raft 在任何时候都保证以上的各个特性。

    5.1 Raft 基础

    一个 Raft 集群包含若干个服务器节点;通常是 5 个,这允许整个系统容忍 2 个节点的失效。在任何时刻,每一个服务器节点都处于这三个状态之一:领导人、跟随者或者候选人。在通常情况下,系统中只有一个领导人并且其他的节点全部都是跟随者。跟随者都是被动的:他们不会发送任何请求,只是简单的响应来自领导者或者候选人的请求。领导人处理所有的客户端请求(如果一个客户端和跟随者联系,那么跟随者会把请求重定向给领导人)。第三种状态,候选人,是用来在 5.2 节描述的选举新领导人时使用。图 4 展示了这些状态和他们之间的转换关系;这些转换关系会在接下来进行讨论。

    图 4

    图 4:服务器状态。跟随者只响应来自其他服务器的请求。如果跟随者接收不到消息,那么他就会变成候选人并发起一次选举。获得集群中大多数选票的候选人将成为领导者。在一个任期内,领导人一直都会是领导人直到自己宕机了。

    图 5

    图 5:时间被划分成一个个的任期,每个任期开始都是一次选举。在选举成功后,领导人会管理整个集群直到任期结束。有时候选举会失败,那么这个任期就会没有领导人而结束。任期之间的切换可以在不同的时间不同的服务器上观察到。

    Raft 把时间分割成任意长度的任期,如图 5。任期用连续的整数标记。每一段任期从一次选举开始,就像章节 5.2 描述的一样,一个或者多个候选人尝试成为领导者。如果一个候选人赢得选举,然后他就在接下来的任期内充当领导人的职责。在某些情况下,一次选举过程会造成选票的瓜分。在这种情况下,这一任期会以没有领导人结束;一个新的任期(和一次新的选举)会很快重新开始。Raft 保证了在一个给定的任期内,最多只有一个领导者。

    不同的服务器节点可能多次观察到任期之间的转换,但在某些情况下,一个节点也可能观察不到任何一次选举或者整个任期全程。任期在 Raft 算法中充当逻辑时钟的作用,这会允许服务器节点查明一些过期的信息比如陈旧的领导者。每一个节点存储一个当前任期号,这一编号在整个时期内单调的增长。当服务器之间通信的时候会交换当前任期号;如果一个服务器的当前任期号比其他人小,那么他会更新自己的编号到较大的编号值。如果一个候选人或者领导者发现自己的任期号过期了,那么他会立即恢复成跟随者状态。如果一个节点接收到一个包含过期的任期号的请求,那么他会直接拒绝这个请求。

    Raft 算法中服务器节点之间通信使用远程过程调用(RPCs),并且基本的一致性算法只需要两种类型的 RPCs。请求投票(RequestVote) RPCs 由候选人在选举期间发起(章节 5.2),然后附加条目(AppendEntries)RPCs 由领导人发起,用来复制日志和提供一种心跳机制(章节 5.3)。第 7 节为了在服务器之间传输快照增加了第三种 RPC。当服务器没有及时的收到 RPC 的响应时,会进行重试, 并且他们能够并行的发起 RPCs 来获得最佳的性能。

    5.2 领导人选举

    Raft 使用一种心跳机制来触发领导人选举。当服务器程序启动时,他们都是跟随者身份。一个服务器节点继续保持着跟随者状态只要他从领导人或者候选者处接收到有效的 RPCs。领导者周期性的向所有跟随者发送心跳包(即不包含日志项内容的附加日志项 RPCs)来维持自己的权威。如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,那么他就会认为系统中没有可用的领导者,并且发起选举以选出新的领导者。

    要开始一次选举过程,跟随者先要增加自己的当前任期号并且转换到候选人状态。然后他会并行的向集群中的其他服务器节点发送请求投票的 RPCs 来给自己投票。候选人会继续保持着当前状态直到以下三件事情之一发生:(a) 他自己赢得了这次的选举,(b) 其他的服务器成为领导者,(c) 一段时间之后没有任何一个获胜的人。这些结果会分别的在下面的段落里进行讨论。

    当一个候选人从整个集群的大多数服务器节点获得了针对同一个任期号的选票,那么他就赢得了这次选举并成为领导人。每一个服务器最多会对一个任期号投出一张选票,按照先来先服务的原则(注意:5.4 节在投票上增加了一点额外的限制)。要求大多数选票的规则确保了最多只会有一个候选人赢得此次选举(图 3 中的选举安全性)。一旦候选人赢得选举,他就立即成为领导人。然后他会向其他的服务器发送心跳消息来建立自己的权威并且阻止新的领导人的产生。

    在等待投票的时候,候选人可能会从其他的服务器接收到声明它是领导人的附加日志项 RPC。如果这个领导人的任期号(包含在此次的 RPC中)不小于候选人当前的任期号,那么候选人会承认领导人合法并回到跟随者状态。 如果此次 RPC 中的任期号比自己小,那么候选人就会拒绝这次的 RPC 并且继续保持候选人状态。

    第三种可能的结果是候选人既没有赢得选举也没有输:如果有多个跟随者同时成为候选人,那么选票可能会被瓜分以至于没有候选人可以赢得大多数人的支持。当这种情况发生的时候,每一个候选人都会超时,然后通过增加当前任期号来开始一轮新的选举。然而,没有其他机制的话,选票可能会被无限的重复瓜分。

    Raft 算法使用随机选举超时时间的方法来确保很少会发生选票瓜分的情况,就算发生也能很快的解决。为了阻止选票起初就被瓜分,选举超时时间是从一个固定的区间(例如 150-300 毫秒)随机选择。这样可以把服务器都分散开以至于在大多数情况下只有一个服务器会选举超时;然后他赢得选举并在其他服务器超时之前发送心跳包。同样的机制被用在选票瓜分的情况下。每一个候选人在开始一次选举的时候会重置一个随机的选举超时时间,然后在超时时间内等待投票的结果;这样减少了在新的选举中另外的选票瓜分的可能性。9.3 节展示了这种方案能够快速的选出一个领导人。

    领导人选举这个例子,体现了可理解性原则是如何指导我们进行方案设计的。起初我们计划使用一种排名系统:每一个候选人都被赋予一个唯一的排名,供候选人之间竞争时进行选择。如果一个候选人发现另一个候选人拥有更高的排名,那么他就会回到跟随者状态,这样高排名的候选人能够更加容易的赢得下一次选举。但是我们发现这种方法在可用性方面会有一点问题(如果高排名的服务器宕机了,那么低排名的服务器可能会超时并再次进入候选人状态。而且如果这个行为发生得足够快,则可能会导致整个选举过程都被重置掉)。我们针对算法进行了多次调整,但是每次调整之后都会有新的问题。最终我们认为随机重试的方法是更加明显和易于理解的。

    5.3 日志复制

    一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。领导人把这条指令作为一条新的日志条目附加到日志中去,然后并行的发起附加条目 RPCs 给其他的服务器,让他们复制这条日志条目。当这条日志条目被安全的复制(下面会介绍),领导人会应用这条日志条目到它的状态机中然后把执行的结果返回给客户端。如果跟随者崩溃或者运行缓慢,再或者网络丢包,领导人会不断的重复尝试附加日志条目 RPCs (尽管已经回复了客户端)直到所有的跟随者都最终存储了所有的日志条目。

    图 6

    图 6:日志由有序序号标记的条目组成。每个条目都包含创建时的任期号(图中框中的数字),和一个状态机需要执行的指令。一个条目当可以安全的被应用到状态机中去的时候,就认为是可以提交了。

    日志以图 6 展示的方式组织。每一个日志条目存储一条状态机指令和从领导人收到这条指令时的任期号。日志中的任期号用来检查是否出现不一致的情况,同时也用来保证图 3 中的某些性质。每一条日志条目同时也都有一个整数索引值来表明它在日志中的位置。

    领导人来决定什么时候把日志条目应用到状态机中是安全的;这种日志条目被称为已提交。Raft 算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。在领导人将创建的日志条目复制到大多数的服务器上的时候,日志条目就会被提交(例如在图 6 中的条目 7)。同时,领导人的日志中之前的所有日志条目也都会被提交,包括由其他领导人创建的条目。5.4 节会讨论某些当在领导人改变之后应用这条规则的隐晦内容,同时他也展示了这种提交的定义是安全的。领导人跟踪了最大的将会被提交的日志项的索引,并且索引值会被包含在未来的所有附加日志 RPCs (包括心跳包),这样其他的服务器才能最终知道领导人的提交位置。一旦跟随者知道一条日志条目已经被提交,那么他也会将这个日志条目应用到本地的状态机中(按照日志的顺序)。

    我们设计了 Raft 的日志机制来维护一个不同服务器的日志之间的高层次的一致性。这么做不仅简化了系统的行为也使得更加可预计,同时他也是安全性保证的一个重要组件。Raft 维护着以下的特性,这些同时也组成了图 3 中的日志匹配特性:

    • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
    • 如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。

    第一个特性来自这样的一个事实,领导人最多在一个任期里在指定的一个日志索引位置创建一条日志条目,同时日志条目在日志中的位置也从来不会改变。第二个特性由附加日志 RPC 的一个简单的一致性检查所保证。在发送附加日志 RPC 的时候,领导人会把新的日志条目紧接着之前的条目的索引位置和任期号包含在里面。如果跟随者在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝接收新的日志条目。一致性检查就像一个归纳步骤:一开始空的日志状态肯定是满足日志匹配特性的,然后一致性检查保护了日志匹配特性当日志扩展的时候。因此,每当附加日志 RPC 返回成功时,领导人就知道跟随者的日志一定是和自己相同的了。

    在正常的操作中,领导人和跟随者的日志保持一致性,所以附加日志 RPC 的一致性检查从来不会失败。然而,领导人崩溃的情况会使得日志处于不一致的状态(老的领导人可能还没有完全复制所有的日志条目)。这种不一致问题会在领导人和跟随者的一系列崩溃下加剧。图 7 展示了跟随者的日志可能和新的领导人不同的方式。跟随者可能会丢失一些在新的领导人中有的日志条目,他也可能拥有一些领导人没有的日志条目,或者两者都发生。丢失或者多出日志条目可能会持续多个任期。

    图 7

    图 7:当一个领导人成功当选时,跟随者可能是任何情况(a-f)。每一个盒子表示是一个日志条目;里面的数字表示任期号。跟随者可能会缺少一些日志条目(a-b),可能会有一些未被提交的日志条目(c-d),或者两种情况都存在(e-f)。例如,场景 f 可能会这样发生,某服务器在任期 2 的时候是领导人,已附加了一些日志条目到自己的日志中,但在提交之前就崩溃了;很快这个机器就被重启了,在任期 3 重新被选为领导人,并且又增加了一些日志条目到自己的日志中;在任期 2 和任期 3 的日志被提交之前,这个服务器又宕机了,并且在接下来的几个任期里一直处于宕机状态。

    在 Raft 算法中,领导人处理不一致是通过强制跟随者直接复制自己的日志来解决了。这意味着在跟随者中的冲突的日志条目会被领导人的日志覆盖。5.4 节会阐述如何通过增加一些限制来使得这样的操作是安全的。

    要使得跟随者的日志进入和自己一致的状态,领导人必须找到最后两者达成一致的地方,然后删除从那个点之后的所有日志条目,发送自己的日志给跟随者。所有的这些操作都在进行附加日志 RPCs 的一致性检查时完成。领导人针对每一个跟随者维护了一个 nextIndex,这表示下一个需要发送给跟随者的日志条目的索引地址。当一个领导人刚获得权力的时候,他初始化所有的 nextIndex 值为自己的最后一条日志的index加1(图 7 中的 11)。如果一个跟随者的日志和领导人不一致,那么在下一次的附加日志 RPC 时的一致性检查就会失败。在被跟随者拒绝之后,领导人就会减小 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得领导人和跟随者的日志达成一致。当这种情况发生,附加日志 RPC 就会成功,这时就会把跟随者冲突的日志条目全部删除并且加上领导人的日志。一旦附加日志 RPC 成功,那么跟随者的日志就会和领导人保持一致,并且在接下来的任期里一直继续保持。

    如果需要的话,算法可以通过减少被拒绝的附加日志 RPCs 的次数来优化。例如,当附加日志 RPC 的请求被拒绝的时候,跟随者可以包含冲突的条目的任期号和自己存储的那个任期的最早的索引地址。借助这些信息,领导人可以减小 nextIndex 越过所有那个任期冲突的所有日志条目;这样就变成每个任期需要一次附加条目 RPC 而不是每个条目一次。在实践中,我们十分怀疑这种优化是否是必要的,因为失败是很少发生的并且也不大可能会有这么多不一致的日志。

    通过这种机制,领导人在获得权力的时候就不需要任何特殊的操作来恢复一致性。他只需要进行正常的操作,然后日志就能自动的在回复附加日志 RPC 的一致性检查失败的时候自动趋于一致。领导人从来不会覆盖或者删除自己的日志(图 3 的领导人只附加特性)。

    日志复制机制展示出了第 2 节中形容的一致性特性:Raft 能够接受,复制并应用新的日志条目只要大部分的机器是工作的;在通常的情况下,新的日志条目可以在一次 RPC 中被复制给集群中的大多数机器;并且单个的缓慢的跟随者不会影响整体的性能。

    5.4 安全性

    前面的章节里描述了 Raft 算法是如何选举和复制日志的。然而,到目前为止描述的机制并不能充分的保证每一个状态机会按照相同的顺序执行相同的指令。例如,一个跟随者可能会进入不可用状态同时领导人已经提交了若干的日志条目,然后这个跟随者可能会被选举为领导人并且覆盖这些日志条目;因此,不同的状态机可能会执行不同的指令序列。

    这一节通过在领导选举的时候增加一些限制来完善 Raft 算法。这一限制保证了任何的领导人对于给定的任期号,都拥有了之前任期的所有被提交的日志条目(图 3 中的领导人完整特性)。增加这一选举时的限制,我们对于提交时的规则也更加清晰。最终,我们将展示对于领导人完整特性的简要证明,并且说明领导人是如何领导复制状态机的做出正确行为的。

    5.4.1 选举限制

    在任何基于领导人的一致性算法中,领导人都必须存储所有已经提交的日志条目。在某些一致性算法中,例如 Viewstamped Replication,某个节点即使是一开始并没有包含所有已经提交的日志条目,它也能被选为领导者。这些算法都包含一些额外的机制来识别丢失的日志条目并把他们传送给新的领导人,要么是在选举阶段要么在之后很快进行。不幸的是,这种方法会导致相当大的额外的机制和复杂性。Raft 使用了一种更加简单的方法,它可以保证所有之前的任期号中已经提交的日志条目在选举的时候都会出现在新的领导人中,不需要传送这些日志条目给领导人。这意味着日志条目的传送是单向的,只从领导人传给跟随者,并且领导人从不会覆盖自身本地日志中已经存在的条目。

    Raft 使用投票的方式来阻止一个候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。候选人为了赢得选举必须联系集群中的大部分节点,这意味着每一个已经提交的日志条目在这些服务器节点中肯定存在于至少一个节点上。如果候选人的日志至少和大多数的服务器节点一样新(这个新的定义会在下面讨论),那么他一定持有了所有已经提交的日志条目。请求投票 RPC 实现了这样的限制: RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求。

    Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。

    5.4.2 提交之前任期内的日志条目

    如同 5.3 节介绍的那样,领导人知道一条当前任期内的日志记录是可以被提交的,只要它被存储到了大多数的服务器上。如果一个领导人在提交日志条目之前崩溃了,未来后续的领导人会继续尝试复制这条日志记录。然而,一个领导人不能断定一个之前任期里的日志条目被保存到大多数服务器上的时候就一定已经提交了。图 8 展示了一种情况,一条已经被存储到大多数节点上的老日志条目,也依然有可能会被未来的领导人覆盖掉。

    图 8

    图 8:如图的时间序列展示了为什么领导人无法决定对老任期号的日志条目进行提交。在 (a) 中,S1 是领导者,部分的复制了索引位置 2 的日志条目。在 (b) 中,S1 崩溃了,然后 S5 在任期 3 里通过 S3、S4 和自己的选票赢得选举,然后从客户端接收了一条不一样的日志条目放在了索引 2 处。然后到 (c),S5 又崩溃了;S1 重新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,但是还没有被提交。如果 S1 在 (d) 中又崩溃了,S5 可以重新被选举成功(通过来自 S2,S3 和 S4 的选票),然后覆盖了他们在索引 2 处的日志。反之,如果在崩溃之前,S1 把自己主导的新任期里产生的日志条目复制到了大多数机器上,就如 (e) 中那样,那么在后面任期里面这些新的日志条目就会被提交(因为S5 就不可能选举成功)。 这样在同一时刻就同时保证了,之前的所有老的日志条目就会被提交。

    为了消除图 8 里描述的情况,Raft 永远不会通过计算副本数目的方式去提交一个之前任期内的日志条目。只有领导人当前任期里的日志条目通过计算副本数目可以被提交;一旦当前任期的日志条目以这种方式被提交,那么由于日志匹配特性,之前的日志条目也都会被间接的提交。在某些情况下,领导人可以安全的知道一个老的日志条目是否已经被提交(例如,该条目是否存储到所有服务器上),但是 Raft 为了简化问题使用一种更加保守的方法。

    当领导人复制之前任期里的日志时,Raft 会为所有日志保留原始的任期号, 这在提交规则上产生了额外的复杂性。在其他的一致性算法中,如果一个新的领导人要重新复制之前的任期里的日志时,它必须使用当前新的任期号。Raft 使用的方法更加容易辨别出日志,因为它可以随着时间和日志的变化对日志维护着同一个任期编号。另外,和其他的算法相比,Raft 中的新领导人只需要发送更少日志条目(其他算法中必须在他们被提交之前发送更多的冗余日志条目来为他们重新编号)。

    5.4.3 安全性论证

    在给定了完整的 Raft 算法之后,我们现在可以更加精确的讨论领导人完整性特性(这一讨论基于 9.2 节的安全性证明)。我们假设领导人完全性特性是不存在的,然后我们推出矛盾来。假设任期 T 的领导人(领导人 T)在任期内提交了一条日志条目,但是这条日志条目没有被存储到未来某个任期的领导人的日志中。设大于 T 的最小任期 U 的领导人 U 没有这条日志条目。

    图 9

    图 9:如果 S1 (任期 T 的领导者)提交了一条新的日志在它的任期里,然后 S5 在之后的任期 U 里被选举为领导人,然后至少会有一个机器,如 S3,既拥有来自 S1 的日志,也给 S5 投票了。

    1. 在领导人 U 选举的时候一定没有那条被提交的日志条目(领导人从不会删除或者覆盖任何条目)。
    2. 领导人 T 复制这条日志条目给集群中的大多数节点,同时,领导人U 从集群中的大多数节点赢得了选票。因此,至少有一个节点(投票者、选民)同时接受了来自领导人T 的日志条目,并且给领导人U 投票了,如图 9。这个投票者是产生这个矛盾的关键。
    3. 这个投票者必须在给领导人 U 投票之前先接受了从领导人 T 发来的已经被提交的日志条目;否则他就会拒绝来自领导人 T 的附加日志请求(因为此时他的任期号会比 T 大)。
    4. 投票者在给领导人 U 投票时依然保存有这条日志条目,因为任何中间的领导人都包含该日志条目(根据上述的假设),领导人从不会删除条目,并且跟随者只有在和领导人冲突的时候才会删除条目。
    5. 投票者把自己选票投给领导人 U 时,领导人 U 的日志必须和投票者自己一样新。这就导致了两者矛盾之一。
    6. 首先,如果投票者和领导人 U 的最后一条日志的任期号相同,那么领导人 U 的日志至少和投票者一样长,所以领导人 U 的日志一定包含所有投票者的日志。这是另一处矛盾,因为投票者包含了那条已经被提交的日志条目,但是在上述的假设里,领导人 U 是不包含的。
    7. 除此之外,领导人 U 的最后一条日志的任期号就必须比投票人大了。此外,他也比 T 大,因为投票人的最后一条日志的任期号至少和 T 一样大(他包含了来自任期 T 的已提交的日志)。创建了领导人 U 最后一条日志的之前领导人一定已经包含了那条被提交的日志(根据上述假设,领导人 U 是第一个不包含该日志条目的领导人)。所以,根据日志匹配特性,领导人 U 一定也包含那条被提交的日志,这里产生矛盾。
    8. 这里完成了矛盾。因此,所有比 T 大的领导人一定包含了所有来自 T 的已经被提交的日志。
    9. 日志匹配原则保证了未来的领导人也同时会包含被间接提交的条目,例如图 8 (d) 中的索引 2。

    通过领导人完全特性,我们就能证明图 3 中的状态机安全特性,即如果服务器已经在某个给定的索引值应用了日志条目到自己的状态机里,那么其他的服务器不会应用一个不一样的日志到同一个索引值上。在一个服务器应用一条日志条目到他自己的状态机中时,他的日志必须和领导人的日志,在该条目和之前的条目上相同,并且已经被提交。现在我们来考虑在任何一个服务器应用一个指定索引位置的日志的最小任期;日志完全特性保证拥有更高任期号的领导人会存储相同的日志条目,所以之后的任期里应用某个索引位置的日志条目也会是相同的值。因此,状态机安全特性是成立的。

    最后,Raft 要求服务器按照日志中索引位置顺序应用日志条目。和状态机安全特性结合起来看,这就意味着所有的服务器会应用相同的日志序列集到自己的状态机中,并且是按照相同的顺序。

    5.5 跟随者和候选人崩溃

    到目前为止,我们都只关注了领导人崩溃的情况。跟随者和候选人崩溃后的处理方式比领导人要简单的多,并且他们的处理方式是相同的。如果跟随者或者候选人崩溃了,那么后续发送给他们的 RPCs 都会失败。Raft 中处理这种失败就是简单的通过无限的重试;如果崩溃的机器重启了,那么这些 RPC 就会完整的成功。如果一个服务器在完成了一个 RPC,但是还没有响应的时候崩溃了,那么在他重新启动之后就会再次收到同样的请求。Raft 的 RPCs 都是幂等的,所以这样重试不会造成任何问题。例如一个跟随者如果收到附加日志请求但是他已经包含了这一日志,那么他就会直接忽略这个新的请求。

    5.6 时间和可用性

    Raft 的要求之一就是安全性不能依赖时间:整个系统不能因为某些事件运行的比预期快一点或者慢一点就产生了错误的结果。但是,可用性(系统可以及时的响应客户端)不可避免的要依赖于时间。例如,如果消息交换比服务器故障间隔时间长,候选人将没有足够长的时间来赢得选举;没有一个稳定的领导人,Raft 将无法工作。

    领导人选举是 Raft 中对时间要求最为关键的方面。Raft 可以选举并维持一个稳定的领导人,只要系统满足下面的时间要求:

    广播时间(broadcastTime) << 选举超时时间(electionTimeout) << 平均故障间隔时间(MTBF)

    在这个不等式中,广播时间指的是从一个服务器并行的发送 RPCs 给集群中的其他服务器并接收响应的平均时间;选举超时时间就是在 5.2 节中介绍的选举的超时时间限制;然后平均故障间隔时间就是对于一台服务器而言,两次故障之间的平均时间。广播时间必须比选举超时时间小一个量级,这样领导人才能够发送稳定的心跳消息来阻止跟随者开始进入选举状态;通过随机化选举超时时间的方法,这个不等式也使得选票瓜分的情况变得不可能。选举超时时间应该要比平均故障间隔时间小上几个数量级,这样整个系统才能稳定的运行。当领导人崩溃后,整个系统会大约相当于选举超时的时间里不可用;我们希望这种情况在整个系统的运行中很少出现。

    广播时间和平均故障间隔时间是由系统决定的,但是选举超时时间是我们自己选择的。Raft 的 RPCs 需要接收方将信息持久化的保存到稳定存储中去,所以广播时间大约是 0.5 毫秒到 20 毫秒,取决于存储的技术。因此,选举超时时间可能需要在 10 毫秒到 500 毫秒之间。大多数的服务器的平均故障间隔时间都在几个月甚至更长,很容易满足时间的需求。

    6 集群成员变化

    到目前为止,我们都假设集群的配置(加入到一致性算法的服务器集合)是固定不变的。但是在实践中,偶尔是会改变集群的配置的,例如替换那些宕机的机器或者改变复制级别。尽管可以通过暂停整个集群,更新所有配置,然后重启整个集群的方式来实现,但是在更改的时候集群会不可用。另外,如果存在手工操作步骤,那么就会有操作失误的风险。为了避免这样的问题,我们决定自动化配置改变并且将其纳入到 Raft 一致性算法中来。

    为了让配置修改机制能够安全,那么在转换的过程中不能够存在任何时间点使得两个领导人同时被选举成功在同一个任期里。不幸的是,任何服务器直接从旧的配置直接转换到新的配置的方案都是不安全的。一次性自动的转换所有服务器是不可能的,所以在转换期间整个集群存在划分成两个独立的大多数群体的可能性(见图 10)。

    图 10

    图 10:直接从一种配置转到新的配置是十分不安全的,因为各个机器可能在任何的时候进行转换。在这个例子中,集群配额从 3 台机器变成了 5 台。不幸的是,存在这样的一个时间点,两个不同的领导人在同一个任期里都可以被选举成功。一个是通过旧的配置,一个通过新的配置。

    为了保证安全性,配置更改必须使用两阶段方法。目前有很多种两阶段的实现。例如,有些系统在第一阶段停掉旧的配置所以集群就不能处理客户端请求;然后在第二阶段在启用新的配置。在 Raft 中,集群先切换到一个过渡的配置,我们称之为共同一致;一旦共同一致已经被提交了,那么系统就切换到新的配置上。共同一致是老配置和新配置的结合:

    • 日志条目被复制给集群中新、老配置的所有服务器。
    • 新、旧配置的服务器都可以成为领导人。
    • 达成一致(针对选举和提交)需要分别在两种配置上获得大多数的支持。

    共同一致允许独立的服务器在不影响安全性的前提下,在不同的时间进行配置转换过程。此外,共同一致可以让集群在配置转换的过程人依然响应客户端的请求。

    集群配置在复制日志中以特殊的日志条目来存储和通信;图 11 展示了配置转换的过程。当一个领导人接收到一个改变配置从 C-old 到 C-new 的请求,他会为了共同一致存储配置(图中的 C-old,new),以前面描述的日志条目和副本的形式。一旦一个服务器将新的配置日志条目增加到它的日志中,他就会用这个配置来做出未来所有的决定(服务器总是使用最新的配置,无论他是否已经被提交)。这意味着领导人要使用 C-old,new 的规则来决定日志条目 C-old,new 什么时候需要被提交。如果领导人崩溃了,被选出来的新领导人可能是使用 C-old 配置也可能是 C-old,new 配置,这取决于赢得选举的候选人是否已经接收到了 C-old,new 配置。在任何情况下, C-new 配置在这一时期都不会单方面的做出决定。

    一旦 C-old,new 被提交,那么无论是 C-old 还是 C-new,在没有经过他人批准的情况下都不可能做出决定,并且领导人完全特性保证了只有拥有 C-old,new 日志条目的服务器才有可能被选举为领导人。这个时候,领导人创建一条关于 C-new 配置的日志条目并复制给集群就是安全的了。再者,每个服务器在见到新的配置的时候就会立即生效。当新的配置在 C-new 的规则下被提交,旧的配置就变得无关紧要,同时不使用新的配置的服务器就可以被关闭了。如图 11,C-old 和 C-new 没有任何机会同时做出单方面的决定;这保证了安全性。

    图 11

    图 11:一个配置切换的时间线。虚线表示已经被创建但是还没有被提交的条目,实线表示最后被提交的日志条目。领导人首先创建了 C-old,new 的配置条目在自己的日志中,并提交到 C-old,new 中(C-old 的大多数和 C-new 的大多数)。然后他创建 C-new 条目并提交到 C-new 中的大多数。这样就不存在 C-new 和 C-old 可以同时做出决定的时间点。

    在关于重新配置还有三个问题需要提出。第一个问题是,新的服务器可能初始化没有存储任何的日志条目。当这些服务器以这种状态加入到集群中,那么他们需要一段时间来更新追赶,这时还不能提交新的日志条目。为了避免这种可用性的间隔时间,Raft 在配置更新的时候使用了一种额外的阶段,在这个阶段,新的服务器以没有投票权身份加入到集群中来(领导人复制日志给他们,但是不考虑他们是大多数)。一旦新的服务器追赶上了集群中的其他机器,重新配置可以像上面描述的一样处理。

    第二个问题是,集群的领导人可能不是新配置的一员。在这种情况下,领导人就会在提交了 C-new 日志之后退位(回到跟随者状态)。这意味着有这样的一段时间,领导人管理着集群,但是不包括他自己;他复制日志但是不把他自己算作是大多数之一。当 C-new 被提交时,会发生领导人过渡,因为这时是最早新的配置可以独立工作的时间点(将总是能够在 C-new 配置下选出新的领导人)。在此之前,可能只能从 C-old 中选出领导人。

    第三个问题是,移除不在 C-new 中的服务器可能会扰乱集群。这些服务器将不会再接收到心跳,所以当选举超时,他们就会进行新的选举过程。他们会发送拥有新的任期号的请求投票 RPCs,这样会导致当前的领导人回退成跟随者状态。新的领导人最终会被选出来,但是被移除的服务器将会再次超时,然后这个过程会再次重复,导致整体可用性大幅降低。

    为了避免这个问题,当服务器确认当前领导人存在时,服务器会忽略请求投票 RPCs。特别的,当服务器在当前最小选举超时时间内收到一个请求投票 RPC,他不会更新当前的任期号或者投出选票。这不会影响正常的选举,每个服务器在开始一次选举之前,至少等待一个最小选举超时时间。然而,这有利于避免被移除的服务器扰乱:如果领导人能够发送心跳给集群,那么他就不会被更大的任期号废黜。

    7 日志压缩

    Raft 的日志在正常操作中不断的增长,但是在实际的系统中,日志不能无限制的增长。随着日志不断增长,他会占用越来越多的空间,花费越来越多的时间来重置。如果没有一定的机制去清除日志里积累的陈旧的信息,那么会带来可用性问题。

    快照是最简单的压缩方法。在快照系统中,整个系统的状态都以快照的形式写入到稳定的持久化存储中,然后到那个时间点之前的日志全部丢弃。快照技术被使用在 Chubby 和 ZooKeeper 中,接下来的章节会介绍 Raft 中的快照技术。

    增量压缩的方法,例如日志清理或者日志结构合并树,都是可行的。这些方法每次只对一小部分数据进行操作,这样就分散了压缩的负载压力。首先,他们先选择一个已经积累的大量已经被删除或者被覆盖对象的区域,然后重写那个区域还活跃的对象,之后释放那个区域。和简单操作整个数据集合的快照相比,需要增加复杂的机制来实现。状态机可以实现 LSM tree 使用和快照相同的接口,但是日志清除方法就需要修改 Raft 了。

    图 12

    图 12:一个服务器用新的快照替换了从 1 到 5 的条目,快照值存储了当前的状态。快照中包含了最后的索引位置和任期号。

    图 12 展示了 Raft 中快照的基础思想。每个服务器独立的创建快照,只包括已经被提交的日志。主要的工作包括将状态机的状态写入到快照中。Raft 也包含一些少量的元数据到快照中:最后被包含索引指的是被快照取代的最后的条目在日志中的索引值(状态机最后应用的日志),最后被包含的任期指的是该条目的任期号。保留这些数据是为了支持快照后紧接着的第一个条目的附加日志请求时的一致性检查,因为这个条目需要前一日志条目的索引值和任期号。为了支持集群成员更新(第 6 节),快照中也将最后的一次配置作为最后一个条目存下来。一旦服务器完成一次快照,他就可以删除最后索引位置之前的所有日志和快照了。

    尽管通常服务器都是独立的创建快照,但是领导人必须偶尔的发送快照给一些落后的跟随者。这通常发生在当领导人已经丢弃了下一条需要发送给跟随者的日志条目的时候。幸运的是这种情况不是常规操作:一个与领导人保持同步的跟随者通常都会有这个条目。然而一个运行非常缓慢的跟随者或者新加入集群的服务器(第 6 节)将不会有这个条目。这时让这个跟随者更新到最新的状态的方式就是通过网络把快照发送给他们。

    安装快照 RPC

    由领导人调用以将快照的分块发送给跟随者。领导者总是按顺序发送分块。

    参数 解释
    term 领导人的任期号
    leaderId 领导人的 Id,以便于跟随者重定向请求
    lastIncludedIndex 快照中包含的最后日志条目的索引值
    lastIncludedTerm 快照中包含的最后日志条目的任期号
    offset 分块在快照中的字节偏移量
    data[] 原始数据
    done 如果这是最后一个分块则为 true
    结果 解释
    term 当前任期号(currentTerm),便于领导人更新自己

    接收者实现

    1. 如果term < currentTerm就立即回复
    2. 如果是第一个分块(offset 为 0)就创建一个新的快照
    3. 在指定偏移量写入数据
    4. 如果 done 是 false,则继续等待更多的数据
    5. 保存快照文件,丢弃具有较小索引的任何现有或部分快照
    6. 如果现存的日志条目与快照中最后包含的日志条目具有相同的索引值和任期号,则保留其后的日志条目并进行回复
    7. 丢弃整个日志
    8. 使用快照重置状态机(并加载快照的集群配置)

    图 13

    图 13:一个关于安装快照的简要概述。为了便于传输,快照都是被分成分块的;每个分块都给了跟随者生命的迹象,所以跟随者可以重置选举超时计时器。

    在这种情况下领导人使用一种叫做安装快照的新的 RPC 来发送快照给太落后的跟随者;见图 13。当跟随者通过这种 RPC 接收到快照时,他必须自己决定对于已经存在的日志该如何处理。通常快照会包含没有在接收者日志中存在的信息。在这种情况下,跟随者丢弃其整个日志;它全部被快照取代,并且可能包含与快照冲突的未提交条目。如果接收到的快照是自己日志的前面部分(由于网络重传或者错误),那么被快照包含的条目将会被全部删除,但是快照后面的条目仍然有效,必须保留。

    这种快照的方式背离了 Raft 的强领导人原则,因为跟随者可以在不知道领导人情况下创建快照。但是我们认为这种背离是值得的。领导人的存在,是为了解决在达成一致性的时候的冲突,但是在创建快照的时候,一致性已经达成,这时不存在冲突了,所以没有领导人也是可以的。数据依然是从领导人传给跟随者,只是跟随者可以重新组织他们的数据了。

    我们考虑过一种替代的基于领导人的快照方案,即只有领导人创建快照,然后发送给所有的跟随者。但是这样做有两个缺点。第一,发送快照会浪费网络带宽并且延缓了快照处理的时间。每个跟随者都已经拥有了所有产生快照需要的信息,而且很显然,自己从本地的状态中创建快照比通过网络接收别人发来的要经济。第二,领导人的实现会更加复杂。例如,领导人需要发送快照的同时并行的将新的日志条目发送给跟随者,这样才不会阻塞新的客户端请求。

    还有两个问题影响了快照的性能。首先,服务器必须决定什么时候应该创建快照。如果快照创建的过于频繁,那么就会浪费大量的磁盘带宽和其他资源;如果创建快照频率太低,他就要承受耗尽存储容量的风险,同时也增加了从日志重建的时间。一个简单的策略就是当日志大小达到一个固定大小的时候就创建一次快照。如果这个阈值设置的显著大于期望的快照的大小,那么快照对磁盘压力的影响就会很小了。

    第二个影响性能的问题就是写入快照需要花费显著的一段时间,并且我们还不希望影响到正常操作。解决方案是通过写时复制的技术,这样新的更新就可以被接收而不影响到快照。例如,具有函数式数据结构的状态机天然支持这样的功能。另外,操作系统的写时复制技术的支持(如 Linux 上的 fork)可以被用来创建完整的状态机的内存快照(我们的实现就是这样的)。

    8 客户端交互

    这一节将介绍客户端是如何和 Raft 进行交互的,包括客户端如何发现领导人和 Raft 是如何支持线性化语义的。这些问题对于所有基于一致性的系统都存在,并且 Raft 的解决方案和其他的也差不多。

    Raft 中的客户端发送所有请求给领导人。当客户端启动的时候,他会随机挑选一个服务器进行通信。如果客户端第一次挑选的服务器不是领导人,那么那个服务器会拒绝客户端的请求并且提供他最近接收到的领导人的信息(附加条目请求包含了领导人的网络地址)。如果领导人已经崩溃了,那么客户端的请求就会超时;客户端之后会再次重试随机挑选服务器的过程。

    我们 Raft 的目标是要实现线性化语义(每一次操作立即执行,只执行一次,在他调用和收到回复之间)。但是,如上述,Raft 是可以执行同一条命令多次的:例如,如果领导人在提交了这条日志之后,但是在响应客户端之前崩溃了,那么客户端会和新的领导人重试这条指令,导致这条命令就被再次执行了。解决方案就是客户端对于每一条指令都赋予一个唯一的序列号。然后,状态机跟踪每条指令最新的序列号和相应的响应。如果接收到一条指令,它的序列号已经被执行了,那么就立即返回结果,而不重新执行指令。

    只读的操作可以直接处理而不需要记录日志。但是,在不增加任何限制的情况下,这么做可能会冒着返回脏数据的风险,因为领导人响应客户端请求时可能已经被新的领导人作废了,但是他还不知道。线性化的读操作必须不能返回脏数据,Raft 需要使用两个额外的措施在不使用日志的情况下保证这一点。首先,领导人必须有关于被提交日志的最新信息。领导人完全特性保证了领导人一定拥有所有已经被提交的日志条目,但是在他任期开始的时候,他可能不知道那些是已经被提交的。为了知道这些信息,他需要在他的任期里提交一条日志条目。Raft 中通过领导人在任期开始的时候提交一个空白的没有任何操作的日志条目到日志中去来实现。第二,领导人在处理只读的请求之前必须检查自己是否已经被废黜了(他自己的信息已经变脏了如果一个更新的领导人被选举出来)。Raft 中通过让领导人在响应只读请求之前,先和集群中的大多数节点交换一次心跳信息来处理这个问题。可选的,领导人可以依赖心跳机制来实现一种租约的机制,但是这种方法依赖时间来保证安全性(假设时间误差是有界的)。

    9 算法实现和评估

    我们已经为 RAMCloud 实现了 Raft 算法作为存储配置信息的复制状态机的一部分,并且帮助 RAMCloud 协调故障转移。这个 Raft 实现包含大约 2000 行 C++ 代码,其中不包括测试、注释和空行。这些代码是开源的。同时也有大约 25 个其他独立的第三方的基于这篇论文草稿的开源实现,针对不同的开发场景。同时,很多公司已经部署了基于 Raft 的系统。

    这一节会从三个方面来评估 Raft 算法:可理解性、正确性和性能。

    9.1 可理解性

    为了和 Paxos 比较 Raft 算法的可理解能力,我们针对高层次的本科生和研究生,在斯坦福大学的高级操作系统课程和加州大学伯克利分校的分布式计算课程上,进行了一次学习的实验。我们分别拍了针对 Raft 和 Paxos 的视频课程,并准备了相应的小测验。Raft 的视频讲课覆盖了这篇论文的所有内容除了日志压缩;Paxos 讲课包含了足够的资料来创建一个等价的复制状态机,包括单决策 Paxos,多决策 Paxos,重新配置和一些实际系统需要的性能优化(例如领导人选举)。小测验测试一些对算法的基本理解和解释一些边角的示例。每个学生都是看完第一个视频,回答相应的测试,再看第二个视频,回答相应的测试。大约有一半的学生先进行 Paxos 部分,然后另一半先进行 Raft 部分,这是为了说明两者从第一部分的算法学习中获得的表现和经验的差异。我们计算参加人员的每一个小测验的得分来看参与者是否在 Raft 算法上更加容易理解。

    我们尽可能的使得 Paxos 和 Raft 的比较更加公平。这个实验偏爱 Paxos 表现在两个方面:43 个参加者中有 15 个人在之前有一些 Paxos 的经验,并且 Paxos 的视频要长 14%。如表格 1 总结的那样,我们采取了一些措施来减轻这种潜在的偏见。我们所有的材料都可供审查。

    关心 缓和偏见采取的手段 可供查看的材料
    相同的讲课质量 两者使用同一个讲师。Paxos 使用的是现在很多大学里经常使用的。Paxos 会长 14%。 视频
    相同的测验难度 问题以难度分组,在两个测验里成对出现。 小测验
    公平评分 使用评价量规。随机顺序打分,两个测验交替进行。 评价量规(rubric)

    表 1:考虑到可能会存在的偏见,对于每种情况的解决方法,和相应的材料。

    参加者平均在 Raft 的测验中比 Paxos 高 4.9 分(总分 60,那么 Raft 的平均得分是 25.7,而 Paxos 是 20.8);图 14 展示了每个参与者的得分。配置t-检验(又称student‘s t-test)表明,在 95% 的可信度下,真实的 Raft 分数分布至少比 Paxos 高 2.5 分。

    图 14

    图 14:一个散点图表示了 43 个学生在 Paxos 和 Raft 的小测验中的成绩。在对角线之上的点表示在 Raft 获得了更高分数的学生。

    我们也建立了一个线性回归模型来预测一个新的学生的测验成绩,基于以下三个因素:他们使用的是哪个小测验,之前对 Paxos 的经验,和学习算法的顺序。模型预测,对小测验的选择会产生 12.5 分的差别。这显著的高于之前的 4.9 分,因为很多学生在之前都已经有了对于 Paxos 的经验,这相当明显的帮助 Paxos,对 Raft 就没什么太大影响了。但是奇怪的是,模型预测对于先进行 Paxos 小测验的人而言,Raft的得分低了6.3分; 虽然我们不知道为什么,这似乎在统计上是有意义的。

    我们同时也在测验之后调查了参与者,他们认为哪个算法更加容易实现和解释;这个的结果在图 15 上。压倒性的结果表明 Raft 算法更加容易实现和解释(41 人中的 33个)。但是,这种自己报告的结果不如参与者的成绩更加可信,并且参与者可能因为我们的 Raft 更加易于理解的假说而产生偏见。

    图 15

    图 15:通过一个 5 分制的问题,参与者(左边)被问哪个算法他们觉得在一个高效正确的系统里更容易实现,右边被问哪个更容易向学生解释。

    关于 Raft 用户学习有一个更加详细的讨论。

    9.2 正确性

    在第 5 节,我们已经制定了正式的规范,和对一致性机制的安全性证明。这个正式规范使用 TLA+ 规范语言使图 2 中总结的信息非常清晰。它长约400行,并作为证明的主题。同时对于任何想实现 Raft 的人也是十分有用的。我们通过 TLA 证明系统非常机械的证明了日志完全特性。然而,这个证明依赖的约束前提还没有被机械证明(例如,我们还没有证明规范的类型安全)。而且,我们已经写了一个非正式的证明关于状态机安全性是完备的,并且是相当清晰的(大约 3500 个词)。

    9.3 性能

    Raft 和其他一致性算法例如 Paxos 有着差不多的性能。在性能方面,最重要的关注点是,当领导人被选举成功时,什么时候复制新的日志条目。Raft 通过很少数量的消息包(一轮从领导人到集群大多数机器的消息)就达成了这个目的。同时,进一步提升 Raft 的性能也是可行的。例如,很容易通过支持批量操作和管道操作来提高吞吐量和降低延迟。对于其他一致性算法已经提出过很多性能优化方案;其中有很多也可以应用到 Raft 中来,但是我们暂时把这个问题放到未来的工作中去。

    我们使用我们自己的 Raft 实现来衡量 Raft 领导人选举的性能并且回答两个问题。首先,领导人选举的过程收敛是否快速?第二,在领导人宕机之后,最小的系统宕机时间是多久?

    图 16

    图 16:发现并替换一个已经崩溃的领导人的时间。上面的图考察了在选举超时时间上的随机化程度,下面的图考察了最小选举超时时间。每条线代表了 1000 次实验(除了 150-150 毫秒只试了 100 次),和相应的确定的选举超时时间。例如,150-155 毫秒意思是,选举超时时间从这个区间范围内随机选择并确定下来。这个实验在一个拥有 5 个节点的集群上进行,其广播时延大约是 15 毫秒。对于 9 个节点的集群,结果也差不多。

    为了衡量领导人选举,我们反复的使一个拥有五个节点的服务器集群的领导人宕机,并计算需要多久才能发现领导人已经宕机并选出一个新的领导人(见图 16)。为了构建一个最坏的场景,在每一的尝试里,服务器都有不同长度的日志,意味着有些候选人是没有成为领导人的资格的。另外,为了促成选票瓜分的情况,我们的测试脚本在终止领导人之前同步的发送了一次心跳广播(这大约和领导人在崩溃前复制一个新的日志给其他机器很像)。领导人均匀的随机的在心跳间隔里宕机,也就是最小选举超时时间的一半。因此,最小宕机时间大约就是最小选举超时时间的一半。

    图 16 中上面的图表明,只需要在选举超时时间上使用很少的随机化就可以大大避免选票被瓜分的情况。在没有随机化的情况下,在我们的测试里,选举过程往往都需要花费超过 10 秒钟由于太多的选票瓜分的情况。仅仅增加 5 毫秒的随机化时间,就大大的改善了选举过程,现在平均的宕机时间只有 287 毫秒。增加更多的随机化时间可以大大改善最坏情况:通过增加 50 毫秒的随机化时间,最坏的完成情况(1000 次尝试)只要 513 毫秒。

    图 16 中下面的图显示,通过减少选举超时时间可以减少系统的宕机时间。在选举超时时间为 12-24 毫秒的情况下,只需要平均 35 毫秒就可以选举出新的领导人(最长的一次花费了 152 毫秒)。然而,进一步降低选举超时时间的话就会违反 Raft 的时间不等式需求:在选举新领导人之前,领导人就很难发送完心跳包。这会导致没有意义的领导人改变并降低了系统整体的可用性。我们建议使用更为保守的选举超时时间,比如 150-300 毫秒;这样的时间不大可能导致没有意义的领导人改变,而且依然提供不错的可用性。

    10 相关工作

    已经有很多关于一致性算法的工作被发表出来,其中很多都可以归到下面的类别中:

    • Lamport 关于 Paxos 的原始描述,和尝试描述的更清晰。
    • 关于 Paxos 的更详尽的描述,补充遗漏的细节并修改算法,使得可以提供更加容易的实现基础。
    • 实现一致性算法的系统,例如 Chubby,ZooKeeper 和 Spanner。对于 Chubby 和 Spanner 的算法并没有公开发表其技术细节,尽管他们都声称是基于 Paxos 的。ZooKeeper 的算法细节已经发表,但是和 Paxos 着实有着很大的差别。
    • Paxos 可以应用的性能优化。
    • Oki 和 Liskov 的 Viewstamped Replication(VR),一种和 Paxos 差不多的替代算法。原始的算法描述和分布式传输协议耦合在了一起,但是核心的一致性算法在最近的更新里被分离了出来。VR 使用了一种基于领导人的方法,和 Raft 有很多相似之处。

    Raft 和 Paxos 最大的不同之处就在于 Raft 的强领导特性:Raft 使用领导人选举作为一致性协议里必不可少的部分,并且将尽可能多的功能集中到了领导人身上。这样就可以使得算法更加容易理解。例如,在 Paxos 中,领导人选举和基本的一致性协议是正交的:领导人选举仅仅是性能优化的手段,而且不是一致性所必须要求的。但是,这样就增加了多余的机制:Paxos 同时包含了针对基本一致性要求的两阶段提交协议和针对领导人选举的独立的机制。相比较而言,Raft 就直接将领导人选举纳入到一致性算法中,并作为两阶段一致性的第一步。这样就减少了很多机制。

    像 Raft 一样,VR 和 ZooKeeper 也是基于领导人的,因此他们也拥有一些 Raft 的优点。但是,Raft 比 VR 和 ZooKeeper 拥有更少的机制因为 Raft 尽可能的减少了非领导人的功能。例如,Raft 中日志条目都遵循着从领导人发送给其他人这一个方向:附加条目 RPC 是向外发送的。在 VR 中,日志条目的流动是双向的(领导人可以在选举过程中接收日志);这就导致了额外的机制和复杂性。根据 ZooKeeper 公开的资料看,它的日志条目也是双向传输的,但是它的实现更像 Raft。

    和上述我们提及的其他基于一致性的日志复制算法中,Raft 的消息类型更少。例如,我们数了一下 VR 和 ZooKeeper 使用的用来基本一致性需要和成员改变的消息数(排除了日志压缩和客户端交互,因为这些都比较独立且和算法关系不大)。VR 和 ZooKeeper 都分别定义了 10 中不同的消息类型,相对的,Raft 只有 4 中消息类型(两种 RPC 请求和对应的响应)。Raft 的消息都稍微比其他算法的要信息量大,但是都很简单。另外,VR 和 ZooKeeper 都在领导人改变时传输了整个日志;所以为了能够实践中使用,额外的消息类型就很必要了。

    Raft 的强领导人模型简化了整个算法,但是同时也排斥了一些性能优化的方法。例如,平等主义 Paxos (EPaxos)在某些没有领导人的情况下可以达到很高的性能。平等主义 Paxos 充分发挥了在状态机指令中的交换性。任何服务器都可以在一轮通信下就提交指令,除非其他指令同时被提出了。然而,如果指令都是并发的被提出,并且互相之间不通信沟通,那么 EPaxos 就需要额外的一轮通信。因为任何服务器都可以提交指令,所以 EPaxos 在服务器之间的负载均衡做的很好,并且很容易在 WAN 网络环境下获得很低的延迟。但是,他在 Paxos 上增加了非常明显的复杂性。

    一些集群成员变换的方法已经被提出或者在其他的工作中被实现,包括 Lamport 的原始的讨论,VR 和 SMART。我们选择使用共同一致的方法因为他对一致性协议的其他部分影响很小,这样我们只需要很少的一些机制就可以实现成员变换。Lamport 的基于 α 的方法之所以没有被 Raft 选择是因为它假设在没有领导人的情况下也可以达到一致性。和 VR 和 SMART 相比较,Raft 的重新配置算法可以在不限制正常请求处理的情况下进行;相比较的,VR 需要停止所有的处理过程,SMART 引入了一个和 α 类似的方法,限制了请求处理的数量。Raft 的方法同时也需要更少的额外机制来实现,和 VR、SMART 比较而言。

    11 结论

    算法的设计通常会把正确性,效率或者简洁作为主要的目标。尽管这些都是很有意义的目标,但是我们相信,可理解性也是一样的重要。在开发者把算法应用到实际的系统中之前,这些目标没有一个会被实现,这些都会必然的偏离发表时的形式。除非开发人员对这个算法有着很深的理解并且有着直观的感觉,否则将会对他们而言很难在实现的时候保持原有期望的特性。

    在这篇论文中,我们尝试解决分布式一致性问题,但是一个广为接受但是十分令人费解的算法 Paxos 已经困扰了无数学生和开发者很多年了。我们创造了一种新的算法 Raft,显而易见的比 Paxos 要容易理解。我们同时也相信,Raft 也可以为实际的实现提供坚实的基础。把可理解性作为设计的目标改变了我们设计 Raft 的方式;随着设计的进展,我们发现自己重复使用了一些技术,比如分解问题和简化状态空间。这些技术不仅提升了 Raft 的可理解性,同时也使我们坚信其正确性。

     

    展开全文
  • 大师Martin Fowler对持续集成是这样定义的:持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括...

    本文转自微信号EAWorld。扫描下方二维码,关注成功后,回复“普元方法+”,将会获得热门课堂免费学习机会!

    大师Martin Fowler对持续集成是这样定义的:持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。

    今天我们就来聊一聊微服务的持续集成。

    目录
    
    一、持续集成之构建
    二、持续集成之部署
    三、持续集成之测试
    四、持续集成之发布
    五、总结

    一、持续集成之构建


    当微服务产生后,持续集成也不得不考虑起针对这种可以独立部署的服务,当有十多个微服务同时运行甚至更多的时候,如何建立起与之的映射,即微服务、CI构建与源码的映射变得极为重要,如果还像简单软件那样集中管理是否还行得通,那可能会是一场灾难?在如此复杂的背景下,优良的持续集成方案同样也会给我们带来焕然一新的便利体验。

    在此,我们就先了解下微服务架构下的三种持续集成构建模式。

    1. 一个代码库、一个CI构建

    图片描述

    这种方式就是将所有的微服务放在同一个代码库中,并且使用一个CI构建。这么做唯一的好处就是只需要管理一个代码库,但随之而来的麻烦会让你应接不暇。每当我们修改一个服务中的一行代码后,我们必须重新构建所有的服务,所有的构建产物都是在同一个构建中完成。

    事实上其他的服务完全没有重新构建的必要,这样大大延长了上线速度。而在众多构建物中要找出保证能够让你修改生效的服务来部署,也是足够让人头疼了,这导致最终让我们选择重新部署所有代码。再试想一下,所有人共享一个CI构建,每个人的修改都有可能造成CI的构建失败,想要将这个CI构建成功稳定下来,头痛又升级了。

    在众所周知的谷歌使用的就是一个代码库,然而他们采用了自己的版本管理系统“piper”来管理超过20亿行代码的超级大库,所以对于更多的小公司来说,谷歌就是一个特例。

    2.一个代码库、多个CI构建

    图片描述

    在这种方式中,代码库还是那个代码库,不过在代码库中我们创建了多个子目录,每个子目录对应一个CI构建。现在的很多项目中都会采取这种持续集成,这让我们可以比较方便的同时提交对多个服务的修改。

    然而,来让我们琢磨一下这种方式依旧会存在的弊端,随着代码修改的增加,无可避免的会在不经意间造成服务耦合度的增加,当部分代码出现问题的时候受到影响的很可能是多个构建。

    3. 多个代码库、多个CI构建

    图片描述

    每个微服务都有一个对应的代码库,每个代码库对应一个CI构建。这时候每个微服务变得独立,修改运行部署不再相互依赖,大大降低了耦合度,方便了代码的管理和维护。另外多代码库还给我们带来另一个好处,就是共享问题,当你只有一个代码库的时候,如果想要共享,那必然所有代码都必须共享,拆分多个代码库,这样根据自己情况,你可以选择性的共享代码。当然,你又要说了,跨服务的修改变的麻烦了啊。没错,这也不是一个十全十美的方案。

    对于很多采用CVS或者SVN的代码库来说,可能第二种方案已经习以为常。而对于Git代码库,划分多个代码库更加合理更加易于管理。

    针对微服务架构,我们采用了多个代码库多个构建的方案。

    图片描述

    当我们创建一个微服务时,会产生一个git代码库,虽然我们每个微服务可以创建多个版本,但是我们是不可以同时编辑多版本的,因为我们开发代码是在git库master支线上,当一个版本发布后我们会给这个发布的版本打一个分支,这时候我们才能编辑新的版本。

    所以我们的CI构建就映射到一个微服务版本中,每个微服务代码提交代码库时会产生一个commitId,通常我们会针对这个commitId来进行一次构建,因此代码库分治也一定程度上避免了commitId不能与某个微服务对应的问题。对于微服务的持续集成,每次构建编译结束,产物可以是一个完整的镜像。

    图片描述

    对于微服务持续集成这里可以分为snapshot版本的构建和release版本的构建。

    Snapshot版本可以理解为日编译,以及开发和测试期的编译构建,构建时要选择对应commitId的代码进行构建。

    Release版本就是产品发布前的编译构建,需要选择具体的build号,每次Snapshot版本编译完成都会生成build号。

    图片描述

    二、持续集成之构建


    微服务的开发我们通常会遇到一个比较麻烦的问题,在每个微服务中不可避免会有众多的配置项,也许每当我们换一个环境,就会有这样那样的不同微服务的配置需要修改,这不仅是一个工作量的问题,这也会给操作人员带来烦躁感,甚至出现遗漏某些配置的修改。

    在部署的环节,可以做一个配置项的功能,在页面中修改或增删每个微服务的配置项,这样给每次微服务的部署提供了极大的便利。

    图片描述

    选择Build号,对产品进行打包操作。打包时会显示打包进度及控制台信息。打包成功后部署,部署时会显示部署进度及控制台信息。部署成功,页面会显示访问地址,开发人员可以点击访问地址进行自测。

    图片描述

    三、持续集成之测试


    在敏捷软件测试中,将测试的类型分为四种:验证测试、单元测试、探索性测试、非功能性测试。

    图片描述

    • 验证测试:是否实现了正确的功能?
    • 探索性测试:可用性测试,如何破坏系统功能
    • 单元测试:是否正确实现了功能?
    • 非功能性测试:响应时间、可扩展性、性能测试、安全测试

    处于象限顶部的测试是面向业务的测试,大部分的验证测试以及探索性测试都习惯于手工测试,而处于象限底部的面向技术的测试,则大部分都可以形成自动化测试。这里我们着重说的是自动化测试么毕竟在微服务的道路上,不能解决自动化的问题,无法很好的体检微服务架构带来的好处。更多关于测试类型的内容可以参考《敏捷软件测试》。

    图片描述

    自动化测试毕竟不同于手工用例,我们从四个方面定义了自动化测试的设计原则与方法:

    • 易管理性
      统一规划、统一版本控制的规范要求;
    • 易实现性
      采用分层设计,测试基础服务层、测试能力支撑层、测试组件层、测试用例层,支持多种技术的测试能力,测试组件复用,用例专注业务逻辑;
    • 易维护性
      组件与用例分离、区分变化与不变、测试用例原则上不互相依赖、测试数据容易维护;
    • 易定位性
      测试用例独立、低复杂度、要求断言信息的准确性。

    更多关于微服务自动化测试的内容可以详见《DevOps之自动化测试》《微服务流程自动化测试》,今天主要说一下mock给持续集成持续测试带来的便利。

    首先我们来看下微服务下的持续测试流程。

    图片描述

    开发人员自测通过,在任务管理中,选择已经完成的功能单任务,已经编译过会显示相应的Build号和编译日期,自测完成任务状态更改为“已完成”。

    项目经理查看计划单任务中相关功能是“已完成”状态,在计划任务中增加任务编译信息,并修改计划单任务的状态为“测试”,并创建测试单任务,可以指派给相应测试人员。

    测试人员查看分派给自己的测试单任务,记录任务编译中的Build,修改状态为“进行中”,测试人员首先需要将测试环境与发开环境同步,即在测试环境打包部署与测试任务中发开环境相同的构建。

    测试人员发现测试缺陷后创建缺陷单任务,指定给相关的开发人员,验证通过则将任务状态改为“验证通过”。

    图片描述

    MockServer的设计思想在于将接口的操作和数据的操作分离,在实现桩程序时,只考虑对各种通信接口的包装,而将【条件】和【结果数据】的构造交给使用者。这样,同样一个桩程序,只要是基于相同的通信协议,就可以模拟出任意的行为,就像mock对象可以模拟任意对象的行为一样。

    对于微服务框架而言,在开发或者测试一个微服务领域的时候,对其它微服务领域的实现细节是不可知的。但是,我们在测试各个微服务领域的SPI以及Service的时候,却又需要在启动依赖的微服务领域系统。导致我们做单元测试的时候,如果想要跑完测试用例,就必须要把依赖的所有系统启动起来。

    这个时候,如果结合Mock Server,用mock data模拟微服务间的交互数据,就可以绕开对其他微服务领域系统的依赖。提高测试覆盖率以及效率。

    四、持续集成之发布


    持续集成的发布:确定开发、测试SignOff,创建预发任务,验收计划,验收产品版本,打Tag,发布上线。

    图片描述

    这里需要提到的是在很多时候我们发布前都会去锁定代码库,但是在当前的过程中不需要。

    开发人员开发代码的时候将代码库中master的代码fork到本地工作空间,经评审后提交master代码库,每次对于代码库的更改会产生commitId,每次构建时选择相应commitId对应状态的代码进行编译构建,每次构建完成产生build号,而我们的预发编译可以选择某个build号对应状态的代码进行打包部署。而我们每次发布版本后都会打一个新分支。故此不用再锁定代码库。

    五、总结


    当微服务的出现,你是否觉得多代码库的git库更利于代码的管理呢?针对git库的结构你是否觉得多代码库多构建的方式更好呢?部署配置的灵活性在微服务的交互上是否变得更加灵活了呢?发布中不用再封闭代码库是不是减少了维护成本呢?相信你或许在我的文章中已找到了自己的答案。

    微服务持续集成四步走,因地制宜,让我们一步步走出属于我们的不同世界。

    如需成为EAii会员加入微信群参与架构设计与讨论直播,享受微课堂PPT抢先下载等权益,请直接回复您的微信号至此公众号。

    关于作者
    叶婉婷
    现任普元信息SOA产品部高级软件工程师,为普元新一代数字化企业云平台开发团队一员。在过去的两年参与流程平台项目,主要负责Eclipse插件开发及自动化测试平台开发。在数字化企业云平台项目中参与了业务平台领域系统的开发。

    代表作品:
    当持续集成遇上微服务:分治优于集中
    程序猿测试媛之友谊的小船升华成巨轮
    微服务RPC框架选美

    图片描述

    关于EAWorld
    微服务,DevOps,元数据,企业架构原创技术分享,EAii(Enterprise Architecture Innovation Institute)企业架构创新研究院旗下官方微信公众号。

    扫描下方二维码,关注成功后,回复“普元方法+”,将会获得热门课堂免费学习机会!
    微信号:EAWorld,长按二维码关注。
    图片描述

    展开全文
  • 说起状态模式游戏开发者们第一个想到的一定是AI的有限状态机FSMs,状态模式确实是实现有限状态机的一种方法。在有限状态机中,一般都是观察者模式与状态模式连用,状态模式把每个状态封装成类,来对每个输入消息...
  • tcp十一种状态及问题处理方法

    万次阅读 2017-06-26 11:45:30
    TCP十一种状态 全部11种状态  1. 客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT 。  2. 服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4...
  • 持续集成之Jenkins+Gitlab简介 []

    千次阅读 2016-12-23 15:18:54
    持续集成之Jenkins+Gitlab简介 []标签(空格分隔): Jenkins持续集成概念持续集成Continuous Integration 持续交付Continuous Delivery 持续部署Continuous Deployment1.1 什么是持续集成:持续集成是指开发者在...
  • 陈树华是阿里巴巴移动安全部负责人,阿里聚安全、阿里钱盾等产品创始人,移动互联网安全体系建设开拓者,也是国内最早的批移动安全专家。10年前,他加入趋势科技,发现了诸多系统安全漏洞,并主...
  • 线程的状态和基本操作

    千次阅读 多人点赞 2019-10-03 23:36:42
    在上篇博客中并发编程的优...文章目录创建线程的四方式线程的状态和生命周期线程状态的基本操作interruptedjoinsleepyield进程和线程线程优先级守护线程和用户线程守护线程详解线程死锁认识线程死锁如何避免线...
  • 比较温和的方式是将一些并不需要登录,但可以给用户带来帮助的东西,第时间展现给他们,让他们产生兴趣,再在合适的时机引导他们注册(比如使用需要使用更高级的功能,或用户需要收藏某个喜欢的信息时)。...
  • 货币的一种互联网体系架构

    万次阅读 2019-05-10 10:50:30
    I. 介绍 中本聪最伟大的发明比特币,可以因为以下两处不同的特征广受赞誉: 比特币是公开的,去中心化的,加密总账(cryptographic ledger),同时拥有基本总账能力。 加密总账能追踪新的...这论文分析加密总...
  • 、TCP/IP 的分层管理 1.首先分为4层:应用层,传输层,网络层,链路层。 应用层:决定了向用户提供应用服务时通信的活动。TCP/IP 协议族内预存了各类通用的应用服务。 比如, FTP(File Transfer Protocol, ...
  • 目录 1.ElasticSearch的简介 ...3.2 cluster集群,ES是个分布式的系统 3.3 Node节点,就是集群中的台服务器 3.4 index 索引(索引库) 3.5 type类型 3.6 document:文档 3.7 Field 字段 3.8 sha...
  • 持续交付之——软件交付的问题

    千次阅读 2016-05-07 07:30:23
    引言本书的核心模式是部署流水线,以持续集成理论作为其理论基石部署流水线有三个目标 让软件构建,部署,测试和发布过程对所有人可见,促进合作 改善反馈,能在整个过程中更早的发现和解决问题(做件事,有问题...
  • 这是一道分值150的web题,打开题目链接之后,看到题目界面 可以看到有处输入的地方可以输入PHP语句 ...phpinfo()输出 PHP 当前状态的大量信息,包含了 PHP 编译选项、启用的扩展、PHP 版本、服务器信息
  • 你可能第次见到这么长的总结文,有点硬核,耐心阅读
  • 这是作者的系列网络安全...前文分享了分享机器学习在安全领域的应用,并复现个基于机器学习(逻辑回归)的恶意请求识别。这篇文章简单叙述了Web安全学习路线,并实现了最简单的木马和病毒代码,希望对读者有所帮助。
  • 30岁转行做初级程序员是一种什么样的体验?

    万次阅读 热门讨论 2018-08-02 11:30:20
    程序员就是条不断朝佛向西取真经的崎岖之路,但是还是有源源不断的人义无反顾地前行。 毕竟它的薪酬一般长成这个样子。 是不是很心动? 是不是想问30岁转行能行吗? 在互联网迅速发展的今天,很多人会产生...
  • Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可理解性,Raft 将一致性算法...
  • 持续集成是一种软件开发的实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快...
  • DevOps企业实践指南(8): 安全机制

    千次阅读 2017-08-28 19:37:57
    安全个被所有人口头上非常重视,但是往往在实际的实践中选择性无视的个话题。通过了解卡巴斯基实验室对于安全性相关的调查状况的解读,了解到了目前企业安全状况不容忽视的现状。同时阐述了随着DevOps持续集成...
  • Linux常用命令(持续更新)

    万次阅读 热门讨论 2017-04-14 12:31:07
    接触过Linux的读者都知道操作Linux的一种十分重要的途径是命令;自己在这摸索的几个月中也总结了些常 用命令:一是自己回顾用,二是供大家参考。 注:常用命令小编一直在更新,故该篇博文也会持续...
  • 什么是功能安全

    万次阅读 2018-07-06 09:15:33
    什么是功能安全(FS)?在现代工业控制领域中,可编程电子硬件、软件系统的大量使用,大大提升了自动化程度。但由于设备设计中的缺失,以及开发制造中风险管理意识的不足,这些存在设计缺陷的产品大量流入相关行业的...
  • 简单图解有限状态机与状态模式

    千次阅读 2018-12-03 01:39:04
    限状态机将一个对象的行为分解为容易处理的“块”或者状态,对象执行了某些变换对象将从一个状态变成了另一种状态,说的简单点就是状态流程图,然后这些状态的数量是有限的。 毛星云的博客里举了一个很好的例子说明...
  • web安全 前端性能 浏览器 算法相关 设计模式 正则表达式 职业规划 参考资料 html/ css 行内元素和块级元素举几个例子? 行内元素:span,a,var ,em,input,img,img,textarea,var,em,s...
  • 持续集成--理论和实践的新进展

    千次阅读 2013-10-07 11:04:18
    最近雷镇同学将Martin Fowler先生的著名论文《持续集成》第二版翻译成中文并发布出来,掀起了国内对于持续集成理论和实践讨论的新的高潮。笔者在本文中将全面对比持续集成论文前后两版的异同,分析并展示Thought...
  • 背景:在JMeter工具那类里,分享了jmeter+ant测试框架的搭建及实践,现在加入时下流行的jenkins集成工具,环境已经搭建完成,现主要是分享jenkins配置job中的报告输出及邮箱配置: 1、jenkins首页系统管理-插件...
  • 京东持续集成实践

    万次阅读 2018-10-18 22:46:47
    目录  京东持续集成实践 1、持续集成简介 2、持续集成实践 3、集成环境的部署及自动化测试 ... 持续集成不仅仅是项项目实践,而是多项项目实践的总和。在尝试这些实践时,不可避免要遇...
  • 详解以太坊世界状态

    万次阅读 2019-05-12 09:21:15
    我们将介绍区块链中“状态”的概念,并探究帕特里夏前缀树(Patricia Trie)数据结构的理论依据,利用 Google 的 leveldb 数据库来阐释以太坊中前缀树的应用。本文同时和篇手把手学习指引相关联,它能指导你安装并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 135,935
精华内容 54,374
关键字:

安全是一种状态通过持续的