从入门到上手:什么是K8S持久卷?

本文是介绍 Kubernetes 的基本概念的系列文章之一, 在第一篇文章中,我们简单介绍了持久卷(Persistent Volumes)。在本文中,我们将学习如何设置数据持久性,并将编写 Kubernetes 脚本以将我们的 Pod 连接到持久卷。在此示例中,将使用 Azure 文件存储(Azure File Storage)来存储来自我们 MongoDB 数据库的数据,但您可以使用任何类型的卷来实现相同的结果(例如 Azure Disk,GCE 持久磁盘,AWS 弹性块存储等)。

如果你想全面了解 K8S 其他概念的话,可以先查看此前发布的文章

请注意:本文提供的脚本不限定于某个平台,因此您可以使用其他类型的云提供商或使用具有 K3S 的本地集群实践本教程。本文建议使用 K3S,因为它非常轻,所有的依赖项被打包在单个二进制中包装大小小于 100MB。它也是一种高可用的认证的 Kubernetes 发行版,用于在资源受限环境中的生产工作负载。想了解更多信息,请查看官方文档:https://docs.rancher.cn/k3s/

前期准备

在开始本教程之前,请确保已安装 Docker。同时安装 Kubectl(如果没有,请访问以下链接安装:

https://kubernetes.io/docs/tasks/tools/#install-kubectl-on-windows

在 kubectl Cheat Sheet.中可以找到整个本教程中使用的 kubectl 命令:

https://kubernetes.io/docs/reference/kubectl/cheatsheet/

本教程中,我们将使用 Visual Studio Code,您也可以使用其他的编辑器。

Kubernetes 持久卷可以解决什么问题?

请记住,我们有一个节点(硬件设备或虚拟机)和在节点内部,我们有一个 Pod(或多个 Pod),在 Pod 中,我们有容器。Pod 的状态是暂时的,所以他们神出鬼没(时常会被删除或重新调度等)。在这种情况下,如果你想在 Pod 被删除之后已经保存其中的数据,你需要数据移动到 Pod 外部。这样它就可以独立于任何 Pod 存在。此外部位置称为卷,它是存储系统的抽象。使用卷,您可以在多个 Pod 保持持久化状态。

什么时候使用持久卷

当容器开始被广泛应用时,它们旨在支持无状态工作负载,其持久性数据存储在其他地方。从那时起,人们做了很多努力以支持容器生态系统中的有状态应用。

每个项目都需要某种数据持久性,因此,您通常需要一个数据库来存储数据。但在简洁的设计中,你不想依赖具体的实现;您想写一个尽可能可以重复使用和独立于平台的应用程序。

一直以来,始终需要向应用程序隐藏存储实现的详细信息。但现在,在云原生应用的时代,云提供商创建了的环境中,想要访问数据的应用程序或用户需要与特定存储系统集成。例如,许多应用程序直接使用特定存储系统,诸如 Amazon S3、AzureFile 或块存储等,这造成了不健康的依赖。Kubernetes 正在尝试通过创建一个名为持久卷的抽象来改变这一情况,它允许云原生应用程序连接到各种云存储系统,而无需与这些系统建立明确的依赖关系。这可以使云存储的消耗更加无缝和消除集成成本。它还可以更容易地迁移云并采用多云策略。

即使有时候,由于金钱,时间或人力等客观条件的限制,你需要做出一些妥协,将你的应用程序与特定的平台或提供商直接耦合,您应该尽量避免尽可能多的直接依赖项。从实际数据库实现中解耦应用程序的一种方法(还有其他解决方案,但这些解决方案更加复杂)是使用容器(和持久卷来防止数据丢失)。这样,您的应用程序将依赖于抽象而不是特定实现。

现在真正的问题是,我们是否应该总是使用带有持久性卷的容器化数据库,或者哪些存储系统类型不应该在容器中使用?

何时使用持久卷并没有通用的黄金法则,但作为起点,您应该考虑可扩展性和集群中节点丢失的处理。

根据可扩展性,我们可以有两种类型的存储系统:

  1. 垂直伸缩——包括传统的 RDMS 解决方案,例如 MySQL、PostgreSQL 以及 SQL Server
  2. 水平伸缩——包括“NoSQL”解决方案,例如 ElasticSearch 或基于 Hadoop 的解决方案

像 MySQL、Postgres、Microsoft SQL 等垂直伸缩的解决方案不应进入容器。这些数据库平台需要高 I / O、共享磁盘、块存储等,并且不能优雅地处理集群中的节点丢失,这通常发生在基于容器的生态系统中。

对于水平伸缩的应用程序(Elastic、Cassandra、Kafka 等),您应该使用容器,因为它们可以承受数据库集群中的节点丢失,并且数据库应用程序可以独立地再平衡。

通常,您可以并且应该分布式数据库容器化,这些数据库使用冗余存储技术,可以承受数据库集群中的节点丢失(Elasticsearch 是一个非常好的例子)。

Kubernetes 持久卷的类型

我们可以根据其生命周期和配置方式对 Kubernetes 卷进行分类。

考虑到卷的生命周期,我们可以分为:

  • 临时卷,即与节点的生命周期紧密耦合(例如 ExpertDir 或 HostPath),如果节点倒闭,则删除它们的截阵数量。
  • 持久卷,即长期存储,并且与 Ppd 或节点生命周期无关。这些可以是云卷(如 gcePersistentDisk、awselasticBlockStore、AzureFile 或 AzureDisk),NFS(网络文件系统)或 Persistent Volume Claim(一系列抽象来连接到底层云提供存储卷)。

根据卷的配置方式,我们可以分为:

  1. 直接访问
  2. 静态配置
  3. 动态配置

直接访问持久卷

在这种情况下,Pod 将直接与 Volume 耦合,因此它将知道存储系统(例如,Pod 将与 Azure 存储帐户耦合)。该解决方案与云无关,它取决于具体实施而不是抽象。因此,如果可能的话尽量避免这样的解决方案。它唯一的优点是速度快,在 Pod 中创建 Secret,并指定应使用的 Secret 和确切的存储类型。

创建 Secret 脚本如下:

apiVersion: v1  kind: Secret  metadata:    name: static-persistence-secret  type: Opaque  data:    azurestorageaccountname: "base64StorageAccountName"    azurestorageaccountkey: "base64StorageAccountKey"

复制代码

在任何 Kubernetes 脚本中,在第 2 行我们指定了资源的类型。在这种情况下,我们称之为 Secret。在第 4 行,我们给它一个名字(我们称之为静态,因为它是由管理员手动创建的,而不是自动生成的)。从 Kubernetes 的角度来看,Opaque 类型意味着该 Secret 的内容(数据)是非结构化的(它可以包含任意键值对)。要了解有关 Kubernetes Secrets 的更多信息,可以参阅 Secrets Design Document 和 ConfigureKubernetes Secrets。

  • https://github.com/kubernetes/community/blob/master/contributors/design-proposals/auth/secrets.md
  • https://kubernetes.io/docs/concepts/configuration/secret/

在数据部分中,我们必须指定帐户名称(在 Azure 中,它是存储帐户的名称)和 Access 键(在 Azure 中,选择存储帐户下的“Settings ”,Access key)。别忘了两者应该使用 Base64 进行编码。

下一步是修改我们的 Deployment 脚本以使用卷(在这种情况下,卷是 Azure File Storage)。

apiVersion: apps/v1  kind: Deployment  metadata:    name: user-db-deployment  spec:    selector:      matchLabels:        app: user-db-app    replicas: 1    template:      metadata:        labels:          app: user-db-app      spec:        containers:          - name: mongo            image: mongo:3.6.4            command:              - mongod              - "--bind_ip_all"              - "--directoryperdb"            ports:              - containerPort: 27017            volumeMounts:              - name: data                mountPath: /data/db            resources:              limits:                memory: "256Mi"                cpu: "500m"        volumes:          - name: data            azureFile:              secretName: static-persistence-secret              shareName: user-mongo-db              readOnly: false

复制代码

我们可以发现,唯一的区别是,从第 32 行我们指定了使用的卷,给它一个名称并指定底层存储系统的确切详细信息。secretName 必须是先前创建的 Secret 的名称。

Kubernetes 存储类

要了解静态或动态配置,首先我们必须了解 Kubernetes 存储类。

通过 StorageClass,管理员可以提供关于可用存储的配置文件或“类”。不同的类可能映射到不同服务质量级别,或备份策略或由集群管理员确定的任意策略。

例如,你可以有一个在 HDD 上存储数据的配置文件,命名为慢速存储,或一个在 SSD 上存储数据的配置文件,命名为快速存储。这些存储的类型由供应者确定。对于 Azure,有两种提供者:AzureFile 和 AzureDisk(区别在于 AzureFile 可以与 Read Wriite Many 访问模式一起使用,而 AzureDisk 只支持 Read Write Once 访问,当您希望同时使用多个 Pod 时,这可能是不利因素)。您可以在此处了解有关不同类型的 Storage Classes:

https://kubernetes.io/docs/concepts/storage/storage-classes/

以下是 Storage Class 的脚本:

kind: StorageClass  apiVersion: storage.k8s.io/v1  metadata:    name: azurefilestorage  provisioner: kubernetes.io/azure-file  parameters:    storageAccount: storageaccountname  reclaimPolicy: Retain  allowVolumeExpansion: true

复制代码

Kubernetes 预定义提供者属性的值(请参阅 Kubernetes 存储类)。保留回收策略意味着在我们删除 PVC 和 PV 之后,未清除实际存储介质。我们可以将其设置为删除和使用此设置,一旦删除 PVC,它也会触发相应的 PV 以及实际存储介质(此处实际存储是 Azure 文件存储)的删除。

持久卷及 Persistent Volume Claim

Kubernetes 对每一个传统的存储操作活动(供应/配置/附加)都有一个匹配的原语。持久卷是供应,存储类正在配置,并且持久卷 Claim 是附加的。

来自初始文档:

Persistent Volume(PV)是集群中的存储,它已由管理员配置或使用存储类动态配置。

Persistent Volume Claim(PVC)是用户存储的请求。它类似于 Pod。Pod 消耗节点资源与 PVC 消耗 PV 资源是类似的。Pod 可以请求特定的资源级别(CPU 和内存)。Claim 可以请求特定的大小和访问模式(例如,它们可以安装一次读/写或多次只读)。

这意味着管理员将创建持久卷以指定 Pod 可以使用的存储大小、访问模式和存储类型。开发人员将创建 Persistent Volume Claim,要求提供一个卷、访问权限和存储类型。这样一来,在“开发侧”和“运维侧”之间就有了明显的区分。开发人员负责要求必要的卷(PVC),运维人员负责准备和配置要求的卷(PV)。

静态和动态配置之间的差异是,如果没有持久卷和管理员手动创建的 Secret,Kubernetes 将尝试自动创建这些资源。

动态配置

在这种情况下,没有手动创建的持久卷和 Secret,因此 Kubernetes 将尝试生成它们。Storage Class 是必要的,我们将使用在前文中创建的 Storage Class。

PersistentVolumeClaim 的脚本如下所示:

apiVersion: v1  kind:Persistent Volume Claim  metadata:    name: persistent-volume-claim-mongo  spec:    accessModes:      - ReadWriteMany    resources:      requests:        storage: 1Gi    storageClassName: azurefilestorage

复制代码

以及我们更新的 Deployment 脚本:

apiVersion: apps/v1  kind: Deployment  metadata:    name: user-db-deployment  spec:    selector:      matchLabels:        app: user-db-app    replicas: 1    template:      metadata:        labels:          app: user-db-app      spec:        containers:          - name: mongo            image: mongo:3.6.4            command:              - mongod              - "--bind_ip_all"              - "--directoryperdb"            ports:              - containerPort: 27017            volumeMounts:              - name: data                mountPath: /data/db            resources:              limits:                memory: "256Mi"                cpu: "500m"        volumes:          - name: data            Persistent Volume Claim:              claimName: persistent-volume-claim-mongo

复制代码

如你所见,在第 34 行中,我们通过名称引用了先前创建的 PVC。在这种情况下,我们没有手动为它创建持久卷或 Secret,因此它将自动创建。

这种方法的最重要的优势是您不必手动创建 PV 和 Secret,而且 Deployment 是与云无关的。存储的底层细节不存在于 Pod 的 spec 中。但是也有一些缺点:您无法配置存储帐户或文件共享,因为它们是自动生成的,并且您无法重复使用 PV 或 Secret ——它们将为每个新 Claim 重新生成。

静态配置

静态和动态配置之间的唯一区别是我们手动创建静态配置中的持久卷和 Secret。这样,我们就可以完全控制在集群中创建的资源。

持久卷脚本如下:

apiVersion: v1  kind: PersistentVolume  metadata:    name: static-persistent-volume-mongo    labels:      storage: azurefile  spec:    capacity:      storage: 1Gi    accessModes:      - ReadWriteMany    storageClassName: azurefilestorage    azureFile:      secretName: static-persistence-secret      shareName: user-mongo-db      readOnly: false

复制代码

重要的是,在第 12 行我们按名称引用 Storage Class。此外,在第 14 行我们引用了 Secret,用于访问底层存储系统。

本文更推荐这个解决方案,即使它需要更多的工作,但它是与云无关的(cloud-agnostic)。它还允许您应用有关角色(集群管理员与开发人员)的关注点分离,并让您控制命名和创建资源。

总结

在本文中,我们了解了如何使用 Volume 持久化数据和状态,并提出了三种不同的方法来设置系统,即为直接访问、动态配置和静态配置,并讨论了每个系统的优缺点。

在下一篇文章中,我们将介绍 CI / CD 流水线以自动部署微服务。点击下方卡片,关注我们并星标,确保您能收到我们下一次的推送噢!

本文转载自:RancherLabs(ID:RancherLabs)

原文链接:从入门到上手:什么是K8S持久卷?

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/uvc68XDb8ILyojs8GqYX
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券