构建Kubernetes的应用程序

考虑到可扩展性,可移植性和健壮性,设计和运行应用程序可能具有挑战性,尤其是随着复杂性的增长。应用程序或系统的体系结构对它的运行方式,它对环境的期望以及...

介绍

考虑到可扩展性,可移植性和健壮性,设计和运行应用程序可能具有挑战性,特别是随着系统复杂性的增长。 应用程序或系统的体系结构会严重影响它的运行方式,它对环境的期望以及它与相关组件的紧密结合程度。 在设计阶段遵循某些模式并遵循某些操作实践可以帮助解决应用程序在高度分布式环境中运行时遇到的一些最常见问题。

虽然软件设计模式和开发方法可以产生具有正确扩展特性的应用程序,但基础架构和环境会影响已部署系统的操作。 DockerKubernetes这样的技术可以帮助团队打包软件,然后在分布式计算机平台上分发,部署和扩展。 学习如何最好地利用这些工具的强大功能可以帮助您以更大的灵活性,控制力和响应性来管理应用程序。

在本指南中,我们将讨论您可能希望采用的一些原则和模式,以帮助您扩展和管理Kubernetes上的工作负载。 尽管Kubernetes可以运行多种类型的工作负载,但您做出的选择可能会影响操作的简便性以及部署时可用的可能性。 您如何构建和构建您的应用程序,将您的服务打包到容器中,以及配置Kubernetes内的生命周期管理和行为都会影响您的体验。

设计应用程序可伸缩性

制作软件时,许多要求会影响您选择使用的模式和架构。 使用Kubernetes,最重要的因素之一是能够水平扩展 ,调整应用程序的相同副本的数量以分配负载并提高可用性。 这是垂直缩放的替代方案,它试图通过在具有更多或更少资源的机器上部署来操纵相同的因素。

特别是, 微服务是一种软件设计模式,适用于群集上的可伸缩部署。 开发人员创建小型可组合应用程序,通过定义良好的REST API在网络上进行通信,而不是通过内部编程机制进行通信的大型复合程序。 将整体应用程序分解为离散的单一用途组件使得可以独立扩展每个功能。 通常在应用程序级别存在的大部分复杂性和组成都转移到可由Kubernetes等平台管理的运营领域。

除了特定的软件模式之外, 云计算本地应用程序的设计还有一些额外的考虑因素。 云本机应用程序遵循微服务架构模式,具有内置的弹性,可观察性和管理功能,以适应云中群集平台提供的环境。

例如,使用运行状况报告指标构建云本机应用程序,以便平台在实例变得不健康时管理生命周期事件。 他们生产(并提供出口)强大的遥测数据,提醒操作员遇到问题,并让他们做出明智的决定。 应用程序旨在处理常规的重新启动和故障,后端可用性的变化以及高负载,而不会导致数据损坏或无响应。

遵循12个因子应用哲学

Twelve-Factor App哲学是一种流行的方法,可帮助您专注于创建云就绪Web应用程序时最重要的特性。 为了帮助开发人员和运营团队了解旨在运行在云中的Web服务共享的核心品质,这些原则非常适用于生活在像Kubernetes这样的集群环境中的软件。 虽然单片应用程序可以从遵循这些建议中受益,但围绕这些原则设计的微服务架构特别适用。

对十二个因素的简要总结是:

  1. Codebase:管理版本控制系统中的所有代码(如Git或Mercurial)。 代码库全面规定了部署的内容。
  2. 依赖关系:依赖关系应该由代码库完全明确地管理,可以是自定义的(存储在代码中),也可以是以包管理器可以从中安装的格式固定的版本。
  3. 配置:从应用程序分离配置参数并在部署环境中定义它们,而不是将它们烘焙到应用程序本身中。
  4. 备份服务:本地和远程服务都被抽象为可通过网络访问的资源,并在配置中设置连接详细信息。
  5. 构建,发布,运行:应用程序的构建阶段应该与应用程序发布和操作过程完全分离。 构建阶段从源代码创建部署工件,发布阶段结合工件和配置,运行阶段执行发布。
  6. 进程:应用程序被实现为不应该依赖本地存储状态的进程。 如第四个因素所述,国家应该被卸载到支持服务。
  7. 端口绑定:应用程序应该本地绑定到端口并监听连接。 路由和请求转发应该在外部处理。
  8. 并发性:应用程序应该依靠通过流程模型进行扩展。 同时运行应用程序的多个副本(可能跨多个服务器)允许在不调整应用程序代码的情况下进行缩放
  9. 处理性流程应该能够快速启动,并且能够在没有严重副作用的情况下正常停止。
  10. 开发/产品平价:您的测试,分期和生产环境应密切配合并保持同步。 环境之间的差异是出现不兼容性和未测试配置的机会。
  11. 日志:应用程序应该将日志流式传输到标准输出,以便外部服务可以决定如何最好地处理它们。
  12. 管理流程:一次性管理流程应针对特定版本运行,并随主要流程代码一起提供。

通过遵守由十二个因素提供的指导方针,您可以使用适合Kubernetes执行环境的模型来创建和运行应用程序。 十二个因素鼓励开发人员专注于其应用程序的主要责任,考虑组件之间的操作条件和接口,并使用输入,输出和标准进程管理功能在Kubernetes中可预测地运行。

容器化应用程序组件

Kubernetes使用容器在其集群节点上运行隔离的打包应用程序。 要在Kubernetes上运行,您的应用程序必须封装在一个或多个容器映像中,并使用像Docker这样的容器运行时执行。 虽然容器化您的组件是Kubernetes的一项要求,但它也有助于强化上面讨论的十二要素应用程序方法中的许多原则,从而实现轻松扩展和管理。

例如,容器提供应用程序环境和外部主机系统之间的隔离,支持面向服务的联网网络通信方式,通常通过环境变量进行配置,并将日志写入标准错误和标准输出。 容器本身鼓励基于进程的并发性,并通过独立扩展并捆绑进程的运行时环境来帮助维护开发/产品兼容性。 这些特性使您可以打包应用程序,以便它们在Kubernetes上顺利运行。

关于优化容器的准则

容器技术的灵活性允许封装应用程序的许多不同方式。 但是,有些方法在Kubernetes环境中比其他方法更好。

大多数关于容器化应用程序的最佳实践都与图像构建有关,您可以在其中定义如何在容器内设置和运行软件。 一般来说,保持图像尺寸小和可组合可提供许多好处。 通过大小优化的图像可以减少启动集群上的新容器所需的时间和资源,方法是保持足迹可管理并在图像更新之间重新使用现有图层。

创建容器图像时的第一步是尽最大努力将构建步骤与将在生产中运行的最终图像分开。 构建软件通常需要额外的工具,需要额外的时间,并且会产生可能在容器之间不一致的工件,或者根据环境的不同,对最终的运行时环境不必要。 将构建过程与运行时环境完全分离的一种方法是使用Docker多阶段构建 多阶段构建配置允许您指定在构建过程中使用的一个基础映像,并定义在运行时使用的另一个基础映像。 这使得可以在安装了所有构建工具的情况下使用图像构建软件,并将生成的构件复制到稍后每次都会使用的纤细流线型图像。

有了这种类型的功能,在最小的父图像之上构建生产图像通常是一个好主意。 如果你想完全避免像ubuntu:16.04 (其中包括一个相当完整的Ubuntu 16.04环境)的“distro”风格的父层中发现的膨胀,你可以scratch构建你的图像 - Docker最小的基本图像 - 作为父级。 但是, scratch基础层不提供许多核心工具的访问权限,并且通常会破坏有关某些软件所处环境的假设。 作为一种替代方案, Alpine Linux alpine映像已经成为一个可靠的,最小的基础环境,它提供了一个小巧但功能全面的Linux发行版。

对于像Python或Ruby这样的解释型语言,由于没有编译阶段,解释器必须可用于在生产环境中运行代码,因此范例略有变化。 但是,由于Slim图像仍然很理想,因此Docker Hub上提供了许多基于Alpine Linux构建的特定于语言的优化图像。 为解释型语言使用较小图像的好处与编译语言相似:Kubernetes将能够快速将所有必要的容器图像提取到新节点上,以开始有意义的工作。

决定容器和豆荚的范围

虽然您的应用程序必须容器化才能在Kubernetes集群上运行,但Pod是Kubernetes可以直接管理的最小抽象单元。 一个吊舱是一个由一个或多个紧密耦合的容器组成的Kubernetes物体。 容器中的容器共享一个生命周期,并作为一个单元一起管理。 例如,容器总是安排在同一个节点上,一起启动或停止,并共享文件系统和IP空间等资源。

首先,很难发现将应用程序划分为容器和Pod的最佳方式。 这使得了解Kubernetes如何处理这些组件以及每个抽象层为您的系统提供什么很重要。 一些注意事项可以帮助您用这些抽象中的每一个来确定您的应用程序的封装自然点。

确定容器有效范围的一种方法是寻找自然发展边界。 如果您的系统使用微服务架构进行操作,则经过精心设计的容器经常构建为表示离散功能单元,这些功能通常可用于各种环境。 这种抽象级别允许您的团队发布对容器映像的更改,然后将此新功能部署到使用这些映像的任何环境中。 可以通过组合各个容器来构建应用程序,每个容器完成一个给定的功能,但可能无法单独完成整个过程。

与上述相反,吊舱通常是通过考虑系统的哪些部分可能从独立管理中受益最大而构建的。 由于Kubernetes使用pod作为其最小的面向用户的抽象,这些是Kubernetes工具和API可直接交互和控制的最原始的单元。 您可以启动,停止并重新启动pod,也可以使用基于pod构建的更高级别的对象来引入复制和生命周期管理功能。 Kubernetes不允许您独立管理容器中的容器,因此您不应将容器分组在一起,这可能会受益于单独管理。

由于许多Kubernetes的特征和抽象直接处理豆荚,所以将单个豆荚应该放在一个豆荚中的物品捆绑在一起并将单独放置的物品分开是有意义的。 例如,将Web服务器与应用程序服务器分离到不同的容器中,可以根据需要独立缩放每个图层。 但是,如果适配器提供Web服务器需要正常工作的基本功能,则将Web服务器和数据库适配器捆绑到同一个容器中可能很有意义。

捆绑支持容器增强Pod功能

考虑到这一点,哪种类型的容器应该捆在一个容器中? 通常,主容器负责完成该容器的核心功能,但可定义额外的容器来修改或扩展主容器或帮助其连接到唯一的部署环境。

例如,在Web服务器窗格中,Nginx容器可以监听请求并提供内容,而当关联容器在存储库更改时更新静态文件。 将这两个组件打包到一个容器中可能是诱人的,但将它们作为单独的容器来实现会有很大的好处。 Web服务器容器和存储库抽取器都可以在不同的上下文中独立使用。 他们可以由不同的团队维护,并且可以分别进行开发,以概括他们的行为以与不同的伴侣容器一起工作。

Brendan Burns和David Oppenheimer确定了三种主要模式,将支持容器捆绑在他们的论文中,描述了基于容器的分布式系统的设计模式 这些代表了将容器包装在一个容器中的一些最常见的用例:

  • Sidecar:在这种模式下,辅助容器扩展并增强了主容器的核心功能。 该模式涉及在单独的容器中执行非标准或实用功能。 例如,转发日志或监视更新配置值的容器可以在不明显改变其主要焦点的情况下增加pod的功能。
  • 大使:大使模式使用补充容器来提取主容器的远程资源。 主容器直接连接到大使容器,大容器依次连接并抽象潜在的复杂外部资源池,如分布式Redis群集。 主容器不必知道或关心连接到外部服务的实际部署环境。
  • 适配器:适配器模式用于翻译主容器的数据,协议或接口,以与外部各方预期的标准保持一致。 适配器容器可以统一访问集中式服务,即使它们所服务的应用程序可能只能本地支持不兼容的接口。

将配置提取到ConfigMaps和Secrets中

虽然可以将应用程序配置烘焙到容器映像中,但最好在运行时配置组件以支持在多个上下文中进行部署,并允许更灵活的管理。 为了管理运行时配置参数,Kubernetes提供了两个名为ConfigMapsSecrets的对象。

ConfigMaps是一种用于存储可在运行时暴露给Pod和其他对象的数据的机制。 存储在ConfigMaps中的数据可以作为环境变量呈现,或者作为文件挂载到窗格中。 通过设计您的应用程序以从这些位置读取数据,您可以在运行时使用ConfigMaps注入配置,并修改组件的行为,而无需重新构建容器映像。

秘密是一种类似的Kubernetes对象类型,用于安全地存储敏感数据并根据需要选择性允许Pod和其他组件访问。 秘密是一种将敏感材料传递给应用程序的便捷方式,无需将它们作为普通文本存储在正常配置的易于访问的位置。 在功能上,它们的工作方式与ConfigMaps非常相似,因此应用程序可以使用相同的机制从ConfigMaps和Secrets中使用数据。

ConfigMaps和Secrets帮助您避免直接在Kubernetes对象定义中进行配置。 您可以映射配置密钥而不是值,允许您通过修改ConfigMap或Secret来即时更新配置。 这使您有机会在不修改资源的Kubernetes定义的情况下,改变Pod和其他Kubernetes对象的活动运行时行为。

实施就绪和生命力探测

Kubernetes包含大量用于管理组件生命周期的现成功能,并确保您的应用程序始终健康可用。 但是,为了利用这些功能,Kubernetes必须了解它应该如何监控和解释应用程序的健康状况。 为此,Kubernetes允许您定义活性准备就绪探测。

活力探测允许Kubernetes确定容器内的应用程序是否处于活动状态并正在运行。 Kubernetes可以定期在容器中运行命令以检查基本的应用程序行为,或者可以将HTTP或TCP网络请求发送到指定的位置,以确定过程是否可用并能够按预期做出响应。 如果活跃度探测失败,Kubernetes会重新启动容器以尝试在Pod内重新建立功能。

准备探测器是一种类似的工具,用于确定吊舱是否准备好提供流量。 容器中的应用程序可能需要在准备好接受客户端请求之前执行初始化过程,或者在收到新配置的通知后可能需要重新加载。 当准备就绪探测失败时,Kubernetes不再重新发送容器,而是暂时向该容器发送请求。 这允许吊舱完成其初始化或维护程序,而不会影响整个组的健康。

通过将生存性和准备性探测相结合,您可以指示Kubernetes自动重启Pod或将其从后端组中删除。 通过配置您的基础架构以利用这些功能,Kubernetes可以管理应用程序的可用性和健康状况,而无需额外的操作。

使用部署来管理规模和可用性

之前,在讨论一些pod设计基础时,我们还提到其他Kubernetes对象基于这些基元来提供更高级的功能。 一个部署 ,一个这样的复合对象,可能是最常被定义和操纵的Kubernetes对象。

部署是构建在其他Kubernetes主体上以增加额外功能的复合对象。 他们将生命周期管理功能添加到称为复制集的中间对象,如执行滚动更新,回滚到早期版本以及状态之间转换的能力。 这些复制数据集允许您定义Pod模板以启动和管理单个Pod设计的多个副本。 这有助于轻松扩展基础架构,管理可用性需求,并在出现故障时自动重新启动Pod。

这些附加功能为相对简单的pod抽象提供了管理框架和自我修复功能。 虽然pod是最终运行您定义的工作负载的单位,但它们不是您通常应该配置和管理的单位。 相反,将pod看作构建块,可以在通过部署等更高级对象进行配置时强健地运行应用程序。

创建服务和入口规则来管理对应用层的访问

通过部署,您可以配置和管理多组可互换吊舱以扩展您的应用程序并满足用户需求。 但是,将流量路由到预配的Pod是另外一个问题。 由于pod作为滚动更新的一部分被换出,重新启动或由于主机故障而移动,之前与运行组关联的网络地址将发生变化。 Kubernetes 服务允许您通过维护动态Pod的池路由信息并控制对基础设施各层的访问来管理这种复杂性。

在Kubernetes中,服务是控制流量如何路由到一组Pod的特定机制。 无论是转发来自外部客户端的流量还是管理多个内部组件之间的连接,服务都允许您控制流量的流向。 即使环境发生变化和网络环境发生变化,Kubernetes也会更新和维护将连接转发到相关吊舱所需的所有信息。

在内部访问服务

要有效使用服务,您首先必须确定每组Pod的预期消费者。 如果您的服务仅由在您的Kubernetes集群中部署的其他应用程序使用,则clusterIP服务类型允许您使用只能从集群内路由的稳定IP地址连接到一组Pod。 部署在群集上的任何对象都可以通过将流量直接发送到服务的IP地址来与复制的群集进行通信。 这是最简单的服务类型,适用于内部应用程序层。

可选的DNS插件使Kubernetes能够为服务提供DNS名称。 这允许窗格和其他对象通过名称而不是通过IP地址与服务进行通信。 该机制不会显着改变服务使用情况,但基于名称的标识可以使组织更简单,或者在不知道服务IP地址的情况下定义交互。

公开消费服务

如果接口应该公开访问,那么最好的选择通常是负载平衡器服务类型。 这会使用您的特定云提供商的API来调配负载均衡器,该负载均衡器通过公开的IP地址向服务窗格提供流量。 这允许您将外部请求路由到服务中的窗格,为您的内部集群网络提供受控网络通道。

由于负载平衡器服务类型为每个服务创建负载平衡器,因此使用此方法公开地公开公开Kubernetes服务可能会变得非常昂贵。 为了帮助缓解这种情况,可以使用Kubernetes 入口对象来描述如何根据预定的规则集将不同类型的请求路由到不同的服务。 例如,对“example.com”的请求可能会转到服务A,而对“sammytheshark.com”的请求可能会被路由到服务B.入口对象提供了一种描述如何从逻辑上将混合请求流路由到其目标的方法基于预定义模式的服务。

入口规则必须由入口控制器解释 - 通常是某种类型的负载均衡,例如Nginx - 作为一个群集部署在群集内,该群集实现入口规则并相应地将流量转发到Kubernetes服务。 目前,入口对象类型处于测试阶段,但有几个工作实现可用于最小化集群所有者需要运行的外部负载均衡器的数量。

用声明语法管理Kubernetes状态

Kubernetes在定义和控制部署到集群的资源方面提供了相当多的灵活性。 使用像kubectl这样的工具,您可以强制定义特别对象以立即部署到群集。 虽然这对于在学习Kubernetes时快速部署资源会很有帮助,但这种方法存在缺陷,使其不适合长期生产管理。

命令式管理的一个主要问题是它不会记录您已部署到集群的更改。 这使得在发生故障时恢复很困难或不可能,或者在应用于系统时跟踪操作更改。

幸运的是,Kubernetes提供了一种替代的声明性语法,允许您在文本文件中完全定义资源,然后使用kubectl来应用配置或更改。 将这些配置文件存储在版本控制存储库中是一种监视更改并与用于组织其他部分的审阅流程集成的简单方法。 基于文件的管理还可以通过复制和编辑现有定义来简化现有模式以适应新资源。 将Kubernetes对象定义存储在版本化目录中允许您在每个时间点维护所需群集状态的快照。 这在恢复操作,迁移或跟踪导致系统意外更改的根本原因时非常有用。

结论

管理将运行应用程序的基础架构并学习如何最好地利用现代业务流程环境提供的功能可能令人望而生畏。 然而,当您的开发和操作实践与工具构建的概念相一致时,像Kubernetes这样的系统和像容器这样的技术提供的许多好处变得更加清晰。 使用模式构建您的系统Kubernetes擅长并了解某些功能如何缓解与高度复杂的部署相关的一些挑战,这可以帮助您改善在该平台上运行的体验。