关键要点
- 面向计算实例的无服务器基础设施被资源类型所取代。
- 管理无服务器基础设施需要一些规划和规范,以确保在系统演化时可以有效地维护它们。
- 根据功能区域和系统级别的功能将大型基础设施拆分为较小的基础设施,可以让部署变得更快,同时降低了风险。
- 我们必须理解和管理独立基础设施之间的依赖关系,从而更有效地管理基础设施。
- 基础设施管理应该被集成到持续交付管道中,让基础设施与代码变更一起更新。
新硬件的味道
我仍然记得多年前的那个早上,一些新的服务器被送到我当时所在公司的办公室里。我们很兴奋地把它们启动起来,并闻到了新设备首次使用时的那种独特的气味。同一天,我们一丝不苟地开始研究一些基本的配置操作。这些新服务器将成为我们公司未来几年的“下一代”硬件平台。
毫不奇怪,我们很快想到了怎么给我们的新服务器取名字。我们使用了托尔金小说中的角色名,所以第一台服务器被命名为甘道夫!经过几周的调整后,服务器被放进了数据中心,它们很快就会开始处理生产流量。
我敢打赌,在阅读这篇文章的人当中,一定有很多人都有类似的故事可以分享。当时,硬件更加贴近基础设施管理员的想法。任何项目都涉及硬件基础设施规划和执行。定期访问数据中心是家常便饭,有时需要花费数小时或数天时间来安装和测试服务器、网络和电源设备。即使在正常的系统操作期间,任何涉及硬件的变更都需要经过仔细规划,甚至需要更频繁地访问数据中心。
基础设施即代码基础
随着云计算成为基础设施交付和管理的事实标准,硬件本身已成为类似电力的商品。除了具有非常特殊硬件要求的公司之外,大多数组织都会根据自身的基础设施需求转向了云端。由于云基础设施的动态特性以及大多数组织所要求的资源配置,仍然以过去的眼光看待硬件基础设施是不可思议的。
2012 年前后,Randy Bias首次用“将你的服务器视为牛而非宠物”这句话来描述在云计算环境中有效管理大规模云基础设施所需的理念。快进到 2018 年,基础设施即代码(IaC)成为云基础设施管理的标准做法。
基础设施即代码要求使用某种形式的版本控制系统来捕获和版本化基础设施配置的所有内容,并提供将配置应用于实际基础设施资源(物理或虚拟)所需的工具。
服务器已死
自从云计算(2006 年)出现以来,它几乎全是关于虚拟服务器(也称为虚拟机或计算实例),因此 IaC 实践和工具在很大程度上围绕着面向服务器的基础设施发展,这通常被称为基础设施即服务(IaaS)。但云端的服务器也在慢慢成为商品。
在过去几年中,一组新的云服务让“去服务器”(也称为计算实例)成为主要的资源类型。这种新范例被称为无服务器。不过,无服务器并不意味着不使用服务器。实际上,有些服务器支持所有类型的无服务器服务,但作为用户,你看不到它们,也不需要与它们发生交互。云平台负责为你管理服务器(以及容器),并提供服务,为创建云原生架构提供通用构建块:计算、存储、API 管理、消息传递、安全、监控等。
云资源万岁
即使服务器已从无服务器版图中消失,但这并不意味着你可以完全忘记基础设施配置。我们现在需要通过配置 function、存储桶或数据表、API、消息队列或主题以及许多其他资源来实现安全和监控,而不是配置传统 IaaS 中常见的计算实例和网络相关资源。
在基础设施管理方面,由于无服务器的细粒度特性,无服务器架构通常需要管理更多的资源。同时,因为不需要面对服务器,所以基础设施配置可以作为单阶段活动来完成,不需要单独管理运行在不同类型服务器上的软件工件(Web 服务器、各种应用程序运行时、容器编配系统、数据库,消息代理等)。
即使采用了这种简化的基础设施资源管理方式,仍然需要使用专用工具来定义和应用基础设施配置。云平台提供商提供了专有的解决方案。AWS 提供了 Cloud Formation ,Azure 提供了 Resource Manager ,谷歌提供了 Cloud Deployment Manager 。或者,可以使用像 HashiCorp Terraform 这样更通用的解决方案。
无服务器在线学习平台
让我们使用一个相对简单但相当现实的无服务器架构示例作为进一步分析和讨论的基础。想象一下,我们需要创建一个系统,为付费客户提供在线学习课程。这样的系统需要满足以下几项功能:
- 管理用户详细信息并提供对系统的身份认证;
- 保存课程详细信息和注册用户信息;
- 保存课程资料,包括教学视频;
- 为注册用户提供课程资料;
- 跟踪课程进度;
- 接受信用卡付款;
- 发送电子邮件通知。
为了快速上市并完全拥抱云理念,我们可以为系统的一些非差异化部分(如用户管理和身份认证、卡支付处理和电子邮件活动管理)使用云服务,以此来缩短产品上市时间。剩余的部分,如课程管理和交付业务领域可以使用 AWS 上的无服务器架构。我们也可以使用其他主要的无服务器平台(如 Azure 或谷歌云),但鉴于 AWS 的受欢迎程度和成熟度——以及我对它的熟悉程度——在本文中,我选择使用 AWS。
系统的第一版设计可能缺乏对可维护性和可操作性方面的考虑,看起来如下图所示。直接映射到云资源的所有应用程序组件都属于单个基础设施。
图 1:使用单个 function 的初始设计
为了简化系统设计,整个后端逻辑包装在单个 AWS Lambda function 中,这个 function 负责处理来自 API 网关的 API 调用、存储和检索来自一组 DynamoDB 表的数据以及与使用第三方服务(有些操作只能在后端组件上安全执行)进行交互。这个初始设计可能是最快的设计,但有很多缺点:
- 单个 function 的职责太多,它的代码很快就会变得难以维护,特别是如果有很多开发人员参与其中;
- 此外,在添加新的功能需求时,可能很难协调开发人员之间的工作,每次变更都需要重新部署 function——随着系统的增长,这种情况会愈演愈烈;
- 这个超级 function 可以访问所有的系统数据以及所有的第三方服务,这可能存在安全风险;
- 由于这个 function 扮演了如此多的角色,因此要确定最佳的资源分配(内存 +CPU)并适当地配置其他属性(超时、并发)相当具有挑战性。
上图描绘的是一种单体架构,应用程序组件之间存在耦合,而且所有资源都属于单个基础设施。
或许这个初始版本是成功提供在线学习平台所需的全部内容,没有进一步投入的必要。但是,在大多数情况下,在最初的成功之后,用户会要求在平台中添加新功能,因此花费更多精力来让当前的解决方案能够为进一步的增长和发展做好准备通常是值得的。
为了缓解初始版本平台中存在的一些问题,我们将单体 function 分解为几个较小的 function,每个 function 负责一个特定的功能区域。
图 2:基于功能区域分解 function
修改后的设计应该更易于维护,更加安全,并且更易于配置,可以满足每种功能的特定流量模式和资源要求。但仍然存在一个重要问题:整个系统位于单个基础设施上。
大型基础设施面临的挑战
为什么使用单个基础设施会出现问题?
关于上面显示的解决方案,需要强调的是它们只包含架构中的主要资源。为了让解决方案能够正常运行、安全且可操作,还需要配置更多的资源。可以成为基础设施一部分的其他资源包括:
- 有关 API 网关路径、请求和响应的特定资源——使用 AWS API Gateway 公开的每个 API 端点都需要一组配置资源,因此对于具有多个端点的 API,仅 API 网关就需要用到数十个基础设施资源。
- 安全角色和策略资源——基础设施资源之间的任何交互几乎都需要定义和附加显式 AWS IAM 角色和访问控制策略。
- DNS 特定资源——如果需要通过自定义域名访问 API 端点或 S3 对象,还需要配置一组 AWS Route53 资源。
- CDN 特定资源——如果需要通过内容交付网络(CDN)访问S3 对象,还需要配置 AWS CloudFront 。
对于这个简单的在线学习系统来说,它可能不是一个巨大的挑战(即使考虑图中未描绘的资源)。但是,对于较大的无服务器(甚至更传统的)系统,使用单个大型基础设施就会遇到许多挑战:
- 基础设施中包含的资源越多,配置或更新它所需的时间就越长;
- 当许多功能区域或逻辑独立的服务依赖于相同的基础设施时,那么就必须为每个变更协调整个基础设施(从当前状态移动到目标状态);
- 某些基础设施资源可能需要花费大量的时间来(重新)创建和更新(例如 CloudFront ),因此将这些资源包含在与应用程序特定资源相同的基础设施中会增加部署的难度;
- 如果更新基础设施时出现问题,那么在将问题解决之前不能进行其他变更,即使它可能只影响了系统的一小部分;
- 由于所有资源都是一起管理的,因此很容易在它们之间引入依赖关系,随着时间的推移,这会导致难以管理的资源之间错综复杂的引用网络;
- 同样,在基础设施资源之间应用的安全策略可能会变得过于宽松并带来安全风险;
- 最后,使用单个基础设施会影响持续交付(CD),因为每次应用程序代码发生变更时,可能都需要对整个基础设施做出修改,或者将基础设施配置步骤从交付管道中移除。
出于这些原因,如果有可能,应尽量避免使用大型的基础设施。无服务器系统可以从单个基础设施开始。但是,过了一段时间之后,将不断增长的基础设施拆分成一组较小的基础设施可能是有好处的,这样可以支持系统内的不同功能区域。通过使用这种方法,可以更明确地识别和管理基础设施资源之间的依赖关系。在后面的部分,我将讨论有关管理共享基础设施和跨基础设施资源依赖关系的最佳实践。
分而治之
在拆分无服务器系统的基础设施时,需要遵循的原则与其他模块化工作类似。我们需要考虑很多事情,但主要的驱动因素应该是尽量减少耦合和最大化聚合。其次,需要注意的是,系统的不同部分将以不同的速度发生变化。
鉴于无服务器架构的基础设施和应用层之间的紧密关系,基础设施级别的模块化工作将会影响应用程序的模块化,反之亦然。这与传统的云架构截然不同,在传统的云架构中,基础设施主要由计算实例组成(容器编排解决方案可能位于顶部),它的演化可以完全独立于应用程序组件或服务。
尽管我们可以在传统的云基础设施中使用多个较小的栈,但这种模式并没有被广泛采用。大多数基础设施都是由(Dev)Ops 团队管理的单个(或少数)大型的基础设施,而且通常变化相当缓慢。此外,近来出现的容器编排解决方案使用大型基础设施来运行容器化的工作负载。
回到我们在线学习平台,我们可以轻松识别出一些功能区域和相应的基础设施资源,这些资源可以分成独立的栈,比如:内容管理、课程管理、用户管理和支付处理。
图 3:为每个功能区域拆分基础设施
现在,每个基础设施栈都可以进行独立管理,当新功能要求对基础设施层做出变更时,只需要对其中一个或几个栈做出变更。
共享基础设施
早些时候,我将 DNS 配置作为基础设施资源类型的示例,但未在解决方案图中明确标出。它也是最有可能在系统级别定义的基础设施资源的一个很好的例子,并且在整个系统的生命周期中不会发生太大变化,即使特定于应用程序的资源需要不断演化以满足新的功能需求。
我们的在线学习平台需要通过自定义域名访问(而不是使用云供应商的域名,如 AWS)。我们需要在 AWS Route53 中注册域名,然后为域名配置托管区域和 API 网关的 DNS 记录。 DNS 特定的基础设施资源不属于任何先前标识的栈,而应该属于 DNS 栈的一部分。如果有必要,负责平台服务的栈可以引用 DNS 栈中的资源。
图 4:DNS 相关资源的共享栈
一旦配置好以后,DNS 基础设施通常不会发生太大变化,因此可以将其与负责系统功能的资源分开。除非 DNS 配置发生变更,否则就无需在每次部署时都检查要不要更新它。
每当系统需要一组不需要立即参与支持功能需求并以不同于基础设施栈其他部分不同的速率发生变更的资源时,它们应该与基础设施栈的应用程序特定元素分开,从而可以独立管理它们。
不过,依赖于共享基础设施的基础设施资源都必须能够在部署时获取其物理标识符。AWS 使用 Amazon 资源名称(ARN)作为基础设施级别引用的主要资源标识符。共享基础设施栈需要在部署时让这些资源标识符可见,因为其他栈有可能会引用这些标识符。我们可以在 AWS Cloud Formation 中声明栈输出来让标识符可见,如果使用的是 HashiCorp Terraform,则可以在栈状态中查找资源。
基础设施栈交叉依赖性
到目前为止,在线学习平台的应用程序特定栈是独立的,除了上一节中讨论的共享 DNS 特定基础设施之外,没有其他任何依赖。现在,我们假设需要记录用户为课程付款的行为(使用 Payment API 完成付款)、激活用户注册的课程,并使用第三方电子邮件服务发送电子邮件通知。
这个新功能可以这样实现:将新的 Payment Publisher function 附加到 Payment Table(Payment Stack 的一部分),并发送一条消息到 Payment Topic(使用 AWS SNS )上,这样 Payment Subscriber function(User Stack 的一部分)就可以调用和更新 User Table 中的相关信息,并发送通知邮件。
图 5:资源之间的跨栈依赖关系
完成这些变更后,User Stack 现在依赖于 Payment Stack,这样它就可以让 Payment Consumer function 订阅 Payment Topic。这种类型的跨栈依赖性对如何进行栈部署提出了一些顺序约束,以确保在用到相关资源之前它们已经准备就绪。如果将系统部署到新环境中(出于演示或测试目的),那么 Payment Stack 必须在 Uesr Stack 之前部署好。
在出现跨栈依赖性的情况下,需要确保资源标识符保持稳定不变,否则跨越多个栈的数据流可能会在其引用的资源突然更改其标识符(AWS 资源对应的是 ARN)时发生中断。
持续交付管道中的基础设施管理
无服务器计算非常独特,因为它能够支持快速交付,同时为使用它的组织提供低成本的可伸缩性。为了能够从无服务器的敏捷性中获得最大的好处,必须采用持续交付,将基础设施管理有效地插入到交付管道中。
对于无服务器架构来说,将基础设施配置与应用程序代码放在相同的代码库中是自然的事情。当交付管道启动时,在更新可能已发生变更的 function 代码之前通常需要确定是否需要创建或更新基础设施。
保持功能区域之间的基础设施栈的小身材和分离有助于确保基础设施的大部分变更都可以在几秒钟或最多几分钟内完成,因为将基础设施资源的当前状态与所需基础设施资源( 由 AWS Cloud Formation 或 HashiCorp Terraform 负责处理)进行比较的任务被限定在了需要做出更新的部分基础设施栈上。
图 6:作为持续交付管道的一部分基础设施栈部署
对于在线学习平台来说,有必要为每个栈定义单独的交付和部署管道,这些管道负责将变更应用于基础设施上,并部署新版本的 function 代码。对特定功能区域(基础设施栈配置或应用程序逻辑)的变更将触发执行管道并仅将变更应用于这个特定的栈。
此外,对于没有代码部署需求的栈(因为不包括任何 function),可以跳过某些管道步骤,例如提交阶段测试或代码部署。
概要
从服务器占主导地位的基础设施栈转变成无服务器架构带来了新的机遇和挑战。无服务器架构采用了更广泛的云服务,让基础设施栈变得更加异构化,从而让组织和管理它们变得更加棘手。
为了在无服务器时代有效地管理云基础设施,相关的理念、实践和工具必须不断发展。因为现代云架构采用了无服务器技术,基础设施管理将成为软件交付的一个组成部分,并需要更多的人参与实践。
与往常一样,通过最小化基础设施资源之间的耦合和最大化它们之间的聚合来推动模块化才是关键。这将有助于避免出现难以管理的大型基础设施栈,同时实现无服务器架构所带来的快速价值交付。
关于作者
Rafal Gancarz 是一名 IT 顾问,目前正在与星巴克合作。他是一位多才多艺的技术专家,拥有超过 15 年的建立高质量分布式系统的经验。Rafal 是一位技术架构师,在众多架构风格和模式方面拥有广泛的专业知识。他还是一名优秀的实践开发人员,能够降低 IT 解决方案的核心复杂性,同时在技术团队中提供指导。他还是一名认证 Scrum Master、经验丰富的敏捷实践者和传播者,热衷于改善项目交付和建立高绩效团队。Rafal 最近花了一年多时间在 AWS 平台上使用无服务器技术开展大型项目,并拥有使用无服务器栈构建企业级分布式系统的经验。
评论 1 条评论