Kubernetes 上运行有状态应用的最佳实践

简介: 在容器化的早期阶段,它们被设定为一种运行无状态应用的机制。

在容器化的早期阶段,它们被设定为一种运行无状态应用的机制。

在过去的几年间,社区意识到在容器中运行有状态工作负载的价值,而且像 Kubernetes 这样的编排器引入了必要的特性。

Kubernetes 提供了持久化卷(Persistent Volume,PV)架构以及像 StatefulSet 和 DaemonSet 这样的控制器,它们能够让我们创建有状态工作负载的Pod,即便是在 Kubernetes 扩展和供应资源的时候,这些工作负载也能保持运行,并且能够确保现有的客户端连接不会中断。

这种方式虽然远远谈不当简单直接,但是能够行之有效,任何采用 Kubernetes 作为运行时基础设施的人都必须熟悉它。

在本文中,我将会阐述在 Kubernetes 中运行有状态应用的重要性,给出运行有状态应用的三个可选方案,并详细描述它们的运行机制。

什么是有状态应用?

有状态应用允许用户重复返回该应用并恢复之前的操作,比如电子邮件或者网上银行应用。有状态的应用会记录之前事务的上下文,这些上下文可能会对当前或未来事务产生影响。所以,有状态的应用必须确保每个用户始终访问同一个应用程序实例,或者有某种在实例之间同步数据的机制。

有状态进程的优点是,应用程序可以存储每个事务的历史和上下文,跟踪最近的活动、配置偏好和窗口位置等元素,并允许用户恢复事务。有状态的事务的表现就像始终和同一台服务器进行对话一样。

如今,大多数的应用都是有状态的。容器和微服务等技术的进步推动了基于云的应用开发,然而由于它们的动态性,使得有状态进程的管理更具挑战性。

容器化有状态应用的使用场景

在容器上运行有状态应用的需求正变得越来越大。容器化的应用可以简化复杂环境中的部署和运维,如边缘云计算和混合云环境。状态性对于持续集成和持续交付(CI/CD)也很重要,因为 CI/CD 流水线必须保持状态,以确保从开发到生产环境部署过程的连贯性。

容器化有状态应用的常见使用场景包括:

机器学习运维(MLOps):在 MLOps 环境中,容器需要是有状态的,这样做有多个目的,包括共享推理和训练的结果以及训练 job 的检查点。

AI 和数据分析处理:数据处理和机器学习框架,如 Apache Spark、Hadoop、Kubeflow、Tensorflow 和 PyTorch,对容器化的支持在不断增强。这些平台必须反复处理大量的数据,需要有保持状态的机制。

消息系统和数据库:你可能更喜欢使用本地闪存来获取低延迟性,但是这会使得容器很难在不同的 worker 节点间进行移动,因为数据会持久化到节点上。高性能共享存储对各种应用都很重要,比如单实例数据库(如 MySQL)、内存数据库(如 Redis)、NoSQL 数据库(如 MongoDB)、业务关键型的应用(如 SAP 或 Oracle)以及消息应用(如 Kafka)。

在 Kubernetes 中实现有状态部署的三个可选方案

在 Kubernetes 集群中运行有状态的工作负载主要有三个可选方案,即在集群之外运行、作为集群旁的云服务或者在 Kubernetes 集群中运行。

1.在 Kubernetes 之外运行有状态的应用

一种常见的方式就是在 VM 或裸机中运行有状态的应用,并让 Kubernetes 中的资源与之进行通信。从集群中 pod 的角度来看,有状态应用会作为一个外部的集成。

这种方式的好处在于,它允许我们按照原样运行现有的有状态应用,无需重构或重新架构。如果应用能够根据 Kubernetes 集群中工作负载的需要进行扩展,那么我们就不需要 Kubernetes 复杂的自动扩展和资源供应机制。

这种方式的缺点在于,在集群外维护非 Kubernetes 的资源,这就需要我们有某种方式来监控进程、执行配置管理,并为应用执行负载均衡和服务发现。我们在 Kubernetes 之外搭建了一个并行的软件工作流,所以基本是在进行重复的工作。

2. 以云服务的形式运行有状态的工作负载

第二种同样常见的方法是将有状态的应用作为托管云服务来运行。例如,如果你需要在一个容器化的应用中运行一个 SQL 数据库,并且应用在 AWS 上运行,那么你可以使用 Amazon 的 Relational Database Service(RDS)。托管数据库往往是可以进行弹性扩展的,所以随着 Kubernetes 资源的扩展,有状态的服务也可以适应不断增加的需求。

这种方式的好处在于,它的搭建过程非常容易,有状态工作负载的持续维护应该会非常简单,而且你使用的是一个与 Kubernetes 兼容的云原生资源。

这种方式的缺点在于,托管云服务是有成本的,它的定制能力通常会比较有限,并且不一定能提供你所需要的性能或延迟属性。同时,采取这种方式,会让你锁定到特定云供应商上。

3. 在 Kubernetes 中运行有状态的工作负载

这种方式最难实现,但是从长远来看,它会带给我们最大的灵活性和运维效率。我们可以使用 Kubernetes 提供的两个原生控制器来运行有状态的应用,即 StatefulSet 和 DaemonSet。

StatefulSet 控制器
StatefulSet是一个 Kubernetes 的控制器,它管理具有唯一身份标识的多个 pod,并且它们是不能互相交换的(这与常规的 Kubernetes Deployment 有所差异,在 Deployment 中,pod 是无状态的,可以根据需要经常销毁和重建)。

在 StatefulSet 中,每个 pod 都有一个持久化的、唯一的 ID。每个 pod 可以有自己的持久化存储卷。如果 Kubernetes 需要扩展和伸缩的话,它会保持与外部用户或者集群中其他应用的现有连接。

DaemonSet 控制器
DaemonSet是一个 pod,Kubernetes 能够确保它会在集群的所有节点,或者通过选择器定义的特定节点子集上运行。每当符合条件的节点被添加到集群中,这个 pod 都会在它上面启动。

对于需要以后台进程的形式运行的有状态应用来说,DaemonSet 非常有用,比如监控或日志聚合应用。一般来讲,DaemonSets 的灵活性较差,但是比 StatefulSet 更易于管理,资源的使用也更加可预测。

Kubernetes 中的持久化存储

卷(volume)是一个 Kubernetes 实体,它提供了持久化的存储。Pod 中所有的容器可以共享卷。我们可以借助持久化卷,让运行在同一个 pod 中的多个服务使用同一个挂载的文件系统。

非持久化存储卷
在 Kubernetes 中,要授予容器对持久化存储的访问权,我们需要声明所需的卷以及所需的位置,以便于在容器的文件系统中挂载该卷。

Kubernetes 中的常规存储卷会有一个确定的生命周期:每个卷都与 pod 的生命周期绑定。当 pod 处于活跃状态的时候,卷会保持在 pod 内,如果重启 pod 的话,卷会被重置。这个模型不适合有状态的工作负载,这也是 Kubernetes 引入持久化卷(Persistent Volumes)概念的原因。

PersistentVolumes (PV)
Kubernetes PersistentVolumes(PV)是存在于集群级别的存储对象。将 PV 绑定到集群上会扩展它们的生命周期,不再局限于 pod 的生命周期。因为 PV 位于集群级别,所以 pod 可以共享数据。我们可以扩展持久化卷的大小和规模,但是不能减少它的大小。

我们有两种方式来提供 PV:

静态方式(Statically) :能够让我们预先分配存储资源。这样会假定集群可用的物理存储资源是静态的。

动态方式(Dynamically):能够让我们扩展可用的存储空间,以满足不断增长的需求。我们可以通过使用 Kubernetes API 服务器启用 DefaultStorageClass admission 控制器来使用该方案。

PersistentVolumeClaim(PVC)
PVC 能够让 Kubernetes 用户请求存储。它的运行方式与 pod 类似,只不过 pod 消费节点资源,而 PVC 消费 PV 资源。除此之外,与 pod 能够请求特定级别的资源一样,PVC 也可以请求特定的访问模式和大小。

PV 和 PVC 的主要差异在于:

image.png

StatefulSets 和 DaemonSets

StatefulSet 可以帮助我们处理提供持久化的存储卷。请注意,即便 StatefulSet 中的单个 pod 很容易发生故障,有状态的工作负载也能对故障保持弹性。持久化的 pod 标识符能够将现有的卷与 Kubernetes 新供应的新 pod 进行匹配,以取代发生故障的 pod。

StatefulSet 是如下场景的理想选择:

稳定的、唯一的网络标识符。

有序、优雅的部署和扩展。

稳定的、持久化的存储。

有序的、自动的滚动更新。

如下是一个来自Kubernetes文档的样例,展示了 StatefulSet 组件。

这个例子使用nginx服务来控制一个网络域。该 StatefulSet 名为 web,它有一个 Spec,表明必须在特定 pod 中启动nginx容器的三个副本。它还声明,当使用由 PV Provisioner 提供的 PV 时,由volumeClaimTemplates提供稳定的存储。

image.png

DaemonSets
DaemonSets 负责确保所有或特定节点上会运行 pod 的副本。一旦节点被添加到集群中,DaemonSet 所声明的 pod 就会添加到节点中。当节点在集群中移除时,DaemonSet pod 就会被垃圾回收掉。删除 DaemonSet 时,会清理掉它所创建的 pod。

如下是 DaemonSets 的常见使用场景:

在每个节点上运行集群存储的 daemon

在每个节点上运行日志收集的 daemon

在每个节点上运行节点监控的 daemon

针对每种 daemon 类型,你可以定义一个 DaemonSet 涵盖所有的节点。也可以为每种 daemon 类型定义多个 DaemonSets,针对不同类型的硬件使用不同的标记、内存和 CPU。

创建 DaemonSet

运行如下的命令在 Kubernetes 集群中创建 DaemonSet:

image.png

定义 DaemonSet 参数

Kubernetes 允许我们使用 YAML 文件来描述 DaemonSet。下面的daemonset.yaml文件样例定义了一个运行fluentd-elasticsearch Docker 镜像的 DaemonSet。这个例子也来自官方文档。
image.png

image.png

Kubernetes 中有状态应用的最佳实践

到此为止,我介绍了在 Kubernetes 上运行有状态工作负载的几种方法。这里有一些建议,可以更有效地运行有状态的应用:

有效利用命名空间:最好是将每个有状态的应用分割到自己的命名空间中,以确保明确的隔离并且更易于进行资源管理。

使用 ConfigMap:所有的脚本和自定义配置应该放到 ConfigMap 中,以确保所有的应用配置都会以声明式的方式来进行处理。

服务路由:随着应用程序的增长,考虑服务路由的可管理性,应该倾向于使用 headless 服务而不是负载均衡器。

Secret 管理:明文 secret 会给生产应用带来严重的安全风险,要确保所有的 secret 都在一个强大的 secret 管理系统中理。

谨慎规划存储:确定应用的持久化存储需求,确保物理存储设备可供集群使用,并以确保每个应用组件所需存储资源的方式定义 Storage Classes 和 PVC。

结论
在本文中,我阐述了有状态容器化应用的基础知识,并介绍了如何在 Kubernetes 中管理有状态工作负载。这包括以下关键的构件:

PersistentVolume(PV):允许我们定义持久化存储单元并将其挂载到 Kubernetes 集群中的 pod 上的构造。

PersistentVolumeClaim(PVC):允许 pod 动态请求符合其要求的存储的机制。

StatefulSet:控制器,允许创建具有持久化 ID 的 pod,即便 Kubernetes 动态扩展集群中的应用,它也会保持原样。

DaemonSets:控制器,允许集群中的所有节点或特定子集上运行有状态的工作负载。

熟悉了这些构件后,你就可以直接在 Kubernetes 集群中创建安全的、可重复运行的有状态的工作负载了。就像 Kubernetes 中的所有内容一样,有状态的机制并不简单,需要时间来掌握,但当你掌握了这些机制后,它就会变得强大而可靠。稍微练习一下,你就能成为一个有状态 Kubernetes 的专家。

作者简介:

Gilad David Maayan 是一位技术作家,曾与 150 多家技术公司合作,包括 SAP、Imperva、三星 NEXT、NetApp 和 Check Point,制作技术和思想领导力相关的内容,为开发者和 IT 领导层阐明技术解决方案。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
10天前
|
人工智能 Kubernetes 安全
赋能加速AI应用交付,F5 BIG-IP Next for Kubernetes方案解读
赋能加速AI应用交付,F5 BIG-IP Next for Kubernetes方案解读
50 13
|
10天前
|
Kubernetes 算法 调度
阿里云 ACK FinOps成本优化最佳实践
本文源自2024云栖大会梁成昊演讲,讨论了成本优化策略的选择与实施。文章首先介绍了成本优化的基本思路,包括优化购买方式、调整资源配置等基础策略,以及使用弹性、资源混部等高级策略。接着,文章详细探讨了集群优化和应用优化的具体方法,如使用抢占式实例降低成本、通过资源画像识别并优化资源配置,以及利用智能应用弹性策略提高资源利用效率。
|
10天前
|
Kubernetes 容灾 调度
阿里云 ACK 高可用稳定性最佳实践
本文整理自2024云栖大会刘佳旭的演讲,主题为《ACK高可用稳定性最佳实践》。文章探讨了云原生高可用架构的重要性,通过Kubernetes的高可用案例分析,介绍了ACK在单集群高可用架构设计、产品能力和最佳实践方面的方法,包括控制面和数据面的高可用策略、工作负载高可用配置、企业版容器镜像服务高可用配置等内容,旨在帮助企业构建更加可靠和高效的应用运行环境。
|
10天前
|
存储 Kubernetes 关系型数据库
阿里云ACK备份中心,K8s集群业务应用数据的一站式灾备方案
本文源自2024云栖大会苏雅诗的演讲,探讨了K8s集群业务为何需要灾备及其重要性。文中强调了集群与业务高可用配置对稳定性的重要性,并指出人为误操作等风险,建议实施周期性和特定情况下的灾备措施。针对容器化业务,提出了灾备的新特性与需求,包括工作负载为核心、云资源信息的备份,以及有状态应用的数据保护。介绍了ACK推出的备份中心解决方案,支持命名空间、标签、资源类型等维度的备份,并具备存储卷数据保护功能,能够满足GitOps流程企业的特定需求。此外,还详细描述了备份中心的使用流程、控制台展示、灾备难点及解决方案等内容,展示了备份中心如何有效应对K8s集群资源和存储卷数据的灾备挑战。
|
1月前
|
Kubernetes 监控 API
深入解析Kubernetes及其在生产环境中的最佳实践
深入解析Kubernetes及其在生产环境中的最佳实践
46 1
|
1月前
|
Kubernetes 监控 安全
容器化技术:Docker与Kubernetes的实战应用
容器化技术:Docker与Kubernetes的实战应用
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
47 0
|
1月前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
71 1
|
2月前
|
JSON Kubernetes 容灾
ACK One应用分发上线:高效管理多集群应用
ACK One应用分发上线,主要介绍了新能力的使用场景
|
2月前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。
下一篇
DataWorks