Kubernetes 应用的容器存储和安全防护指南(全)
原文:
zh.annas-archive.org/md5/b12457620515cfda3f141934f38dfcae译者:飞龙
协议:CC BY-NC-SA 4.0
前言
本报告适用于 DevOps 工程师、平台架构师、可靠性工程团队以及对管理 Kubernetes 应用和服务存储感兴趣的应用程序所有者。接下来的页面讨论了在规模化分布式应用程序的数据存储方面面临的挑战,如何解决这些挑战,以及考虑迁移到 Kubernetes 的考虑因素。
第一章概述了在企业生产环境中采用 Kubernetes 的复杂原因,重点是传统存储如何无法满足现代云原生应用的需求。随后讨论了 Kubernetes 存储基元,包括容器存储接口以及软件定义存储如何支持 Kubernetes 工作负载的简要解释。在奠定这一基础之后,其余章节列举了企业数据需求及通过 Kubernetes 存储满足这些需求的策略。主题包括规模和效率的拓扑策略、安全性以及数据保护的挑战。
继续阅读,了解 Kubernetes 如何改变存储和数据管理格局,Kubernetes 原生存储解决方案的关键要素,以及在 Kubernetes 上运行有状态企业应用的最佳实践。
致谢
我要感谢 Portworx 的以下人员贡献他们的专业知识:Sarvesh Jagannivas、Bhavin Shah、Rajiv Thakkar、Ryan Wallner 和 Andy Gower。
第一章:简介
云原生应用架构依赖于 Kubernetes,这是主要的容器管理平台。企业需要一种存储解决方案,能够跟上这些应用处理的数据规模和数据量。传统的基于虚拟机(VMs)运行的单片应用的本地和云端存储解决方案,并不适合在云原生环境中运行,并且不足以满足今天企业的需求。
现代应用程序作为独立服务实现,可以打包并独立部署在各自的容器中,运行在不同的机器上。云原生基础设施是弹性的,意味着可以通过程序控制或声明式地预测性地满足不断变化的计算需求。这种设计使应用程序能够根据需求快速扩展和收缩,但需要灵活的分布式数据存储,能够与应用程序一起扩展。传统存储显然无法胜任这一任务。为了满足企业需求,Kubernetes 存储必须是动态的、自动化的,具有应用程序意识,并且必须在容器级别提供智能备份和恢复的数据保护。
随着应用架构的分布化发展,应用组件的所有权变得更加分散。这是存储策略成为团队共同责任的一个原因,传统上存储策略是专门管理员和经理的领域,现在包括应用所有者在内的团队共同承担。随着更广泛的团队承担这些任务,管理存储的复杂性的负担必须转向自动化。
传统的数据平台以虚拟机作为计算和存储单元,无法有效地管理或保护分布式应用程序的存储。需要的是一种方法,将动态、分布式的 Kubernetes 带给数据存储,就像 Kubernetes 带给计算的革新一样。本报告为理解使 Kubernetes 数据高度可用和安全的存储范式提供了基础,提供动态的扩展和数据保护。
第一章:为什么 Kubernetes 在生产环境中很难
Kubernetes 是现代云原生应用架构的基础。通过在容器中运行微服务,企业可以从硬件定义模型(与物理或虚拟机绑定)转向软件定义范式,实现跨本地、云和混合环境的水平扩展。
当迁移到 Kubernetes 时,第一天的工作并不像简单部署软件那样直接。虽然了解 Kubernetes 操作的人数正在增加,但仍然存在挑战。以下部分重点讨论了三个最重要的挑战:自动化存储、维护集群以及管理为不同计算时代设计的存储。
自动化存储
Kubernetes 最初设计用于处理 无状态 工作负载,即不存储有关先前操作的数据或其他信息。虽然 Kubernetes 已经发展到能够处理 有状态 应用程序,即存储持久数据的应用程序,但它并没有像处理计算资源那样自动化存储。相反,Kubernetes 依赖于位于编排系统外部的存储。
自动化存储并不容易,存储管理员通常有大量工作要做。随着工作负载的出现和消失,或者应用程序的扩展和收缩,提供存储可能是耗时的操作,即使有自动化的帮助也是如此。根据阈值和事件配置系统以响应添加、更改和删除资源是一项复杂的工作。
虽然 Kubernetes 在集群内负载均衡服务和请求,但它并不真正知道如何负载均衡存储。不同的工作负载、应用程序和用例具有不同的存储使用模式。存储管理员必须动态响应变化的存储需求,不断考虑何时以及在哪里扩展存储、增长容量以及如何在集群中负载均衡存储请求。如果没有集成工具,要充分自动化这些任务以确保应用程序平稳运行将非常困难。
在现代 DevOps 软件开发环境中,自动化是关键。企业要采用 Kubernetes,必须能够根据需求自动扩展、平衡和保护数据。对于存储管理员来说,这通常是高度互动的工作。管理存储容量可能会耗费大量时间,并且会影响正在运行的应用程序。为了将精力集中在应用程序开发上,通过尽可能多地自动化来减少这种开销是必要的。这需要存储平台和工具了解每个应用程序的 API 和存储需求,并能够以类似 Kubernetes 处理计算资源的方式自动管理存储。
维护 Kubernetes 集群
Kubernetes 抽象了分布式云原生应用的计算基础设施,将它们与运行它们的硬件解耦。硬件健康、故障和其他问题不再直接影响软件的性能。当硬件出现故障时,包含软件的容器会自动移动,应用程序继续运行。
然而,支持集群的底层硬件需要持续监控,以确保应用程序及其用户的可用性。虽然 Kubernetes 根据变化的条件和需求管理和移动容器,但任何拥有本地部署的企业都必须维护物理服务器以保持集群运行。IT 团队必须确保能够支持业务所需的服务级别协议(SLAs)。这涉及在硬件组件出现故障时更换硬盘和其他组件,这在大规模中非常常见。
此类维护通常需要将大量复制数据从一种存储基础设施迁移到另一种,这受到使用存储的各种应用程序不同需求的复杂性的影响。几乎不可能在没有停机并且没有丢失任何应用程序操作状态的情况下进行重大手动迁移。智能存储平台可以帮助简化此任务,在硬件配置和管理时或将应用程序移至新环境时保护和迁移数据。
另一时代的存储
传统应用直接在物理或虚拟机上运行,其存储需求比分布式容器化应用更简单。单体应用程序状态通常存储在共享的可变表中,可以轻松备份。事实上,一些在虚拟机上运行的应用程序仅依赖于本地存储。在这种情况下,备份虚拟机就足以备份应用程序的数据、配置和运行状态。
容器化应用程序不同。由于容器是不可变且短暂的,需要持久数据的任何工作负载必须连接到外部系统。备份容器不足以捕获持久数据,因为数据不存储在容器本身中。相反,Kubernetes 提供了一种机制,可以将持久存储单元与一个Pod关联起来,一个或多个容器在单个主机节点上运行并共享资源。
Kubernetes 通常维护每个活动 pod 的一组副本,以确保应用程序的可用性。每个 pod 可以定义一个或多个本地或远程volumes,或者说是持久存储空间的集成单元。Volumes 提供了持久化数据的能力,超出了容器或 pod 本身的生命周期。一个 pod 中的多个容器可以挂载和访问同一个 volume,因此应用程序可以在具有不同任务的容器之间共享数据。例如,一个初始化容器可以在服务启动前运行,为应用程序运行环境创建自定义配置文件。
最初,扩展 Kubernetes 存储的能力意味着改变 Kubernetes 代码库本身。Kubernetes 已经弃用了这种“内置”方法,取而代之的是一种称为容器存储接口(CSI)的 API,它允许开发存储插件而无需更改任何 Kubernetes 代码。CSI 提供了一种支持多种存储接口的方式,允许容器化工作负载在不同类型的外部管理存储池上存储持久数据(图 1-1)。

图 1-1. 使用容器存储接口的 Kubernetes 存储
虽然这种模型解决了一些问题,但未能将存储引入到容器化应用程序运行的虚拟化、软件定义基础设施中。虽然现代企业应用程序使用的计算资源运行在应用程序感知的基础设施上,通过最终用户声明式地配置和管理,存储仍然是与虚拟或物理服务器绑定的物理概念。因为云原生应用程序是分布式的,备份任何给定的虚拟机很可能会捕捉来自多个应用程序的部分数据,同时未能存储任何单个应用程序的完整数据。
因此,为物理或虚拟机设计的本地和基于云的存储系统不适合容器化应用程序的规模和复杂性。虽然容器是软件定义的、可丢弃的、可替换的,并且与硬件解耦,但传统存储涉及管理物理媒体池。容器化应用程序具有高度动态性,根据需求的变化通过自动创建、销毁和移动容器来快速缩放。传统的存储方法无法快速响应以支持这些现代架构。
对于文件、块和对象存储,云原生存储解决方案出现了,用于协调跨 Kubernetes 集群访问的持久存储的软件定义池,采用容器化架构以提供规模化的动态存储。这些解决方案运作良好,但它们通常针对特定类型的存储、少数文件系统和一些数据库服务进行优化。
企业需要软件定义的通用存储,能够扩展以满足大数据的需求:流式处理、批处理、事务性数据库、高可用性和灾难恢复、数据局部性和数据迁移。为了支持快速恢复时间目标,备份必须包含所有应用状态,包括数据和配置。
特定的应用类型有额外的更严格需求。例如,数据库应用需要保证数据事务是 ACID(原子性、一致性、隔离性和持久性),通常需要次要索引。因为 Pod 是短暂且可移动的,很难在容器化环境中满足这些保证。
要使 Kubernetes 适用于企业,存储解决方案必须在容器级别而不是 VM 级别工作,必须了解 Kubernetes 命名空间,并且必须能够备份跨 VM 的整个应用程序,包括其配置和状态。
第二章:企业级 Kubernetes 存储
要使 Kubernetes 存储适合企业使用,必须解决弹性和规模的问题,以满足大数据的速度和量。云原生存储解决方案已经出现,用于编排持久存储池,这些存储池是软件定义的,意味着它们与底层硬件分离。与基于 VM 的环境中的软件定义存储(SDS)解决方案不同,云原生软件定义存储以容器的形式本地运行,可以由处理应用容器的同一编排系统进行管理。这为在 Kubernetes 上运行规模化的容器化应用提供了动态、弹性的存储。本章讨论了 Kubernetes 存储概念,软件定义存储如何为 Kubernetes 带来规模,并介绍了 CSI 如何与软件定义存储一起工作。
Kubernetes 存储概念
Kubernetes 将应用实体表示为原语,这些原语是 Kubernetes 的基本构建块。原语表示真实或逻辑实体,使得 Kubernetes 可以像管理软件对象一样管理它们。存储也不例外。Kubernetes 提供了许多存储原语,包括以下内容:
持久卷(PV)
持久存储单元
持久卷声明(PVC)
一个存储请求,成为 PV 和 Pod 之间的绑定
StatefulSet
一个对象,用于管理一组 Pod 的身份
StorageClass
描述集群提供的存储类别
当考虑如何为 Kubernetes 存储提供企业级规模时,当然还有许多其他原语,但这四个是有趣的。
持久卷(PV)
PV 是表示特定位置存储的对象。PV 提供了在使用卷的工作负载或 Pod 的生命周期之外保留数据的能力。PV 可以将工作负载的数据存储在网络存储上,云提供商上,或者本地运行工作负载的 Pod 上(参见图 2-1)。

图 2-1 本地、云和网络持久卷位置
Kubernetes 管理每个 PV 的生命周期,定义以下阶段:
配置
存储管理员预先静态创建 PV,或者使用StorageClass动态创建 PV,后者是一种允许管理员描述存储方式的资源。
绑定
PVC 将 PV 绑定到特定容器。
使用
运行在容器上的工作负载使用 PVC 来访问 PV。
发布
容器通过删除 PVC 来解除对卷的索赔。
保留
在需要数据时,PV 保留数据,即使在容器和 Pod 的生命周期之间也是如此。
删除和回收
当数据不再需要时,Kubernetes 删除数据,回收存储空间供其他卷使用。
存储管理员可以根据预测的存储需求动态配置 PV,或者提前创建 PV。
PersistentVolumeClaim
PVC 同时是存储请求和标识符,一旦授予存储就建立对存储的索赔。PV 本身不归属于特定的应用程序或项目。PVC 使用以下访问模式之一请求对 PV 的访问:
ReadWriteOnce(RWO)
单个节点上所有 Pod 的读写访问
ReadOnlyMany(ROX)
多个节点上的只读访问
ReadWriteMany(RWX)
多个节点的读写访问
ReadWriteOncePod(RWOP)
单个 Pod 的读写访问
PV 和 PVC 一起工作的详细过程如下:
-
应用程序开发人员创建一个或多个 PVC(Persistent Volume Claim),描述应用程序所需的存储资源。
-
存储管理员可以根据需要显式创建 PV,或者创建一个可以根据需要动态配置新 PV 的 StorageClass。
-
Kubernetes 管理将 PVC 绑定到 PV。
PV(Persistent Volume)和 PVC(Persistent Volume Claim)共同为 Pod 提供一种基于容器和应用程序存储需求定义请求的方式。存储管理员可以配置动态的 provisioners 来响应这些请求并为其分配存储和 PV,或者根据应用程序的存储需求提前创建 PV。当存储被授予时,集群找到与 PVC 关联的 PV 并将其挂载到 Pod 上。换句话说,Pod 使用 PVC 作为卷。PV 在需要时专门提供给 Pod 使用。
StatefulSet
StatefulSet 是一种原语,用于管理一组 Pod 的部署和扩展,为每个 Pod 维护一个唯一的 ID,以便在需要持久数据或网络时能够识别该 Pod,或者在 Pod 迁移到不同节点时。通过为 Pod 提供唯一的持久化 ID,StatefulSet API 允许管理员管理一组 Pod 的部署和扩展。当单个 Pod 失败时,持久化 ID 有助于恢复其替换品与现有卷之间的连接。
StorageClass
StorageClass 原语允许集群管理员描述集群提供的不同存储类别。存储类别可以表示不同的策略或服务级别。例如,管理员可以设置不同的 StorageClass 对象来表示不同的备份策略。用户可以通过名称请求特定的存储类别。
软件定义存储
就像容器化应用架构将应用逻辑与代码运行的硬件解耦一样,软件定义存储将持久数据和存储策略与存储介质解耦。SDS 是分布式的且与硬件无关,并可以在包括云环境在内的各种环境中运行。通过将存储与其硬件抽象化,SDS 可以将硬件容量呈现给应用程序、用户和其他客户端作为统一的存储池。SDS 通过使得可以一致地管理各种不同类型的存储而使存储管理员的工作变得更加容易,而不必担心每种存储类型的不同特性(Figure 2-2)。

图 2-2. 软件定义存储
就像 Kubernetes 为计算资源带来弹性、扩展性和高可用性一样,SDS 是将这些重要特性引入存储的基础。因为存储被抽象化了,可以构建在不同环境中分发和扩展存储的系统,并将其与 Kubernetes 或其他编排系统集成,并内置容错和高可用性。
企业的优势显而易见:
-
开发者和用户不需要考虑存储硬件。
-
规模变成了按需配置的问题。
-
数据可以根据需要放置在任何地方,无论环境如何。
因为 SDS 将所有可用的物理存储呈现为共享池,资源可以高效分配,减少存储空间的浪费。
使用 CSI 将 SDS 连接到 Kubernetes
CSI 是容器化工作负载与第三方存储层之间的接口,使得云原生应用可以在 Kubernetes 外部创建、管理和使用存储。CSI 使得存储在一个池中可供每个应用程序实例访问,保持实例同步,并使得能够一致地备份应用程序。CSI 提供了通过接口对 Kubernetes 进行抽象化访问的功能,意味着第三方可以创建插件,从而在不触及核心 Kubernetes 代码的情况下访问存储系统。使用 CSI,容器化应用可以在这些存储系统上使用正常的 Kubernetes 存储原语(Figure 2-3)。

图 2-3. 使用容器存储接口将软件定义存储连接到 Kubernetes
CSI 给予存储供应商自由去设计和管理存储,使得 Kubernetes 和其他编排平台能够通过本地抽象透明地提供和管理存储。在撰写本文时,Kubernetes 和 CSI 并不知道如何提供应用程序感知的备份、高可用性或其他企业所需的功能,但 CSI 使得这些能力可以在存储层面添加。这就是 SDS 的作用所在。
软件设计的存储不受硬件或标准的限制,因此可以实现并从底层机制中抽象出所需的任何能力。因此,CSI 是一个进展缓慢的标准并不重要,因为标准应如此。通过将 SDS 连接到 Kubernetes,CSI 使得可以构建适用于 Kubernetes 的存储,该存储在容器级别是粒度化的、自愈的,并且具有拓扑感知,而不需要 Kubernetes 本身具备这些能力。
云原生存储:为 Kubernetes 存储引入规模
为容器化工作负载适应传统 SDS 在很多方面都是具有挑战性的。
首先,由于容器是动态且短暂的,它们需要可以在需要时即刻进行提供、挂载和删除的存储。为了高可用性,必须能够根据需要创建和移动卷,并具有拓扑意识,并且定期和自动地备份它们。在规模化时,手动存储配置无法跟上这些需求。
其次,物理和虚拟服务器支持的卷数量通常不足以满足需要存储的 pod 或容器数量。单个主机可能运行数百个小型容器,需要的卷比操作系统提供的还要多。
最后,由于容器的目标是基础架构不可知,它们不应关心所使用的物理存储。不同的环境提供不同类型的存储,通常在单个部署中提供多种类型。必须能够在不影响容器化应用程序运行方式的情况下在存储池之间移动数据。
进入云原生存储,这是为分布式、容器化应用程序设计的 SDS 新模型。云原生存储在集群中以容器形式运行,这意味着它可以被提供和编排,并提供数据本地性以及像 Stork (Kubernetes 存储操作器运行时) 这样的 Kubernetes 集成功能来提供存储感知调度。图 2-4 展示了云原生存储如何成为 Kubernetes 的一部分。

图 2-4. 云原生存储
云原生存储必须具备容器感知能力,并且所有操作必须在应用程序级别进行。快照、备份、压缩、加密以及其他操作与整个集群或存储池无关,而是与容器本身相关。这一关键点赋予了应用程序所有者对数据的操作控制权,减轻了 IT 管理员对存储配置和应用程序数据保护的责任。
显然,为了在规模化的 Kubernetes 环境中重新定义存储,必须重新思考存储本身的性质。为了满足规模化的容器化应用程序的需求,存储范式必须具备弹性、敏捷性,能够同时为多个应用服务实例提供多个数据副本,并且与应用程序逻辑本身解耦。换句话说,为了为 Kubernetes 数据带来规模化,存储必须是云原生的,这意味着它必须同时具备软件定义和容器化的特性。
第三章:Kubernetes 性能与安全性
随着 Kubernetes 部署变得越来越大规模和普及,企业正在关注性能和安全性,这两者对于业务连续性和成本控制至关重要。性能通过最大化基础设施使用和防止不必要的资源扩展来节省成本。安全性通过保护资产,包括客户数据和其他专有信息,来控制成本并确保业务连续性。
没有改变的内容:性能、可用性和安全性要求
随着应用程序从单块式本地部署演变为虚拟机、软件即服务(SaaS)和平台即服务(PaaS)在云中运行,再到现代容器化云原生应用程序,业务需求并未改变。在 Kubernetes 上运行的企业应用程序必须满足诸如高可用性、数据保护和严格的性能指标等不可妥协的业务需求,同时要在混合云或多数据中心间运作。
性能需要适当使用资源,确保有足够的能力满足业务需求,而不过度提供或超支。可用性包括处理硬件故障和网络延迟,并在不动声色地满足 SLA。安全性意味着通过加密、访问控制和深度防御保护敏感资源和数据,同时使它们对有合法业务需求的人员和流程可用。
传统的可用性主要意味着运行时间监控和警报。确保应用程序前端运行不仅足以识别停机,而且通常是问题的唯一可用迹象。幸运的是,单块软件相对简单,用户交互可预测。端到端请求监控不重要,可能在当时对 IT 团队来说显得荒谬。性能不太重要;只要应用程序正常运行,IT 和站点可靠性团队就会满意。
现在必须在容器级别管理性能、可用性和安全性。
今天容器化应用程序的服务高度相互依赖,意味着一个服务的失败可能会导致高度复杂、关键任务的应用程序的部分或全部崩溃。保持应用程序正常运行涉及维护多个关键服务实例,负载平衡它们,并在发生故障时移动或重新启动它们。
在全球范围内实现高可用性
对企业来说,全球高可用性是一项不可妥协的业务要求。这必须超越当一些控制平面节点或工作节点失败时保持单个集群的运行。应用状态,包括配置和分布式数据,在预期的任何地方都必须无中断地可用。总之,云原生高可用性要求了解每个应用程序的持久数据、元数据、配置和运行状态。
集群内的高可用性可以简单地通过复制容器、卷和 Kubernetes 服务来实现,以便每种至少有一个实例持续可用。目标是确保没有单点故障。跨本地或地理区域的高可用性涉及在不同数据中心之间同步或异步复制数据和应用程序。这些策略使企业能够选择平衡硬件或云资源成本与业务目标的策略。
高可用性的关键,无论是在本地还是全球范围内,是存储和数据复制机制保持应用一致性。这意味着它们捕获了保持服务持续运行或重新启动业务流程所需的所有数据和元数据。这不仅包括持久数据,还包括应用配置和运行状态。仅仅备份虚拟机或复制卷不够,因为它们只捕获了服务或应用程序继续运行所需的部分数据。此外,由于无法保证单个卷和容器的持久性,必须有一种方式来识别应用程序和数据,以便正确关联替代实例。
数据流动性
高可用性的关键之一是数据流动性,定义为在单个数据中心或云内部,或者在云之间快速移动或复制数据的能力。这种能力不仅支持高可用性,还支持升级、迁移、扩展和灾难恢复。
随着企业采用云计算,数据流动性的重要性变得越来越明显。很明显,单一的部署环境或提供商已不足以满足所有业务需求。组织需要灵活性,在公共云、私有云、本地服务器和边缘数据中心之间进行权衡,以增加灵活性,满足数据位置的法规要求,提供更好的服务,并控制成本。
随着 Kubernetes 的出现,能够在不同的多云和混合云环境中移动大量数据变得至关重要。虽然 Kubernetes 可以轻松地在任何地方部署应用程序,但长期以来,移动基础数据一直更加困难。需要的是一种方法,使移动持久卷与迁移应用容器同样简单。
容器本机存储只是一部分战斗。通过在任何部署环境中提供数据和存储管理,容器本机存储池为数据提供了一个家园。但是,传统的数据迁移和复制方法耗时且操作复杂,无法在需要时迁移数据。需要的是一个容器感知的数据编排层,它可以为应用程序本身提供 Kubernetes 提供的声明性自动化。在应用程序迁移时,通常不实际在短时间内移动所有应用程序数据。数据移动必须包括首先移动最重要数据的能力,以便通过最小化必须移动的数据量,使应用程序在新位置快速启动。
数据移动能够使企业在多方面改善运营。通过将低优先级应用程序移动到辅助集群,企业可以释放关键集群的容量,为额外数据或复制腾出空间。自动化数据移动使得测试新版本更加容易,促进从开发到分段集群的工作负载或维护两个或更多生产环境。在在环境之间移动应用程序和数据或者将集群脱机以进行硬件维护和升级时,数据移动对于持续可用性至关重要。
调整 Kubernetes 数据以实现企业级性能
随着企业推动云原生应用程序规模越来越大,调整 Kubernetes 数据对于平衡性能、成本和可用性日益重要。尽管有许多因素影响 Kubernetes 集群的性能,但其中一个最重要的因素是做出关于数据的正确选择。有两个重要的考虑因素:整体存储配置和数据放置。
存储配置意味着确定所需的存储硬件类型及其设置方式。您可以设置单个存储池或多个每个定义其自身 StorageClass 的存储池,例如。您需要考虑成本与容量以及速度之间的权衡,确定存储层允许多少内存和 CPU 的使用,以及您将需要多少种不同类型的存储。在本地数据中心环境中,这些问题尤为重要,因为决策可能会对未来数年产生影响。在云端或混合环境中,可以更灵活地从小开始,然后根据需要进行扩展或更改。
数据放置,或数据拓扑,指的是管理数据存储位置的策略,通常是为了实现性能或可用性目标。为了获得最大性能,一种策略是超融合,它将工作负载及其数据保持在单个节点中(图 3-1)。如果节点故障,则数据丢失,但这可能是可以接受的。

图 3-1. 超融合拓扑
在超融合拓扑中,存储通常是软件定义的层,通常在计算节点共享的普通服务器上。超融合非常灵活,允许存储与计算工作负载同步扩展。然而,由于单个节点的故障不仅影响该服务器的工作负载,还会影响存储系统,因此从操作和安全的角度来看,超融合拓扑的管理可能更为复杂。在其他节点上存储数据的额外副本,可以在故障后使新的工作负载实例恢复到真正的超融合拓扑,从最近写入副本的数据开始。
为了提高可用性,数据可以与工作负载分开。将计算和存储节点分开(称为去分配)虽然会降低性能,但在节点故障时可以防止数据丢失。对于一个非常动态的环境,其中计算节点的数量会根据工作负载需求的变化而增加或减少,一个策略是将计算和存储分离到各自的集群中(图 3-2)。这样,计算集群的扩展和管理操作不会干扰存储集群,反之亦然。

图 3-2. 去分配拓扑
保持集群安全
现代安全实践采用深度防护策略,这意味着多个安全控制层次协同工作,提供冗余,以便在某一层次的漏洞不会导致对关键系统的访问。云原生安全也不例外:云层、集群、容器和代码本身的保护相互依赖,旨在防止应用和数据被未经授权的访问。
环境是第一层防护。数据中心或云的安全性较高,保护集群及其服务就更加容易。集群则必须在物理节点、虚拟机、容器等层面提供安全机制。
在容器层面,服务必须能够与 SDS 安全地通信。Kubernetes 提供了控制容器访问权限、限制资源使用和防止容器执行危险或不需要的操作的机制。存储层必须通过加密静态和动态数据、限制对特定客户端的访问以及利用应用和 API 知识,仅允许合理的数据事务来履行其职责。
第四章:数据保护与 Kubernetes
数据保护涵盖了广泛的实践和概念,包括高可用性、备份、灾难恢复以及支持业务连续性的其他过程。每个企业都维护和测试数据保护政策和程序,以最小化停机时间,并确保在中断后能够继续运营。在过去几年中,数据保护也已成为合规性的重要组成部分。此外,数据保护策略必须提供数据隐私,因为法规开始解决企业日常处理的大量敏感个人数据。
Kubernetes 数据保护挑战
传统数据保护主要集中在物理或虚拟机的层面上,通过保护服务器本身来保护应用程序和数据。这种方法对于在单个主机上运行的应用程序非常有效。然而,对于容器化应用程序来说,仅在服务器级别进行保护显然不够细粒度化。针对整个服务器进行保护会导致应用程序、存储和配置无法分离,并混合了需要不同数据保护策略的应用程序。
只有通过在容器级别提供数据保护,才能按照应用程序、容器或 Kubernetes 存储单元的个体应用策略。在 Kubernetes 环境中,数据保护必须在 Kubernetes 资源级别可用,并且必须包括对整个集群中分布式数据层的认证和授权。备份和灾难恢复必须集成到容器编排环境本身。
因为每个应用程序都很复杂,并且由于企业集群中运行的应用程序数量庞大,手动备份策略是不切实际的。唯一的前进之路是自动化;然而,这里面也充满了复杂性。
规模
容器化应用程序关注的是规模,包括必须以高速处理的大量数据。大规模数据需要备份解决方案能够从各种环境中的多个来源收集数据,并有效地管理和聚合这些数据。这些需求包括能够备份多节点和多容器应用程序,包括数据、应用程序配置和状态。
分布式架构
云原生架构使数据保护变得具有挑战性。由于应用程序由容器管理的松耦合微服务组成,传统的机器集中型数据保护策略已经不再适用。传统的备份解决方案仅捕获应用程序运行的虚拟机的整个状态。然而,对于分布式、容器化的应用程序来说,这种方法不适用,因为状态本身是分布式的。图 4-1 展示了传统应用程序在虚拟机上运行与云原生、容器化应用程序在 Kubernetes 上运行之间的区别。

图 4-1. 传统应用与容器化应用
云原生应用程序通常高度分布,经常跨越多个地理位置的多个云。容器化应用程序部署通常跨越公共和私有云,有时也在本地部署。在这些环境中,数据保护必须能够识别容器,并能够与容器编排框架集成。
命名空间
Kubernetes 的命名空间是将单个集群分成不同隔离组的机制,可以用来为不同业务单元分配资源或将应用程序分组在一起。IT 管理员可能需要同时备份运行在特定命名空间中的所有应用程序。由于命名空间可以包含大量的 Pod,因此尝试手动操作通常是不切实际的。但传统的备份工具不了解 Kubernetes 命名空间模型,无法与 Kubernetes API 集成,因此只能手动备份每台机器,别无选择。
恢复点目标和恢复时间目标
一些应用程序处理的是即时关键数据。其他应用程序可能需要长期的数据保真性,同时能够容忍瞬时中断,或者处理的数据既不是时效性敏感也不是关键任务。明智的做法是根据合规性、重要性以及对业务重要性的其他方面为每个应用程序设置不同的策略。
每个应用程序和每种数据类型可能需要自己的数据保护策略,具有自己的恢复点目标(RPO)和恢复时间目标(RTO):
-
RPO 决定了备份频率,表示可恢复数据的最新性。
-
RTO 是在中断后恢复运营所允许的最大时间量。
RPO 和 RTO 策略由业务中断的潜在影响以及业务根据涉及的应用程序和数据类型可以实际承受的数据量决定。
在大规模分布式应用程序中,为了满足特定的 RPO 和 RTO,通常需要维护额外的数据集群或快速移动大量数据以实现高可用性和快速恢复。在现代的云原生环境中,为了达到这些目标,您需要自动化和应用程序感知的工具,能够与 Kubernetes 的抽象和 API 配合工作,保护容器级别的数据。
持续集成/持续交付(CI/CD)流水线是一个能够容忍中等 RPO 和 RTO 的环境的良好示例。开发工具很重要,但它们不会立即影响客户体验。对于这些应用程序,一些数据丢失或工作流中断是可以接受的。
对于客户直接接触的应用程序,特别是处理敏感客户数据的应用程序,数据保护必须更加严格。对于这些应用程序,RPO 和 RTO 都非常重要。能够恢复到最近的数据点并快速解决中断是关键。有时需要在数据中心之间进行灾难恢复,或者从一个活动数据中心切换到另一个。
最关键的应用程序是那些不能承受任何数据丢失并且只能容忍非常短暂中断的应用程序,例如事务处理工具。这意味着非常短的 RTO 时间和零的 RPO,这是需要满足的严格要求。这只能通过每个集群内的高可用性、备份与恢复,并且具有合规焦点的多数据中心同步来实现,从而支持地理区域间的高可用性和故障转移。
应用程序类型的数据保护
不同的应用程序不仅存储不同类型和格式的数据,还采用不同的数据策略。例如,数据库应用程序将大部分数据存储在表中,但也维护应用程序状态、写入应用程序日志并消耗配置文件。对于在 VM 上运行的传统应用程序,解决方案很简单:VM 的备份保留了所有内容,包括应用程序状态。对于分布式应用程序,情况并非如此。Kubernetes 的备份、灾难恢复和其他数据保护过程必须了解应用程序和 API 的特定需求,而不是采用通用方法。
由于应用程序状态是分布式的,备份解决方案必须了解每个应用程序如何使用数据,并且必须能够找到和保护应用程序使用的所有不同类型的数据。例如,一个应用程序感知的备份工具可能知道特定应用程序在缓存中保留了待写入的队列,并可能要求应用程序在拍摄快照之前将其写入磁盘,以便捕捉应用程序状态的完整视图。
Kubernetes 数据保护策略
Kubernetes 数据保护策略必须提供高可用性、备份与恢复以及数据中心内外的故障转移。这些功能必须是自动化的、应用感知的,并且能够为企业规模进行扩展。数据保护必须对容器级别进行细化,并了解 Kubernetes 的拓扑、存储和抽象。最后,任何解决方案都必须支持企业可能需要的不同应用程序、业务需求、数据和 SLA。
容器感知的备份与恢复
传统的备份和恢复方法是在机器级别保护应用程序和数据。通过备份机器,可以恢复机器上应用程序的先前运行状态。这种方法适用于在单个主机上离散运行的应用程序,但不适用于分布在集群中多个节点上的基于微服务的应用程序。因此,您必须能够定位集群中应用程序的数据,以创建应用程序一致的备份,这意味着该过程一次性复制所有应用程序的卷和状态。应用程序一致的备份需要具有应用程序领域特定知识,以正确定位所有卷并捕获应用程序状态。未能以应用程序感知的方式备份数据可能导致数据损坏和丢失。
Kubernetes 备份工具必须能够与 Kubernetes API 集成,了解 Kubernetes 计算和存储资源,并能够映射集群拓扑,以便能够备份一组 Pod 或整个命名空间。最后,Kubernetes 备份工具必须了解不同应用程序的不同要求,并能够捕获和恢复持久存储以及应用程序状态和配置。换句话说,系统必须知道如何处理构成应用程序的不同 Kubernetes 对象,而不是尝试备份节点本身。
随着云原生架构的普及,数据保护正在从 IT 部门转变为多个团队共同承担的责任,包括应用程序所有者在内。传统备份集中在一个地方,而容器感知备份为应用程序所有者提供了基于角色的自助服务能力,包括设置他们自己的备份策略和规则,以确保备份是应用程序一致的。
容器化应用程序设计用于扩展,备份解决方案必须能够随之扩展。单个应用程序可以扩展到数千个对象,企业可能运行数百或数千个这样的应用程序。Kubernetes 备份解决方案必须处理成千上万个对象和存储卷。
单数据中心内的数据保护
在单个 Kubernetes 集群中的数据保护涉及确保 Kubernetes 组件和应用程序的高可用性,并维护适当的数据复制。这主要意味着设置 Kubernetes 以避免任何服务或卷的单点故障。
复制策略因应用类型和业务需求而异。简单的应用程序不处理自己的复制,依赖底层存储层可靠地可用。当这些应用程序处理的数据是短暂的且价值较低时,只保留一份副本可能是可以接受的,假设存储卷所在节点的故障不会对其造成太大影响。对于更重要的数据,当然应该配置存储层以提供足够的复制,既用于数据保护又用于可用性。对于重要且需求量大的数据,跨更多集群的更多副本可以为更多客户提供低延迟的服务。一些应用程序处理自己的复制。对于这些应用程序,存储的主要任务是在卷(或其节点)失败时提供替代存储。
跨数据中心的灾难恢复
综合的数据保护策略应该在数据中心内部的保护之外,通过故障转移和灾难恢复到位于不同数据中心的次级或备用集群。在这种情况下,活动集群使用由 RPO 和 RTO 要求确定的时间轴向备用集群复制数据和配置,保持同步,以便在活动集群故障时接管。图 4-2 显示了从一个数据中心到另一个数据中心的复制。

图 4-2. 在单独的数据中心中从活动集群到备用集群的复制
当将数据复制到另一个数据中心时,重要的是要了解接收集群的拓扑结构,以便将副本适当地分布在节点之间,无论是为了性能还是安全性的原因。在节点故障的情况下,复制的数据必须提供高可用性和高性能的访问,并防范数据丢失。
有两种策略来保持数据中心同步:同步复制适用于需要严格的 RPO 和 RTO 时间或立即故障转移的数据;而异步复制适用于可以容忍一些数据丢失或不可用性的数据。
同步复制确保所有写入活动集群的数据都复制到备用集群。只有在写入到备用集群完成时,写入到活动集群才被认为是完成的。这种方法具有挑战性,因为它需要非常低的延迟来保持写入性能,但其好处是支持如交易等重要数据的 RPO 为零。在无法实现足够低延迟的环境中,必须能够在个别卷和对象的级别设置迁移策略,允许较不关键的数据异步复制以节省带宽。
异步复制按计划将数据从一个集群复制到另一个集群,不保证集群完全同步,但节省网络带宽。可以容忍适量数据丢失或停机时间的应用可以使用异步复制来达到适当的 RPO 和 RTO。RPO 取决于从活动集群复制到备用集群的数据的最新程度。RTO 取决于在活动集群不可用时恢复应用到完全功能所需的时间。
第五章:迁移到 Kubernetes
在当今世界,消费者和企业客户都要求更快的速度。复杂的现代应用程序在个性化、人工智能和各个行业的欺诈检测等领域必须更快地提供更好的结果。集中管理和人工操作的应用程序对苦苦挣扎的 IT 团队构成负担,并且提供回报逐渐减少;传统应用架构无法跟上当今巨大的数据量、全球部署需求以及对水平扩展的需求。
PaaS 和 SaaS 是在面对不可预测需求时效果最佳的范式。与传统的单体式本地应用程序不同,PaaS 和 SaaS 必须进行水平扩展,在需要时立即添加物理或虚拟资源。由于在本地添加物理服务器通常是一个缓慢且时间长的过程,这意味着现代应用程序必须能够在任何地方运行:在本地环境、云环境或二者组合的混合环境中。
出现的答案是将应用程序分解为在容器中运行的微服务。这引入了超出 IT 团队手动管理能力的复杂性,需要自动化管理、配置、负载平衡和故障解决。这些需求本质上构成了 Kubernetes 的功能列表。Kubernetes 是一个平台,能够在各种环境中编排容器集合,使得构建基于云原生、微服务的应用程序并在任何地方运行成为可能。因此,Kubernetes 已成为企业大规模部署应用程序的主要方式。
即使 DevOps 团队拥有运行生产 Kubernetes 集群所需的所有技能,数据存储仍然具有挑战性。每个应用程序和服务都有自己的存储需求、元数据以及处理状态的方式。每种数据都有其围绕安全性、可扩展性、合规性和可用性的要求。如果这还不够,企业所需的应用程序和数据类型数量庞大,意味着复杂性呈指数级增加。
Kubernetes 采用的挑战
很少有企业是从头开始的。典型的企业运行大量最初设计为在物理服务器或虚拟机上运行的遗留应用程序。尽管云原生、容器化应用程序是明确的前进路径,但立即放弃支持组织运行的所有遗留应用程序并不现实。
采用 Kubernetes 意味着投资于云原生、容器化应用程序,同时支持更传统的应用程序架构。为两种软件提供单独的存储既因操作成本高昂又因其减慢数据处理速度而不切实际。有两种方法可以帮助解决问题:在 VM 内部运行容器,或在容器内部运行 VM。后一种方法的优势在于允许 Kubernetes 管理传统应用程序运行的 VM,并根据需要移动它们。
对于每个组织来说,持久存储在数据量增长时至关重要。企业需要无缝管理大量数据,且在数据安全性、高可用性和灾难恢复方面具有严格的需求。数据必须在多个位置可用,并且在地理上可控以符合合规要求,并具有高性能。最初设计为无状态进程的 Kubernetes 并不是企业公司数据需求的本能适配器。
要在规模上运行应用程序,企业需要 Kubernetes。但要在规模上运行 Kubernetes,公司还需要制定策略,提供一个与计算容器解耦的底层存储层,以支持复杂的大规模业务应用程序的需求。
在 Kubernetes 上运行大规模系统
在规模化运行 Kubernetes 的关键在于能够根据需求优化资源分配,根据需要自动缩放资源和服务。虽然 Kubernetes 允许您声明式地自动化集群管理和计算资源,但仍然需要额外的工具来有效管理存储。
一个重要因素是确保存储被适当地分配并且不被浪费。因为块存储最初并不是为大量容器使用而设计的,所以在 Kubernetes 中实现资源效率可能具有挑战性。开箱即用,Kubernetes 每个主机提供的卷数量有限,并且不提供广泛的工具来实现故障转移、备份和灾难恢复等功能。这经常导致块存储的过度配置,通常是两到三倍,并导致应用程序效率降低。
在公共云上,供应商通常限制单个虚拟机可以附加的块设备数量,从而导致另一种过度配置。如果虚拟机的计算能力足以为更多容器提供服务,超出了供应商允许的限制,那么在某些时候就必须为存储预留额外的虚拟机。这会增加计算成本并增加管理开销。
在管理大规模存储资源时,数据可移植性变得更加重要。为了最大化运营存储效率,必须能够将工作负载移动到成本最低的存储中。这意味着企业必须能够在环境之间、提供商之间或从一个团队到另一个团队之间敏捷地移动数据和应用程序。超融合拓扑结构可以帮助降低成本和复杂性,使扩展变得更加容易。
软件定义存储将所有可用存储抽象为单一池,是在大规模高效运行 Kubernetes 中的一个重要组成部分,因为它可以支持传统应用程序和容器化工作负载。您仍然需要就底层计算和存储硬件做出决策。仅仅运行计算和存储平台需要每个节点的最低资源集。在此基础上,您必须做出有关每个节点应具备多少工作能力的决策,例如:
-
每个节点将运行多少个应用程序
-
每个节点将支持多少个容器
-
每个容器获得多少 CPU
对于相同总集群工作负载,您必须决定是运行少量更强大的节点还是更多数量的性能较弱的节点。
云和混合环境使得这些问题更容易解决,因为它们允许您从小规模开始,并根据需要提供更多的计算和存储资源。无法预料每种可能应用程序和工作负载的需求。无法为每个人都适用的集群进行规划。通过仅提供所需的内容并随着业务增长进行扩展,您可以持续地调整规模化的 Kubernetes 以满足企业的需求。
Kubernetes 在生产环境中最耗时且破坏性最大的一个方面是存储容量管理。在高度动态的环境中,存储需求可能非常不稳定,使得没有复杂工具和自动化几乎无法管理存储。正确的存储管理解决方案可以帮助您智能地配置存储,避免浪费,同时根据需求进行扩展。
最终,大规模运行 Kubernetes 部署的目标是为企业应用提供服务,包括 PaaS 和 SaaS。这些部署必须满足数据安全性、可移植性、合规性、本地性和可用性等需求。没有帮助,Kubernetes 无法满足这些业务关键需求。为了满足市场数据需求,您需要一个协调的存储层,可以管理容器卷的可靠性和性能,提供高可用性、复制、存储分层、灾难恢复、备份以及现代企业应用所需的其他功能。
