【云计算】Container and Microservice Driven Design for Cloud Infrastructure DevOps

在这里插入图片描述

摘要

新兴的容器技术,如Docker,在云环境中开发和运行应用程序时提供了前所未有的灵活性,特别是与微服务风格的体系结构相结合时。然而,在不牺牲容器提供的许多好处的情况下,通常很难使用容器来管理云基础设施。本文指出了阻碍实现容器化基础设施服务全面承诺的关键挑战。以OpenStack为例,我们探讨了这些挑战的解决方案。具体来说,我们重新设计了OpenStack部署架构,以实现动态服务注册和发现,探索在容器中管理服务状态的不同方法,并使容器能够访问主机内核和设备。与基于VM的方法相比,我们量化了基于容器的微服务风格DevOps的效率,并研究了无状态和有状态容器化组件的可伸缩性。我们还讨论了当前设计中的局限性,并强调了开放式研究问题,这些问题如果得到解决,将导致在云基础设施管理中更广泛地采用容器。

1. 前言

基础设施即服务(IaaS)为用户提供了对可扩展的、看似“无限”的基础设施资源的即时访问,而无需维护和管理自己的数据中心的昂贵开销和负担。为了提供IaaS,提供商必须部署复杂的软件栈(即云基础设施管理软件)来分配、管理、监视和配置底层物理资源,例如、CloudStack[1]、Eucalyptus[2]和OpenStack。随着IaaS迅速变为商品化,随着企业更多地依赖IaaS作为建立更有利可图的管理服务的基础,有效地部署和操作这些基础设施服务的重要性越来越大。
容器技术的最新进展导致了人们对在云中使用容器的新兴趣。PaaS系统,例如CloudFoundry[3]、Heroku[4]、OpenShift[5],一直是容器的早期采用者。在这些系统中,容器通常用于承载应用程序并将资源分配给应用程序。最近,已经出现了将云基础设施服务进行容器化的努力,即在容器中运行基础设施管理软件[6]、[7]、[8]。无论是应用于应用程序还是云基础设施,容器都很有吸引力,因为它们提供了良好的隔离,具有低开销和快速的启动时间,从而带来高度灵活的解决方案[9],[10]。当与微服务架构相结合时,这种灵活性可以进一步增强,其中软件被分解成小的功能单元;每个单元可以独立地发生故障、扩展、维护和重用[11]。
DevOps是一套将软件开发过程与所述软件的部署和运营简化和集成的技术。DevOps面临的一些挑战包括,例如,如何轻松地部署和升级云基础设施,如何顺利地将代码从开发交付到生产,如何在较少人工干预的情况下扩展基础设施,等等。使DevOps正确是基于云的软件解决方案总体成功的关键之一。在处理云基础设施软件时,除了DevOps的挑战外,能够有效地利用底层物理资源也非常关键,即如何用更少的资源运行更多的服务。高效的DevOps与高效的资源利用相结合,可以带来高IT性能,这是当今竞争激烈的IaaS业务所必须的。由于高度灵活和轻量级,容器与微服务体系结构相结合,在实现所需的效率水平方面显示了巨大的希望。
尽管前景看好,但在将微服务设计原则应用到云基础设施服务的容器化和应用方面仍然存在重大挑战。首先,由于基础设施管理软件被分解为具有不同角色的单个组件,因此在微服务体系结构中,这些独立组件之间的最终接口是什么还不清楚。其次,由于对容器中数据可移植性的支持有限,很难将基础设施层的有状态组件(例如,数据库、消息服务器、内存键值存储)进行容器化[12]。尽管这些挑战也适用于应用层,但云应用可以使用解决方案,例如作为外部服务运行有状态组件(例如数据库)[3](因此,不需要容器化)。然而,这是基础设施软件的次优解决方案,因为服务状态是运行时环境的一个组成部分。第三,基础设施服务通常与物理资源(如计算、网络和存储)交互。虽然物理资源越来越成为“软件定义的”,但它们仍然缺乏通用的、可编程的接口。这从根本上阻碍了可移植性,因为为一个系统构建的容器化服务可能无法与另一个系统上的物理资源进行接口。
在本文中,我们将探讨在使用容器和应用微服务设计原则来运营和管理云基础设施服务方面的机遇和挑战。这项工作的目标是为此类基础设施软件开发一种更灵活、可靠和高效的DevOps方法。具体来说,为了使讨论更加具体,我们提出了一个基于微服务的Docker化OpenStack实例的设计、实现和部署。我们使用这个实例来度量和量化上面讨论的操作效率的不同方面。我们的部署经验教训以及我们方法的局限性也将与关键的开放研究问题一起讨论,这些问题如果得到解决,将导致在云中更广泛地采用容器。
本文的其余部分组织如下:在第二节中,我们将描述如何使用容器和微服务风格的设计来实现操作效率。在第三节中,我们描述了我们的原型,即使用Docker容器将微服务风格的DevOps应用到OpenStack。在第四节中,我们提出了将原型架构的操作效率量化为参考OpenStack架构的度量。第五节讨论了当前实施的局限性,第六节介绍了相关工作。

2. 云基础设施开发

在本节中,我们将讨论如何使用容器和微服务风格的设计来实现云基础设施软件的高运营效率。我们首先讨论云基础设施软件的特点及其对DevOps的影响(第II-A节),容器和微服务风格设计如何提高运营效率(第II-B节),最后讨论将容器和微服务风格设计应用于云基础设施软件的挑战(第II-C节)。

A. 云基础设施软件的特点

大配置空间:由于需要支持和预期大量的用例,基础设施软件通常提供无数配置选项。这些配置选项允许对基础结构的软件和硬件组件进行细粒度的优化。不幸的是,这种能力会导致复杂性增加,进而导致操作失败。这种失败通常是由于操作人员对某些参数的不正确设置造成的,这些操作人员不具备作为开发团队的详细知识或对软件的良好理解。为了减少失败的机会,只应向软件公开最少的一组配置。
松耦合组件:云基础设施软件通常由许多组件组成(例如,数据库、网络、计算、存储等)。这些组件相互协作以提供管理基础设施资源的总体功能。为了提高性能和高可用性(HA),每个组件在多个实例中实例化(例如,多个虚拟路由器)。当新实例加入或现有实例失败时,必须将此事件通知集群的其他成员。因此,为了更容易管理云基础设施,各个组件应该松散耦合。
短期发布周期:为了适应用户需求和需求的不断变化,基础设施软件的特性和补丁也在不断增加。对不同组件的更新是由不同的团队和不同的发布周期完成的。例如,OpenStack有一个6个月的发布周期,每1个半月发布一次里程碑版本。每个版本可以包含数百个功能和错误修复。例如,从OpenStack Icehouse到Juno版本,添加了300多个新功能,并在所有组件和库中提交了3000多个bug修复程序[13]。为了跟上更新的速度,运营商需要自动化和不断集成更新的软件。
运行时一致性:基础设施软件要求运行时环境配置适当的操作系统版本、内核模块和支持库。这些依赖关系比应用层软件更具限制性和数量。例如,在开发环境中成功的部署/升级并不一定意味着在操作系统或库版本可能不同的登台或生产环境中相同。因此,实现一致执行环境的机制至关重要。

B. 基于容器和微服务的DevOps

软件定义的系统使开发人员能够将基础设施视为代码。这使得以自动化、灵活和高效的方式管理基础设施成为可能。不幸的是,由于基础设施软件的性质和复杂性(见上一节),其中许多潜力没有完全实现。通过利用容器和基于微服务的设计来实现代码可移植性、简化生命周期管理和更有效地利用资源,开发人员更接近于充分发挥软件定义系统的潜力。
代码可移植性:容器最吸引人的特性之一是它对代码可移植性的支持。通过将软件及其依赖项打包到单个镜像中[14]、[15]、[16],容器实现了基于镜像的部署过程,从而提供了“一次开发,到处部署”的自由。应该注意的是,虽然基于镜像的管理也可以通过vm实现,但vm不像容器那样可移植,也不像容器那样轻量级,因此降低方法的有效性。
基于镜像的方法可以减轻上面讨论的关于基础设施软件的配置复杂性和运行时一致性的许多痛点。云管理软件的传统部署通常依赖于自动化工具(如Chef、Puppet),这些工具往往会创建许多配置点,以便将软件与部署环境之间的依赖性编码。这使得安装/卸载或更新过程很麻烦。如果不小心处理,这些配置点可能会变成断点或错误。相反,在容器中运行服务简化了部署过程,因为大多数软件依赖项打包在一个镜像中,留给操作员配置的必要参数很少。
在这里插入图片描述
为了说明上述要点,表1比较了在使用为我们自己的云操作环境开发的Chef cookbooks[17]或Docker[14]容器镜像时,OpenStack安装中的配置点数量。从这个例子可以看出,基于镜像的方法消除了配置步骤中的许多复杂性,从而简化了整个部署过程。
生命周期管理:如前一小节所述,今天的云基础设施软件需要持续交付和集成。为了满足这一需求,运营商更喜欢使用基于镜像的方法(例如,版本化的Docker镜像)来管理基础设施代码的生命周期,以避免就地更新。此外,他们还希望能够在不同的环境中同时轻松部署和跟踪不同版本、在线“A/B测试”、快速回滚等功能。当新版本出现问题时,操作员不需要在生产环境中调试和修复它们。相反,它们只需回滚到以前的容器镜像(参见图1),然后等待新的镜像和修复可用。基于镜像的方法与微服务风格的设计相结合,放大了系统的好处,因为系统的组件是分离的,防止在维护期间相互干扰。
在这里插入图片描述
资源利用:容器从两个方面提高了计算资源的利用率。首先,像VM一样,可以在一台服务器上托管多个容器,以提高其资源利用率。其次,容器在访问内存和I/O设备方面比vm更有效,同时在不同容器中的应用程序之间仍然提供良好的隔离[10]。因此,在单主机级别,容器可以比vm运行更多的云服务,同时获得相同的性能。此外,轻量级、可移植和快速引导的容器镜像通过将许多云基础设施组件作为“微服务”运行,从而促进了微服务风格设计的应用,这些“微服务”可以随着工作负载的变化而轻松地向外(或向后)扩展。这将在多主机级别提高资源利用率。

C. 设计挑战

本小节描述了将容器和微服务风格设计应用于云基础设施管理软件的三个主要挑战。
最小化交叉配置:云基础设施软件,如OpenStack,通常采用组件化和分布式架构。因此,不同的服务组件自然成为容器化的单元。如何管理和组织这些单独的组件会极大地影响设计的可移植性和可伸缩性(见第III-A小节)。
有些服务通常是为了容错和高可用性(HA)而复制的。对于容器化方法,一个主要的挑战是允许从同一个容器镜像实例化同一服务的多个实例,同时仍然允许对服务进行某种程度的定制和配置。太多的配置点会使基于镜像的部署非常复杂(请参阅第II-A小节),而太少的配置点会使容器镜像的可移植性降低。另一个挑战是最小化整个设置中发生的交叉配置数量。当对一个组件的更改将导致对所有其他组件的配置更新时,会发生交叉配置。因此,添加或删除服务可能会影响所有其他现有服务。这种现象可以在使用诸如Pacemaker[18]等集群管理工具的传统HA设置中发现,其中每个节点必须跟踪集群中所有其他节点的IP地址。这种方法不适合微服务风格的部署。
维护状态:云基础设施层管理许多软件状态。一种状态是云基础设施的运行时配置,例如服务IP地址和端口、HA设置中的主从关系等。这些运行时配置应以适合基于容器的部署的方式进行管理,即1)需要在容器外部管理的配置很少,以及2)必须在容器外部管理的任何配置都可以自动输入服务注册和发现过程(见第III-A小节)。
另一种类型的状态来自容器中正在运行的应用程序。应用程序状态可以在磁盘上,也可以在内存中。例如,数据库可以将用户配置文件存储在磁盘上,而组件之间的消息可以存储在消息服务器的内存中。要做到容错和/或支持服务的动态扩展,必须开发允许应用程序状态复制的机制。
对于容器中的磁盘上状态,现有选项包括使用共享存储、磁盘卷迁移[19]和应用程序级数据复制。根据应用程序的不同,这些选择会导致不同的性能和可伸缩性权衡。但是,对于内存中的状态,还没有一种机制可以让容器透明地执行内存复制。在这种情况下,当前的做法是使用容器化服务来避免将状态完全存储在内存中(例如缓存),或者依赖客户端来容忍由于失败服务的丢失或不一致状态而导致的错误。我们在第III-C小节中讨论了我们在处理记忆中的状态方面所做的一些努力。
提供主机资源访问:由于容器中缺少专用内核,因此很难访问某些基础设施服务组件所需的主机资源。例如,有些服务需要通过IOCTL或/proc与内核交互。其他人需要访问主机的硬件设备来处理高性能任务(例如,软件路由器)。一个潜在的解决方案是增强这些容器的能力或特权。但是,这会降低此类容器的可移植性,并且在多个特权容器访问同一主机资源时可能导致冲突。

3. 案例研究:将容器化和微服务风格设计应用于OPENSTACK

OpenStack是一种广泛采用的开源基础设施云管理软件。它的分布式、面向服务的体系结构使它成为展示云基础设施软件容器化可行性的理想候选。在本节中,我们将重点讨论第II-C小节中确定的挑战,同时保留第II-B小节中讨论的容器运输的好处。
在这里插入图片描述

A. 基于容器的部署

典型的OpenStack架构由三种类型的节点组成:控制平面、计算和网络。控制平面节点运行管理服务,而计算节点和网络节点负责提供vm和网络资源。
基于DevOps需求,不同节点上的服务可以在不同的粒度级别上进行容器化。在我们当前的实现中,我们选择在控制平面中对服务进行相对简单的设置:一个容器承载数据库服务(MySQL),另一个承载消息服务(RabbitMQ[20]),第三个承载控制器服务(包括Keystone、Glance、Nova、Neutron服务器等)。为了支持HA,每个控制平面容器被复制到另外两个主机上。在计算节点和网络节点上,有一个容器运行Nova和Neutron代理服务。图2(a)显示了基于Docker容器的OpenStack部署,我们将在本文的其余部分中引用它。
OpenStack中的每个服务都由其应用程序代码和配置文件组成。我们已经将应用程序代码及其所有依赖库构建到容器镜像中,这些镜像可以下载并用于在任何Docker主机上创建容器。同时,容器镜像包含特定于应用程序的脚本,以便在运行时生成配置文件。

B. 微服务架构

容器化OpenStack不仅仅是在容器中运行那些管理服务。例如,在控制平面中,每当新创建、从故障中恢复或关闭这些服务时,应通知控制器容器数据库和消息传递服务的位置。具体来说,这意味着服务必须有方法注册自己、被其他服务发现、记录其配置/运行时状态,并且通常在其部署和更新过程中进行编排。因此,我们需要一个架构来简化向外扩展、HA和负载平衡的操作。基于这些要求,拟议架构的关键组件包括:

  • 服务代理:所有服务都配置为只能通过代理访问(例如HA Proxy[21]),这样对服务实例的更改不会影响HA设置中的其他服务实例。同时,代理必须具有每个服务的最新配置,例如IP地址和端口号。将检索这些配置从配置状态管理器。
  • 配置状态管理器(CSM):管理控制平面中所有服务的配置。我们基于etcd[22]将CSM实现为一个分布式的密钥值存储。要在CSM中注册服务,每个容器都有其“sidekick”进程。sidekick进程定期监视容器中服务的状态(例如,通过测试API调用);根据状态,sidekick进程将在CSM中注册或取消注册服务。
  • 服务编排器:所有服务都由编排器(构建于fleet[23])管理,该编排器根据某些策略(例如,同一位置或反同一位置)决定需要运行服务的多少实例以及运行位置。orchestrator不仅在同一主机上启动容器及其助手,还跟踪容器的状态并相应地执行操作。例如,如果现有容器由于重新启动主机而终止,则编排器将自动重新启动新容器。

图2(b)说明了这三个组件如何在提议的体系结构中协作。在步骤(1)中,服务编排器在主机上启动控制器容器及其sidekick进程。在步骤(2)中,当sidekick进程发现服务已启动并正在运行时,它将已知的服务名(例如/controller/ip)和ip地址(例如172.16.1.101)注册为CSM中的键值对。使用代理运行的confd[24]进程监视CSM中对键值对的更改,生成新的配置文件,并重新加载HAProxy进程以使用新的配置(步骤3)。稍后,如果容器中的控制器容器或服务失败,将通知服务编排器并启动新的控制器容器。然后将开始相同的服务注册过程。

C. 处理状态服务

上述架构为OpenStack中的无状态服务提供了明显的好处。然而,处理有状态的服务需要仔细考虑,以避免从容器化带来的好处。第一种服务状态是驻留在磁盘上的应用程序数据。主要的挑战是如何在HA设置中有效地复制这些状态。这里我们使用MySQL数据库作为一个示例。
MySQL支持两种HA模式:active-standby和active-active。在active-standby模式下,只有主节点为客户机请求提供服务,而其余节点为从节点并从主节点复制数据。此模式要求区分主角色和从角色,并在故障转移或扩展期间触发交叉配置。在active-active模式下,集群中的每个节点都是相同的,并主动服务于客户机请求;单个节点的加入或丢失不会影响其他节点。因此,active-active设置更适合于基于容器的体系结构。
由于active-active模式的MySQL集群中的每个节点都可以更新其本地状态,因此必须通过在实例之间同步数据来保证一致性。数据同步的常用方法是共享存储和应用程序级复制。在前一种情况下,所有MySQL服务器都通过一个分布式文件系统(如全局文件系统2(GFS2))或OCFS访问同一个数据卷。在后一种情况下,数据库进程(例如,Galera补丁MySQL[25])在集群中的所有节点之间同步复制数据。在这两种设置中,磁盘上的数据在任何数据库服务器丢失后都不会丢失。但是,如下所示,它们有不同的性能和可伸缩性权衡。
表II比较了使用共享存储和应用程序级数据复制访问数据库的延迟。在这个实验中,两台主机上的两个MySQL容器形成一个HA集群,其中数据同步要么通过GFS2实现,要么通过Galera复制实现。我们使用SysBench[26]来模拟各种工作负载组合和访问这两个服务器的并发用户数。我们观察到,虽然只读工作负载的结果相似,但是Galera设置在读写工作负载方面明显优于GFS2设置(快100×和15×)。这是因为对于共享存储,写请求将导致整个表文件上的文件系统锁定(也称为表级锁定)。相反,Galera允许多个MySQL服务器同时更新表中的不同行,因为它使用行级锁定。这导致了Galera设置的优越性能。然而,Galera设置的好处在于在扩展或恢复期间以状态复制为代价。也就是说,当创建新的MySQL容器以加入现有集群时,需要将DB状态传输到新服务器。即使存在最小化这种状态传输的方法,例如通过增量状态传输(IST),数据传输仍然会在启动新服务实例时引起网络开销和延迟。
在这里插入图片描述
另一种状态是内存中的应用程序数据,如网络会话或缓存值。要处理内存状态,需要容器内存快照、复制和迁移机制。这些机制使运营商能够优雅地处理由于操作系统/硬件升级或HA的零停机负载平衡和故障转移而计划的主机维护。
快照和恢复应用程序的功能可用于容器[27]和虚拟机[28]。在虚拟机中,这些机制已经被许多现代VM管理工具(如VMware的vCenter、virt manager等)广泛采用。Linux实现的容器内存快照也适用于LXC[15]容器,但不幸的是,不适用于Docker容器。我们与开源社区合作,通过CRIU[29]项目将LXC容器的快照功能集成到Docker容器[30]。实质上,快照包括将容器中进程的所有运行时信息(例如,映射内存页、CPU寄存器的内容、挂起的信号)以及容器的上下文(例如,命名空间、网络接口、配置)转储到位于磁盘本地的一组映像文件中。图像文件的大小与容器活动使用的内存量成正比。对容器的内存状态进行快照的能力是其他服务(如复制和迁移)的基础,这些服务也正在被社区开发。
鉴于这项技术还不成熟,我们还没有处理部署的OpenStack服务的内存状态。不过,在第四节中,我们确实对容器内存快照功能的性能进行了一些评估。

D. 处理访问主机资源的服务

在OpenStack中,计算、存储和网络节点上的基础设施服务分别负责创建VM实例、提供存储和网络资源。由于这些资源与主机操作系统紧密耦合,需要一定程度的访问权限,因此在容器中以安全和可移植的方式运行计算、存储或网络功能是一项挑战,如下所述。
运行需要访问主机资源的服务的容器需要对主机内核的特权访问和对主机上特定目录的访问。因此,这可能危及主机的安全。例如,必须共享/proc/modules目录,以便容器可以加载内核模块(如KVM)来启动VM实例[31]。对该目录的访问允许容器访问主机上它不知道的其他模块。缺乏对特权访问的细粒度控制是当前容器技术的一个限制,我们将在第五节中简要讨论。
幸运的是,并非所有需要访问主机资源的服务都需要在特权容器中运行。例如,需要访问主机上用于存储VM镜像的数据卷的容器不需要特权访问。

4. 实验评估

本节评估并验证了第三节中讨论的设计选择。特别是,我们强调了在OpenStack的DevOps中使用容器和微服务风格的体系结构相对于更传统的基于VM的方法的好处。我们没有分析基于脚本的DevOps机制,例如使用Chef或自定义shell脚本,因为这些技术缺乏在云中高效DevOps所必需的灵活性、灵活性和速度。例如,基于脚本的技术非常耗时,因为它们需要在实例化新的服务实例时下载和安装软件包。此外,由于处理安装和配置失败所需的复杂逻辑,它们很容易出错,因此需要开发人员具备专门的知识才能进行调试(见表一中的结果)。
在第IV-A小节中,我们简要描述了对其最为关键的常见DevOps实践,以及使用基于虚拟机的方法与基于容器的方法的DevOps之间的主要区别。具体来说,我们表明使用容器的OpenStack DevOps从(1)快速的部署时间,(2)易于滚动升级,以及(3)简化故障恢复和HA中受益匪浅。在第IV-B小节中,我们评估了控制平面在不同工作负荷下的伸缩性。
我们的实验装置由一个1Gb开关连接的机器组成。每台机器有两个XeonE5649处理器,64GB内存,运行Linux3.13。KVM用作在Linux主机上实例化vm的管理程序。对于使用容器的实验,我们使用Docker1.6。每个Docker主机还运行etcd和fleet agent,形成完整的微服务架构。这些编排工具在大多数Linux发行版中都可用,因此选择特定的Linux发行版(例如Ubuntu、CentOS、CoreOS[37])对我们的评估并不重要。对于OpenStack控制平面,我们使用OpenStack Juno release、MySQL Galera和RabbitMQ中的组件。我们使用Rally基准工具[32]生成OpenStack工作负载。对于涉及容器内存状态捕捉的实验,我们使用CRIU 1.7。实验测试台的结构如图2所示。

A. DevOps实践:基于虚拟机vs基于容器的方法

OpenStack DevOps指的是一系列操作单个OpenStack组件生命周期的任务。表III的第一列总结了最重要的操作。执行这些操作(部署、滚动升级和故障检测/恢复)的延迟用于量化基于容器和基于虚拟机的方法的效率。
在这里插入图片描述
表4比较了使用基于VM和基于容器的方法执行这三个操作的时间。结果表明,基于容器的方法在执行DevOps任务的时间上优于基于VM的方法。一般来说,vm和容器之间相对于DevOps的主要区别在于配置开销和速度。启动容器不仅比启动VM快,而且使用更少的系统资源。除了快速的容器创建时间外,容器镜像还由不同的层组成,这一特性避免了必须传输镜像的完整历史,从而加快了镜像传输时间和更细粒度的版本控制。以下段落详细分析了每个DevOps任务。
在这里插入图片描述
部署(验证部分III-A):如图2所示,OpenStack控制平面由三种类型的节点组成:消息传递、数据库和控制器。这些节点的部署有两个阶段:初始化和扩展。在初始化阶段,我们在每个主机上创建每个节点类型的一个实例(创建一个VM/container),以建立一个功能齐全的OpenStack控制平面。然后,我们通过添加每种类型的另外两个实例来扩展服务,以形成HA集群。如表四第一行所示,基于容器的部署是1.5条×比基于虚拟机的部署快。
为了解释为什么基于容器的方法导致更快的部署,我们在图3中显示了每种节点类型的部署时间分解。图3(a)显示了每种节点类型的第一个实例的初始化阶段。数据库和消息的实例化是并行的(深灰色),而控制器实例中服务的实例化(浅灰色)必须等待数据库和消息组件的完成,因为控制器节点中的服务需要在数据库和某些服务(例如。,Nova conductor)通过消息服务进行协调。可以看到,控制器节点完成的时间最长,而且VM和container之间的持续时间几乎相同。因此,尽管启动容器比VM快得多(具体来说,<1s vs.30s),但是在三种节点类型中使用容器完成初始化阶段的实际好处相对较小。
在这里插入图片描述
相反,使用容器时的扩展时间减少了50%以上,如图3(b)所示。在扩展阶段,我们将启动每种节点类型的另外两个vm/容器。虽然启动容器所需的时间与初始化阶段相同,但单个VM的启动时间却是原来的三倍(90秒对27秒)。这是由VM创建引起的资源争用的结果(图3(c))。通过串行或并行创建实例,vm通常比容器消耗更多的系统资源,并产生更多的干扰,从而导致更长的启动时间。因此,对于整个部署,基于容器的部署比基于VM的方法完成得更快。此外,由于初始化是OpenStack部署的一次性过程,我们希望使用容器扩展服务会带来更多好处。
滚动升级(验证第III-B节):滚动升级是导致零停机时间的升级过程。这种能力对于运营商处理高修复率的错误和现代云部署中典型的新特性至关重要。图4说明了如何在容器和微服务风格的DevOps中执行滚动升级。在步骤1中,将在与现有服务相同的主机上创建包含升级软件的容器。然后,服务的活动版本由升级事件手动或自动触发(步骤2)。通过持续监视版本密钥,将更新代理的配置文件(步骤3),然后将流量重定向到新容器中运行的服务(步骤4)。这个过程对外部客户机是透明的,对OpenStack的其他组件造成的中断最小。
在这里插入图片描述
基于镜像的DevOps,无论基于VM还是基于容器,都可以通过消除传统软件升级过程中的复杂性(例如卸载/安装旧/新二进制文件以及依赖项和解决冲突)来简化滚动升级。本质上,升级过程是用一个新实例替换现有的VM/容器,同时最小化所提供服务的中断。由于容器映像的大小通常小于VM,并且启动容器的速度比VM快,因此使用容器升级服务的总时间比使用VM快。这可以在我们的结果中看到(表四,第2行)。在测试台中替换三个控制器容器比替换运行同一服务的三个vm快1.6倍。然而,虽然没有显示,两种方法所经历的停机时间并没有明显的不同。
故障转移/恢复(验证第III-C节):服务可用性在任何生产环境中都是至关重要的,但故障是不可避免的。由于计划内维护或计划外停机,服务变得不可用。根据服务中断的原因,维护服务可用性需要执行不同的操作。对于计划的维护,典型的解决方案包括快照服务的运行时状态,然后在同一主机上恢复服务(在修复后),或者临时将服务迁移到另一台主机。计划外服务中断的解决方案更加复杂,依赖于支持连续冗余的HA框架。在下面的段落中,我们将比较VM和基于容器的DevOps在故障转移和恢复方面的差异。
高可用性:高可用性系统的设计旨在最小化服务中断期间失败事务的数量,并快速使系统重新联机。此外,鉴于OpenStack中服务的多样性,从DevOps的角度来看,HA框架可以提供一个统一且简单的接口来管理这些服务。
对于现有的基于VM的方法,故障检测/恢复依赖于集群软件,如Pacemaker(OpenStack安装指南推荐的方法),而对于容器,我们可以利用通用的服务发现/注册机制,如etcd和fleet。在Pacemaker中,各个服务必须使用特定于Pacemaker的方式来编写它们自己的自定义资源代理,以监视服务可用性,并运行特定于应用程序的脚本以在失败后执行服务恢复。在我们的微服务体系结构中,一个更简单、更通用的机制被用于监视可用性(例如,sidekick用于探测的shell脚本)以及恢复(终止失败的服务并实例化新服务)。在下面的段落中,我们首先评估并比较在VMs中使用Pacemaker for HA部署的失败OpenStack服务的恢复时间和事务错误率,以及本文中探索的基于容器的微服务样式HA。然后我们描述了使用容器和起搏器实现HA的复杂性。
第一个实验比较了OpenStack的MySQL数据库组件出现故障时的恢复时间。恢复时间由检测故障的延迟和恢复故障服务的时间决定。在这项工作中,两种方法的检测延迟配置为相同。因此,这两种方法的主要区别在于恢复失败实例的时间。为了导致失败,我们显式地分别关闭一个VM和一个容器1。因此,在Pacemaker情况下,恢复操作是重新启动VM,并以基于容器的方法启动运行服务的新容器。如预期的那样,恢复失败的容器大约需要重启VM的一半时间(表4中的第三行)。
第二个实验检查节点故障期间的事务错误率。在active-activeHA模式下,用于失败实例的通信量将定向到其余节点。失败的实例仍然可以返回那些未完成事务的错误,然后代理才能重定向通信量。我们运行Rally生成添加新用户和查询现有用户的事务。表V列出了不同失败节点类型下失败请求的百分比。可以看出,MySQL服务器的失败导致了1.8%的失败请求,而控制器的失败导致了5.8%的失败请求。这是因为并非所有用户请求都触发数据库事务(控制器节点使用memcached缓存频繁发出的请求的返回值,以避免不必要的数据库事务)。因此,当控制器容器发生故障时,包含正在进行的会话的所有内存状态都将丢失,从而导致观察到的较高错误率。
在这里插入图片描述
微服务架构中的HA框架简化了管理、监视和恢复OpenStack服务的过程。在Pacemaker集群中,每个服务都被视为一个资源,并由可执行文件(也称为资源代理[34],[35])管理。资源代理必须遵循某些规范,如开放集群框架(Open Cluster Framework,OCF),这种复杂性不仅会给开发人员带来额外的负担,而且众所周知,对于操作人员来说,调试起来非常困难。此外,由于服务彼此不同,运营商必须为单个资源操纵资源代理。在微服务架构中,所有资源都部署在容器中。因此,HA框架只需要实现监控相应服务状态的机制。无论服务如何失败,HA框架都将执行相同的操作:终止现有容器并在其位置创建一个新容器。与传统的HA方法相比,微服务架构中的HA遵循“cattle vs. pet”的原则,实现了简单、灵活和高效。
快照/迁移:快照和迁移机制提供了从由于硬件升级、软重新启动等原因而计划的主机维护中恢复系统的能力。但是,快照和迁移在将状态持久化到本地磁盘或跨主机传输状态时会带来开销。在下面的段落中,我们将根据捕捉和迁移OpenStack中包含MySQL数据库服务的VM/容器的时间和状态大小来度量这样的开销。
要执行VM快照,我们使用virsh save;对于Docker容器,我们使用Docker checkpoint。考虑到容器内存迁移机制的不成熟(见第III-C小节),我们通过使用scp将检查点状态从源主机发送到目标主机来模拟迁移。
表六显示了我们的实验结果。对于VM和container,大多数快照操作都涉及将内存页转储到本地磁盘。因此,持续时间与进程的驻留集大小(RSS)成比例。由于VM进程2通常由大量进程组成,VM的RSS明显大于存在少量微服务进程的容器。因此,我们观察到对VM进行快照大约需要7秒(RSS为530MB),而对container(RSS为107MB)则需要<1秒。正如预期的那样,容器的RSS越小,迁移的预拷贝轮的传输时间就越快。
在这里插入图片描述
总体而言,轻量级容器从故障中恢复更快,并且在快照/迁移中的开销更少。此外,微服务体系结构的HA模型的简单性使得它比Pacemaker示例的现有HA模型更容易扩展以支持不同的服务。

B. 扩展

本小节介绍我们在处理越来越多的请求时对所建议体系结构的可伸缩性的评估。生成工作负载请求是为了强调Keystone组件(无状态)和MySQL galera集群(有状态),以及各种并发连接。由于这些工作负载既不是CPU也不是内存密集型的,vm和容器具有可比的性能。在这里,我们报告的结果展示了容器通过将新的服务容器快速实例化到系统中来处理不断增加的工作负载的能力。
图5将Keystone的用户身份验证延迟曲线绘制为不同控制器容器数量下并发用户的函数。对于每个曲线,延迟随着并发使用的数量从4到512的变化而不断增加每个Keystone进程必须处理更多的请求。当集群中添加了额外的控制器容器时,负载平衡器可以向每个Keystone进程分发更少的请求,从而加快身份验证时间。因此,在并发用户数较高(从128到512)的情况下,与1个控制器情况相比,3个控制器情况显著地将延迟减少到一半以上。由于容器通常可以在几秒钟内实例化,因此基于容器的微服务风格DevOps允许OpenStack控制平面更好地及时响应工作负载变化。
在这里插入图片描述
图6描绘了具有不同集群大小的MySQL数据库的吞吐量。工作负载是通过改变并发用户的数量(最多512个)和事务类型(只读和读写)生成的。结果证明了扩展数据库集群的性能优势。对于这两种工作负载设置,3节点集群的性能都优于1节点和2节点集群。这是因为与Keystone一样,集群中更多的数据库节点意味着单个节点上的开销更少。但是,对于读写事务(图6(b)),性能的提高小于只读事务(图6(a)),因为只有当集群中的所有节点都验证了写事务之后,才能提交写操作。结果是附加节点可能会降低写入性能。我们不尝试只写工作负载,因为实际上,OpenStack工作负载是读写事务的混合。
我们注意到,虽然像数据库这样的有状态组件有用于写操作的复制开销,但由于写操作不支配工作负载,因此开销是可以容忍的。因此,当发现某些组件存在瓶颈时,可以通过提供更多相应节点类型的容器来提高性能。

5. 限制和讨论

这项工作揭示了当前容器技术中的几个开放性问题,在容器化基础设施云服务的上下文中,容器是一种非典型的使用场景。基础设施服务需要访问操作系统级设施,例如内核模块、内存状态和物理设备。这样的需求使得在不牺牲可移植性和安全性的情况下很难将这些类型的服务包含进来,我们将在下面进一步阐述这一点。
容器操作系统:我们当前用于与主机操作系统交互的容器化方法有些特别,在某些情况下,我们以特权模式运行容器,并开发特定的解决方法来规避Linux内核的限制。这是集装箱化的障碍,暴露了安全风险。
为了解决这个问题,我们认为OS应该通过特殊的接口向容器公开更多的内核函数。例如,可以为特权操作系统服务(例如加载内核模块)定义细粒度访问控制,而不牺牲安全性。Atomic[36]和CoreOS[37]是用于容器的Linux发行版,但它们具有指定访问控制的复杂机制。潜在的解决方案包括微内核和多内核操作系统架构,它们在用户级公开选定的内核功能[38],[39]。因此,容器可以通过定义良好的接口管理内核服务。Multikernel还允许在容器之间迁移内核状态,这为云基础设施服务提供了额外的好处,例如升级计算/网络节点而无需重新启动。
容器状态复制:我们认为应该进一步改进容器状态复制。需要考虑的事情包括权衡复制的开销和此机制在允许在容器中部署关键服务方面带来的新机会。启用状态复制后,我们希望在故障转移期间降低错误率(请参见表V)。
容器状态复制也可以直接用于容器实时迁移[40]。尽管在这方面有一些积极的开发工作[29],但在集装箱世界中,这种能力是否真的有益值得商榷。
容器中的直接I/O:访问I/O设备(如NIC)对于某些基础设施服务(如OpenStack Neutron)至关重要。然而,用于访问主机设备的现有容器机制要么是低效的(通过NAT)[10],要么是特别的(例如,通过特权容器)。我们相信,从容器启用直接I/O访问可以解决性能问题,但会引入可移植性和安全性问题,特别是在需要支持这些容器的迁移时。

6. 相关工作

我们的工作得益于现代容器技术的进步,并从部署和操作大型分布式系统的最佳实践中获得灵感。具体来说,我们的设计主要基于使用Docker容器交付和管理应用程序的技术。
Docker已经发布了大量的应用程序,因为Docker提供了一个可移植的执行环境,包括代码、运行时、系统库等。Docker Hub[41]中的例子包括从前端web服务器(如httpd、nginx)到后端数据存储(如MySQL、redis、mongodb),从微型操作系统(如。,从独立的分析服务(如single Spark实例)到复杂的分布式应用程序(如storm、hadoop)。我们的工作将范围扩展到云基础设施管理软件,如OpenStack。
最近的一个研究OpenStack容器化的项目是Kolla[8]。Kolla的目标是提供生产就绪的容器,允许快速的OpenStack部署和适当的定制。然而,Kolla缺乏对动态配置或高可用性的支持。我们的工作与Kolla是正交的,因为我们表明,在Kolla中缺少的DevOps任务很难通过人工干预实现扩展和故障恢复。我们通过将容器集成到微服务体系结构中,使DevOps更加高效和可扩展,从而完全自动化DevOps。
微服务架构需要一个功能生态系统,允许基于容器的DevOps获得所有期望的好处。例如,和fleet一样,kubernetes[42]和Docker Swarm[43]都是集群工具,它们将跨主机的容器集群作为一个系统进行管理。consun[44]、Eureka[45]、SkyDNS[46]和ZooKeeper[47]是etcd的替代品,etcd提供密钥/值存储以支持动态服务注册和发现。动态配置也可以通过组合Registrator[48]和HttpRouter[49]来实现。然而,基于这些工具构建的现有工作仅限于用户空间应用程序。本文通过对不同工具的比较,论证了微服务体系结构的合理实现,有利于加快基础设施管理软件的开发。

7. 结论

我们以OpenStack为例,讨论并展示了将云基础设施服务封装起来,并与微服务风格的体系结构相结合,可以极大地提高操作效率。在处理基础设施服务时,我们已经表明,仅仅在容器中运行服务不足以获得容器化和微服务风格设计的全部好处。我们确定了提高操作效率的三个主要挑战:(1)最小化服务的交叉配置;(2)保持服务的运行状态;(3)提供对主机资源的安全访问。我们解决了大多数这些挑战,并确定了当前容器技术的基本缺点,这些缺点需要在使用容器作为基础设施软件的完整解决方案出现之前加以解决。我们的工作探索并评估了提供可移植容器映像和处理状态服务的不同方法。结合我们提出的编排架构,我们的原型提供了动态的服务注册和发现,这对于处理服务伸缩和失败是必不可少的。基于我们的工作,我们确定了需要进一步研究的领域,包括:内存状态复制、容器的主机内核状态管理以及容器之间的高效设备访问/共享。

参考

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值