如何在 Kubernetes pods 之间共享存储?

How to share storage between Kubernetes pods?

我正在评估 Kubernetes 作为我们新应用程序的平台。现在,它看起来非常令人兴奋!但是,我 运行 遇到了一个问题:我在 GCE 上托管我的集群,我需要一些机制在两个 pods - 持续集成服务器和我的应用程序服务器之间共享存储。使用 kubernetes 执行此操作的最佳方法是什么? None 卷类型似乎符合我的需要,因为如果一个 pod 需要写入磁盘,则无法共享 GCE 磁盘。 NFS 会是完美的,但似乎需要为 kubernetes 集群提供特殊的构建选项?

编辑:共享存储似乎是我现在使用 Kubernetes 多次遇到的问题。在多个用例中,我只想拥有一个卷并将其连接到多个 pods(具有写入权限)。我只能假设这是一个常见的用例,不是吗?

EDIT2:例如,this page describes how to set up an Elasticsearch cluster, but wiring it up with persistent storage is impossible (as described here),这使它变得毫无意义:(

更新:最好的选择可能是 Cloud Filestore,一个托管的 NFS 系统。这使您可以完全随机 read/write 访问文件,这与仅支持 upload/download 的 GCS 不同。请参阅文档 here.

原文: 你试过Google Cloud Storage? You might even be able to use the FUSE adapter像网盘一样映射吗

如果您要写入磁盘的是日志,我建议您查看 logspout https://github.com/gliderlabs/logspout。这将收集每个 pod 的日志记录,然后您可以使用 google 云平台的使用 fluentd 的相当新的日志记录服务。这样每个 pod 的所有日志都收集到一个地方。

如果是通常会写入数据库或类似性质的数据,我建议在 kubernetes 集群之外有一个单独的服务器 运行 数据库。

编辑

为了在 pods 之间共享文件,我建议将 google 云存储驱动器安装到 kubernetes 集群中的每个节点,然后将其设置为一个卷到安装到该安装的每个 pod 中目录 在节点 上,而不是直接到驱动器。将它挂载到每个节点很好,因为 pods 不会 运行 在指定节点上,所以在这种情况下最好将它集中。

NFS 是一个内置的卷插件,支持多个 pod writer。没有特殊的构建选项可以让 NFS 在 Kube 中工作。

我在 Red Hat 的 Kubernetes 工作,主要专注于存储。

@Marco - 关于与 Maven 相关的问题,我的建议是停止将其视为集中式存储问题,或许将其视为服务问题。

我过去在 HTTP 下 运行 Maven 存储库(只读)。我会简单地创建一个 Maven 存储库并将其公开到 Apache/Nginx 在它自己的 pod(docker 容器)中,其中包含您只需要该 pod 的专用存储,然后使用服务发现 link它到您的应用程序和构建系统。

首先,您真的需要多个读者/作者吗?

根据我对 Kubernetes / 微服务架构 (MSA) 的经验,问题通常与您的设计模式有关。 MSA 的基本设计模式之一是服务的适当封装,这包括每个服务拥有的数据。

与 OOP 的方式大致相同,您的服务应处理与其关注领域相关的数据,并应允许其他服务通过接口访问此数据。该接口可以是 API,直接或通过代理服务处理消息,或使用协议缓冲区和 gRPC。通常,对数据的多服务访问是一种类似于 OOP 和大多数编程语言中的全局变量的反模式。

举个例子,如果你想写日志,你应该有一个日志服务,每个服务都可以调用它需要记录的相关数据。直接写入共享磁盘意味着如果您更改日志目录结构,或者决定添加额外的功能(例如针对某些类型的错误发送电子邮件),则需要更新每个容器。

在大多数情况下,您应该在求助于使用文件系统之前使用某种形式的最小界面,以避免在使用文件时遇到 Hyrum's law 的意外副作用系统。如果您的服务之间没有适当的接口/契约,您将大大降低构建可维护和弹性服务的能力。

好的,你的情况最好使用文件系统解决。有很多选择...

显然,有时可以处理多个并发编写器的文件系统提供了比 'traditional' MSA 通信形式更好的解决方案。 Kubernetes 支持大量卷类型,可以找到 here。虽然这个列表很长,但其中许多卷类型不支持多个写入器(在 Kubernetes 中也称为 ReadWriteMany)。

可以在 this table 中找到支持 ReadWriteMany 的那些卷类型,在撰写本文时是 AzureFile、CephFS、Glusterfs、Quobyte、NFS 和 PortworxVolume。

还有一些运算符,例如流行的 rook.io,它们功能强大并提供了一些很棒的功能,但是当您只想要一个简单的解决方案并继续前进时,此类系统的学习曲线可能很难攀登.

最简单的方法。

根据我的经验,最好的初始选择是 NFS。这是学习 ReadWriteMany Kubernetes 存储基本思想的好方法,适用于大多数用例,并且最容易实现。在您掌握了多服务持久性的应用知识之后,您就可以做出更明智的决定,以使用功能更丰富的产品,而这通常需要更多的工作才能实现。

设置 NFS 的细节因集群的方式和位置而异 运行 以及 NFS 服务的细节,我之前写过两篇关于如何为 [=19 设置 NFS 的文章=].这两篇文章很好地对比了如何根据您的特定情况实现不同的实现。

举个最简单的例子,您首先需要一个 NFS 服务。如果您希望进行快速测试或者您的 SLO 要求较低,那么遵循 this DO article 是在 Ubuntu 上设置 NFS 的一个很好的快速入门。如果您现有的 NAS 提供 NFS 并且可以从您的集群访问,这也将有效。

拥有 NFS 服务后,您可以创建类似于以下内容的持久卷:

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-name
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  nfs:
    server: 255.0.255.0 # IP address of your NFS service
    path: "/desired/path/in/nfs"

这里需要注意的是,您的节点需要安装二进制文件才能使用 NFS,我在 on-prem cluster 文章中对此进行了更多讨论。这也是您 需要 在 EKS 上 运行 使用 EFS 的原因,因为您的节点无法连接到 NFS。

一旦设置了持久卷,就可以像使用任何其他卷一样简单地使用它。

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-name
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: p-name
          volumeMounts:
            - mountPath: /data
              name: v-name
      volumes:
        - name: v-name
          persistentVolumeClaim:
            claimName: pvc-name

你看过 kubernetes Volumes 吗?您可能正在考虑创建一个 gcePersistentDisk

A gcePersistentDisk volume mounts a Google Compute Engine (GCE) Persistent Disk into your pod. Unlike emptyDir, which is erased when a Pod is removed, the contents of a PD are preserved and the volume is merely unmounted. This means that a PD can be pre-populated with data, and that data can be “handed off” between pods. Important: You must create a PD using gcloud or the GCE API or UI before you can use it There are some restrictions when using a gcePersistentDisk: the nodes on which pods are running must be GCE VMs those VMs need to be in the same GCE project and zone as the PD A feature of PD is that they can be mounted as read-only by multiple consumers simultaneously. This means that you can pre-populate a PD with your dataset and then serve it in parallel from as many pods as you need. Unfortunately, PDs can only be mounted by a single consumer in read-write mode - no simultaneous writers allowed. Using a PD on a pod controlled by a ReplicationController will fail unless the PD is read-only or the replica count is 0 or 1.

要支持来自各种 pods 的多次写入,您可能需要创建一个强大的 pod,它公开一个 thrift 或套接字类型服务,该服务公开 readFromDisk 和 WriteToDisk 方法。

Google 最近发布了云文件存储,这里有教程:https://cloud.google.com/filestore/docs/accessing-fileshares

在某些情况下,可能是云 storage/buckets 的一个很好的替代品。

首先。 Kubernetes 没有在主机之间共享存储的集成功能。下面有几个选项。但如果您已经设置了一些卷,首先如何共享存储。

要在多个 pods 之间共享一个卷,您需要创建访问模式为 ReadWriteMany

的 PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
    name: my-pvc
spec:
    accessModes:
      - ReadWriteMany
    storageClassName: myvolume
    resources:
        requests:
            storage: 1Gi

之后可以挂载到多个pods:

apiVersion: v1
kind: Pod
metadata:
  name: myapp1
spec:
  containers:
...
      volumeMounts:
        - mountPath: /data
          name: data
          subPath: app1
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: 'my-pvc'
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp2
spec:
  containers:
...
      volumeMounts:
        - mountPath: /data
          name: data
          subPath: app2
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: 'my-pvc'

当然,持久卷必须可以通过网络访问。否则,您需要确保所有 pods 都安排到具有该卷的节点。

有几种卷类型适用于此并且不依赖于任何云提供商:

  • NFS
  • RBD(Ceph 块设备)
  • CephFS
  • Glusterfs
  • Portworx 卷

当然,要使用卷,您需要先拥有它。也就是说,如果你想使用 NFS,你需要在 K8s 集群的所有节点上设置 NFS。如果要消费Ceph,需要设置Ceph集群等。

开箱即用地支持 Kubernetes 的唯一卷类型是 Portworks。 how to set it up in GKE.

有说明

要在 K8s 中设置 Ceph 集群,有一个项目正在开发中,名为 Rook

但是,如果您只想让一个节点中的文件夹在另一个节点中可用,那么这就太过分了。在这种情况下,只需设置 NFS 服务器。它不会比配置其他卷类型更难,并且会消耗更少的 cpu/memory/disk 资源。

头盔: 如果你使用 helm 部署

如果您有一个仅支持 RWO 的 PVC,并且您希望许多 pods 能够从同一个 PVC 读取并共享该存储,那么您可以安装 helm chart stable/nfs-server-provisioner 如果您的云提供商不支持 RWX 访问模式。

此图表提供 "out-of-tree" 具有 RWX 访问模式的存储 PVC,这些 PVC 从仅支持 RWO 的云提供商(如 Digital Ocean)访问底层 PVC。

在您的 pods 中,您安装了由 nfs 服务器提供的 PVC,并且您可以在它们从同一 PVC 读取和写入时扩展它们。

重要!

您必须修改值文件以添加适合您的部署的配置,例如您的 storage class

有关图表的更多信息: https://github.com/helm/charts/tree/master/stable/nfs-server-provisioner

我只是用一个用 3 个容器化 micro-services 制作的应用程序实现了这一点,我有一个负责存储和共享文件的应用程序,所以该应用程序正在存储文件并在文件夹中检索它们,这个文件夹通过应用程序 属性 传递。有一个安全的 rest 入口点,允许提交和检索文件(基本上在每次提交时它都会创建一个返回的唯一 id,可用于扫描文件夹中的文件)。 将此应用程序从 docker-compose 传递到 kubernetes 我遇到了同样的问题:我需要一个全局磁盘以便我可以拥有容器的多个副本,因此当另一个 micro-services 将向其中一个副本发送请求时,他们将始终能够发送任何提交的文件,而不仅仅是提交时管理的副本文件。 我通过创建一个 持久卷 来解决,关联到一个 持久卷声明 ,这个卷声明关联到一个 部署 (注意:不是Statefulset,它会为每个pod创建一个磁盘),此时你必须将挂载的卷路径与容器存储文件夹路径相关联。

因此,重要的只是持久卷声明名称和 PV 具有更多 PVC 可用内存这一事实,显然与具有正确标签的部署相匹配。 然后在部署中你可以传入规范:

volumes:
      - name: store-folder
        persistentVolumeClaim:
          claimName: [pvc_name]

进入容器设置:

volumeMounts:
        - name: store-folder
          mountPath: "/stored-files"

在环境中。区块:

containers:
....
      - env:
        - name: any-property-used-inside-the-application-for-saving-files
          value: /stored-files

因此,从卷中,您将 pvc 绑定到部署,然后从卷装载中,您将磁盘绑定到一个目录,然后通过环境变量,您可以传递永久磁盘目录。 声明 PVC 和 PV 是基本的,没有 PV 它将像任何 pods 都有自己的文件夹一样工作。